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