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