No empty .Rs/.Re
[netbsd-mini2440.git] / sys / fs / efs / efs_vfsops.c
blobbb95b0263e10950d457ae78f5f8613a83e5bb635
1 /* $NetBSD: efs_vfsops.c,v 1.18 2009/04/20 21:29:01 elad Exp $ */
3 /*
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>
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>
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.
66 static int
67 efs_mount_common(struct mount *mp, const char *path, struct vnode *devvp,
68 struct efs_args *args)
70 int err;
71 struct buf *bp;
72 const char *why;
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;
79 emp->em_mnt = mp;
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);
89 memcpy(&emp->em_sb, bp->b_data, sizeof(emp->em_sb));
90 brelse(bp, 0);
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);
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) {
110 struct buf *rbp;
111 bool skip = false;
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);
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);
138 brelse(rbp, 0);
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);
152 brelse(bp, 0);
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);
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);
166 return (err);
170 * mount syscall vfsop.
172 * Returns 0 on success.
174 static int
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;
181 struct vnode *devvp;
182 int err, mode;
184 if (*data_len < sizeof *args)
185 return EINVAL;
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;
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)))
202 return (err);
204 devvp = devndp.ni_vp;
205 if (devvp->v_type != VBLK) {
206 vput(devvp);
207 return (ENOTBLK);
210 /* XXX - rdonly */
211 mode = FREAD;
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);
218 if (err) {
219 vput(devvp);
220 return (err);
223 if ((err = VOP_OPEN(devvp, mode, l->l_cred))) {
224 vput(devvp);
225 return (err);
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);
235 VOP_UNLOCK(devvp, 0);
237 return (0);
241 * Initialisation routine.
243 * Returns 0 on success.
245 static int
246 efs_start(struct mount *mp, int flags)
249 return (0);
253 * unmount syscall vfsop.
255 * Returns 0 on success.
257 static int
258 efs_unmount(struct mount *mp, int mntflags)
260 struct efs_mount *emp;
261 struct lwp *l = curlwp;
262 int err;
264 emp = VFSTOEFS(mp);
266 err = vflush(mp, NULL, (mntflags & MNT_FORCE) ? FORCECLOSE : 0);
267 if (err)
268 return (err);
270 cache_purgevfs(mp);
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);
276 free(mp->mnt_data, M_EFSMNT);
277 mp->mnt_data = NULL;
278 mp->mnt_flag &= ~MNT_LOCAL;
280 return (err);
284 * Return the root vnode.
286 * Returns 0 on success.
288 static int
289 efs_root(struct mount *mp, struct vnode **vpp)
291 int err;
292 struct vnode *vp;
294 if ((err = VFS_VGET(mp, EFS_ROOTINO, &vp)))
295 return (err);
297 *vpp = vp;
298 return (0);
302 * statvfs syscall vfsop.
304 * Returns 0 on success.
306 static int
307 efs_statvfs(struct mount *mp, struct statvfs *sbp)
309 struct efs_mount *emp;
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);
328 return (0);
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.
340 static int
341 efs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
343 int err;
344 struct vnode *vp;
345 struct efs_inode *eip;
346 struct efs_mount *emp;
348 emp = VFSTOEFS(mp);
350 while (true) {
351 *vpp = efs_ihashget(emp->em_dev, ino, LK_EXCLUSIVE);
352 if (*vpp != NULL)
353 return (0);
355 err = getnewvnode(VT_EFS, mp, efs_vnodeop_p, &vp);
356 if (err)
357 return (err);
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.
365 efs_ihashlock();
367 if (efs_ihashget(emp->em_dev, ino, 0) == NULL)
368 break;
370 efs_ihashunlock();
371 ungetnewvnode(vp);
372 pool_put(&efs_inode_pool, eip);
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;
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.
388 efs_ihashins(eip);
389 efs_ihashunlock();
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);
398 if (err) {
399 vput(vp);
400 *vpp = NULL;
401 return (err);
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",
408 (ulong)EFS_ROOTINO);
409 vput(vp);
410 *vpp = NULL;
411 return (EIO);
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);
451 uvm_vnp_setsize(vp, eip->ei_size);
452 *vpp = vp;
454 KASSERT(VOP_ISLOCKED(vp));
456 return (0);
460 * Convert the provided opaque, unique file handle into a vnode.
462 * Returns 0 on success.
464 static int
465 efs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
467 int err;
468 struct vnode *vp;
469 struct efs_fid *efp;
470 struct efs_inode *eip;
472 if (fhp->fid_len != sizeof(struct efs_fid))
473 return (EINVAL);
475 efp = (struct efs_fid *)fhp;
477 if ((err = VFS_VGET(mp, efp->ef_ino, &vp))) {
478 *vpp = NULL;
479 return (err);
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);
489 *vpp = vp;
490 return (0);
494 * Convert the provided vnode into an opaque, unique file handle.
496 * Returns 0 on success.
498 static int
499 efs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size)
501 struct efs_fid *efp;
502 struct efs_inode *eip;
504 if (*fh_size < sizeof(struct efs_fid)) {
505 *fh_size = sizeof(struct efs_fid);
506 return (E2BIG);
508 *fh_size = sizeof(struct efs_fid);
510 eip = EFS_VTOI(vp);
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;
517 return (0);
521 * Globally initialise the filesystem.
523 static void
524 efs_init(void)
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);
536 * Globally reinitialise the filesystem.
538 static void
539 efs_reinit(void)
542 efs_ihashreinit();
546 * Globally clean up the filesystem.
548 static void
549 efs_done(void)
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);
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,
567 NULL
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 */
595 static int
596 efs_modcmd(modcmd_t cmd, void *arg)
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;