1 /* $NetBSD: efs_vfsops.c,v 1.21 2010/11/19 06:44:41 dholland 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.21 2010/11/19 06:44:41 dholland 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 pathbuf *pb; 180 struct nameidata devnd; 181 struct efs_mount *emp; 182 struct vnode *devvp; 183 int err, mode; 184 185 if (*data_len < sizeof *args) 186 return EINVAL; 187 188 if (mp->mnt_flag & MNT_GETARGS) { 189 if ((emp = VFSTOEFS(mp)) == NULL) 190 return (EIO); 191 args->fspec = NULL; 192 args->version = EFS_MNT_VERSION; 193 *data_len = sizeof *args; 194 return 0; 195 } 196 197 if (mp->mnt_flag & MNT_UPDATE) 198 return (EOPNOTSUPP); /* XXX read-only */ 199 200 /* look up our device's vnode. it is returned locked */ 201 err = pathbuf_copyin(args->fspec, &pb); 202 if (err) { 203 return err; 204 } 205 NDINIT(&devnd, LOOKUP, FOLLOW | LOCKLEAF, pb); 206 if ((err = namei(&devnd))) { 207 pathbuf_destroy(pb); 208 return (err); 209 } 210 211 devvp = devnd.ni_vp; 212 pathbuf_destroy(pb); 213 214 if (devvp->v_type != VBLK) { 215 vput(devvp); 216 return (ENOTBLK); 217 } 218 219 /* XXX - rdonly */ 220 mode = FREAD; 221 222 /* 223 * If mount by non-root, then verify that user has necessary 224 * permissions on the device. 225 */ 226 err = genfs_can_mount(devvp, VREAD, l->l_cred); 227 if (err) { 228 vput(devvp); 229 return (err); 230 } 231 232 if ((err = VOP_OPEN(devvp, mode, l->l_cred))) { 233 vput(devvp); 234 return (err); 235 } 236 237 err = efs_mount_common(mp, path, devvp, args); 238 if (err) { 239 VOP_CLOSE(devvp, mode, l->l_cred); 240 vput(devvp); 241 return (err); 242 } 243 244 VOP_UNLOCK(devvp); 245 246 return (0); 247 } 248 249 /* 250 * Initialisation routine. 251 * 252 * Returns 0 on success. 253 */ 254 static int 255 efs_start(struct mount *mp, int flags) 256 { 257 258 return (0); 259 } 260 261 /* 262 * unmount syscall vfsop. 263 * 264 * Returns 0 on success. 265 */ 266 static int 267 efs_unmount(struct mount *mp, int mntflags) 268 { 269 struct efs_mount *emp; 270 struct lwp *l = curlwp; 271 int err; 272 273 emp = VFSTOEFS(mp); 274 275 err = vflush(mp, NULL, (mntflags & MNT_FORCE) ? FORCECLOSE : 0); 276 if (err) 277 return (err); 278 279 cache_purgevfs(mp); 280 281 vn_lock(emp->em_devvp, LK_EXCLUSIVE | LK_RETRY); 282 err = VOP_CLOSE(emp->em_devvp, FREAD, l->l_cred); 283 vput(emp->em_devvp); 284 285 free(mp->mnt_data, M_EFSMNT); 286 mp->mnt_data = NULL; 287 mp->mnt_flag &= ~MNT_LOCAL; 288 289 return (err); 290 } 291 292 /* 293 * Return the root vnode. 294 * 295 * Returns 0 on success. 296 */ 297 static int 298 efs_root(struct mount *mp, struct vnode **vpp) 299 { 300 int err; 301 struct vnode *vp; 302 303 if ((err = VFS_VGET(mp, EFS_ROOTINO, &vp))) 304 return (err); 305 306 *vpp = vp; 307 return (0); 308 } 309 310 /* 311 * statvfs syscall vfsop. 312 * 313 * Returns 0 on success. 314 */ 315 static int 316 efs_statvfs(struct mount *mp, struct statvfs *sbp) 317 { 318 struct efs_mount *emp; 319 320 emp = VFSTOEFS(mp); 321 sbp->f_bsize = EFS_BB_SIZE; 322 sbp->f_frsize = EFS_BB_SIZE; 323 sbp->f_iosize = EFS_BB_SIZE; 324 sbp->f_blocks = be32toh(emp->em_sb.sb_size); 325 sbp->f_bfree = be32toh(emp->em_sb.sb_tfree); 326 sbp->f_bavail = sbp->f_bfree; // XXX same?? 327 sbp->f_bresvd = 0; 328 sbp->f_files = be32toh(emp->em_sb.sb_tinode); 329 sbp->f_ffree = be16toh(emp->em_sb.sb_cgisize) * 330 be16toh(emp->em_sb.sb_ncg) * 331 EFS_DINODES_PER_BB; 332 sbp->f_favail = sbp->f_ffree; // XXX same?? 333 sbp->f_fresvd = 0; 334 sbp->f_namemax = EFS_DIRENT_NAMELEN_MAX; 335 copy_statvfs_info(sbp, mp); 336 337 return (0); 338 } 339 340 /* 341 * Obtain a locked vnode for the given on-disk inode number. 342 * 343 * We currently allocate a new vnode from getnewnode(), tack it with 344 * our in-core inode structure (efs_inode), and read in the inode from 345 * disk. The returned inode must be locked. 346 * 347 * Returns 0 on success. 348 */ 349 static int 350 efs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) 351 { 352 int err; 353 struct vnode *vp; 354 struct efs_inode *eip; 355 struct efs_mount *emp; 356 357 emp = VFSTOEFS(mp); 358 359 while (true) { 360 *vpp = efs_ihashget(emp->em_dev, ino, LK_EXCLUSIVE); 361 if (*vpp != NULL) 362 return (0); 363 364 err = getnewvnode(VT_EFS, mp, efs_vnodeop_p, &vp); 365 if (err) 366 return (err); 367 368 eip = pool_get(&efs_inode_pool, PR_WAITOK); 369 370 /* 371 * See if anybody has raced us here. If not, continue 372 * setting up the new inode, otherwise start over. 373 */ 374 efs_ihashlock(); 375 376 if (efs_ihashget(emp->em_dev, ino, 0) == NULL) 377 break; 378 379 efs_ihashunlock(); 380 ungetnewvnode(vp); 381 pool_put(&efs_inode_pool, eip); 382 } 383 384 vp->v_vflag |= VV_LOCKSWORK; 385 eip->ei_mode = 0; 386 eip->ei_lockf = NULL; 387 eip->ei_number = ino; 388 eip->ei_dev = emp->em_dev; 389 eip->ei_vp = vp; 390 vp->v_data = eip; 391 392 /* 393 * Place the vnode on the hash chain. Doing so will lock the 394 * vnode, so it's okay to drop the global lock and read in 395 * the inode from disk. 396 */ 397 efs_ihashins(eip); 398 efs_ihashunlock(); 399 400 /* 401 * Init genfs early, otherwise we'll trip up on genfs_node_destroy 402 * in efs_reclaim when vput()ing in an error branch here. 403 */ 404 genfs_node_init(vp, &efs_genfsops); 405 406 err = efs_read_inode(emp, ino, NULL, &eip->ei_di); 407 if (err) { 408 vput(vp); 409 *vpp = NULL; 410 return (err); 411 } 412 413 efs_sync_dinode_to_inode(eip); 414 415 if (ino == EFS_ROOTINO && !S_ISDIR(eip->ei_mode)) { 416 printf("efs: root inode (%lu) is not a directory!\n", 417 (ulong)EFS_ROOTINO); 418 vput(vp); 419 *vpp = NULL; 420 return (EIO); 421 } 422 423 switch (eip->ei_mode & S_IFMT) { 424 case S_IFIFO: 425 vp->v_type = VFIFO; 426 vp->v_op = efs_fifoop_p; 427 break; 428 case S_IFCHR: 429 vp->v_type = VCHR; 430 vp->v_op = efs_specop_p; 431 spec_node_init(vp, eip->ei_dev); 432 break; 433 case S_IFDIR: 434 vp->v_type = VDIR; 435 if (ino == EFS_ROOTINO) 436 vp->v_vflag |= VV_ROOT; 437 break; 438 case S_IFBLK: 439 vp->v_type = VBLK; 440 vp->v_op = efs_specop_p; 441 spec_node_init(vp, eip->ei_dev); 442 break; 443 case S_IFREG: 444 vp->v_type = VREG; 445 break; 446 case S_IFLNK: 447 vp->v_type = VLNK; 448 break; 449 case S_IFSOCK: 450 vp->v_type = VSOCK; 451 break; 452 default: 453 printf("efs: invalid mode 0x%x in inode %lu on mount %s\n", 454 eip->ei_mode, (ulong)ino, mp->mnt_stat.f_mntonname); 455 vput(vp); 456 *vpp = NULL; 457 return (EIO); 458 } 459 460 uvm_vnp_setsize(vp, eip->ei_size); 461 *vpp = vp; 462 463 KASSERT(VOP_ISLOCKED(vp)); 464 465 return (0); 466 } 467 468 /* 469 * Convert the provided opaque, unique file handle into a vnode. 470 * 471 * Returns 0 on success. 472 */ 473 static int 474 efs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) 475 { 476 int err; 477 struct vnode *vp; 478 struct efs_fid *efp; 479 struct efs_inode *eip; 480 481 if (fhp->fid_len != sizeof(struct efs_fid)) 482 return (EINVAL); 483 484 efp = (struct efs_fid *)fhp; 485 486 if ((err = VFS_VGET(mp, efp->ef_ino, &vp))) { 487 *vpp = NULL; 488 return (err); 489 } 490 491 eip = EFS_VTOI(vp); 492 if (eip->ei_mode == 0 || eip->ei_gen != efp->ef_gen) { 493 vput(vp); 494 *vpp = NULL; 495 return (ESTALE); 496 } 497 498 *vpp = vp; 499 return (0); 500 } 501 502 /* 503 * Convert the provided vnode into an opaque, unique file handle. 504 * 505 * Returns 0 on success. 506 */ 507 static int 508 efs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size) 509 { 510 struct efs_fid *efp; 511 struct efs_inode *eip; 512 513 if (*fh_size < sizeof(struct efs_fid)) { 514 *fh_size = sizeof(struct efs_fid); 515 return (E2BIG); 516 } 517 *fh_size = sizeof(struct efs_fid); 518 519 eip = EFS_VTOI(vp); 520 efp = (struct efs_fid *)fhp; 521 522 fhp->fid_len = sizeof(struct efs_fid); 523 efp->ef_ino = eip->ei_number; 524 efp->ef_gen = eip->ei_gen; 525 526 return (0); 527 } 528 529 /* 530 * Globally initialise the filesystem. 531 */ 532 static void 533 efs_init(void) 534 { 535 536 malloc_type_attach(M_EFSMNT); 537 malloc_type_attach(M_EFSINO); 538 malloc_type_attach(M_EFSTMP); 539 efs_ihashinit(); 540 pool_init(&efs_inode_pool, sizeof(struct efs_inode), 0, 0, 0, 541 "efsinopl", &pool_allocator_nointr, IPL_NONE); 542 } 543 544 /* 545 * Globally reinitialise the filesystem. 546 */ 547 static void 548 efs_reinit(void) 549 { 550 551 efs_ihashreinit(); 552 } 553 554 /* 555 * Globally clean up the filesystem. 556 */ 557 static void 558 efs_done(void) 559 { 560 561 pool_destroy(&efs_inode_pool); 562 efs_ihashdone(); 563 malloc_type_detach(M_EFSMNT); 564 malloc_type_detach(M_EFSINO); 565 malloc_type_detach(M_EFSTMP); 566 } 567 568 extern const struct vnodeopv_desc efs_vnodeop_opv_desc; 569 extern const struct vnodeopv_desc efs_specop_opv_desc; 570 extern const struct vnodeopv_desc efs_fifoop_opv_desc; 571 572 const struct vnodeopv_desc * const efs_vnodeopv_descs[] = { 573 &efs_vnodeop_opv_desc, 574 &efs_specop_opv_desc, 575 &efs_fifoop_opv_desc, 576 NULL 577 }; 578 579 struct vfsops efs_vfsops = { 580 .vfs_name = MOUNT_EFS, 581 .vfs_min_mount_data = sizeof (struct efs_args), 582 .vfs_mount = efs_mount, 583 .vfs_start = efs_start, 584 .vfs_unmount = efs_unmount, 585 .vfs_root = efs_root, 586 .vfs_quotactl = (void *)eopnotsupp, 587 .vfs_statvfs = efs_statvfs, 588 .vfs_sync = (void *)nullop, 589 .vfs_vget = efs_vget, 590 .vfs_fhtovp = efs_fhtovp, 591 .vfs_vptofh = efs_vptofh, 592 .vfs_init = efs_init, 593 .vfs_reinit = efs_reinit, 594 .vfs_done = efs_done, 595 .vfs_mountroot = (void *)eopnotsupp, 596 .vfs_snapshot = (void *)eopnotsupp, 597 .vfs_extattrctl = vfs_stdextattrctl, 598 .vfs_suspendctl = (void *)eopnotsupp, 599 .vfs_opv_descs = efs_vnodeopv_descs 600 /* .vfs_refcount */ 601 /* .vfs_list */ 602 }; 603 604 static int 605 efs_modcmd(modcmd_t cmd, void *arg) 606 { 607 608 switch (cmd) { 609 case MODULE_CMD_INIT: 610 return vfs_attach(&efs_vfsops); 611 case MODULE_CMD_FINI: 612 return vfs_detach(&efs_vfsops); 613 default: 614 return ENOTTY; 615 } 616 } 617