1 /* $NetBSD: efs_vfsops.c,v 1.29 2020/01/17 20:08:07 ad 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.29 2020/01/17 20:08:07 ad 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 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 return (err); 86 } 87 memcpy(&emp->em_sb, bp->b_data, sizeof(emp->em_sb)); 88 brelse(bp, 0); 89 90 /* validate the superblock */ 91 if (efs_sb_validate(&emp->em_sb, &why)) { 92 printf("efs: invalid superblock: %s\n", why); 93 if (!(mp->mnt_flag & MNT_FORCE)) { 94 free(emp, M_EFSMNT); 95 return (EIO); 96 } 97 } 98 99 /* check that it's clean */ 100 if (be16toh(emp->em_sb.sb_dirty) != EFS_SB_CLEAN) { 101 printf("efs: filesystem is dirty (sb_dirty = 0x%x); please " 102 "run fsck_efs(8)\n", be16toh(emp->em_sb.sb_dirty)); 103 /* XXX - default to readonly unless forced?? */ 104 } 105 106 /* if the superblock was replicated, verify that it is the same */ 107 if (be32toh(emp->em_sb.sb_replsb) != 0) { 108 struct buf *rbp; 109 bool skip = false; 110 111 err = efs_bread(emp, be32toh(emp->em_sb.sb_replsb), l, &rbp); 112 if (err) { 113 printf("efs: read of superblock replicant failed; " 114 "please run fsck_efs(8)\n"); 115 if (mp->mnt_flag & MNT_FORCE) { 116 skip = true; 117 } else { 118 free(emp, M_EFSMNT); 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 brelse(rbp, 0); 135 } 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 return (err); 146 } 147 } else { 148 brelse(bp, 0); 149 } 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 pathbuf *pb; 177 struct nameidata devnd; 178 struct efs_mount *emp; 179 struct vnode *devvp; 180 int err, mode; 181 182 if (args == NULL) 183 return EINVAL; 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 err = pathbuf_copyin(args->fspec, &pb); 201 if (err) { 202 return err; 203 } 204 NDINIT(&devnd, LOOKUP, FOLLOW | LOCKLEAF, pb); 205 if ((err = namei(&devnd))) { 206 pathbuf_destroy(pb); 207 return (err); 208 } 209 210 devvp = devnd.ni_vp; 211 pathbuf_destroy(pb); 212 213 if (devvp->v_type != VBLK) { 214 vput(devvp); 215 return (ENOTBLK); 216 } 217 218 /* XXX - rdonly */ 219 mode = FREAD; 220 221 /* 222 * If mount by non-root, then verify that user has necessary 223 * permissions on the device. 224 */ 225 err = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT, 226 KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp, KAUTH_ARG(VREAD)); 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, int lktype, struct vnode **vpp) 299 { 300 int err; 301 struct vnode *vp; 302 303 if ((err = VFS_VGET(mp, EFS_ROOTINO, lktype, &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 * Returns 0 on success. 344 */ 345 static int 346 efs_vget(struct mount *mp, ino_t ino, int lktype, struct vnode **vpp) 347 { 348 int error; 349 350 error = vcache_get(mp, &ino, sizeof(ino), vpp); 351 if (error) 352 return error; 353 error = vn_lock(*vpp, lktype); 354 if (error) { 355 vrele(*vpp); 356 *vpp = NULL; 357 return error; 358 } 359 return 0; 360 } 361 362 /* 363 * Initialize this vnode / inode pair. 364 * Caller assures no other thread will try to load this inode. 365 */ 366 static int 367 efs_loadvnode(struct mount *mp, struct vnode *vp, 368 const void *key, size_t key_len, const void **new_key) 369 { 370 int error; 371 ino_t ino; 372 struct efs_inode *eip; 373 struct efs_mount *emp; 374 375 KASSERT(key_len == sizeof(ino)); 376 memcpy(&ino, key, key_len); 377 emp = VFSTOEFS(mp); 378 379 eip = pool_get(&efs_inode_pool, PR_WAITOK); 380 eip->ei_mode = 0; 381 eip->ei_lockf = NULL; 382 eip->ei_number = ino; 383 eip->ei_dev = emp->em_dev; 384 eip->ei_vp = vp; 385 386 error = efs_read_inode(emp, ino, NULL, &eip->ei_di); 387 if (error) { 388 pool_put(&efs_inode_pool, eip); 389 return error; 390 } 391 392 efs_sync_dinode_to_inode(eip); 393 394 if (ino == EFS_ROOTINO && !S_ISDIR(eip->ei_mode)) { 395 printf("efs: root inode (%lu) is not a directory!\n", 396 (ulong)EFS_ROOTINO); 397 pool_put(&efs_inode_pool, eip); 398 return EIO; 399 } 400 401 switch (eip->ei_mode & S_IFMT) { 402 case S_IFIFO: 403 vp->v_type = VFIFO; 404 vp->v_op = efs_fifoop_p; 405 break; 406 case S_IFCHR: 407 vp->v_type = VCHR; 408 vp->v_op = efs_specop_p; 409 spec_node_init(vp, eip->ei_dev); 410 break; 411 case S_IFDIR: 412 vp->v_type = VDIR; 413 vp->v_op = efs_vnodeop_p; 414 if (ino == EFS_ROOTINO) 415 vp->v_vflag |= VV_ROOT; 416 break; 417 case S_IFBLK: 418 vp->v_type = VBLK; 419 vp->v_op = efs_specop_p; 420 spec_node_init(vp, eip->ei_dev); 421 break; 422 case S_IFREG: 423 vp->v_type = VREG; 424 vp->v_op = efs_vnodeop_p; 425 break; 426 case S_IFLNK: 427 vp->v_type = VLNK; 428 vp->v_op = efs_vnodeop_p; 429 break; 430 case S_IFSOCK: 431 vp->v_type = VSOCK; 432 vp->v_op = efs_vnodeop_p; 433 break; 434 default: 435 printf("efs: invalid mode 0x%x in inode %lu on mount %s\n", 436 eip->ei_mode, (ulong)ino, mp->mnt_stat.f_mntonname); 437 pool_put(&efs_inode_pool, eip); 438 return EIO; 439 } 440 441 vp->v_tag = VT_EFS; 442 vp->v_vflag |= VV_LOCKSWORK; 443 vp->v_data = eip; 444 genfs_node_init(vp, &efs_genfsops); 445 uvm_vnp_setsize(vp, eip->ei_size); 446 *new_key = &eip->ei_number; 447 return 0; 448 } 449 450 /* 451 * Convert the provided opaque, unique file handle into a vnode. 452 * 453 * Returns 0 on success. 454 */ 455 static int 456 efs_fhtovp(struct mount *mp, struct fid *fhp, int lktype, struct vnode **vpp) 457 { 458 int err; 459 struct vnode *vp; 460 struct efs_fid *efp; 461 struct efs_inode *eip; 462 463 if (fhp->fid_len != sizeof(struct efs_fid)) 464 return (EINVAL); 465 466 efp = (struct efs_fid *)fhp; 467 468 if ((err = VFS_VGET(mp, efp->ef_ino, lktype, &vp))) { 469 *vpp = NULL; 470 return (err); 471 } 472 473 eip = EFS_VTOI(vp); 474 if (eip->ei_mode == 0 || eip->ei_gen != efp->ef_gen) { 475 vput(vp); 476 *vpp = NULL; 477 return (ESTALE); 478 } 479 480 *vpp = vp; 481 return (0); 482 } 483 484 /* 485 * Convert the provided vnode into an opaque, unique file handle. 486 * 487 * Returns 0 on success. 488 */ 489 static int 490 efs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size) 491 { 492 struct efs_fid *efp; 493 struct efs_inode *eip; 494 495 if (*fh_size < sizeof(struct efs_fid)) { 496 *fh_size = sizeof(struct efs_fid); 497 return (E2BIG); 498 } 499 *fh_size = sizeof(struct efs_fid); 500 501 eip = EFS_VTOI(vp); 502 efp = (struct efs_fid *)fhp; 503 504 fhp->fid_len = sizeof(struct efs_fid); 505 efp->ef_ino = eip->ei_number; 506 efp->ef_gen = eip->ei_gen; 507 508 return (0); 509 } 510 511 /* 512 * Globally initialise the filesystem. 513 */ 514 static void 515 efs_init(void) 516 { 517 518 malloc_type_attach(M_EFSMNT); 519 malloc_type_attach(M_EFSINO); 520 malloc_type_attach(M_EFSTMP); 521 pool_init(&efs_inode_pool, sizeof(struct efs_inode), 0, 0, 0, 522 "efsinopl", &pool_allocator_nointr, IPL_NONE); 523 } 524 525 /* 526 * Globally reinitialise the filesystem. 527 */ 528 static void 529 efs_reinit(void) 530 { 531 532 } 533 534 /* 535 * Globally clean up the filesystem. 536 */ 537 static void 538 efs_done(void) 539 { 540 541 pool_destroy(&efs_inode_pool); 542 malloc_type_detach(M_EFSMNT); 543 malloc_type_detach(M_EFSINO); 544 malloc_type_detach(M_EFSTMP); 545 } 546 547 extern const struct vnodeopv_desc efs_vnodeop_opv_desc; 548 extern const struct vnodeopv_desc efs_specop_opv_desc; 549 extern const struct vnodeopv_desc efs_fifoop_opv_desc; 550 551 const struct vnodeopv_desc * const efs_vnodeopv_descs[] = { 552 &efs_vnodeop_opv_desc, 553 &efs_specop_opv_desc, 554 &efs_fifoop_opv_desc, 555 NULL 556 }; 557 558 struct vfsops efs_vfsops = { 559 .vfs_name = MOUNT_EFS, 560 .vfs_min_mount_data = sizeof (struct efs_args), 561 .vfs_mount = efs_mount, 562 .vfs_start = efs_start, 563 .vfs_unmount = efs_unmount, 564 .vfs_root = efs_root, 565 .vfs_quotactl = (void *)eopnotsupp, 566 .vfs_statvfs = efs_statvfs, 567 .vfs_sync = (void *)nullop, 568 .vfs_vget = efs_vget, 569 .vfs_loadvnode = efs_loadvnode, 570 .vfs_fhtovp = efs_fhtovp, 571 .vfs_vptofh = efs_vptofh, 572 .vfs_init = efs_init, 573 .vfs_reinit = efs_reinit, 574 .vfs_done = efs_done, 575 .vfs_mountroot = (void *)eopnotsupp, 576 .vfs_snapshot = (void *)eopnotsupp, 577 .vfs_extattrctl = vfs_stdextattrctl, 578 .vfs_suspendctl = genfs_suspendctl, 579 .vfs_opv_descs = efs_vnodeopv_descs 580 /* .vfs_refcount */ 581 /* .vfs_list */ 582 }; 583 584 static int 585 efs_modcmd(modcmd_t cmd, void *arg) 586 { 587 588 switch (cmd) { 589 case MODULE_CMD_INIT: 590 return vfs_attach(&efs_vfsops); 591 case MODULE_CMD_FINI: 592 return vfs_detach(&efs_vfsops); 593 default: 594 return ENOTTY; 595 } 596 } 597