Home | History | Annotate | Line # | Download | only in chfs
chfs_subr.c revision 1.4.2.2
      1 /*	$NetBSD: chfs_subr.c,v 1.4.2.2 2012/04/17 00:08:54 yamt 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 <miscfs/genfs/genfs.h>
     60 #include "chfs.h"
     61 //#include <fs/chfs/chfs_vnops.h>
     62 //#include </root/xipffs/netbsd.chfs/chfs.h>
     63 
     64 /* --------------------------------------------------------------------- */
     65 
     66 /*
     67  * Returns information about the number of available memory pages,
     68  * including physical and virtual ones.
     69  *
     70  * If 'total' is true, the value returned is the total amount of memory
     71  * pages configured for the system (either in use or free).
     72  * If it is FALSE, the value returned is the amount of free memory pages.
     73  *
     74  * Remember to remove DUMMYFS_PAGES_RESERVED from the returned value to avoid
     75  * excessive memory usage.
     76  *
     77  */
     78 size_t
     79 chfs_mem_info(bool total)
     80 {
     81 	size_t size;
     82 
     83 	size = 0;
     84 	size += uvmexp.swpgavail;
     85 	if (!total) {
     86 		size -= uvmexp.swpgonly;
     87 	}
     88 	size += uvmexp.free;
     89 	size += uvmexp.filepages;
     90 	if (size > uvmexp.wired) {
     91 		size -= uvmexp.wired;
     92 	} else {
     93 		size = 0;
     94 	}
     95 
     96 	return size;
     97 }
     98 
     99 
    100 /* --------------------------------------------------------------------- */
    101 
    102 /*
    103  * Looks for a directory entry in the directory represented by node.
    104  * 'cnp' describes the name of the entry to look for.  Note that the .
    105  * and .. components are not allowed as they do not physically exist
    106  * within directories.
    107  *
    108  * Returns a pointer to the entry when found, otherwise NULL.
    109  */
    110 struct chfs_dirent *
    111 chfs_dir_lookup(struct chfs_inode *ip, struct componentname *cnp)
    112 {
    113 	bool found;
    114 	struct chfs_dirent *fd;
    115 	dbg("dir_lookup()\n");
    116 
    117 	KASSERT(IMPLIES(cnp->cn_namelen == 1, cnp->cn_nameptr[0] != '.'));
    118 	KASSERT(IMPLIES(cnp->cn_namelen == 2, !(cnp->cn_nameptr[0] == '.' &&
    119 		    cnp->cn_nameptr[1] == '.')));
    120 	//CHFS_VALIDATE_DIR(node);
    121 
    122 	//node->chn_status |= CHFS_NODE_ACCESSED;
    123 
    124 	found = false;
    125 //	fd = ip->dents;
    126 //	while(fd) {
    127 	TAILQ_FOREACH(fd, &ip->dents, fds) {
    128 		KASSERT(cnp->cn_namelen < 0xffff);
    129 		if (fd->vno == 0)
    130 			continue;
    131 		/*dbg("dirent dump:\n");
    132 		  dbg(" ->vno:     %d\n", fd->vno);
    133 		  dbg(" ->version: %ld\n", fd->version);
    134 		  dbg(" ->nhash:   0x%x\n", fd->nhash);
    135 		  dbg(" ->nsize:   %d\n", fd->nsize);
    136 		  dbg(" ->name:    %s\n", fd->name);
    137 		  dbg(" ->type:    %d\n", fd->type);*/
    138 		if (fd->nsize == (uint16_t)cnp->cn_namelen &&
    139 		    memcmp(fd->name, cnp->cn_nameptr, fd->nsize) == 0) {
    140 			found = true;
    141 			break;
    142 		}
    143 //		fd = fd->next;
    144 	}
    145 
    146 	return found ? fd : NULL;
    147 }
    148 
    149 /* --------------------------------------------------------------------- */
    150 
    151 int
    152 chfs_filldir(struct uio* uio, ino_t ino, const char *name,
    153     int namelen, enum chtype type)
    154 {
    155 	struct dirent dent;
    156 	int error;
    157 
    158 	memset(&dent, 0, sizeof(dent));
    159 
    160 	dent.d_fileno = ino;
    161 	switch (type) {
    162 	case CHT_BLK:
    163 		dent.d_type = DT_BLK;
    164 		break;
    165 
    166 	case CHT_CHR:
    167 		dent.d_type = DT_CHR;
    168 		break;
    169 
    170 	case CHT_DIR:
    171 		dent.d_type = DT_DIR;
    172 		break;
    173 
    174 	case CHT_FIFO:
    175 		dent.d_type = DT_FIFO;
    176 		break;
    177 
    178 	case CHT_LNK:
    179 		dent.d_type = DT_LNK;
    180 		break;
    181 
    182 	case CHT_REG:
    183 		dent.d_type = DT_REG;
    184 		break;
    185 
    186 	case CHT_SOCK:
    187 		dent.d_type = DT_SOCK;
    188 		break;
    189 
    190 	default:
    191 		KASSERT(0);
    192 	}
    193 	dent.d_namlen = namelen;
    194 	(void)memcpy(dent.d_name, name, dent.d_namlen);
    195 	dent.d_reclen = _DIRENT_SIZE(&dent);
    196 
    197 	if (dent.d_reclen > uio->uio_resid) {
    198 		error = -1;
    199 	} else {
    200 		error = uiomove(&dent, dent.d_reclen, uio);
    201 	}
    202 
    203 	return error;
    204 }
    205 
    206 
    207 /* --------------------------------------------------------------------- */
    208 
    209 /*
    210  * Change size of the given vnode.
    211  * Caller should execute chfs_update on vp after a successful execution.
    212  * The vnode must be locked on entry and remain locked on exit.
    213  */
    214 int
    215 chfs_chsize(struct vnode *vp, u_quad_t size, kauth_cred_t cred)
    216 {
    217 	struct chfs_mount *chmp;
    218 	struct chfs_inode *ip;
    219 	struct buf *bp;
    220 	int blknum, append;
    221 	int error = 0;
    222 	char *buf = NULL;
    223 	struct chfs_full_dnode *fd;
    224 
    225 	ip = VTOI(vp);
    226 	chmp = ip->chmp;
    227 
    228 	dbg("chfs_chsize\n");
    229 
    230 	switch (ip->ch_type) {
    231 	case CHT_DIR:
    232 		return EISDIR;
    233 	case CHT_LNK:
    234 	case CHT_REG:
    235 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
    236 			return EROFS;
    237 		break;
    238 	case CHT_BLK:
    239 	case CHT_CHR:
    240 	case CHT_FIFO:
    241 		return 0;
    242 	default:
    243 		return EOPNOTSUPP; /* XXX why not ENODEV? */
    244 	}
    245 
    246 	vflushbuf(vp, 0);
    247 
    248 	mutex_enter(&chmp->chm_lock_mountfields);
    249 	chfs_flush_pending_wbuf(chmp);
    250 
    251 	/* handle truncate to zero as a special case */
    252 	if (size == 0) {
    253 		dbg("truncate to zero");
    254 		chfs_truncate_fragtree(ip->chmp,
    255 		    &ip->fragtree, size);
    256 		chfs_set_vnode_size(vp, size);
    257 
    258 		mutex_exit(&chmp->chm_lock_mountfields);
    259 
    260 		return 0;
    261 	}
    262 
    263 
    264 	/* allocate zeros for the new data */
    265 	buf = kmem_zalloc(size, KM_SLEEP);
    266 	bp = getiobuf(vp, true);
    267 
    268 	if (ip->size != 0) {
    269 		/* read the whole data */
    270 		bp->b_blkno = 0;
    271 		bp->b_bufsize = bp->b_resid = bp->b_bcount = ip->size;
    272 		bp->b_data = kmem_alloc(ip->size, KM_SLEEP);
    273 
    274 		error = chfs_read_data(chmp, vp, bp);
    275 		if (error) {
    276 			mutex_exit(&chmp->chm_lock_mountfields);
    277 			putiobuf(bp);
    278 
    279 			return error;
    280 		}
    281 
    282 		/* create the new data */
    283 		dbg("create new data vap%llu ip%llu\n",
    284 			(unsigned long long)size, (unsigned long long)ip->size);
    285 		append = size - ip->size;
    286 		if (append > 0) {
    287 			memcpy(buf, bp->b_data, ip->size);
    288 		} else {
    289 			memcpy(buf, bp->b_data, size);
    290 			chfs_truncate_fragtree(ip->chmp,
    291 				&ip->fragtree, size);
    292 		}
    293 
    294 		kmem_free(bp->b_data, ip->size);
    295 
    296 		struct chfs_node_frag *lastfrag = frag_last(&ip->fragtree);
    297 		fd = lastfrag->node;
    298 		chfs_mark_node_obsolete(chmp, fd->nref);
    299 
    300 		blknum = lastfrag->ofs / PAGE_SIZE;
    301 		lastfrag->size = append > PAGE_SIZE ? PAGE_SIZE : size % PAGE_SIZE;
    302 	} else {
    303 		fd = chfs_alloc_full_dnode();
    304 		blknum = 0;
    305 	}
    306 
    307 	chfs_set_vnode_size(vp, size);
    308 
    309 	// write the new data
    310 	for (bp->b_blkno = blknum; bp->b_blkno * PAGE_SIZE < size; bp->b_blkno++) {
    311 		uint64_t writesize = MIN(size - bp->b_blkno * PAGE_SIZE, PAGE_SIZE);
    312 
    313 		bp->b_bufsize = bp->b_resid = bp->b_bcount = writesize;
    314 		bp->b_data = kmem_alloc(writesize, KM_SLEEP);
    315 
    316 		memcpy(bp->b_data, buf + (bp->b_blkno * PAGE_SIZE), writesize);
    317 
    318 		if (bp->b_blkno != blknum) {
    319 			fd = chfs_alloc_full_dnode();
    320 		}
    321 
    322 		error = chfs_write_flash_dnode(chmp, vp, bp, fd);
    323 		if (error) {
    324 			mutex_exit(&chmp->chm_lock_mountfields);
    325 			kmem_free(bp->b_data, writesize);
    326 			putiobuf(bp);
    327 
    328 			return error;
    329 		}
    330 		if (bp->b_blkno != blknum) {
    331 			chfs_add_full_dnode_to_inode(chmp, ip, fd);
    332 		}
    333 		kmem_free(bp->b_data, writesize);
    334 	}
    335 
    336 	mutex_exit(&chmp->chm_lock_mountfields);
    337 
    338 	kmem_free(buf, size);
    339 	putiobuf(bp);
    340 
    341 	return 0;
    342 }
    343 #if 0
    344 	int error;
    345 	struct chfs_node *node;
    346 
    347 	KASSERT(VOP_ISLOCKED(vp));
    348 
    349 	node = VP_TO_CHFS_NODE(vp);
    350 
    351 	// Decide whether this is a valid operation based on the file type.
    352 	error = 0;
    353 	switch (vp->v_type) {
    354 	case VDIR:
    355 		return EISDIR;
    356 
    357 	case VREG:
    358 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
    359 			return EROFS;
    360 		break;
    361 
    362 	case VBLK:
    363 	case VCHR:
    364 	case VFIFO:
    365 		// Allow modifications of special files even if in the file
    366 		// system is mounted read-only (we are not modifying the
    367 		// files themselves, but the objects they represent).
    368 		return 0;
    369 
    370 	default:
    371 		return ENODEV;
    372 	}
    373 
    374 	// Immutable or append-only files cannot be modified, either.
    375 	if (node->chn_flags & (IMMUTABLE | APPEND))
    376 		return EPERM;
    377 
    378 	error = chfs_truncate(vp, size);
    379 	// chfs_truncate will raise the NOTE_EXTEND and NOTE_ATTRIB kevents
    380 	// for us, as will update dn_status; no need to do that here.
    381 
    382 	KASSERT(VOP_ISLOCKED(vp));
    383 
    384 	return error;
    385 #endif
    386 
    387 /* --------------------------------------------------------------------- */
    388 
    389 /*
    390  * Change flags of the given vnode.
    391  * Caller should execute chfs_update on vp after a successful execution.
    392  * The vnode must be locked on entry and remain locked on exit.
    393  */
    394 int
    395 chfs_chflags(struct vnode *vp, int flags, kauth_cred_t cred)
    396 {
    397 	struct chfs_mount *chmp;
    398 	struct chfs_inode *ip;
    399 	int error = 0;
    400 	kauth_action_t action = KAUTH_VNODE_WRITE_FLAGS;
    401 	bool changing_sysflags = false;
    402 
    403 	ip = VTOI(vp);
    404 	chmp = ip->chmp;
    405 
    406 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
    407 		return EROFS;
    408 
    409 	if ((flags & SF_SNAPSHOT) != (ip->flags & SF_SNAPSHOT))
    410 		return EPERM;
    411 
    412 	/* Indicate we're changing system flags if we are. */
    413 	if ((ip->flags & SF_SETTABLE) != (flags & SF_SETTABLE) ||
    414 	    (flags & UF_SETTABLE) != flags) {
    415 		action |= KAUTH_VNODE_WRITE_SYSFLAGS;
    416 		changing_sysflags = true;
    417 	}
    418 
    419 	/* Indicate the node has system flags if it does. */
    420 	if (ip->flags & (SF_IMMUTABLE | SF_APPEND)) {
    421 		action |= KAUTH_VNODE_HAS_SYSFLAGS;
    422 	}
    423 
    424 	error = kauth_authorize_vnode(cred, action, vp, NULL,
    425 	    genfs_can_chflags(cred, CHTTOVT(ip->ch_type), ip->uid, changing_sysflags));
    426 	if (error)
    427 		return error;
    428 
    429 	if (changing_sysflags) {
    430 		ip->flags = flags;
    431 	} else {
    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