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