xref: /netbsd-src/sys/fs/efs/efs_vfsops.c (revision 9ea3b23dafd978027b28369eda07bf00fb169b23)
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@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
efs_mount_common(struct mount * mp,const char * path,struct vnode * devvp,struct efs_args * args)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
efs_mount(struct mount * mp,const char * path,void * data,size_t * data_len)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
efs_start(struct mount * mp,int flags)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
efs_unmount(struct mount * mp,int mntflags)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
efs_root(struct mount * mp,int lktype,struct vnode ** vpp)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
efs_statvfs(struct mount * mp,struct statvfs * sbp)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
efs_vget(struct mount * mp,ino_t ino,int lktype,struct vnode ** vpp)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
efs_loadvnode(struct mount * mp,struct vnode * vp,const void * key,size_t key_len,const void ** new_key)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
efs_fhtovp(struct mount * mp,struct fid * fhp,int lktype,struct vnode ** vpp)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
efs_vptofh(struct vnode * vp,struct fid * fhp,size_t * fh_size)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
efs_init(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
efs_reinit(void)528 efs_reinit(void)
529 {
530 
531 }
532 
533 /*
534  * Globally clean up the filesystem.
535  */
536 static void
efs_done(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
efs_modcmd(modcmd_t cmd,void * arg)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