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