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