Home | History | Annotate | Line # | Download | only in efs
      1 /*	$NetBSD: efs_vfsops.c,v 1.30 2022/03/19 13:53:32 hannken Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2006 Stephen M. Rumble <rumble (at) ephemeral.org>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 
     19 #include <sys/cdefs.h>
     20 __KERNEL_RCSID(0, "$NetBSD: efs_vfsops.c,v 1.30 2022/03/19 13:53:32 hannken Exp $");
     21 
     22 #include <sys/param.h>
     23 #include <sys/systm.h>
     24 #include <sys/malloc.h>
     25 #include <sys/mount.h>
     26 #include <sys/fstypes.h>
     27 #include <sys/vnode.h>
     28 #include <sys/buf.h>
     29 #include <sys/namei.h>
     30 #include <sys/fcntl.h>
     31 #include <sys/stat.h>
     32 #include <sys/kauth.h>
     33 #include <sys/proc.h>
     34 #include <sys/module.h>
     35 
     36 #include <miscfs/genfs/genfs_node.h>
     37 #include <miscfs/genfs/genfs.h>
     38 
     39 #include <miscfs/specfs/specdev.h>
     40 
     41 #include <fs/efs/efs.h>
     42 #include <fs/efs/efs_sb.h>
     43 #include <fs/efs/efs_dir.h>
     44 #include <fs/efs/efs_genfs.h>
     45 #include <fs/efs/efs_mount.h>
     46 #include <fs/efs/efs_extent.h>
     47 #include <fs/efs/efs_dinode.h>
     48 #include <fs/efs/efs_inode.h>
     49 #include <fs/efs/efs_subr.h>
     50 
     51 MODULE(MODULE_CLASS_VFS, efs, NULL);
     52 
     53 MALLOC_JUSTDEFINE(M_EFSMNT, "efsmnt", "efs mount structure");
     54 MALLOC_JUSTDEFINE(M_EFSINO, "efsino", "efs in-core inode structure");
     55 MALLOC_JUSTDEFINE(M_EFSTMP, "efstmp", "efs temporary allocations");
     56 
     57 extern int (**efs_vnodeop_p)(void *); 	/* for getnewvnode() */
     58 extern int (**efs_specop_p)(void *); 	/* for getnewvnode() */
     59 extern int (**efs_fifoop_p)(void *); 	/* for getnewvnode() */
     60 static int efs_statvfs(struct mount *, struct statvfs *);
     61 
     62 /*
     63  * efs_mount and efs_mountroot common functions.
     64  */
     65 static int
     66 efs_mount_common(struct mount *mp, const char *path, struct vnode *devvp,
     67     struct efs_args *args)
     68 {
     69 	int err;
     70 	struct buf *bp;
     71 	const char *why;
     72 	struct efs_mount *emp;
     73 	struct lwp *l = curlwp;
     74 
     75 	emp = malloc(sizeof(*emp), M_EFSMNT, M_WAITOK);
     76 	emp->em_dev = devvp->v_rdev;
     77 	emp->em_devvp = devvp;
     78 	emp->em_mnt = mp;
     79 
     80 	/* read in the superblock */
     81 	err = efs_bread(emp, EFS_BB_SB, l, &bp);
     82 	if (err) {
     83 		EFS_DPRINTF(("superblock read failed\n"));
     84 		free(emp, M_EFSMNT);
     85 		return (err);
     86 	}
     87 	memcpy(&emp->em_sb, bp->b_data, sizeof(emp->em_sb));
     88 	brelse(bp, 0);
     89 
     90 	/* validate the superblock */
     91 	if (efs_sb_validate(&emp->em_sb, &why)) {
     92 		printf("efs: invalid superblock: %s\n", why);
     93 		if (!(mp->mnt_flag & MNT_FORCE)) {
     94 			free(emp, M_EFSMNT);
     95 			return (EIO);
     96 		}
     97 	}
     98 
     99 	/* check that it's clean */
    100 	if (be16toh(emp->em_sb.sb_dirty) != EFS_SB_CLEAN) {
    101 		printf("efs: filesystem is dirty (sb_dirty = 0x%x); please "
    102 		    "run fsck_efs(8)\n", be16toh(emp->em_sb.sb_dirty));
    103 		/* XXX - default to readonly unless forced?? */
    104 	}
    105 
    106 	/* if the superblock was replicated, verify that it is the same */
    107 	if (be32toh(emp->em_sb.sb_replsb) != 0) {
    108 		struct buf *rbp;
    109 		bool skip = false;
    110 
    111 		err = efs_bread(emp, be32toh(emp->em_sb.sb_replsb), l, &rbp);
    112 		if (err) {
    113 			printf("efs: read of superblock replicant failed; "
    114 			    "please run fsck_efs(8)\n");
    115 			if (mp->mnt_flag & MNT_FORCE) {
    116 				skip = true;
    117 			} else {
    118 				free(emp, M_EFSMNT);
    119 				return (err);
    120 			}
    121 		}
    122 
    123 		if (!skip) {
    124 			if (memcmp(rbp->b_data, &emp->em_sb,
    125 			    sizeof(emp->em_sb))) {
    126 				printf("efs: superblock differs from "
    127 				    "replicant; please run fsck_efs(8)\n");
    128 				if (!(mp->mnt_flag & MNT_FORCE)) {
    129 					brelse(rbp, 0);
    130 					free(emp, M_EFSMNT);
    131 					return (EIO);
    132 				}
    133 			}
    134 			brelse(rbp, 0);
    135 		}
    136 	}
    137 
    138 	/* ensure we can read last block */
    139 	err = efs_bread(emp, be32toh(emp->em_sb.sb_size) - 1, l, &bp);
    140 	if (err) {
    141 		printf("efs: cannot access all filesystem blocks; please run "
    142 		    "fsck_efs(8)\n");
    143 		if (!(mp->mnt_flag & MNT_FORCE)) {
    144 			free(emp, M_EFSMNT);
    145 			return (err);
    146 		}
    147 	} else {
    148 		brelse(bp, 0);
    149 	}
    150 
    151 	mp->mnt_data = emp;
    152 	mp->mnt_flag |= MNT_LOCAL;
    153 	mp->mnt_fs_bshift = EFS_BB_SHFT;
    154 	mp->mnt_dev_bshift = DEV_BSHIFT;
    155 	vfs_getnewfsid(mp);
    156 	efs_statvfs(mp, &mp->mnt_stat);
    157 
    158 	err = set_statvfs_info(path, UIO_USERSPACE, args->fspec,
    159 	    UIO_USERSPACE, mp->mnt_op->vfs_name, mp, l);
    160 	if (err)
    161 		free(emp, M_EFSMNT);
    162 
    163 	return (err);
    164 }
    165 
    166 /*
    167  * mount syscall vfsop.
    168  *
    169  * Returns 0 on success.
    170  */
    171 static int
    172 efs_mount(struct mount *mp, const char *path, void *data, size_t *data_len)
    173 {
    174 	struct lwp *l = curlwp;
    175 	struct efs_args *args = data;
    176 	struct pathbuf *pb;
    177 	struct nameidata devnd;
    178 	struct efs_mount *emp;
    179 	struct vnode *devvp;
    180 	int err, mode;
    181 
    182 	if (args == NULL)
    183 		return EINVAL;
    184 	if (*data_len < sizeof *args)
    185 		return EINVAL;
    186 
    187 	if (mp->mnt_flag & MNT_GETARGS) {
    188 		if ((emp = VFSTOEFS(mp)) == NULL)
    189 			return (EIO);
    190 		args->fspec = NULL;
    191 		args->version = EFS_MNT_VERSION;
    192 		*data_len = sizeof *args;
    193 		return 0;
    194 	}
    195 
    196 	if (mp->mnt_flag & MNT_UPDATE)
    197 		return (EOPNOTSUPP);	/* XXX read-only */
    198 
    199 	/* look up our device's vnode. it is returned locked */
    200 	err = pathbuf_copyin(args->fspec, &pb);
    201 	if (err) {
    202 		return err;
    203 	}
    204 	NDINIT(&devnd, LOOKUP, FOLLOW | LOCKLEAF, pb);
    205 	if ((err = namei(&devnd))) {
    206 		pathbuf_destroy(pb);
    207 		return (err);
    208 	}
    209 
    210 	devvp = devnd.ni_vp;
    211 	pathbuf_destroy(pb);
    212 
    213 	if (devvp->v_type != VBLK) {
    214 		vput(devvp);
    215 		return (ENOTBLK);
    216 	}
    217 
    218 	/* XXX - rdonly */
    219 	mode = FREAD;
    220 
    221 	/*
    222 	 * If mount by non-root, then verify that user has necessary
    223 	 * permissions on the device.
    224 	 */
    225 	err = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT,
    226 	    KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp, KAUTH_ARG(VREAD));
    227 	if (err) {
    228 		vput(devvp);
    229 		return (err);
    230 	}
    231 
    232 	if ((err = VOP_OPEN(devvp, mode, l->l_cred))) {
    233 		vput(devvp);
    234 		return (err);
    235 	}
    236 
    237 	err = efs_mount_common(mp, path, devvp, args);
    238 	if (err) {
    239 		VOP_CLOSE(devvp, mode, l->l_cred);
    240 		vput(devvp);
    241 		return (err);
    242 	}
    243 
    244 	VOP_UNLOCK(devvp);
    245 
    246 	return (0);
    247 }
    248 
    249 /*
    250  * Initialisation routine.
    251  *
    252  * Returns 0 on success.
    253  */
    254 static int
    255 efs_start(struct mount *mp, int flags)
    256 {
    257 
    258 	return (0);
    259 }
    260 
    261 /*
    262  * unmount syscall vfsop.
    263  *
    264  * Returns 0 on success.
    265  */
    266 static int
    267 efs_unmount(struct mount *mp, int mntflags)
    268 {
    269 	struct efs_mount *emp;
    270 	struct lwp *l = curlwp;
    271 	int err;
    272 
    273 	emp = VFSTOEFS(mp);
    274 
    275 	err = vflush(mp, NULL, (mntflags & MNT_FORCE) ? FORCECLOSE : 0);
    276 	if (err)
    277 		return (err);
    278 
    279 	cache_purgevfs(mp);
    280 
    281 	vn_lock(emp->em_devvp, LK_EXCLUSIVE | LK_RETRY);
    282         err = VOP_CLOSE(emp->em_devvp, FREAD, l->l_cred);
    283 	vput(emp->em_devvp);
    284 
    285 	free(mp->mnt_data, M_EFSMNT);
    286 	mp->mnt_data = NULL;
    287 	mp->mnt_flag &= ~MNT_LOCAL;
    288 
    289 	return (err);
    290 }
    291 
    292 /*
    293  * Return the root vnode.
    294  *
    295  * Returns 0 on success.
    296  */
    297 static int
    298 efs_root(struct mount *mp, int lktype, struct vnode **vpp)
    299 {
    300 	int err;
    301 	struct vnode *vp;
    302 
    303 	if ((err = VFS_VGET(mp, EFS_ROOTINO, lktype, &vp)))
    304 		return (err);
    305 
    306 	*vpp = vp;
    307 	return (0);
    308 }
    309 
    310 /*
    311  * statvfs syscall vfsop.
    312  *
    313  * Returns 0 on success.
    314  */
    315 static int
    316 efs_statvfs(struct mount *mp, struct statvfs *sbp)
    317 {
    318 	struct efs_mount *emp;
    319 
    320 	emp = VFSTOEFS(mp);
    321 	sbp->f_bsize	= EFS_BB_SIZE;
    322 	sbp->f_frsize	= EFS_BB_SIZE;
    323 	sbp->f_iosize	= EFS_BB_SIZE;
    324 	sbp->f_blocks	= be32toh(emp->em_sb.sb_size);
    325 	sbp->f_bfree	= be32toh(emp->em_sb.sb_tfree);
    326 	sbp->f_bavail	= sbp->f_bfree; // XXX same??
    327 	sbp->f_bresvd	= 0;
    328 	sbp->f_files	= be32toh(emp->em_sb.sb_tinode);
    329 	sbp->f_ffree	= be16toh(emp->em_sb.sb_cgisize) *
    330 			  be16toh(emp->em_sb.sb_ncg) *
    331 			  EFS_DINODES_PER_BB;
    332 	sbp->f_favail	= sbp->f_ffree; // XXX same??
    333 	sbp->f_fresvd	= 0;
    334 	sbp->f_namemax	= EFS_DIRENT_NAMELEN_MAX;
    335 	copy_statvfs_info(sbp, mp);
    336 
    337 	return (0);
    338 }
    339 
    340 /*
    341  * Obtain a locked vnode for the given on-disk inode number.
    342  *
    343  * Returns 0 on success.
    344  */
    345 static int
    346 efs_vget(struct mount *mp, ino_t ino, int lktype, struct vnode **vpp)
    347 {
    348 	int error;
    349 
    350 	error = vcache_get(mp, &ino, sizeof(ino), vpp);
    351 	if (error)
    352 		return error;
    353 	error = vn_lock(*vpp, lktype);
    354 	if (error) {
    355 		vrele(*vpp);
    356 		*vpp = NULL;
    357 		return error;
    358 	}
    359 	return 0;
    360 }
    361 
    362 /*
    363  * Initialize this vnode / inode pair.
    364  * Caller assures no other thread will try to load this inode.
    365  */
    366 static int
    367 efs_loadvnode(struct mount *mp, struct vnode *vp,
    368     const void *key, size_t key_len, const void **new_key)
    369 {
    370 	int error;
    371 	ino_t ino;
    372 	struct efs_inode *eip;
    373 	struct efs_mount *emp;
    374 
    375 	KASSERT(key_len == sizeof(ino));
    376 	memcpy(&ino, key, key_len);
    377 	emp = VFSTOEFS(mp);
    378 
    379 	eip = pool_get(&efs_inode_pool, PR_WAITOK);
    380 	eip->ei_mode = 0;
    381 	eip->ei_lockf = NULL;
    382 	eip->ei_number = ino;
    383 	eip->ei_dev = emp->em_dev;
    384 	eip->ei_vp = vp;
    385 
    386 	error = efs_read_inode(emp, ino, NULL, &eip->ei_di);
    387 	if (error) {
    388 		pool_put(&efs_inode_pool, eip);
    389 		return error;
    390 	}
    391 
    392 	efs_sync_dinode_to_inode(eip);
    393 
    394 	if (ino == EFS_ROOTINO && !S_ISDIR(eip->ei_mode)) {
    395 		printf("efs: root inode (%lu) is not a directory!\n",
    396 		    (ulong)EFS_ROOTINO);
    397 		pool_put(&efs_inode_pool, eip);
    398 		return EIO;
    399 	}
    400 
    401 	switch (eip->ei_mode & S_IFMT) {
    402 	case S_IFIFO:
    403 		vp->v_type = VFIFO;
    404 		vp->v_op = efs_fifoop_p;
    405 		break;
    406 	case S_IFCHR:
    407 		vp->v_type = VCHR;
    408 		vp->v_op = efs_specop_p;
    409 		spec_node_init(vp, eip->ei_dev);
    410 		break;
    411 	case S_IFDIR:
    412 		vp->v_type = VDIR;
    413 		vp->v_op = efs_vnodeop_p;
    414 		if (ino == EFS_ROOTINO)
    415 			vp->v_vflag |= VV_ROOT;
    416 		break;
    417 	case S_IFBLK:
    418 		vp->v_type = VBLK;
    419 		vp->v_op = efs_specop_p;
    420 		spec_node_init(vp, eip->ei_dev);
    421 		break;
    422 	case S_IFREG:
    423 		vp->v_type = VREG;
    424 		vp->v_op = efs_vnodeop_p;
    425 		break;
    426 	case S_IFLNK:
    427 		vp->v_type = VLNK;
    428 		vp->v_op = efs_vnodeop_p;
    429 		break;
    430 	case S_IFSOCK:
    431 		vp->v_type = VSOCK;
    432 		vp->v_op = efs_vnodeop_p;
    433 		break;
    434 	default:
    435 		printf("efs: invalid mode 0x%x in inode %lu on mount %s\n",
    436 		    eip->ei_mode, (ulong)ino, mp->mnt_stat.f_mntonname);
    437 		pool_put(&efs_inode_pool, eip);
    438 		return EIO;
    439 	}
    440 
    441 	vp->v_tag = VT_EFS;
    442 	vp->v_data = eip;
    443 	genfs_node_init(vp, &efs_genfsops);
    444 	uvm_vnp_setsize(vp, eip->ei_size);
    445 	*new_key = &eip->ei_number;
    446 	return 0;
    447 }
    448 
    449 /*
    450  * Convert the provided opaque, unique file handle into a vnode.
    451  *
    452  * Returns 0 on success.
    453  */
    454 static int
    455 efs_fhtovp(struct mount *mp, struct fid *fhp, int lktype, struct vnode **vpp)
    456 {
    457 	int err;
    458 	struct vnode *vp;
    459 	struct efs_fid *efp;
    460 	struct efs_inode *eip;
    461 
    462 	if (fhp->fid_len != sizeof(struct efs_fid))
    463 		return (EINVAL);
    464 
    465 	efp = (struct efs_fid *)fhp;
    466 
    467 	if ((err = VFS_VGET(mp, efp->ef_ino, lktype, &vp))) {
    468 		*vpp = NULL;
    469 		return (err);
    470 	}
    471 
    472 	eip = EFS_VTOI(vp);
    473 	if (eip->ei_mode == 0 || eip->ei_gen != efp->ef_gen) {
    474 		vput(vp);
    475 		*vpp = NULL;
    476 		return (ESTALE);
    477 	}
    478 
    479 	*vpp = vp;
    480 	return (0);
    481 }
    482 
    483 /*
    484  * Convert the provided vnode into an opaque, unique file handle.
    485  *
    486  * Returns 0 on success.
    487  */
    488 static int
    489 efs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size)
    490 {
    491 	struct efs_fid *efp;
    492 	struct efs_inode *eip;
    493 
    494 	if (*fh_size < sizeof(struct efs_fid)) {
    495 		*fh_size = sizeof(struct efs_fid);
    496 		return (E2BIG);
    497 	}
    498 	*fh_size = sizeof(struct efs_fid);
    499 
    500 	eip = EFS_VTOI(vp);
    501 	efp = (struct efs_fid *)fhp;
    502 
    503 	fhp->fid_len = sizeof(struct efs_fid);
    504 	efp->ef_ino = eip->ei_number;
    505 	efp->ef_gen = eip->ei_gen;
    506 
    507 	return (0);
    508 }
    509 
    510 /*
    511  * Globally initialise the filesystem.
    512  */
    513 static void
    514 efs_init(void)
    515 {
    516 
    517 	malloc_type_attach(M_EFSMNT);
    518 	malloc_type_attach(M_EFSINO);
    519 	malloc_type_attach(M_EFSTMP);
    520 	pool_init(&efs_inode_pool, sizeof(struct efs_inode), 0, 0, 0,
    521 	    "efsinopl", &pool_allocator_nointr, IPL_NONE);
    522 }
    523 
    524 /*
    525  * Globally reinitialise the filesystem.
    526  */
    527 static void
    528 efs_reinit(void)
    529 {
    530 
    531 }
    532 
    533 /*
    534  * Globally clean up the filesystem.
    535  */
    536 static void
    537 efs_done(void)
    538 {
    539 
    540 	pool_destroy(&efs_inode_pool);
    541 	malloc_type_detach(M_EFSMNT);
    542 	malloc_type_detach(M_EFSINO);
    543 	malloc_type_detach(M_EFSTMP);
    544 }
    545 
    546 extern const struct vnodeopv_desc efs_vnodeop_opv_desc;
    547 extern const struct vnodeopv_desc efs_specop_opv_desc;
    548 extern const struct vnodeopv_desc efs_fifoop_opv_desc;
    549 
    550 const struct vnodeopv_desc * const efs_vnodeopv_descs[] = {
    551 	&efs_vnodeop_opv_desc,
    552 	&efs_specop_opv_desc,
    553 	&efs_fifoop_opv_desc,
    554 	NULL
    555 };
    556 
    557 struct vfsops efs_vfsops = {
    558 	.vfs_name	= MOUNT_EFS,
    559 	.vfs_min_mount_data = sizeof (struct efs_args),
    560 	.vfs_mount	= efs_mount,
    561 	.vfs_start	= efs_start,
    562 	.vfs_unmount	= efs_unmount,
    563 	.vfs_root	= efs_root,
    564 	.vfs_quotactl	= (void *)eopnotsupp,
    565 	.vfs_statvfs	= efs_statvfs,
    566 	.vfs_sync	= (void *)nullop,
    567 	.vfs_vget	= efs_vget,
    568 	.vfs_loadvnode	= efs_loadvnode,
    569 	.vfs_fhtovp	= efs_fhtovp,
    570 	.vfs_vptofh	= efs_vptofh,
    571 	.vfs_init	= efs_init,
    572 	.vfs_reinit	= efs_reinit,
    573 	.vfs_done	= efs_done,
    574 	.vfs_mountroot	= (void *)eopnotsupp,
    575 	.vfs_snapshot	= (void *)eopnotsupp,
    576 	.vfs_extattrctl	= vfs_stdextattrctl,
    577 	.vfs_suspendctl = genfs_suspendctl,
    578 	.vfs_opv_descs	= efs_vnodeopv_descs
    579 /*	.vfs_refcount */
    580 /*	.vfs_list */
    581 };
    582 
    583 static int
    584 efs_modcmd(modcmd_t cmd, void *arg)
    585 {
    586 
    587 	switch (cmd) {
    588 	case MODULE_CMD_INIT:
    589 		return vfs_attach(&efs_vfsops);
    590 	case MODULE_CMD_FINI:
    591 		return vfs_detach(&efs_vfsops);
    592 	default:
    593 		return ENOTTY;
    594 	}
    595 }
    596