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