Home | History | Annotate | Line # | Download | only in h_dtfs
      1 /*	$NetBSD: dtfs_vnops.c,v 1.10 2013/10/19 17:45:00 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2006  Antti Kantee.  All Rights Reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     16  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     18  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     25  * SUCH DAMAGE.
     26  */
     27 
     28 #include <sys/types.h>
     29 #include <sys/poll.h>
     30 
     31 #include <assert.h>
     32 #include <errno.h>
     33 #include <puffs.h>
     34 #include <stdio.h>
     35 #include <stdlib.h>
     36 #include <string.h>
     37 #include <unistd.h>
     38 #include <util.h>
     39 
     40 #include "dtfs.h"
     41 
     42 int
     43 dtfs_node_lookup(struct puffs_usermount *pu, void *opc,
     44 	struct puffs_newinfo *pni, const struct puffs_cn *pcn)
     45 {
     46 	struct puffs_node *pn_dir = opc;
     47 	struct dtfs_file *df = DTFS_CTOF(opc);
     48 	struct dtfs_dirent *dfd;
     49 	extern int straightflush;
     50 	int rv;
     51 
     52 	/* parent dir? */
     53 	if (PCNISDOTDOT(pcn)) {
     54 		if (df->df_dotdot == NULL)
     55 			return ENOENT;
     56 
     57 		assert(df->df_dotdot->pn_va.va_type == VDIR);
     58 		puffs_newinfo_setcookie(pni, df->df_dotdot);
     59 		puffs_newinfo_setvtype(pni, df->df_dotdot->pn_va.va_type);
     60 
     61 		return 0;
     62 	}
     63 
     64 	dfd = dtfs_dirgetbyname(df, pcn->pcn_name, pcn->pcn_namelen);
     65 	if (dfd) {
     66 		if ((pcn->pcn_flags & NAMEI_ISLASTCN) &&
     67 		    (pcn->pcn_nameiop == NAMEI_DELETE)) {
     68 			rv = puffs_access(VDIR, pn_dir->pn_va.va_mode,
     69 			    pn_dir->pn_va.va_uid, pn_dir->pn_va.va_gid,
     70 			    PUFFS_VWRITE, pcn->pcn_cred);
     71 			if (rv)
     72 				return rv;
     73 		}
     74 		puffs_newinfo_setcookie(pni, dfd->dfd_node);
     75 		puffs_newinfo_setvtype(pni, dfd->dfd_node->pn_va.va_type);
     76 		puffs_newinfo_setsize(pni, dfd->dfd_node->pn_va.va_size);
     77 		puffs_newinfo_setrdev(pni, dfd->dfd_node->pn_va.va_rdev);
     78 
     79 		if (straightflush)
     80 			puffs_flush_pagecache_node(pu, dfd->dfd_node);
     81 
     82 		return 0;
     83 	}
     84 
     85 	if ((pcn->pcn_flags & NAMEI_ISLASTCN)
     86 	    && (pcn->pcn_nameiop == NAMEI_CREATE ||
     87 	        pcn->pcn_nameiop == NAMEI_RENAME)) {
     88 		rv = puffs_access(VDIR, pn_dir->pn_va.va_mode,
     89 		    pn_dir->pn_va.va_uid, pn_dir->pn_va.va_gid,
     90 		    PUFFS_VWRITE, pcn->pcn_cred);
     91 		if (rv)
     92 			return rv;
     93 	}
     94 
     95 	return ENOENT;
     96 }
     97 
     98 int
     99 dtfs_node_access(struct puffs_usermount *pu, void *opc, int acc_mode,
    100 	const struct puffs_cred *pcr)
    101 {
    102 	struct puffs_node *pn = opc;
    103 
    104 	return puffs_access(pn->pn_va.va_type, pn->pn_va.va_mode,
    105 	    pn->pn_va.va_uid, pn->pn_va.va_gid, acc_mode, pcr);
    106 }
    107 
    108 int
    109 dtfs_node_setattr(struct puffs_usermount *pu, void *opc,
    110 	const struct vattr *va, const struct puffs_cred *pcr)
    111 {
    112 	struct puffs_node *pn = opc;
    113 	int rv;
    114 
    115 	/* check permissions */
    116 	if (va->va_flags != PUFFS_VNOVAL)
    117 		return EOPNOTSUPP;
    118 
    119 	if (va->va_uid != PUFFS_VNOVAL || va->va_gid != PUFFS_VNOVAL) {
    120 		rv = puffs_access_chown(pn->pn_va.va_uid, pn->pn_va.va_gid,
    121 		    va->va_uid, va->va_gid, pcr);
    122 		if (rv)
    123 			return rv;
    124 	}
    125 
    126 	if (va->va_mode != PUFFS_VNOVAL) {
    127 		rv = puffs_access_chmod(pn->pn_va.va_uid, pn->pn_va.va_gid,
    128 		    pn->pn_va.va_type, va->va_mode, pcr);
    129 		if (rv)
    130 			return rv;
    131 	}
    132 
    133 	if ((va->va_atime.tv_sec != PUFFS_VNOVAL
    134 	      && va->va_atime.tv_nsec != PUFFS_VNOVAL)
    135 	    || (va->va_mtime.tv_sec != PUFFS_VNOVAL
    136 	      && va->va_mtime.tv_nsec != PUFFS_VNOVAL)) {
    137 		rv = puffs_access_times(pn->pn_va.va_uid, pn->pn_va.va_gid,
    138 		    pn->pn_va.va_mode, va->va_vaflags & VA_UTIMES_NULL, pcr);
    139 		if (rv)
    140 			return rv;
    141 	}
    142 
    143 	if (va->va_size != PUFFS_VNOVAL) {
    144 		switch (pn->pn_va.va_type) {
    145 		case VREG:
    146 			dtfs_setsize(pn, va->va_size);
    147 			pn->pn_va.va_bytes = va->va_size;
    148 			break;
    149 		case VBLK:
    150 		case VCHR:
    151 		case VFIFO:
    152 			break;
    153 		case VDIR:
    154 			return EISDIR;
    155 		default:
    156 			return EOPNOTSUPP;
    157 		}
    158 	}
    159 
    160 	puffs_setvattr(&pn->pn_va, va);
    161 
    162 	return 0;
    163 }
    164 
    165 /* create a new node in the parent directory specified by opc */
    166 int
    167 dtfs_node_create(struct puffs_usermount *pu, void *opc,
    168 	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
    169 	const struct vattr *va)
    170 {
    171 	struct puffs_node *pn_parent = opc;
    172 	struct puffs_node *pn_new;
    173 
    174 	if (!(va->va_type == VREG || va->va_type == VSOCK))
    175 		return ENODEV;
    176 
    177 	pn_new = dtfs_genfile(pn_parent, pcn, va->va_type);
    178 	puffs_setvattr(&pn_new->pn_va, va);
    179 
    180 	puffs_newinfo_setcookie(pni, pn_new);
    181 
    182 	return 0;
    183 }
    184 
    185 int
    186 dtfs_node_remove(struct puffs_usermount *pu, void *opc, void *targ,
    187 	const struct puffs_cn *pcn)
    188 {
    189 	struct puffs_node *pn_parent = opc;
    190 	struct puffs_node *pn = targ;
    191 
    192 	if (pn->pn_va.va_type == VDIR)
    193 		return EPERM;
    194 
    195 	dtfs_nukenode(targ, pn_parent, pcn->pcn_name, pcn->pcn_namelen);
    196 
    197 	if (pn->pn_va.va_nlink == 0)
    198 		puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
    199 
    200 	return 0;
    201 }
    202 
    203 int
    204 dtfs_node_mkdir(struct puffs_usermount *pu, void *opc,
    205 	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
    206 	const struct vattr *va)
    207 {
    208 	struct puffs_node *pn_parent = opc;
    209 	struct puffs_node *pn_new;
    210 
    211 	pn_new = dtfs_genfile(pn_parent, pcn, VDIR);
    212 	puffs_setvattr(&pn_new->pn_va, va);
    213 
    214 	puffs_newinfo_setcookie(pni, pn_new);
    215 
    216 	return 0;
    217 }
    218 
    219 int
    220 dtfs_node_rmdir(struct puffs_usermount *pu, void *opc, void *targ,
    221 	const struct puffs_cn *pcn)
    222 {
    223 	struct puffs_node *pn_parent = opc;
    224 	struct dtfs_file *df = DTFS_CTOF(targ);
    225 
    226 	if (!LIST_EMPTY(&df->df_dirents))
    227 		return ENOTEMPTY;
    228 
    229 	dtfs_nukenode(targ, pn_parent, pcn->pcn_name, pcn->pcn_namelen);
    230 	puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
    231 
    232 	return 0;
    233 }
    234 
    235 int
    236 dtfs_node_readdir(struct puffs_usermount *pu, void *opc,
    237 	struct dirent *dent, off_t *readoff, size_t *reslen,
    238 	const struct puffs_cred *pcr,
    239 	int *eofflag, off_t *cookies, size_t *ncookies)
    240 {
    241 	struct puffs_node *pn = opc;
    242 	struct puffs_node *pn_nth;
    243 	struct dtfs_dirent *dfd_nth;
    244 
    245 	if (pn->pn_va.va_type != VDIR)
    246 		return ENOTDIR;
    247 
    248 	dtfs_updatetimes(pn, 1, 0, 0);
    249 
    250 	*ncookies = 0;
    251  again:
    252 	if (*readoff == DENT_DOT || *readoff == DENT_DOTDOT) {
    253 		puffs_gendotdent(&dent, pn->pn_va.va_fileid, *readoff, reslen);
    254 		(*readoff)++;
    255 		PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff);
    256 		goto again;
    257 	}
    258 
    259 	for (;;) {
    260 		dfd_nth = dtfs_dirgetnth(pn->pn_data, DENT_ADJ(*readoff));
    261 		if (!dfd_nth) {
    262 			*eofflag = 1;
    263 			break;
    264 		}
    265 		pn_nth = dfd_nth->dfd_node;
    266 
    267 		if (!puffs_nextdent(&dent, dfd_nth->dfd_name,
    268 		    pn_nth->pn_va.va_fileid,
    269 		    puffs_vtype2dt(pn_nth->pn_va.va_type),
    270 		    reslen))
    271 			break;
    272 
    273 		(*readoff)++;
    274 		PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff);
    275 	}
    276 
    277 	return 0;
    278 }
    279 
    280 int
    281 dtfs_node_poll(struct puffs_usermount *pu, void *opc, int *events)
    282 {
    283 	struct dtfs_mount *dtm = puffs_getspecific(pu);
    284 	struct dtfs_poll dp;
    285 	struct itimerval it;
    286 
    287 	memset(&it, 0, sizeof(struct itimerval));
    288 	it.it_value.tv_sec = 4;
    289 	if (setitimer(ITIMER_REAL, &it, NULL) == -1)
    290 		return errno;
    291 
    292 	dp.dp_pcc = puffs_cc_getcc(pu);
    293 	LIST_INSERT_HEAD(&dtm->dtm_pollent, &dp, dp_entries);
    294 	puffs_cc_yield(dp.dp_pcc);
    295 
    296 	*events = *events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM);
    297 	return 0;
    298 }
    299 
    300 int
    301 dtfs_node_mmap(struct puffs_usermount *pu, void *opc, vm_prot_t prot,
    302 	const struct puffs_cred *pcr)
    303 {
    304 	struct dtfs_mount *dtm = puffs_getspecific(pu);
    305 
    306 	if ((dtm->dtm_allowprot & prot) != prot)
    307 		return EACCES;
    308 
    309 	return 0;
    310 }
    311 
    312 int
    313 dtfs_node_rename(struct puffs_usermount *pu, void *opc, void *src,
    314 	const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
    315 	const struct puffs_cn *pcn_targ)
    316 {
    317 	struct dtfs_dirent *dfd_src;
    318 	struct dtfs_file *df_targ;
    319 	struct puffs_node *pn_sdir = opc;
    320 	struct puffs_node *pn_sfile = src;
    321 	struct puffs_node *pn_tdir = targ_dir;
    322 	struct puffs_node *pn_tfile = targ;
    323 
    324 	/* check that we don't do the old amigados trick */
    325 	if (pn_sfile->pn_va.va_type == VDIR) {
    326 		if (dtfs_isunder(pn_tdir, pn_sfile))
    327 			return EINVAL;
    328 
    329 		if ((pcn_src->pcn_namelen == 1 && pcn_src->pcn_name[0]=='.') ||
    330 		    opc == src ||
    331 		    PCNISDOTDOT(pcn_src) ||
    332 		    PCNISDOTDOT(pcn_targ)) {
    333 			return EINVAL;
    334 		}
    335 	}
    336 
    337 	dfd_src = dtfs_dirgetbyname(DTFS_PTOF(pn_sdir),
    338 	    pcn_src->pcn_name, pcn_src->pcn_namelen);
    339 
    340 	/* does it still exist, or did someone race us here? */
    341 	if (dfd_src == NULL) {
    342 		return ENOENT;
    343 	}
    344 
    345 	/* if there's a target file, nuke it for atomic replacement */
    346 	if (pn_tfile) {
    347 		if (pn_tfile->pn_va.va_type == VDIR) {
    348 			df_targ = DTFS_CTOF(pn_tfile);
    349 			if (!LIST_EMPTY(&df_targ->df_dirents))
    350 				return ENOTEMPTY;
    351 		}
    352 		dtfs_nukenode(pn_tfile, pn_tdir,
    353 		    pcn_targ->pcn_name, pcn_targ->pcn_namelen);
    354 	}
    355 
    356 	/* out with the old */
    357 	dtfs_removedent(pn_sdir, dfd_src);
    358 	/* and in with the new */
    359 	dtfs_adddent(pn_tdir, dfd_src);
    360 
    361 	/* update name */
    362 	free(dfd_src->dfd_name);
    363 	dfd_src->dfd_name = estrndup(pcn_targ->pcn_name,pcn_targ->pcn_namelen);
    364 	dfd_src->dfd_namelen = strlen(dfd_src->dfd_name);
    365 
    366 	dtfs_updatetimes(src, 0, 1, 0);
    367 
    368 	return 0;
    369 }
    370 
    371 int
    372 dtfs_node_link(struct puffs_usermount *pu, void *opc, void *targ,
    373 	const struct puffs_cn *pcn)
    374 {
    375 	struct puffs_node *pn_dir = opc;
    376 	struct dtfs_dirent *dfd;
    377 
    378 	dfd = emalloc(sizeof(struct dtfs_dirent));
    379 	dfd->dfd_node = targ;
    380 	dfd->dfd_name = estrndup(pcn->pcn_name, pcn->pcn_namelen);
    381 	dfd->dfd_namelen = strlen(dfd->dfd_name);
    382 	dtfs_adddent(pn_dir, dfd);
    383 
    384 	dtfs_updatetimes(targ, 0, 1, 0);
    385 
    386 	return 0;
    387 }
    388 
    389 int
    390 dtfs_node_symlink(struct puffs_usermount *pu, void *opc,
    391 	struct puffs_newinfo *pni, const struct puffs_cn *pcn_src,
    392 	const struct vattr *va, const char *link_target)
    393 {
    394 	struct puffs_node *pn_parent = opc;
    395 	struct puffs_node *pn_new;
    396 	struct dtfs_file *df_new;
    397 
    398 	if (va->va_type != VLNK)
    399 		return ENODEV;
    400 
    401 	pn_new = dtfs_genfile(pn_parent, pcn_src, VLNK);
    402 	puffs_setvattr(&pn_new->pn_va, va);
    403 	df_new = DTFS_PTOF(pn_new);
    404 	df_new->df_linktarget = estrdup(link_target);
    405 	pn_new->pn_va.va_size = strlen(df_new->df_linktarget);
    406 
    407 	puffs_newinfo_setcookie(pni, pn_new);
    408 
    409 	return 0;
    410 }
    411 
    412 int
    413 dtfs_node_readlink(struct puffs_usermount *pu, void *opc,
    414 	const struct puffs_cred *cred, char *linkname, size_t *linklen)
    415 {
    416 	struct dtfs_file *df = DTFS_CTOF(opc);
    417 	struct puffs_node *pn = opc;
    418 
    419 	assert(pn->pn_va.va_type == VLNK);
    420 	strlcpy(linkname, df->df_linktarget, *linklen);
    421 	*linklen = strlen(linkname);
    422 
    423 	return 0;
    424 }
    425 
    426 int
    427 dtfs_node_mknod(struct puffs_usermount *pu, void *opc,
    428 	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
    429 	const struct vattr *va)
    430 {
    431 	struct puffs_node *pn_parent = opc;
    432 	struct puffs_node *pn_new;
    433 
    434 	if (!(va->va_type == VBLK || va->va_type == VCHR
    435 	    || va->va_type == VFIFO))
    436 		return EINVAL;
    437 
    438 	pn_new = dtfs_genfile(pn_parent, pcn, va->va_type);
    439 	puffs_setvattr(&pn_new->pn_va, va);
    440 
    441 	puffs_newinfo_setcookie(pni, pn_new);
    442 
    443 	return 0;
    444 }
    445 
    446 #define BLOCKOFF(a,b) ((a) & ((b)-1))
    447 #define BLOCKLEFT(a,b) ((b) - BLOCKOFF(a,b))
    448 
    449 /*
    450  * Read operation, used both for VOP_READ and VOP_GETPAGES
    451  */
    452 int
    453 dtfs_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf,
    454 	off_t offset, size_t *resid, const struct puffs_cred *pcr, int ioflag)
    455 {
    456 	struct puffs_node *pn = opc;
    457 	struct dtfs_file *df = DTFS_CTOF(opc);
    458 	quad_t xfer, origxfer;
    459 	uint8_t *src, *dest;
    460 	size_t copylen;
    461 
    462 	if (pn->pn_va.va_type != VREG)
    463 		return EISDIR;
    464 
    465 	xfer = MIN(*resid, df->df_datalen - offset);
    466 	if (xfer < 0)
    467 		return EINVAL;
    468 
    469 	dest = buf;
    470 	origxfer = xfer;
    471 	while (xfer > 0) {
    472 		copylen = MIN(xfer, BLOCKLEFT(offset, DTFS_BLOCKSIZE));
    473 		src = df->df_blocks[BLOCKNUM(offset, DTFS_BLOCKSHIFT)]
    474 		    + BLOCKOFF(offset, DTFS_BLOCKSIZE);
    475 		memcpy(dest, src, copylen);
    476 		offset += copylen;
    477 		dest += copylen;
    478 		xfer -= copylen;
    479 	}
    480 	*resid -= origxfer;
    481 
    482 	dtfs_updatetimes(pn, 1, 0, 0);
    483 
    484 	return 0;
    485 }
    486 
    487 /*
    488  * write operation on the wing
    489  */
    490 int
    491 dtfs_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf,
    492 	off_t offset, size_t *resid, const struct puffs_cred *pcr, int ioflag)
    493 {
    494 	struct puffs_node *pn = opc;
    495 	struct dtfs_file *df = DTFS_CTOF(opc);
    496 	uint8_t *src, *dest;
    497 	size_t copylen;
    498 
    499 	if (pn->pn_va.va_type != VREG)
    500 		return EISDIR;
    501 
    502 	if (ioflag & PUFFS_IO_APPEND)
    503 		offset = pn->pn_va.va_size;
    504 
    505 	if (*resid + offset > pn->pn_va.va_size)
    506 		dtfs_setsize(pn, *resid + offset);
    507 
    508 	src = buf;
    509 	while (*resid > 0) {
    510 		int i;
    511 		copylen = MIN(*resid, BLOCKLEFT(offset, DTFS_BLOCKSIZE));
    512 		i = BLOCKNUM(offset, DTFS_BLOCKSHIFT);
    513 		dest = df->df_blocks[i]
    514 		    + BLOCKOFF(offset, DTFS_BLOCKSIZE);
    515 		memcpy(dest, src, copylen);
    516 		offset += copylen;
    517 		dest += copylen;
    518 		*resid -= copylen;
    519 	}
    520 
    521 	dtfs_updatetimes(pn, 0, 1, 1);
    522 
    523 	return 0;
    524 }
    525 
    526 int
    527 dtfs_node_pathconf(struct puffs_usermount *pu, puffs_cookie_t opc,
    528 	int name, register_t *retval)
    529 {
    530 
    531 	switch (name) {
    532 	case _PC_LINK_MAX:
    533 		*retval = LINK_MAX;
    534 		return 0;
    535 	case _PC_NAME_MAX:
    536 		*retval = NAME_MAX;
    537 		return 0;
    538 	case _PC_PATH_MAX:
    539 		*retval = PATH_MAX;
    540 		return 0;
    541 	case _PC_PIPE_BUF:
    542 		*retval = PIPE_BUF;
    543 		return 0;
    544 	case _PC_CHOWN_RESTRICTED:
    545 		*retval = 1;
    546 		return 0;
    547 	case _PC_NO_TRUNC:
    548 		*retval = 1;
    549 		return 0;
    550 	case _PC_SYNC_IO:
    551 		*retval = 1;
    552 		return 0;
    553 	case _PC_FILESIZEBITS:
    554 		*retval = 43; /* this one goes to 11 */
    555 		return 0;
    556 	case _PC_SYMLINK_MAX:
    557 		*retval = MAXPATHLEN;
    558 		return 0;
    559 	case _PC_2_SYMLINKS:
    560 		*retval = 1;
    561 		return 0;
    562 	default:
    563 		return EINVAL;
    564 	}
    565 }
    566 
    567 int
    568 dtfs_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc)
    569 {
    570 	struct puffs_node *pn = opc;
    571 
    572 	if (pn->pn_va.va_nlink == 0)
    573 		puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N1);
    574 	return 0;
    575 }
    576 
    577 int
    578 dtfs_node_reclaim(struct puffs_usermount *pu, void *opc)
    579 {
    580 	struct puffs_node *pn = opc;
    581 
    582 	if (pn->pn_va.va_nlink == 0)
    583 		dtfs_freenode(pn);
    584 
    585 	return 0;
    586 }
    587