Home | History | Annotate | Line # | Download | only in chfs
chfs_subr.c revision 1.2
      1 /*	$NetBSD: chfs_subr.c,v 1.2 2011/11/24 21:09:37 agc Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2010 Department of Software Engineering,
      5  *		      University of Szeged, Hungary
      6  * Copyright (C) 2010 Tamas Toth <ttoth (at) inf.u-szeged.hu>
      7  * Copyright (C) 2010 Adam Hoka <ahoka (at) NetBSD.org>
      8  * All rights reserved.
      9  *
     10  * This code is derived from software contributed to The NetBSD Foundation
     11  * by the Department of Software Engineering, University of Szeged, Hungary
     12  *
     13  * Redistribution and use in source and binary forms, with or without
     14  * modification, are permitted provided that the following conditions
     15  * are met:
     16  * 1. Redistributions of source code must retain the above copyright
     17  *    notice, this list of conditions and the following disclaimer.
     18  * 2. Redistributions in binary form must reproduce the above copyright
     19  *    notice, this list of conditions and the following disclaimer in the
     20  *    documentation and/or other materials provided with the distribution.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  * SUCH DAMAGE.
     33  */
     34 
     35 /*
     36  * Efficient memory file system supporting functions.
     37  */
     38 
     39 #include <sys/cdefs.h>
     40 
     41 #include <sys/param.h>
     42 #include <sys/dirent.h>
     43 #include <sys/event.h>
     44 #include <sys/kmem.h>
     45 #include <sys/mount.h>
     46 #include <sys/namei.h>
     47 #include <sys/time.h>
     48 #include <sys/stat.h>
     49 #include <sys/systm.h>
     50 #include <sys/swap.h>
     51 #include <sys/vnode.h>
     52 #include <sys/kauth.h>
     53 #include <sys/proc.h>
     54 #include <sys/atomic.h>
     55 
     56 #include <uvm/uvm.h>
     57 
     58 #include <miscfs/specfs/specdev.h>
     59 #include "chfs.h"
     60 //#include <fs/chfs/chfs_vnops.h>
     61 //#include </root/xipffs/netbsd.chfs/chfs.h>
     62 
     63 /* --------------------------------------------------------------------- */
     64 
     65 /*
     66  * Returns information about the number of available memory pages,
     67  * including physical and virtual ones.
     68  *
     69  * If 'total' is true, the value returned is the total amount of memory
     70  * pages configured for the system (either in use or free).
     71  * If it is FALSE, the value returned is the amount of free memory pages.
     72  *
     73  * Remember to remove DUMMYFS_PAGES_RESERVED from the returned value to avoid
     74  * excessive memory usage.
     75  *
     76  */
     77 size_t
     78 chfs_mem_info(bool total)
     79 {
     80 	size_t size;
     81 
     82 	size = 0;
     83 	size += uvmexp.swpgavail;
     84 	if (!total) {
     85 		size -= uvmexp.swpgonly;
     86 	}
     87 	size += uvmexp.free;
     88 	size += uvmexp.filepages;
     89 	if (size > uvmexp.wired) {
     90 		size -= uvmexp.wired;
     91 	} else {
     92 		size = 0;
     93 	}
     94 
     95 	return size;
     96 }
     97 
     98 
     99 /* --------------------------------------------------------------------- */
    100 
    101 /*
    102  * Looks for a directory entry in the directory represented by node.
    103  * 'cnp' describes the name of the entry to look for.  Note that the .
    104  * and .. components are not allowed as they do not physically exist
    105  * within directories.
    106  *
    107  * Returns a pointer to the entry when found, otherwise NULL.
    108  */
    109 struct chfs_dirent *
    110 chfs_dir_lookup(struct chfs_inode *ip, struct componentname *cnp)
    111 {
    112 	bool found;
    113 	struct chfs_dirent *fd;
    114 	dbg("dir_lookup()\n");
    115 
    116 	KASSERT(IMPLIES(cnp->cn_namelen == 1, cnp->cn_nameptr[0] != '.'));
    117 	KASSERT(IMPLIES(cnp->cn_namelen == 2, !(cnp->cn_nameptr[0] == '.' &&
    118 		    cnp->cn_nameptr[1] == '.')));
    119 	//CHFS_VALIDATE_DIR(node);
    120 
    121 	//node->chn_status |= CHFS_NODE_ACCESSED;
    122 
    123 	found = false;
    124 //	fd = ip->dents;
    125 //	while(fd) {
    126 	TAILQ_FOREACH(fd, &ip->dents, fds) {
    127 		KASSERT(cnp->cn_namelen < 0xffff);
    128 		if (fd->vno == 0)
    129 			continue;
    130 		/*dbg("dirent dump:\n");
    131 		  dbg(" ->vno:     %d\n", fd->vno);
    132 		  dbg(" ->version: %ld\n", fd->version);
    133 		  dbg(" ->nhash:   0x%x\n", fd->nhash);
    134 		  dbg(" ->nsize:   %d\n", fd->nsize);
    135 		  dbg(" ->name:    %s\n", fd->name);
    136 		  dbg(" ->type:    %d\n", fd->type);*/
    137 		if (fd->nsize == (uint16_t)cnp->cn_namelen &&
    138 		    memcmp(fd->name, cnp->cn_nameptr, fd->nsize) == 0) {
    139 			found = true;
    140 			break;
    141 		}
    142 //		fd = fd->next;
    143 	}
    144 
    145 	return found ? fd : NULL;
    146 }
    147 
    148 /* --------------------------------------------------------------------- */
    149 
    150 int
    151 chfs_filldir(struct uio* uio, ino_t ino, const char *name,
    152     int namelen, enum vtype type)
    153 {
    154 	struct dirent dent;
    155 	int error;
    156 
    157 	memset(&dent, 0, sizeof(dent));
    158 
    159 	dent.d_fileno = ino;
    160 	switch (type) {
    161 	case VBLK:
    162 		dent.d_type = DT_BLK;
    163 		break;
    164 
    165 	case VCHR:
    166 		dent.d_type = DT_CHR;
    167 		break;
    168 
    169 	case VDIR:
    170 		dent.d_type = DT_DIR;
    171 		break;
    172 
    173 	case VFIFO:
    174 		dent.d_type = DT_FIFO;
    175 		break;
    176 
    177 	case VLNK:
    178 		dent.d_type = DT_LNK;
    179 		break;
    180 
    181 	case VREG:
    182 		dent.d_type = DT_REG;
    183 		break;
    184 
    185 	case VSOCK:
    186 		dent.d_type = DT_SOCK;
    187 		break;
    188 
    189 	default:
    190 		KASSERT(0);
    191 	}
    192 	dent.d_namlen = namelen;
    193 	(void)memcpy(dent.d_name, name, dent.d_namlen);
    194 	dent.d_reclen = _DIRENT_SIZE(&dent);
    195 
    196 	if (dent.d_reclen > uio->uio_resid) {
    197 		error = -1;
    198 	} else {
    199 		error = uiomove(&dent, dent.d_reclen, uio);
    200 	}
    201 
    202 	return error;
    203 }
    204 
    205 
    206 /* --------------------------------------------------------------------- */
    207 
    208 /*
    209  * Change size of the given vnode.
    210  * Caller should execute chfs_update on vp after a successful execution.
    211  * The vnode must be locked on entry and remain locked on exit.
    212  */
    213 int
    214 chfs_chsize(struct vnode *vp, u_quad_t size, kauth_cred_t cred)
    215 {
    216 	struct chfs_mount *chmp;
    217 	struct chfs_inode *ip;
    218 	struct buf *bp;
    219 	int blknum, append;
    220 	int error = 0;
    221 	char *buf = NULL;
    222 	struct chfs_full_dnode *fd;
    223 
    224 	ip = VTOI(vp);
    225 	chmp = ip->chmp;
    226 
    227 	dbg("chfs_chsize\n");
    228 
    229 	switch (vp->v_type) {
    230 	case VDIR:
    231 		return EISDIR;
    232 	case VLNK:
    233 	case VREG:
    234 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
    235 			return EROFS;
    236 		break;
    237 	case VBLK:
    238 	case VCHR:
    239 	case VFIFO:
    240 		return 0;
    241 	default:
    242 		return EOPNOTSUPP; /* XXX why not ENODEV? */
    243 	}
    244 
    245 	vflushbuf(vp, 0);
    246 
    247 	mutex_enter(&chmp->chm_lock_mountfields);
    248 	chfs_flush_pending_wbuf(chmp);
    249 
    250 	/* handle truncate to zero as a special case */
    251 	if (size == 0) {
    252 		dbg("truncate to zero");
    253 		chfs_truncate_fragtree(ip->chmp,
    254 		    &ip->fragtree, size);
    255 		chfs_set_vnode_size(vp, size);
    256 
    257 		mutex_exit(&chmp->chm_lock_mountfields);
    258 
    259 		return 0;
    260 	}
    261 
    262 
    263 	/* allocate zeros for the new data */
    264 	buf = kmem_zalloc(size, KM_SLEEP);
    265 	bp = getiobuf(vp, true);
    266 
    267 	if (ip->size != 0) {
    268 		/* read the whole data */
    269 		bp->b_blkno = 0;
    270 		bp->b_bufsize = bp->b_resid = bp->b_bcount = ip->size;
    271 		bp->b_data = kmem_alloc(ip->size, KM_SLEEP);
    272 
    273 		error = chfs_read_data(chmp, vp, bp);
    274 		if (error) {
    275 			mutex_exit(&chmp->chm_lock_mountfields);
    276 			putiobuf(bp);
    277 
    278 			return error;
    279 		}
    280 
    281 		/* create the new data */
    282 		dbg("create new data vap%llu ip%llu\n",
    283 			(unsigned long long)size, (unsigned long long)ip->size);
    284 		append = size - ip->size;
    285 		if (append > 0) {
    286 			memcpy(buf, bp->b_data, ip->size);
    287 		} else {
    288 			memcpy(buf, bp->b_data, size);
    289 			chfs_truncate_fragtree(ip->chmp,
    290 				&ip->fragtree, size);
    291 		}
    292 
    293 		kmem_free(bp->b_data, ip->size);
    294 
    295 		struct chfs_node_frag *lastfrag = frag_last(&ip->fragtree);
    296 		fd = lastfrag->node;
    297 		chfs_mark_node_obsolete(chmp, fd->nref);
    298 
    299 		blknum = lastfrag->ofs / PAGE_SIZE;
    300 		lastfrag->size = append > PAGE_SIZE ? PAGE_SIZE : size % PAGE_SIZE;
    301 	} else {
    302 		fd = chfs_alloc_full_dnode();
    303 		blknum = 0;
    304 	}
    305 
    306 	chfs_set_vnode_size(vp, size);
    307 
    308 	// write the new data
    309 	for (bp->b_blkno = blknum; bp->b_blkno * PAGE_SIZE < size; bp->b_blkno++) {
    310 		uint64_t writesize = MIN(size - bp->b_blkno * PAGE_SIZE, PAGE_SIZE);
    311 
    312 		bp->b_bufsize = bp->b_resid = bp->b_bcount = writesize;
    313 		bp->b_data = kmem_alloc(writesize, KM_SLEEP);
    314 
    315 		memcpy(bp->b_data, buf + (bp->b_blkno * PAGE_SIZE), writesize);
    316 
    317 		if (bp->b_blkno != blknum) {
    318 			fd = chfs_alloc_full_dnode();
    319 		}
    320 
    321 		error = chfs_write_flash_dnode(chmp, vp, bp, fd);
    322 		if (error) {
    323 			mutex_exit(&chmp->chm_lock_mountfields);
    324 			kmem_free(bp->b_data, writesize);
    325 			putiobuf(bp);
    326 
    327 			return error;
    328 		}
    329 		if (bp->b_blkno != blknum) {
    330 			chfs_add_full_dnode_to_inode(chmp, ip, fd);
    331 		}
    332 		kmem_free(bp->b_data, writesize);
    333 	}
    334 
    335 	mutex_exit(&chmp->chm_lock_mountfields);
    336 
    337 	kmem_free(buf, size);
    338 	putiobuf(bp);
    339 
    340 	return 0;
    341 }
    342 #if 0
    343 	int error;
    344 	struct chfs_node *node;
    345 
    346 	KASSERT(VOP_ISLOCKED(vp));
    347 
    348 	node = VP_TO_CHFS_NODE(vp);
    349 
    350 	// Decide whether this is a valid operation based on the file type.
    351 	error = 0;
    352 	switch (vp->v_type) {
    353 	case VDIR:
    354 		return EISDIR;
    355 
    356 	case VREG:
    357 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
    358 			return EROFS;
    359 		break;
    360 
    361 	case VBLK:
    362 	case VCHR:
    363 	case VFIFO:
    364 		// Allow modifications of special files even if in the file
    365 		// system is mounted read-only (we are not modifying the
    366 		// files themselves, but the objects they represent).
    367 		return 0;
    368 
    369 	default:
    370 		return ENODEV;
    371 	}
    372 
    373 	// Immutable or append-only files cannot be modified, either.
    374 	if (node->chn_flags & (IMMUTABLE | APPEND))
    375 		return EPERM;
    376 
    377 	error = chfs_truncate(vp, size);
    378 	// chfs_truncate will raise the NOTE_EXTEND and NOTE_ATTRIB kevents
    379 	// for us, as will update dn_status; no need to do that here.
    380 
    381 	KASSERT(VOP_ISLOCKED(vp));
    382 
    383 	return error;
    384 #endif
    385 
    386 /* --------------------------------------------------------------------- */
    387 
    388 /*
    389  * Change flags of the given vnode.
    390  * Caller should execute chfs_update on vp after a successful execution.
    391  * The vnode must be locked on entry and remain locked on exit.
    392  */
    393 int
    394 chfs_chflags(struct vnode *vp, int flags, kauth_cred_t cred)
    395 {
    396 	struct chfs_mount *chmp;
    397 	struct chfs_inode *ip;
    398 	int error = 0;
    399 
    400 	ip = VTOI(vp);
    401 	chmp = ip->chmp;
    402 
    403 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
    404 		return EROFS;
    405 
    406 	if (kauth_cred_geteuid(cred) != ip->uid &&
    407 	    (error = kauth_authorize_generic(cred,
    408 		KAUTH_GENERIC_ISSUSER, NULL)))
    409 		return error;
    410 
    411 	if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
    412 		NULL) == 0) {
    413 		if ((ip->flags & (SF_IMMUTABLE | SF_APPEND)) &&
    414 		    kauth_authorize_system(curlwp->l_cred,
    415 			KAUTH_SYSTEM_CHSYSFLAGS, 0, NULL, NULL, NULL))
    416 			return EPERM;
    417 
    418 		if ((flags & SF_SNAPSHOT) !=
    419 		    (ip->flags & SF_SNAPSHOT))
    420 			return EPERM;
    421 
    422 		ip->flags = flags;
    423 	} else {
    424 		if ((ip->flags & (SF_IMMUTABLE | SF_APPEND)) ||
    425 		    (flags & UF_SETTABLE) != flags)
    426 			return EPERM;
    427 
    428 		if ((ip->flags & SF_SETTABLE) !=
    429 		    (flags & SF_SETTABLE))
    430 			return EPERM;
    431 
    432 		ip->flags &= SF_SETTABLE;
    433 		ip->flags |= (flags & UF_SETTABLE);
    434 	}
    435 	ip->iflag |= IN_CHANGE;
    436 	error = chfs_update(vp, NULL, NULL, UPDATE_WAIT);
    437 	if (error)
    438 		return error;
    439 
    440 	if (flags & (IMMUTABLE | APPEND))
    441 		return 0;
    442 
    443 	return error;
    444 }
    445 
    446 /* --------------------------------------------------------------------- */
    447 
    448 void
    449 chfs_itimes(struct chfs_inode *ip, const struct timespec *acc,
    450     const struct timespec *mod, const struct timespec *cre)
    451 {
    452 	//dbg("itimes\n");
    453 	struct timespec now;
    454 
    455 	if (!(ip->iflag & (IN_ACCESS | IN_CHANGE | IN_UPDATE | IN_MODIFY))) {
    456 		return;
    457 	}
    458 
    459 	vfs_timestamp(&now);
    460 	if (ip->iflag & IN_ACCESS) {
    461 		if (acc == NULL)
    462 			acc = &now;
    463 		ip->atime = acc->tv_sec;
    464 	}
    465 	if (ip->iflag & (IN_UPDATE | IN_MODIFY)) {
    466 		if (mod == NULL)
    467 			mod = &now;
    468 		ip->mtime = mod->tv_sec;
    469 		//ip->i_modrev++;
    470 	}
    471 	if (ip->iflag & (IN_CHANGE | IN_MODIFY)) {
    472 		if (cre == NULL)
    473 			cre = &now;
    474 		ip->ctime = cre->tv_sec;
    475 	}
    476 	if (ip->iflag & (IN_ACCESS | IN_MODIFY))
    477 		ip->iflag |= IN_ACCESSED;
    478 	if (ip->iflag & (IN_UPDATE | IN_CHANGE))
    479 		ip->iflag |= IN_MODIFIED;
    480 	ip->iflag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE | IN_MODIFY);
    481 }
    482 
    483 /* --------------------------------------------------------------------- */
    484 
    485 int
    486 chfs_update(struct vnode *vp, const struct timespec *acc,
    487     const struct timespec *mod, int flags)
    488 {
    489 
    490 	struct chfs_inode *ip;
    491 
    492 	/* XXX ufs_reclaim calls this function unlocked! */
    493 //	KASSERT(VOP_ISLOCKED(vp));
    494 
    495 #if 0
    496 	if (flags & UPDATE_CLOSE)
    497 		; /* XXX Need to do anything special? */
    498 #endif
    499 
    500 	ip = VTOI(vp);
    501 	chfs_itimes(ip, acc, mod, NULL);
    502 
    503 //	KASSERT(VOP_ISLOCKED(vp));
    504 	return (0);
    505 }
    506 
    507 /* --------------------------------------------------------------------- */
    508 /*
    509   int
    510   chfs_truncate(struct vnode *vp, off_t length)
    511   {
    512   bool extended;
    513   int error;
    514   struct chfs_node *node;
    515   printf("CHFS: truncate()\n");
    516 
    517   node = VP_TO_CHFS_NODE(vp);
    518   extended = length > node->chn_size;
    519 
    520   if (length < 0) {
    521   error = EINVAL;
    522   goto out;
    523   }
    524 
    525   if (node->chn_size == length) {
    526   error = 0;
    527   goto out;
    528   }
    529 
    530   error = chfs_reg_resize(vp, length);
    531   if (error == 0)
    532   node->chn_status |= CHFS_NODE_CHANGED | CHFS_NODE_MODIFIED;
    533 
    534   out:
    535   chfs_update(vp, NULL, NULL, 0);
    536 
    537   return error;
    538   }*/
    539 
    540 
    541