xref: /netbsd-src/sys/fs/efs/efs_vfsops.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: efs_vfsops.c,v 1.25 2014/04/16 18:55:18 maxv 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.25 2014/04/16 18:55:18 maxv 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 #include <fs/efs/efs_ihash.h>
51 
52 MODULE(MODULE_CLASS_VFS, efs, NULL);
53 
54 MALLOC_JUSTDEFINE(M_EFSMNT, "efsmnt", "efs mount structure");
55 MALLOC_JUSTDEFINE(M_EFSINO, "efsino", "efs in-core inode structure");
56 MALLOC_JUSTDEFINE(M_EFSTMP, "efstmp", "efs temporary allocations");
57 
58 extern int (**efs_vnodeop_p)(void *); 	/* for getnewvnode() */
59 extern int (**efs_specop_p)(void *); 	/* for getnewvnode() */
60 extern int (**efs_fifoop_p)(void *); 	/* for getnewvnode() */
61 static int efs_statvfs(struct mount *, struct statvfs *);
62 
63 /*
64  * efs_mount and efs_mountroot common functions.
65  */
66 static int
67 efs_mount_common(struct mount *mp, const char *path, struct vnode *devvp,
68     struct efs_args *args)
69 {
70 	int err;
71 	struct buf *bp;
72 	const char *why;
73 	struct efs_mount *emp;
74 	struct lwp *l = curlwp;
75 
76 	emp = malloc(sizeof(*emp), M_EFSMNT, M_WAITOK);
77 	emp->em_dev = devvp->v_rdev;
78 	emp->em_devvp = devvp;
79 	emp->em_mnt = mp;
80 
81 	/* read in the superblock */
82 	err = efs_bread(emp, EFS_BB_SB, l, &bp);
83 	if (err) {
84 		EFS_DPRINTF(("superblock read failed\n"));
85 		free(emp, M_EFSMNT);
86 		return (err);
87 	}
88 	memcpy(&emp->em_sb, bp->b_data, sizeof(emp->em_sb));
89 	brelse(bp, 0);
90 
91 	/* validate the superblock */
92 	if (efs_sb_validate(&emp->em_sb, &why)) {
93 		printf("efs: invalid superblock: %s\n", why);
94 		if (!(mp->mnt_flag & MNT_FORCE)) {
95 			free(emp, M_EFSMNT);
96 			return (EIO);
97 		}
98 	}
99 
100 	/* check that it's clean */
101 	if (be16toh(emp->em_sb.sb_dirty) != EFS_SB_CLEAN) {
102 		printf("efs: filesystem is dirty (sb_dirty = 0x%x); please "
103 		    "run fsck_efs(8)\n", be16toh(emp->em_sb.sb_dirty));
104 		/* XXX - default to readonly unless forced?? */
105 	}
106 
107 	/* if the superblock was replicated, verify that it is the same */
108 	if (be32toh(emp->em_sb.sb_replsb) != 0) {
109 		struct buf *rbp;
110 		bool skip = false;
111 
112 		err = efs_bread(emp, be32toh(emp->em_sb.sb_replsb), l, &rbp);
113 		if (err) {
114 			printf("efs: read of superblock replicant failed; "
115 			    "please run fsck_efs(8)\n");
116 			if (mp->mnt_flag & MNT_FORCE) {
117 				skip = true;
118 			} else {
119 				free(emp, M_EFSMNT);
120 				return (err);
121 			}
122 		}
123 
124 		if (!skip) {
125 			if (memcmp(rbp->b_data, &emp->em_sb,
126 			    sizeof(emp->em_sb))) {
127 				printf("efs: superblock differs from "
128 				    "replicant; please run fsck_efs(8)\n");
129 				if (!(mp->mnt_flag & MNT_FORCE)) {
130 					brelse(rbp, 0);
131 					free(emp, M_EFSMNT);
132 					return (EIO);
133 				}
134 			}
135 			brelse(rbp, 0);
136 		}
137 	}
138 
139 	/* ensure we can read last block */
140 	err = efs_bread(emp, be32toh(emp->em_sb.sb_size) - 1, l, &bp);
141 	if (err) {
142 		printf("efs: cannot access all filesystem blocks; please run "
143 		    "fsck_efs(8)\n");
144 		if (!(mp->mnt_flag & MNT_FORCE)) {
145 			free(emp, M_EFSMNT);
146 			return (err);
147 		}
148 	} else {
149 		brelse(bp, 0);
150 	}
151 
152 	mp->mnt_data = emp;
153 	mp->mnt_flag |= MNT_LOCAL;
154 	mp->mnt_fs_bshift = EFS_BB_SHFT;
155 	mp->mnt_dev_bshift = DEV_BSHIFT;
156 	vfs_getnewfsid(mp);
157 	efs_statvfs(mp, &mp->mnt_stat);
158 
159 	err = set_statvfs_info(path, UIO_USERSPACE, args->fspec,
160 	    UIO_USERSPACE, mp->mnt_op->vfs_name, mp, l);
161 	if (err)
162 		free(emp, M_EFSMNT);
163 
164 	return (err);
165 }
166 
167 /*
168  * mount syscall vfsop.
169  *
170  * Returns 0 on success.
171  */
172 static int
173 efs_mount(struct mount *mp, const char *path, void *data, size_t *data_len)
174 {
175 	struct lwp *l = curlwp;
176 	struct efs_args *args = data;
177 	struct pathbuf *pb;
178 	struct nameidata devnd;
179 	struct efs_mount *emp;
180 	struct vnode *devvp;
181 	int err, mode;
182 
183 	if (args == NULL)
184 		return EINVAL;
185 	if (*data_len < sizeof *args)
186 		return EINVAL;
187 
188 	if (mp->mnt_flag & MNT_GETARGS) {
189 		if ((emp = VFSTOEFS(mp)) == NULL)
190 			return (EIO);
191 		args->fspec = NULL;
192 		args->version = EFS_MNT_VERSION;
193 		*data_len = sizeof *args;
194 		return 0;
195 	}
196 
197 	if (mp->mnt_flag & MNT_UPDATE)
198 		return (EOPNOTSUPP);	/* XXX read-only */
199 
200 	/* look up our device's vnode. it is returned locked */
201 	err = pathbuf_copyin(args->fspec, &pb);
202 	if (err) {
203 		return err;
204 	}
205 	NDINIT(&devnd, LOOKUP, FOLLOW | LOCKLEAF, pb);
206 	if ((err = namei(&devnd))) {
207 		pathbuf_destroy(pb);
208 		return (err);
209 	}
210 
211 	devvp = devnd.ni_vp;
212 	pathbuf_destroy(pb);
213 
214 	if (devvp->v_type != VBLK) {
215 		vput(devvp);
216 		return (ENOTBLK);
217 	}
218 
219 	/* XXX - rdonly */
220 	mode = FREAD;
221 
222 	/*
223 	 * If mount by non-root, then verify that user has necessary
224 	 * permissions on the device.
225 	 */
226 	err = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT,
227 	    KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp, KAUTH_ARG(VREAD));
228 	if (err) {
229 		vput(devvp);
230 		return (err);
231 	}
232 
233 	if ((err = VOP_OPEN(devvp, mode, l->l_cred))) {
234 		vput(devvp);
235 		return (err);
236 	}
237 
238 	err = efs_mount_common(mp, path, devvp, args);
239 	if (err) {
240 		VOP_CLOSE(devvp, mode, l->l_cred);
241 		vput(devvp);
242 		return (err);
243 	}
244 
245 	VOP_UNLOCK(devvp);
246 
247 	return (0);
248 }
249 
250 /*
251  * Initialisation routine.
252  *
253  * Returns 0 on success.
254  */
255 static int
256 efs_start(struct mount *mp, int flags)
257 {
258 
259 	return (0);
260 }
261 
262 /*
263  * unmount syscall vfsop.
264  *
265  * Returns 0 on success.
266  */
267 static int
268 efs_unmount(struct mount *mp, int mntflags)
269 {
270 	struct efs_mount *emp;
271 	struct lwp *l = curlwp;
272 	int err;
273 
274 	emp = VFSTOEFS(mp);
275 
276 	err = vflush(mp, NULL, (mntflags & MNT_FORCE) ? FORCECLOSE : 0);
277 	if (err)
278 		return (err);
279 
280 	cache_purgevfs(mp);
281 
282 	vn_lock(emp->em_devvp, LK_EXCLUSIVE | LK_RETRY);
283         err = VOP_CLOSE(emp->em_devvp, FREAD, l->l_cred);
284 	vput(emp->em_devvp);
285 
286 	free(mp->mnt_data, M_EFSMNT);
287 	mp->mnt_data = NULL;
288 	mp->mnt_flag &= ~MNT_LOCAL;
289 
290 	return (err);
291 }
292 
293 /*
294  * Return the root vnode.
295  *
296  * Returns 0 on success.
297  */
298 static int
299 efs_root(struct mount *mp, struct vnode **vpp)
300 {
301 	int err;
302 	struct vnode *vp;
303 
304 	if ((err = VFS_VGET(mp, EFS_ROOTINO, &vp)))
305 		return (err);
306 
307 	*vpp = vp;
308 	return (0);
309 }
310 
311 /*
312  * statvfs syscall vfsop.
313  *
314  * Returns 0 on success.
315  */
316 static int
317 efs_statvfs(struct mount *mp, struct statvfs *sbp)
318 {
319 	struct efs_mount *emp;
320 
321 	emp = VFSTOEFS(mp);
322 	sbp->f_bsize	= EFS_BB_SIZE;
323 	sbp->f_frsize	= EFS_BB_SIZE;
324 	sbp->f_iosize	= EFS_BB_SIZE;
325 	sbp->f_blocks	= be32toh(emp->em_sb.sb_size);
326 	sbp->f_bfree	= be32toh(emp->em_sb.sb_tfree);
327 	sbp->f_bavail	= sbp->f_bfree; // XXX same??
328 	sbp->f_bresvd	= 0;
329 	sbp->f_files	= be32toh(emp->em_sb.sb_tinode);
330 	sbp->f_ffree	= be16toh(emp->em_sb.sb_cgisize) *
331 			  be16toh(emp->em_sb.sb_ncg) *
332 			  EFS_DINODES_PER_BB;
333 	sbp->f_favail	= sbp->f_ffree; // XXX same??
334 	sbp->f_fresvd	= 0;
335 	sbp->f_namemax	= EFS_DIRENT_NAMELEN_MAX;
336 	copy_statvfs_info(sbp, mp);
337 
338 	return (0);
339 }
340 
341 /*
342  * Obtain a locked vnode for the given on-disk inode number.
343  *
344  * We currently allocate a new vnode from getnewnode(), tack it with
345  * our in-core inode structure (efs_inode), and read in the inode from
346  * disk. The returned inode must be locked.
347  *
348  * Returns 0 on success.
349  */
350 static int
351 efs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
352 {
353 	int err;
354 	struct vnode *vp;
355 	struct efs_inode *eip;
356 	struct efs_mount *emp;
357 
358 	emp = VFSTOEFS(mp);
359 
360 	while (true) {
361 		*vpp = efs_ihashget(emp->em_dev, ino, LK_EXCLUSIVE);
362 		if (*vpp != NULL)
363 			return (0);
364 
365 		err = getnewvnode(VT_EFS, mp, efs_vnodeop_p, NULL, &vp);
366 		if (err)
367 			return (err);
368 
369 		eip = pool_get(&efs_inode_pool, PR_WAITOK);
370 
371 		/*
372 		 * See if anybody has raced us here.  If not, continue
373 		 * setting up the new inode, otherwise start over.
374 		 */
375 		efs_ihashlock();
376 
377 		if (efs_ihashget(emp->em_dev, ino, 0) == NULL)
378 			break;
379 
380 		efs_ihashunlock();
381 		ungetnewvnode(vp);
382 		pool_put(&efs_inode_pool, eip);
383 	}
384 
385 	vp->v_vflag |= VV_LOCKSWORK;
386 	eip->ei_mode = 0;
387 	eip->ei_lockf = NULL;
388 	eip->ei_number = ino;
389 	eip->ei_dev = emp->em_dev;
390 	eip->ei_vp = vp;
391 	vp->v_data = eip;
392 
393 	/*
394 	 * Place the vnode on the hash chain. Doing so will lock the
395 	 * vnode, so it's okay to drop the global lock and read in
396 	 * the inode from disk.
397 	 */
398 	efs_ihashins(eip);
399 	efs_ihashunlock();
400 
401 	/*
402 	 * Init genfs early, otherwise we'll trip up on genfs_node_destroy
403 	 * in efs_reclaim when vput()ing in an error branch here.
404 	 */
405 	genfs_node_init(vp, &efs_genfsops);
406 
407 	err = efs_read_inode(emp, ino, NULL, &eip->ei_di);
408 	if (err) {
409 		vput(vp);
410 		*vpp = NULL;
411 		return (err);
412 	}
413 
414 	efs_sync_dinode_to_inode(eip);
415 
416 	if (ino == EFS_ROOTINO && !S_ISDIR(eip->ei_mode)) {
417 		printf("efs: root inode (%lu) is not a directory!\n",
418 		    (ulong)EFS_ROOTINO);
419 		vput(vp);
420 		*vpp = NULL;
421 		return (EIO);
422 	}
423 
424 	switch (eip->ei_mode & S_IFMT) {
425 	case S_IFIFO:
426 		vp->v_type = VFIFO;
427 		vp->v_op = efs_fifoop_p;
428 		break;
429 	case S_IFCHR:
430 		vp->v_type = VCHR;
431 		vp->v_op = efs_specop_p;
432 		spec_node_init(vp, eip->ei_dev);
433 		break;
434 	case S_IFDIR:
435 		vp->v_type = VDIR;
436 		if (ino == EFS_ROOTINO)
437 			vp->v_vflag |= VV_ROOT;
438 		break;
439 	case S_IFBLK:
440 		vp->v_type = VBLK;
441 		vp->v_op = efs_specop_p;
442 		spec_node_init(vp, eip->ei_dev);
443 		break;
444 	case S_IFREG:
445 		vp->v_type = VREG;
446 		break;
447 	case S_IFLNK:
448 		vp->v_type = VLNK;
449 		break;
450 	case S_IFSOCK:
451 		vp->v_type = VSOCK;
452 		break;
453 	default:
454 		printf("efs: invalid mode 0x%x in inode %lu on mount %s\n",
455 		    eip->ei_mode, (ulong)ino, mp->mnt_stat.f_mntonname);
456 		vput(vp);
457 		*vpp = NULL;
458 		return (EIO);
459 	}
460 
461 	uvm_vnp_setsize(vp, eip->ei_size);
462 	*vpp = vp;
463 
464 	KASSERT(VOP_ISLOCKED(vp));
465 
466 	return (0);
467 }
468 
469 /*
470  * Convert the provided opaque, unique file handle into a vnode.
471  *
472  * Returns 0 on success.
473  */
474 static int
475 efs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
476 {
477 	int err;
478 	struct vnode *vp;
479 	struct efs_fid *efp;
480 	struct efs_inode *eip;
481 
482 	if (fhp->fid_len != sizeof(struct efs_fid))
483 		return (EINVAL);
484 
485 	efp = (struct efs_fid *)fhp;
486 
487 	if ((err = VFS_VGET(mp, efp->ef_ino, &vp))) {
488 		*vpp = NULL;
489 		return (err);
490 	}
491 
492 	eip = EFS_VTOI(vp);
493 	if (eip->ei_mode == 0 || eip->ei_gen != efp->ef_gen) {
494 		vput(vp);
495 		*vpp = NULL;
496 		return (ESTALE);
497 	}
498 
499 	*vpp = vp;
500 	return (0);
501 }
502 
503 /*
504  * Convert the provided vnode into an opaque, unique file handle.
505  *
506  * Returns 0 on success.
507  */
508 static int
509 efs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size)
510 {
511 	struct efs_fid *efp;
512 	struct efs_inode *eip;
513 
514 	if (*fh_size < sizeof(struct efs_fid)) {
515 		*fh_size = sizeof(struct efs_fid);
516 		return (E2BIG);
517 	}
518 	*fh_size = sizeof(struct efs_fid);
519 
520 	eip = EFS_VTOI(vp);
521 	efp = (struct efs_fid *)fhp;
522 
523 	fhp->fid_len = sizeof(struct efs_fid);
524 	efp->ef_ino = eip->ei_number;
525 	efp->ef_gen = eip->ei_gen;
526 
527 	return (0);
528 }
529 
530 /*
531  * Globally initialise the filesystem.
532  */
533 static void
534 efs_init(void)
535 {
536 
537 	malloc_type_attach(M_EFSMNT);
538 	malloc_type_attach(M_EFSINO);
539 	malloc_type_attach(M_EFSTMP);
540 	efs_ihashinit();
541 	pool_init(&efs_inode_pool, sizeof(struct efs_inode), 0, 0, 0,
542 	    "efsinopl", &pool_allocator_nointr, IPL_NONE);
543 }
544 
545 /*
546  * Globally reinitialise the filesystem.
547  */
548 static void
549 efs_reinit(void)
550 {
551 
552 	efs_ihashreinit();
553 }
554 
555 /*
556  * Globally clean up the filesystem.
557  */
558 static void
559 efs_done(void)
560 {
561 
562 	pool_destroy(&efs_inode_pool);
563 	efs_ihashdone();
564 	malloc_type_detach(M_EFSMNT);
565 	malloc_type_detach(M_EFSINO);
566 	malloc_type_detach(M_EFSTMP);
567 }
568 
569 extern const struct vnodeopv_desc efs_vnodeop_opv_desc;
570 extern const struct vnodeopv_desc efs_specop_opv_desc;
571 extern const struct vnodeopv_desc efs_fifoop_opv_desc;
572 
573 const struct vnodeopv_desc * const efs_vnodeopv_descs[] = {
574 	&efs_vnodeop_opv_desc,
575 	&efs_specop_opv_desc,
576 	&efs_fifoop_opv_desc,
577 	NULL
578 };
579 
580 struct vfsops efs_vfsops = {
581 	.vfs_name	= MOUNT_EFS,
582 	.vfs_min_mount_data = sizeof (struct efs_args),
583 	.vfs_mount	= efs_mount,
584 	.vfs_start	= efs_start,
585 	.vfs_unmount	= efs_unmount,
586 	.vfs_root	= efs_root,
587 	.vfs_quotactl	= (void *)eopnotsupp,
588 	.vfs_statvfs	= efs_statvfs,
589 	.vfs_sync	= (void *)nullop,
590 	.vfs_vget	= efs_vget,
591 	.vfs_fhtovp	= efs_fhtovp,
592 	.vfs_vptofh	= efs_vptofh,
593 	.vfs_init	= efs_init,
594 	.vfs_reinit	= efs_reinit,
595 	.vfs_done	= efs_done,
596 	.vfs_mountroot	= (void *)eopnotsupp,
597 	.vfs_snapshot	= (void *)eopnotsupp,
598 	.vfs_extattrctl	= vfs_stdextattrctl,
599 	.vfs_suspendctl = (void *)eopnotsupp,
600 	.vfs_opv_descs	= efs_vnodeopv_descs
601 /*	.vfs_refcount */
602 /*	.vfs_list */
603 };
604 
605 static int
606 efs_modcmd(modcmd_t cmd, void *arg)
607 {
608 
609 	switch (cmd) {
610 	case MODULE_CMD_INIT:
611 		return vfs_attach(&efs_vfsops);
612 	case MODULE_CMD_FINI:
613 		return vfs_detach(&efs_vfsops);
614 	default:
615 		return ENOTTY;
616 	}
617 }
618