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