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