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