Home | History | Annotate | Line # | Download | only in chfs
chfs_subr.c revision 1.7
      1 /*	$NetBSD: chfs_subr.c,v 1.7 2012/08/22 09:20:13 ttoth 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 
    220 	ip = VTOI(vp);
    221 	chmp = ip->chmp;
    222 
    223 	dbg("chfs_chsize\n");
    224 
    225 	switch (ip->ch_type) {
    226 	case CHT_DIR:
    227 		return EISDIR;
    228 	case CHT_LNK:
    229 	case CHT_REG:
    230 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
    231 			return EROFS;
    232 		break;
    233 	case CHT_BLK:
    234 	case CHT_CHR:
    235 	case CHT_FIFO:
    236 		return 0;
    237 	default:
    238 		return EOPNOTSUPP; /* XXX why not ENODEV? */
    239 	}
    240 
    241 	vflushbuf(vp, 0);
    242 
    243 	mutex_enter(&chmp->chm_lock_mountfields);
    244 
    245 	if (ip->size < size) {
    246 		uvm_vnp_setsize(vp, size);
    247 		chfs_set_vnode_size(vp, size);
    248 		ip->iflag |= IN_CHANGE | IN_UPDATE;
    249 
    250 		mutex_exit(&chmp->chm_lock_mountfields);
    251 		return 0;
    252 	}
    253 
    254 	if (size != 0) {
    255 		ubc_zerorange(&vp->v_uobj, size, ip->size - size, UBC_UNMAP_FLAG(vp));
    256 	}
    257 
    258 	chfs_truncate_fragtree(ip->chmp, &ip->fragtree, size);
    259 	uvm_vnp_setsize(vp, size);
    260 	chfs_set_vnode_size(vp, size);
    261 	ip->iflag |= IN_CHANGE | IN_UPDATE;
    262 	mutex_exit(&chmp->chm_lock_mountfields);
    263 	return 0;
    264 }
    265 #if 0
    266 	int error;
    267 	struct chfs_node *node;
    268 
    269 	KASSERT(VOP_ISLOCKED(vp));
    270 
    271 	node = VP_TO_CHFS_NODE(vp);
    272 
    273 	// Decide whether this is a valid operation based on the file type.
    274 	error = 0;
    275 	switch (vp->v_type) {
    276 	case VDIR:
    277 		return EISDIR;
    278 
    279 	case VREG:
    280 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
    281 			return EROFS;
    282 		break;
    283 
    284 	case VBLK:
    285 	case VCHR:
    286 	case VFIFO:
    287 		// Allow modifications of special files even if in the file
    288 		// system is mounted read-only (we are not modifying the
    289 		// files themselves, but the objects they represent).
    290 		return 0;
    291 
    292 	default:
    293 		return ENODEV;
    294 	}
    295 
    296 	// Immutable or append-only files cannot be modified, either.
    297 	if (node->chn_flags & (IMMUTABLE | APPEND))
    298 		return EPERM;
    299 
    300 	error = chfs_truncate(vp, size);
    301 	// chfs_truncate will raise the NOTE_EXTEND and NOTE_ATTRIB kevents
    302 	// for us, as will update dn_status; no need to do that here.
    303 
    304 	KASSERT(VOP_ISLOCKED(vp));
    305 
    306 	return error;
    307 #endif
    308 
    309 /* --------------------------------------------------------------------- */
    310 
    311 /*
    312  * Change flags of the given vnode.
    313  * Caller should execute chfs_update on vp after a successful execution.
    314  * The vnode must be locked on entry and remain locked on exit.
    315  */
    316 int
    317 chfs_chflags(struct vnode *vp, int flags, kauth_cred_t cred)
    318 {
    319 	struct chfs_mount *chmp;
    320 	struct chfs_inode *ip;
    321 	int error = 0;
    322 	kauth_action_t action = KAUTH_VNODE_WRITE_FLAGS;
    323 	bool changing_sysflags = false;
    324 
    325 	ip = VTOI(vp);
    326 	chmp = ip->chmp;
    327 
    328 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
    329 		return EROFS;
    330 
    331 	if ((flags & SF_SNAPSHOT) != (ip->flags & SF_SNAPSHOT))
    332 		return EPERM;
    333 
    334 	/* Indicate we're changing system flags if we are. */
    335 	if ((ip->flags & SF_SETTABLE) != (flags & SF_SETTABLE) ||
    336 	    (flags & UF_SETTABLE) != flags) {
    337 		action |= KAUTH_VNODE_WRITE_SYSFLAGS;
    338 		changing_sysflags = true;
    339 	}
    340 
    341 	/* Indicate the node has system flags if it does. */
    342 	if (ip->flags & (SF_IMMUTABLE | SF_APPEND)) {
    343 		action |= KAUTH_VNODE_HAS_SYSFLAGS;
    344 	}
    345 
    346 	error = kauth_authorize_vnode(cred, action, vp, NULL,
    347 	    genfs_can_chflags(cred, CHTTOVT(ip->ch_type), ip->uid, changing_sysflags));
    348 	if (error)
    349 		return error;
    350 
    351 	if (changing_sysflags) {
    352 		ip->flags = flags;
    353 	} else {
    354 		ip->flags &= SF_SETTABLE;
    355 		ip->flags |= (flags & UF_SETTABLE);
    356 	}
    357 	ip->iflag |= IN_CHANGE;
    358 	error = chfs_update(vp, NULL, NULL, UPDATE_WAIT);
    359 	if (error)
    360 		return error;
    361 
    362 	if (flags & (IMMUTABLE | APPEND))
    363 		return 0;
    364 
    365 	return error;
    366 }
    367 
    368 /* --------------------------------------------------------------------- */
    369 
    370 void
    371 chfs_itimes(struct chfs_inode *ip, const struct timespec *acc,
    372     const struct timespec *mod, const struct timespec *cre)
    373 {
    374 	//dbg("itimes\n");
    375 	struct timespec now;
    376 
    377 	if (!(ip->iflag & (IN_ACCESS | IN_CHANGE | IN_UPDATE | IN_MODIFY))) {
    378 		return;
    379 	}
    380 
    381 	vfs_timestamp(&now);
    382 	if (ip->iflag & IN_ACCESS) {
    383 		if (acc == NULL)
    384 			acc = &now;
    385 		ip->atime = acc->tv_sec;
    386 	}
    387 	if (ip->iflag & (IN_UPDATE | IN_MODIFY)) {
    388 		if (mod == NULL)
    389 			mod = &now;
    390 		ip->mtime = mod->tv_sec;
    391 		//ip->i_modrev++;
    392 	}
    393 	if (ip->iflag & (IN_CHANGE | IN_MODIFY)) {
    394 		if (cre == NULL)
    395 			cre = &now;
    396 		ip->ctime = cre->tv_sec;
    397 	}
    398 	if (ip->iflag & (IN_ACCESS | IN_MODIFY))
    399 		ip->iflag |= IN_ACCESSED;
    400 	if (ip->iflag & (IN_UPDATE | IN_CHANGE))
    401 		ip->iflag |= IN_MODIFIED;
    402 	ip->iflag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE | IN_MODIFY);
    403 }
    404 
    405 /* --------------------------------------------------------------------- */
    406 
    407 int
    408 chfs_update(struct vnode *vp, const struct timespec *acc,
    409     const struct timespec *mod, int flags)
    410 {
    411 
    412 	struct chfs_inode *ip;
    413 
    414 	/* XXX ufs_reclaim calls this function unlocked! */
    415 //	KASSERT(VOP_ISLOCKED(vp));
    416 
    417 #if 0
    418 	if (flags & UPDATE_CLOSE)
    419 		; /* XXX Need to do anything special? */
    420 #endif
    421 
    422 	ip = VTOI(vp);
    423 	chfs_itimes(ip, acc, mod, NULL);
    424 
    425 //	KASSERT(VOP_ISLOCKED(vp));
    426 	return (0);
    427 }
    428 
    429 /* --------------------------------------------------------------------- */
    430 /*
    431   int
    432   chfs_truncate(struct vnode *vp, off_t length)
    433   {
    434   bool extended;
    435   int error;
    436   struct chfs_node *node;
    437   printf("CHFS: truncate()\n");
    438 
    439   node = VP_TO_CHFS_NODE(vp);
    440   extended = length > node->chn_size;
    441 
    442   if (length < 0) {
    443   error = EINVAL;
    444   goto out;
    445   }
    446 
    447   if (node->chn_size == length) {
    448   error = 0;
    449   goto out;
    450   }
    451 
    452   error = chfs_reg_resize(vp, length);
    453   if (error == 0)
    454   node->chn_status |= CHFS_NODE_CHANGED | CHFS_NODE_MODIFIED;
    455 
    456   out:
    457   chfs_update(vp, NULL, NULL, 0);
    458 
    459   return error;
    460   }*/
    461 
    462 
    463