1 /* $NetBSD: efs_vfsops.c,v 1.18 2009/04/20 21:29:01 elad Exp $ */
4 * Copyright (c) 2006 Stephen M. Rumble <rumble@ephemeral.org>
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.
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.
19 #include <sys/cdefs.h>
20 __KERNEL_RCSID(0, "$NetBSD: efs_vfsops.c,v 1.18 2009/04/20 21:29:01 elad Exp $");
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>
29 #include <sys/namei.h>
30 #include <sys/fcntl.h>
32 #include <sys/kauth.h>
34 #include <sys/module.h>
36 #include <miscfs/genfs/genfs_node.h>
37 #include <miscfs/genfs/genfs.h>
39 #include <miscfs/specfs/specdev.h>
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>
52 MODULE(MODULE_CLASS_VFS
, efs
, NULL
);
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");
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
*);
64 * efs_mount and efs_mountroot common functions.
67 efs_mount_common(struct mount
*mp
, const char *path
, struct vnode
*devvp
,
68 struct efs_args
*args
)
73 struct efs_mount
*emp
;
74 struct lwp
*l
= curlwp
;
76 emp
= malloc(sizeof(*emp
), M_EFSMNT
, M_WAITOK
);
77 emp
->em_dev
= devvp
->v_rdev
;
78 emp
->em_devvp
= devvp
;
81 /* read in the superblock */
82 err
= efs_bread(emp
, EFS_BB_SB
, l
, &bp
);
84 EFS_DPRINTF(("superblock read failed\n"));
89 memcpy(&emp
->em_sb
, bp
->b_data
, sizeof(emp
->em_sb
));
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
)) {
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?? */
108 /* if the superblock was replicated, verify that it is the same */
109 if (be32toh(emp
->em_sb
.sb_replsb
) != 0) {
113 err
= efs_bread(emp
, be32toh(emp
->em_sb
.sb_replsb
), l
, &rbp
);
115 printf("efs: read of superblock replicant failed; "
116 "please run fsck_efs(8)\n");
117 if (mp
->mnt_flag
& MNT_FORCE
) {
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
)) {
141 /* ensure we can read last block */
142 err
= efs_bread(emp
, be32toh(emp
->em_sb
.sb_size
) - 1, l
, &bp
);
144 printf("efs: cannot access all filesystem blocks; please run "
146 if (!(mp
->mnt_flag
& MNT_FORCE
)) {
155 mp
->mnt_flag
|= MNT_LOCAL
;
156 mp
->mnt_fs_bshift
= EFS_BB_SHFT
;
157 mp
->mnt_dev_bshift
= DEV_BSHIFT
;
159 efs_statvfs(mp
, &mp
->mnt_stat
);
161 err
= set_statvfs_info(path
, UIO_USERSPACE
, args
->fspec
,
162 UIO_USERSPACE
, mp
->mnt_op
->vfs_name
, mp
, l
);
170 * mount syscall vfsop.
172 * Returns 0 on success.
175 efs_mount(struct mount
*mp
, const char *path
, void *data
, size_t *data_len
)
177 struct lwp
*l
= curlwp
;
178 struct efs_args
*args
= data
;
179 struct nameidata devndp
;
180 struct efs_mount
*emp
;
184 if (*data_len
< sizeof *args
)
187 if (mp
->mnt_flag
& MNT_GETARGS
) {
188 if ((emp
= VFSTOEFS(mp
)) == NULL
)
191 args
->version
= EFS_MNT_VERSION
;
192 *data_len
= sizeof *args
;
196 if (mp
->mnt_flag
& MNT_UPDATE
)
197 return (EOPNOTSUPP
); /* XXX read-only */
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
)))
204 devvp
= devndp
.ni_vp
;
205 if (devvp
->v_type
!= VBLK
) {
214 * If mount by non-root, then verify that user has necessary
215 * permissions on the device.
217 err
= genfs_can_mount(devvp
, VREAD
, l
->l_cred
);
223 if ((err
= VOP_OPEN(devvp
, mode
, l
->l_cred
))) {
228 err
= efs_mount_common(mp
, path
, devvp
, args
);
230 VOP_CLOSE(devvp
, mode
, l
->l_cred
);
235 VOP_UNLOCK(devvp
, 0);
241 * Initialisation routine.
243 * Returns 0 on success.
246 efs_start(struct mount
*mp
, int flags
)
253 * unmount syscall vfsop.
255 * Returns 0 on success.
258 efs_unmount(struct mount
*mp
, int mntflags
)
260 struct efs_mount
*emp
;
261 struct lwp
*l
= curlwp
;
266 err
= vflush(mp
, NULL
, (mntflags
& MNT_FORCE
) ? FORCECLOSE
: 0);
272 vn_lock(emp
->em_devvp
, LK_EXCLUSIVE
| LK_RETRY
);
273 err
= VOP_CLOSE(emp
->em_devvp
, FREAD
, l
->l_cred
);
276 free(mp
->mnt_data
, M_EFSMNT
);
278 mp
->mnt_flag
&= ~MNT_LOCAL
;
284 * Return the root vnode.
286 * Returns 0 on success.
289 efs_root(struct mount
*mp
, struct vnode
**vpp
)
294 if ((err
= VFS_VGET(mp
, EFS_ROOTINO
, &vp
)))
302 * statvfs syscall vfsop.
304 * Returns 0 on success.
307 efs_statvfs(struct mount
*mp
, struct statvfs
*sbp
)
309 struct efs_mount
*emp
;
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??
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
) *
323 sbp
->f_favail
= sbp
->f_ffree
; // XXX same??
325 sbp
->f_namemax
= EFS_DIRENT_NAMELEN_MAX
;
326 copy_statvfs_info(sbp
, mp
);
332 * Obtain a locked vnode for the given on-disk inode number.
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.
338 * Returns 0 on success.
341 efs_vget(struct mount
*mp
, ino_t ino
, struct vnode
**vpp
)
345 struct efs_inode
*eip
;
346 struct efs_mount
*emp
;
351 *vpp
= efs_ihashget(emp
->em_dev
, ino
, LK_EXCLUSIVE
);
355 err
= getnewvnode(VT_EFS
, mp
, efs_vnodeop_p
, &vp
);
359 eip
= pool_get(&efs_inode_pool
, PR_WAITOK
);
362 * See if anybody has raced us here. If not, continue
363 * setting up the new inode, otherwise start over.
367 if (efs_ihashget(emp
->em_dev
, ino
, 0) == NULL
)
372 pool_put(&efs_inode_pool
, eip
);
375 vp
->v_vflag
|= VV_LOCKSWORK
;
377 eip
->ei_lockf
= NULL
;
378 eip
->ei_number
= ino
;
379 eip
->ei_dev
= emp
->em_dev
;
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.
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.
395 genfs_node_init(vp
, &efs_genfsops
);
397 err
= efs_read_inode(emp
, ino
, NULL
, &eip
->ei_di
);
404 efs_sync_dinode_to_inode(eip
);
406 if (ino
== EFS_ROOTINO
&& !S_ISDIR(eip
->ei_mode
)) {
407 printf("efs: root inode (%lu) is not a directory!\n",
414 switch (eip
->ei_mode
& S_IFMT
) {
417 vp
->v_op
= efs_fifoop_p
;
421 vp
->v_op
= efs_specop_p
;
422 spec_node_init(vp
, eip
->ei_dev
);
426 if (ino
== EFS_ROOTINO
)
427 vp
->v_vflag
|= VV_ROOT
;
431 vp
->v_op
= efs_specop_p
;
432 spec_node_init(vp
, eip
->ei_dev
);
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
);
451 uvm_vnp_setsize(vp
, eip
->ei_size
);
454 KASSERT(VOP_ISLOCKED(vp
));
460 * Convert the provided opaque, unique file handle into a vnode.
462 * Returns 0 on success.
465 efs_fhtovp(struct mount
*mp
, struct fid
*fhp
, struct vnode
**vpp
)
470 struct efs_inode
*eip
;
472 if (fhp
->fid_len
!= sizeof(struct efs_fid
))
475 efp
= (struct efs_fid
*)fhp
;
477 if ((err
= VFS_VGET(mp
, efp
->ef_ino
, &vp
))) {
483 if (eip
->ei_mode
== 0 || eip
->ei_gen
!= efp
->ef_gen
) {
494 * Convert the provided vnode into an opaque, unique file handle.
496 * Returns 0 on success.
499 efs_vptofh(struct vnode
*vp
, struct fid
*fhp
, size_t *fh_size
)
502 struct efs_inode
*eip
;
504 if (*fh_size
< sizeof(struct efs_fid
)) {
505 *fh_size
= sizeof(struct efs_fid
);
508 *fh_size
= sizeof(struct efs_fid
);
511 efp
= (struct efs_fid
*)fhp
;
513 fhp
->fid_len
= sizeof(struct efs_fid
);
514 efp
->ef_ino
= eip
->ei_number
;
515 efp
->ef_gen
= eip
->ei_gen
;
521 * Globally initialise the filesystem.
527 malloc_type_attach(M_EFSMNT
);
528 malloc_type_attach(M_EFSINO
);
529 malloc_type_attach(M_EFSTMP
);
531 pool_init(&efs_inode_pool
, sizeof(struct efs_inode
), 0, 0, 0,
532 "efsinopl", &pool_allocator_nointr
, IPL_NONE
);
536 * Globally reinitialise the filesystem.
546 * Globally clean up the filesystem.
552 pool_destroy(&efs_inode_pool
);
554 malloc_type_detach(M_EFSMNT
);
555 malloc_type_detach(M_EFSINO
);
556 malloc_type_detach(M_EFSTMP
);
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
;
563 const struct vnodeopv_desc
* const efs_vnodeopv_descs
[] = {
564 &efs_vnodeop_opv_desc
,
565 &efs_specop_opv_desc
,
566 &efs_fifoop_opv_desc
,
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
596 efs_modcmd(modcmd_t cmd
, void *arg
)
600 case MODULE_CMD_INIT
:
601 return vfs_attach(&efs_vfsops
);
602 case MODULE_CMD_FINI
:
603 return vfs_detach(&efs_vfsops
);