1 /* $NetBSD: efs_vfsops.c,v 1.19 2009/04/25 18:53:44 elad 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.19 2009/04/25 18:53:44 elad 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 nameidata devndp; 180 struct efs_mount *emp; 181 struct vnode *devvp; 182 int err, mode; 183 184 if (*data_len < sizeof *args) 185 return EINVAL; 186 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; 194 } 195 196 if (mp->mnt_flag & MNT_UPDATE) 197 return (EOPNOTSUPP); /* XXX read-only */ 198 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); 203 204 devvp = devndp.ni_vp; 205 if (devvp->v_type != VBLK) { 206 vput(devvp); 207 return (ENOTBLK); 208 } 209 210 /* XXX - rdonly */ 211 mode = FREAD; 212 213 /* 214 * If mount by non-root, then verify that user has necessary 215 * permissions on the device. 216 */ 217 err = genfs_can_mount(devvp, VREAD, l->l_cred); 218 if (err) { 219 vput(devvp); 220 return (err); 221 } 222 223 if ((err = VOP_OPEN(devvp, mode, l->l_cred))) { 224 vput(devvp); 225 return (err); 226 } 227 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); 233 } 234 235 VOP_UNLOCK(devvp, 0); 236 237 return (0); 238 } 239 240 /* 241 * Initialisation routine. 242 * 243 * Returns 0 on success. 244 */ 245 static int 246 efs_start(struct mount *mp, int flags) 247 { 248 249 return (0); 250 } 251 252 /* 253 * unmount syscall vfsop. 254 * 255 * Returns 0 on success. 256 */ 257 static int 258 efs_unmount(struct mount *mp, int mntflags) 259 { 260 struct efs_mount *emp; 261 struct lwp *l = curlwp; 262 int err; 263 264 emp = VFSTOEFS(mp); 265 266 err = vflush(mp, NULL, (mntflags & MNT_FORCE) ? FORCECLOSE : 0); 267 if (err) 268 return (err); 269 270 cache_purgevfs(mp); 271 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); 275 276 free(mp->mnt_data, M_EFSMNT); 277 mp->mnt_data = NULL; 278 mp->mnt_flag &= ~MNT_LOCAL; 279 280 return (err); 281 } 282 283 /* 284 * Return the root vnode. 285 * 286 * Returns 0 on success. 287 */ 288 static int 289 efs_root(struct mount *mp, struct vnode **vpp) 290 { 291 int err; 292 struct vnode *vp; 293 294 if ((err = VFS_VGET(mp, EFS_ROOTINO, &vp))) 295 return (err); 296 297 *vpp = vp; 298 return (0); 299 } 300 301 /* 302 * statvfs syscall vfsop. 303 * 304 * Returns 0 on success. 305 */ 306 static int 307 efs_statvfs(struct mount *mp, struct statvfs *sbp) 308 { 309 struct efs_mount *emp; 310 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); 327 328 return (0); 329 } 330 331 /* 332 * Obtain a locked vnode for the given on-disk inode number. 333 * 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. 337 * 338 * Returns 0 on success. 339 */ 340 static int 341 efs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) 342 { 343 int err; 344 struct vnode *vp; 345 struct efs_inode *eip; 346 struct efs_mount *emp; 347 348 emp = VFSTOEFS(mp); 349 350 while (true) { 351 *vpp = efs_ihashget(emp->em_dev, ino, LK_EXCLUSIVE); 352 if (*vpp != NULL) 353 return (0); 354 355 err = getnewvnode(VT_EFS, mp, efs_vnodeop_p, &vp); 356 if (err) 357 return (err); 358 359 eip = pool_get(&efs_inode_pool, PR_WAITOK); 360 361 /* 362 * See if anybody has raced us here. If not, continue 363 * setting up the new inode, otherwise start over. 364 */ 365 efs_ihashlock(); 366 367 if (efs_ihashget(emp->em_dev, ino, 0) == NULL) 368 break; 369 370 efs_ihashunlock(); 371 ungetnewvnode(vp); 372 pool_put(&efs_inode_pool, eip); 373 } 374 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; 382 383 /* 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. 387 */ 388 efs_ihashins(eip); 389 efs_ihashunlock(); 390 391 /* 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. 394 */ 395 genfs_node_init(vp, &efs_genfsops); 396 397 err = efs_read_inode(emp, ino, NULL, &eip->ei_di); 398 if (err) { 399 vput(vp); 400 *vpp = NULL; 401 return (err); 402 } 403 404 efs_sync_dinode_to_inode(eip); 405 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); 412 } 413 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); 449 } 450 451 uvm_vnp_setsize(vp, eip->ei_size); 452 *vpp = vp; 453 454 KASSERT(VOP_ISLOCKED(vp)); 455 456 return (0); 457 } 458 459 /* 460 * Convert the provided opaque, unique file handle into a vnode. 461 * 462 * Returns 0 on success. 463 */ 464 static int 465 efs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) 466 { 467 int err; 468 struct vnode *vp; 469 struct efs_fid *efp; 470 struct efs_inode *eip; 471 472 if (fhp->fid_len != sizeof(struct efs_fid)) 473 return (EINVAL); 474 475 efp = (struct efs_fid *)fhp; 476 477 if ((err = VFS_VGET(mp, efp->ef_ino, &vp))) { 478 *vpp = NULL; 479 return (err); 480 } 481 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); 487 } 488 489 *vpp = vp; 490 return (0); 491 } 492 493 /* 494 * Convert the provided vnode into an opaque, unique file handle. 495 * 496 * Returns 0 on success. 497 */ 498 static int 499 efs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size) 500 { 501 struct efs_fid *efp; 502 struct efs_inode *eip; 503 504 if (*fh_size < sizeof(struct efs_fid)) { 505 *fh_size = sizeof(struct efs_fid); 506 return (E2BIG); 507 } 508 *fh_size = sizeof(struct efs_fid); 509 510 eip = EFS_VTOI(vp); 511 efp = (struct efs_fid *)fhp; 512 513 fhp->fid_len = sizeof(struct efs_fid); 514 efp->ef_ino = eip->ei_number; 515 efp->ef_gen = eip->ei_gen; 516 517 return (0); 518 } 519 520 /* 521 * Globally initialise the filesystem. 522 */ 523 static void 524 efs_init(void) 525 { 526 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); 533 } 534 535 /* 536 * Globally reinitialise the filesystem. 537 */ 538 static void 539 efs_reinit(void) 540 { 541 542 efs_ihashreinit(); 543 } 544 545 /* 546 * Globally clean up the filesystem. 547 */ 548 static void 549 efs_done(void) 550 { 551 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); 557 } 558 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; 562 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 568 }; 569 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 */ 593 }; 594 595 static int 596 efs_modcmd(modcmd_t cmd, void *arg) 597 { 598 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; 606 } 607 } 608