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