Home | History | Annotate | Line # | Download | only in chfs
      1 /*	$NetBSD: chfs_subr.c,v 1.15 2020/09/05 16:30:12 riastradh 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 #include <sys/cdefs.h>
     36 
     37 #include <sys/param.h>
     38 #include <sys/dirent.h>
     39 #include <sys/event.h>
     40 #include <sys/kmem.h>
     41 #include <sys/mount.h>
     42 #include <sys/namei.h>
     43 #include <sys/time.h>
     44 #include <sys/stat.h>
     45 #include <sys/systm.h>
     46 #include <sys/swap.h>
     47 #include <sys/vnode.h>
     48 #include <sys/kauth.h>
     49 #include <sys/proc.h>
     50 #include <sys/atomic.h>
     51 
     52 #include <uvm/uvm_extern.h>
     53 
     54 #include <miscfs/specfs/specdev.h>
     55 #include <miscfs/genfs/genfs.h>
     56 #include "chfs.h"
     57 
     58 
     59 /*
     60  * chfs_mem_info -
     61  * Returns information about the number of available memory pages,
     62  * including physical and virtual ones.
     63  *
     64  * If 'total' is true, the value returned is the total amount of memory
     65  * pages configured for the system (either in use or free).
     66  * If it is FALSE, the value returned is the amount of free memory pages.
     67  *
     68  * Remember to remove DUMMYFS_PAGES_RESERVED from the returned value to avoid
     69  * excessive memory usage.
     70  *
     71  */
     72 size_t
     73 chfs_mem_info(bool total)
     74 {
     75 	size_t size;
     76 
     77 	size = 0;
     78 	size += uvmexp.swpgavail;
     79 	if (!total) {
     80 		size -= uvmexp.swpgonly;
     81 	}
     82 	size += uvm_availmem(true);
     83 	size += uvmexp.filepages;
     84 	if (size > uvmexp.wired) {
     85 		size -= uvmexp.wired;
     86 	} else {
     87 		size = 0;
     88 	}
     89 
     90 	return size;
     91 }
     92 
     93 
     94 /*
     95  * chfs_dir_lookup -
     96  * Looks for a directory entry in the directory represented by node.
     97  * 'cnp' describes the name of the entry to look for.  Note that the .
     98  * and .. components are not allowed as they do not physically exist
     99  * within directories.
    100  *
    101  * Returns a pointer to the entry when found, otherwise NULL.
    102  */
    103 struct chfs_dirent *
    104 chfs_dir_lookup(struct chfs_inode *ip, struct componentname *cnp)
    105 {
    106 	bool found;
    107 	struct chfs_dirent *fd;
    108 	dbg("dir_lookup()\n");
    109 
    110 	KASSERT(IMPLIES(cnp->cn_namelen == 1, cnp->cn_nameptr[0] != '.'));
    111 	KASSERT(IMPLIES(cnp->cn_namelen == 2, !(cnp->cn_nameptr[0] == '.' &&
    112 		    cnp->cn_nameptr[1] == '.')));
    113 
    114 	found = false;
    115 	TAILQ_FOREACH(fd, &ip->dents, fds) {
    116 		KASSERT(cnp->cn_namelen < 0xffff);
    117 		if (fd->vno == 0)
    118 			continue;
    119 		if (fd->nsize == (uint16_t)cnp->cn_namelen &&
    120 		    memcmp(fd->name, cnp->cn_nameptr, fd->nsize) == 0) {
    121 			found = true;
    122 			break;
    123 		}
    124 	}
    125 
    126 	return found ? fd : NULL;
    127 }
    128 
    129 /*
    130  * chfs_filldir -
    131  * Creates a (kernel) dirent and moves it to the given memory address.
    132  * Used during readdir.
    133  */
    134 int
    135 chfs_filldir(struct uio* uio, ino_t ino, const char *name,
    136     int namelen, enum chtype type)
    137 {
    138 	struct dirent dent;
    139 	int error;
    140 
    141 	memset(&dent, 0, sizeof(dent));
    142 
    143 	dent.d_fileno = ino;
    144 	switch (type) {
    145 	case CHT_BLK:
    146 		dent.d_type = DT_BLK;
    147 		break;
    148 
    149 	case CHT_CHR:
    150 		dent.d_type = DT_CHR;
    151 		break;
    152 
    153 	case CHT_DIR:
    154 		dent.d_type = DT_DIR;
    155 		break;
    156 
    157 	case CHT_FIFO:
    158 		dent.d_type = DT_FIFO;
    159 		break;
    160 
    161 	case CHT_LNK:
    162 		dent.d_type = DT_LNK;
    163 		break;
    164 
    165 	case CHT_REG:
    166 		dent.d_type = DT_REG;
    167 		break;
    168 
    169 	case CHT_SOCK:
    170 		dent.d_type = DT_SOCK;
    171 		break;
    172 
    173 	default:
    174 		KASSERT(0);
    175 	}
    176 	dent.d_namlen = namelen;
    177 	(void)memcpy(dent.d_name, name, dent.d_namlen);
    178 	dent.d_reclen = _DIRENT_SIZE(&dent);
    179 
    180 	if (dent.d_reclen > uio->uio_resid) {
    181 		error = -1;
    182 	} else {
    183 		error = uiomove(&dent, dent.d_reclen, uio);
    184 	}
    185 
    186 	return error;
    187 }
    188 
    189 /*
    190  * chfs_chsize - change size of the given vnode
    191  * Caller should execute chfs_update on vp after a successful execution.
    192  * The vnode must be locked on entry and remain locked on exit.
    193  */
    194 int
    195 chfs_chsize(struct vnode *vp, u_quad_t size, kauth_cred_t cred)
    196 {
    197 	struct chfs_mount *chmp;
    198 	struct chfs_inode *ip;
    199 
    200 	ip = VTOI(vp);
    201 	chmp = ip->chmp;
    202 
    203 	dbg("chfs_chsize\n");
    204 
    205 	switch (ip->ch_type) {
    206 	case CHT_DIR:
    207 		return EISDIR;
    208 	case CHT_LNK:
    209 	case CHT_REG:
    210 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
    211 			return EROFS;
    212 		break;
    213 	case CHT_BLK:
    214 	case CHT_CHR:
    215 	case CHT_FIFO:
    216 		return 0;
    217 	default:
    218 		return EOPNOTSUPP; /* XXX why not ENODEV? */
    219 	}
    220 
    221 	vflushbuf(vp, 0);
    222 
    223 	mutex_enter(&chmp->chm_lock_mountfields);
    224 
    225 	if (ip->size < size) {
    226 		uvm_vnp_setsize(vp, size);
    227 		chfs_set_vnode_size(vp, size);
    228 		ip->iflag |= IN_CHANGE | IN_UPDATE;
    229 
    230 		mutex_exit(&chmp->chm_lock_mountfields);
    231 		return 0;
    232 	}
    233 
    234 	if (size != 0) {
    235 		ubc_zerorange(&vp->v_uobj, size, ip->size - size, UBC_VNODE_FLAGS(vp));
    236 	}
    237 
    238 	/* drop unused fragments */
    239 	chfs_truncate_fragtree(ip->chmp, &ip->fragtree, size);
    240 
    241 	uvm_vnp_setsize(vp, size);
    242 	chfs_set_vnode_size(vp, size);
    243 	ip->iflag |= IN_CHANGE | IN_UPDATE;
    244 	mutex_exit(&chmp->chm_lock_mountfields);
    245 	return 0;
    246 }
    247 
    248 /*
    249  * chfs_chflags - change flags of the given vnode
    250  * Caller should execute chfs_update on vp after a successful execution.
    251  * The vnode must be locked on entry and remain locked on exit.
    252  */
    253 int
    254 chfs_chflags(struct vnode *vp, int flags, kauth_cred_t cred)
    255 {
    256 	struct chfs_inode *ip;
    257 	int error = 0;
    258 	kauth_action_t action = KAUTH_VNODE_WRITE_FLAGS;
    259 	bool changing_sysflags = false;
    260 
    261 	ip = VTOI(vp);
    262 
    263 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
    264 		return EROFS;
    265 
    266 	if ((flags & SF_SNAPSHOT) != (ip->flags & SF_SNAPSHOT))
    267 		return EPERM;
    268 
    269 	/* Indicate we're changing system flags if we are. */
    270 	if ((ip->flags & SF_SETTABLE) != (flags & SF_SETTABLE) ||
    271 	    (flags & UF_SETTABLE) != flags) {
    272 		action |= KAUTH_VNODE_WRITE_SYSFLAGS;
    273 		changing_sysflags = true;
    274 	}
    275 
    276 	/* Indicate the node has system flags if it does. */
    277 	if (ip->flags & (SF_IMMUTABLE | SF_APPEND)) {
    278 		action |= KAUTH_VNODE_HAS_SYSFLAGS;
    279 	}
    280 
    281 	error = kauth_authorize_vnode(cred, action, vp, NULL,
    282 	    genfs_can_chflags(vp, cred, ip->uid, changing_sysflags));
    283 	if (error)
    284 		return error;
    285 
    286 	if (changing_sysflags) {
    287 		ip->flags = flags;
    288 	} else {
    289 		ip->flags &= SF_SETTABLE;
    290 		ip->flags |= (flags & UF_SETTABLE);
    291 	}
    292 	ip->iflag |= IN_CHANGE;
    293 	error = chfs_update(vp, NULL, NULL, UPDATE_WAIT);
    294 	if (error)
    295 		return error;
    296 
    297 	if (flags & (IMMUTABLE | APPEND))
    298 		return 0;
    299 
    300 	return error;
    301 }
    302 
    303 
    304 /* chfs_itimes - updates a vnode times to the given data */
    305 void
    306 chfs_itimes(struct chfs_inode *ip, const struct timespec *acc,
    307     const struct timespec *mod, const struct timespec *cre)
    308 {
    309 	struct timespec now;
    310 
    311 	if (!(ip->iflag & (IN_ACCESS | IN_CHANGE | IN_UPDATE | IN_MODIFY))) {
    312 		return;
    313 	}
    314 
    315 	vfs_timestamp(&now);
    316 	if (ip->iflag & IN_ACCESS) {
    317 		if (acc == NULL)
    318 			acc = &now;
    319 		ip->atime = acc->tv_sec;
    320 	}
    321 	if (ip->iflag & (IN_UPDATE | IN_MODIFY)) {
    322 		if (mod == NULL)
    323 			mod = &now;
    324 		ip->mtime = mod->tv_sec;
    325 	}
    326 	if (ip->iflag & (IN_CHANGE | IN_MODIFY)) {
    327 		if (cre == NULL)
    328 			cre = &now;
    329 		ip->ctime = cre->tv_sec;
    330 	}
    331 	if (ip->iflag & (IN_ACCESS | IN_MODIFY))
    332 		ip->iflag |= IN_ACCESSED;
    333 	if (ip->iflag & (IN_UPDATE | IN_CHANGE))
    334 		ip->iflag |= IN_MODIFIED;
    335 	ip->iflag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE | IN_MODIFY);
    336 }
    337 
    338 /* chfs_update - updates a vnode times */
    339 int
    340 chfs_update(struct vnode *vp, const struct timespec *acc,
    341     const struct timespec *mod, int flags)
    342 {
    343 	struct chfs_inode *ip;
    344 
    345 	/* XXX ufs_reclaim calls this function unlocked! */
    346 
    347 	ip = VTOI(vp);
    348 	chfs_itimes(ip, acc, mod, NULL);
    349 
    350 	return (0);
    351 }
    352 
    353