1 /* $NetBSD: v7fs_vfsops.c,v 1.9 2013/11/23 13:35:36 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by UCHIYAMA Yasushi. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: v7fs_vfsops.c,v 1.9 2013/11/23 13:35:36 christos Exp $"); 34 #if defined _KERNEL_OPT 35 #include "opt_v7fs.h" 36 #endif 37 38 #include <sys/types.h> 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/pool.h> 42 #include <sys/time.h> 43 #include <sys/ucred.h> 44 #include <sys/mount.h> 45 #include <sys/disk.h> 46 #include <sys/device.h> 47 #include <sys/fcntl.h> 48 #include <sys/kmem.h> 49 #include <sys/kauth.h> 50 #include <sys/proc.h> 51 52 /* v-node */ 53 #include <sys/namei.h> 54 #include <sys/vnode.h> 55 /* devsw */ 56 #include <sys/conf.h> 57 58 #include "v7fs_extern.h" 59 #include "v7fs.h" 60 #include "v7fs_impl.h" 61 #include "v7fs_inode.h" 62 #include "v7fs_superblock.h" 63 64 #ifdef V7FS_VFSOPS_DEBUG 65 #define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) 66 #else 67 #define DPRINTF(arg...) ((void)0) 68 #endif 69 70 struct pool v7fs_node_pool; 71 72 static int v7fs_mountfs(struct vnode *, struct mount *, int); 73 static int v7fs_openfs(struct vnode *, struct mount *, struct lwp *); 74 static void v7fs_closefs(struct vnode *, struct mount *); 75 static int is_v7fs_partition(struct vnode *); 76 static enum vtype v7fs_mode_to_vtype(v7fs_mode_t mode); 77 int v7fs_vnode_reload(struct mount *, struct vnode *); 78 79 int 80 v7fs_mount(struct mount *mp, const char *path, void *data, size_t *data_len) 81 { 82 struct lwp *l = curlwp; 83 struct v7fs_args *args = data; 84 struct v7fs_mount *v7fsmount = (void *)mp->mnt_data; 85 struct vnode *devvp = NULL; 86 int error = 0; 87 bool update = mp->mnt_flag & MNT_UPDATE; 88 89 DPRINTF("mnt_flag=%x %s\n", mp->mnt_flag, update ? "update" : ""); 90 91 if (*data_len < sizeof(*args)) 92 return EINVAL; 93 94 if (mp->mnt_flag & MNT_GETARGS) { 95 if (!v7fsmount) 96 return EIO; 97 args->fspec = NULL; 98 args->endian = v7fsmount->core->endian; 99 *data_len = sizeof(*args); 100 return 0; 101 } 102 103 DPRINTF("args->fspec=%s endian=%d\n", args->fspec, args->endian); 104 if (args->fspec == NULL) { 105 /* nothing to do. */ 106 return EINVAL; 107 } 108 109 if (args->fspec != NULL) { 110 /* Look up the name and verify that it's sane. */ 111 error = namei_simple_user(args->fspec, 112 NSM_FOLLOW_NOEMULROOT, &devvp); 113 if (error != 0) 114 return (error); 115 DPRINTF("mount device=%lx\n", (long)devvp->v_rdev); 116 117 if (!update) { 118 /* 119 * Be sure this is a valid block device 120 */ 121 if (devvp->v_type != VBLK) 122 error = ENOTBLK; 123 else if (bdevsw_lookup(devvp->v_rdev) == NULL) 124 error = ENXIO; 125 } else { 126 KDASSERT(v7fsmount); 127 /* 128 * Be sure we're still naming the same device 129 * used for our initial mount 130 */ 131 if (devvp != v7fsmount->devvp) { 132 DPRINTF("devvp %p != %p rootvp=%p\n", devvp, 133 v7fsmount->devvp, rootvp); 134 if (rootvp == v7fsmount->devvp) { 135 vrele(devvp); 136 devvp = rootvp; 137 vref(devvp); 138 } else { 139 error = EINVAL; 140 } 141 } 142 } 143 } 144 145 /* 146 * If mount by non-root, then verify that user has necessary 147 * permissions on the device. 148 * 149 * Permission to update a mount is checked higher, so here we presume 150 * updating the mount is okay (for example, as far as securelevel goes) 151 * which leaves us with the normal check. 152 */ 153 if (error == 0) { 154 int accessmode = VREAD; 155 if (update ? 156 (mp->mnt_iflag & IMNT_WANTRDWR) != 0 : 157 (mp->mnt_flag & MNT_RDONLY) == 0) 158 accessmode |= VWRITE; 159 error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT, 160 KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp, 161 KAUTH_ARG(accessmode)); 162 } 163 164 if (error) { 165 vrele(devvp); 166 return error; 167 } 168 169 if (!update) { 170 if ((error = v7fs_openfs(devvp, mp, l))) { 171 vrele(devvp); 172 return error; 173 } 174 175 if ((error = v7fs_mountfs(devvp, mp, args->endian))) { 176 v7fs_closefs(devvp, mp); 177 VOP_UNLOCK(devvp); 178 vrele(devvp); 179 return error; 180 } 181 VOP_UNLOCK(devvp); 182 } else if (mp->mnt_flag & MNT_RDONLY) { 183 /* XXX: r/w -> read only */ 184 } 185 186 return set_statvfs_info(path, UIO_USERSPACE, args->fspec, UIO_USERSPACE, 187 mp->mnt_op->vfs_name, mp, l); 188 } 189 190 static int 191 is_v7fs_partition(struct vnode *devvp) 192 { 193 struct dkwedge_info dkw; 194 int error; 195 196 if ((error = getdiskinfo(devvp, &dkw)) != 0) { 197 DPRINTF("getdiskinfo=%d\n", error); 198 return error; 199 } 200 DPRINTF("ptype=%s size=%" PRIu64 "\n", dkw.dkw_ptype, dkw->dkw_size); 201 202 return strcmp(dkw.dkw_ptype, DKW_PTYPE_V7) == 0 ? 0 : EINVAL; 203 } 204 205 static int 206 v7fs_openfs(struct vnode *devvp, struct mount *mp, struct lwp *l) 207 { 208 kauth_cred_t cred = l->l_cred; 209 int oflags; 210 int error; 211 212 /* Flush buffer */ 213 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 214 if ((error = vinvalbuf(devvp, V_SAVE, cred, l, 0, 0))) 215 goto unlock_exit; 216 217 /* Open block device */ 218 oflags = FREAD; 219 if ((mp->mnt_flag & MNT_RDONLY) == 0) 220 oflags |= FWRITE; 221 222 if ((error = VOP_OPEN(devvp, oflags, NOCRED)) != 0) { 223 DPRINTF("VOP_OPEN=%d\n", error); 224 goto unlock_exit; 225 } 226 227 return 0; /* lock held */ 228 229 unlock_exit: 230 VOP_UNLOCK(devvp); 231 232 return error; 233 } 234 235 static void 236 v7fs_closefs(struct vnode *devvp, struct mount *mp) 237 { 238 int oflags = FREAD; 239 240 if ((mp->mnt_flag & MNT_RDONLY) == 0) 241 oflags |= FWRITE; 242 243 VOP_CLOSE(devvp, oflags, NOCRED); 244 } 245 246 static int 247 v7fs_mountfs(struct vnode *devvp, struct mount *mp, int endian) 248 { 249 struct v7fs_mount *v7fsmount; 250 int error; 251 struct v7fs_mount_device mount; 252 253 DPRINTF("%d\n",endian); 254 255 v7fsmount = kmem_zalloc(sizeof(*v7fsmount), KM_SLEEP); 256 if (v7fsmount == NULL) { 257 return ENOMEM; 258 } 259 v7fsmount->devvp = devvp; 260 v7fsmount->mountp = mp; 261 262 mount.device.vnode = devvp; 263 mount.endian = endian; 264 265 if ((error = v7fs_io_init(&v7fsmount->core, &mount, V7FS_BSIZE))) { 266 goto err_exit; 267 } 268 struct v7fs_self *fs = v7fsmount->core; 269 270 if ((error = v7fs_superblock_load(fs))) { 271 v7fs_io_fini(fs); 272 goto err_exit; 273 } 274 275 LIST_INIT(&v7fsmount->v7fs_node_head); 276 277 mp->mnt_data = v7fsmount; 278 mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)devvp->v_rdev; 279 mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_V7FS); 280 mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0]; 281 mp->mnt_stat.f_namemax = V7FS_NAME_MAX; 282 mp->mnt_flag |= MNT_LOCAL; 283 mp->mnt_dev_bshift = V7FS_BSHIFT; 284 mp->mnt_fs_bshift = V7FS_BSHIFT; 285 286 return 0; 287 288 err_exit: 289 kmem_free(v7fsmount, sizeof(*v7fsmount)); 290 return error; 291 } 292 293 int 294 v7fs_start(struct mount *mp, int flags) 295 { 296 297 DPRINTF("\n"); 298 /* Nothing to do. */ 299 return 0; 300 } 301 302 int 303 v7fs_unmount(struct mount *mp, int mntflags) 304 { 305 struct v7fs_mount *v7fsmount = (void *)mp->mnt_data; 306 int error; 307 308 DPRINTF("%p\n", v7fsmount); 309 310 if ((error = vflush(mp, NULLVP, 311 mntflags & MNT_FORCE ? FORCECLOSE : 0)) != 0) 312 return error; 313 314 vn_lock(v7fsmount->devvp, LK_EXCLUSIVE | LK_RETRY); 315 error = VOP_CLOSE(v7fsmount->devvp, FREAD, NOCRED); 316 vput(v7fsmount->devvp); 317 318 v7fs_io_fini(v7fsmount->core); 319 320 kmem_free(v7fsmount, sizeof(*v7fsmount)); 321 mp->mnt_data = NULL; 322 mp->mnt_flag &= ~MNT_LOCAL; 323 324 return 0; 325 } 326 327 int 328 v7fs_root(struct mount *mp, struct vnode **vpp) 329 { 330 struct vnode *vp; 331 int error; 332 333 DPRINTF("\n"); 334 if ((error = VFS_VGET(mp, V7FS_ROOT_INODE, &vp)) != 0) { 335 DPRINTF("error=%d\n", error); 336 return error; 337 } 338 *vpp = vp; 339 DPRINTF("done.\n"); 340 341 return 0; 342 } 343 344 int 345 v7fs_statvfs(struct mount *mp, struct statvfs *f) 346 { 347 struct v7fs_mount *v7fsmount = mp->mnt_data; 348 struct v7fs_self *fs = v7fsmount->core; 349 350 DPRINTF("scratch remain=%d\n", fs->scratch_remain); 351 352 v7fs_superblock_status(fs); 353 354 f->f_bsize = V7FS_BSIZE; 355 f->f_frsize = V7FS_BSIZE; 356 f->f_iosize = V7FS_BSIZE; 357 f->f_blocks = fs->stat.total_blocks; 358 f->f_bfree = fs->stat.free_blocks; 359 f->f_bavail = fs->stat.free_blocks; 360 f->f_bresvd = 0; 361 f->f_files = fs->stat.total_files; 362 f->f_ffree = fs->stat.free_inode; 363 f->f_favail = f->f_ffree; 364 f->f_fresvd = 0; 365 copy_statvfs_info(f, mp); 366 367 return 0; 368 } 369 370 int 371 v7fs_sync(struct mount *mp, int waitfor, kauth_cred_t cred) 372 { 373 struct v7fs_mount *v7fsmount = mp->mnt_data; 374 struct v7fs_self *fs = v7fsmount->core; 375 struct v7fs_node *v7fs_node; 376 struct v7fs_inode *inode; 377 struct vnode *v; 378 int err, error; 379 int retry_cnt; 380 381 DPRINTF("\n"); 382 383 v7fs_superblock_writeback(fs); 384 for (retry_cnt = 0; retry_cnt < 2; retry_cnt++) { 385 error = 0; 386 387 mutex_enter(&mntvnode_lock); 388 for (v7fs_node = LIST_FIRST(&v7fsmount->v7fs_node_head); 389 v7fs_node != NULL; v7fs_node = LIST_NEXT(v7fs_node, link)) { 390 inode = &v7fs_node->inode; 391 if (!v7fs_inode_allocated(inode)) { 392 continue; 393 } 394 v = v7fs_node->vnode; 395 mutex_enter(v->v_interlock); 396 mutex_exit(&mntvnode_lock); 397 err = vget(v, LK_EXCLUSIVE | LK_NOWAIT); 398 if (err == 0) { 399 err = VOP_FSYNC(v, cred, FSYNC_WAIT, 0, 0); 400 vput(v); 401 } 402 if (err != 0) 403 error = err; 404 mutex_enter(&mntvnode_lock); 405 } 406 mutex_exit(&mntvnode_lock); 407 408 if (error == 0) 409 break; 410 } 411 412 return error; 413 } 414 415 static enum vtype 416 v7fs_mode_to_vtype (v7fs_mode_t mode) 417 { 418 enum vtype table[] = { VCHR, VDIR, VBLK, VREG, VLNK, VSOCK }; 419 420 if ((mode & V7FS_IFMT) == V7FSBSD_IFFIFO) 421 return VFIFO; 422 423 return table[((mode >> 13) & 7) - 1]; 424 } 425 426 int 427 v7fs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) 428 { 429 struct v7fs_mount *v7fsmount = mp->mnt_data; 430 struct v7fs_self *fs = v7fsmount->core; 431 struct vnode *vp; 432 struct v7fs_node *v7fs_node; 433 struct v7fs_inode inode; 434 int error; 435 436 /* Lookup requested i-node */ 437 if ((error = v7fs_inode_load(fs, &inode, ino))) { 438 DPRINTF("v7fs_inode_load failed.\n"); 439 return error; 440 } 441 442 retry: 443 mutex_enter(&mntvnode_lock); 444 for (v7fs_node = LIST_FIRST(&v7fsmount->v7fs_node_head); 445 v7fs_node != NULL; v7fs_node = LIST_NEXT(v7fs_node, link)) { 446 if (v7fs_node->inode.inode_number == ino) { 447 vp = v7fs_node->vnode; 448 mutex_enter(vp->v_interlock); 449 mutex_exit(&mntvnode_lock); 450 if (vget(vp, LK_EXCLUSIVE) == 0) { 451 *vpp = vp; 452 return 0; 453 } else { 454 DPRINTF("retry!\n"); 455 goto retry; 456 } 457 } 458 } 459 mutex_exit(&mntvnode_lock); 460 461 /* Allocate v-node. */ 462 if ((error = getnewvnode(VT_V7FS, mp, v7fs_vnodeop_p, NULL, &vp))) { 463 DPRINTF("getnewvnode error.\n"); 464 return error; 465 } 466 /* Lock vnode here */ 467 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 468 469 /* Allocate i-node */ 470 vp->v_data = pool_get(&v7fs_node_pool, PR_WAITOK); 471 memset(vp->v_data, 0, sizeof(*v7fs_node)); 472 v7fs_node = vp->v_data; 473 mutex_enter(&mntvnode_lock); 474 LIST_INSERT_HEAD(&v7fsmount->v7fs_node_head, v7fs_node, link); 475 mutex_exit(&mntvnode_lock); 476 v7fs_node->vnode = vp; 477 v7fs_node->v7fsmount = v7fsmount; 478 v7fs_node->inode = inode;/*structure copy */ 479 v7fs_node->lockf = NULL; /* advlock */ 480 481 genfs_node_init(vp, &v7fs_genfsops); 482 uvm_vnp_setsize(vp, v7fs_inode_filesize(&inode)); 483 484 if (ino == V7FS_ROOT_INODE) { 485 vp->v_type = VDIR; 486 vp->v_vflag |= VV_ROOT; 487 } else { 488 vp->v_type = v7fs_mode_to_vtype(inode.mode); 489 490 if (vp->v_type == VBLK || vp->v_type == VCHR) { 491 dev_t rdev = inode.device; 492 vp->v_op = v7fs_specop_p; 493 spec_node_init(vp, rdev); 494 } else if (vp->v_type == VFIFO) { 495 vp->v_op = v7fs_fifoop_p; 496 } 497 } 498 499 *vpp = vp; 500 501 return 0; 502 } 503 504 505 int 506 v7fs_fhtovp(struct mount *mp, struct fid *fid, struct vnode **vpp) 507 { 508 509 DPRINTF("\n"); 510 /* notyet */ 511 return EOPNOTSUPP; 512 } 513 514 int 515 v7fs_vptofh(struct vnode *vpp, struct fid *fid, size_t *fh_size) 516 { 517 518 DPRINTF("\n"); 519 /* notyet */ 520 return EOPNOTSUPP; 521 } 522 523 void 524 v7fs_init(void) 525 { 526 527 DPRINTF("\n"); 528 pool_init(&v7fs_node_pool, sizeof(struct v7fs_node), 0, 0, 0, 529 "v7fs_node_pool", &pool_allocator_nointr, IPL_NONE); 530 } 531 532 void 533 v7fs_reinit(void) 534 { 535 536 /* Nothing to do. */ 537 DPRINTF("\n"); 538 } 539 540 void 541 v7fs_done(void) 542 { 543 544 DPRINTF("\n"); 545 pool_destroy(&v7fs_node_pool); 546 } 547 548 int 549 v7fs_gop_alloc(struct vnode *vp, off_t off, off_t len, int flags, 550 kauth_cred_t cred) 551 { 552 553 DPRINTF("\n"); 554 return 0; 555 } 556 557 int 558 v7fs_mountroot(void) 559 { 560 struct mount *mp; 561 int error; 562 563 DPRINTF(""); 564 /* On mountroot, devvp (rootdev) is opened by vfs_mountroot */ 565 if ((error = is_v7fs_partition (rootvp))) 566 return error; 567 568 if ((error = vfs_rootmountalloc(MOUNT_V7FS, "root_device", &mp))) { 569 DPRINTF("mountalloc error=%d\n", error); 570 vrele(rootvp); 571 return error; 572 } 573 574 if ((error = v7fs_mountfs(rootvp, mp, _BYTE_ORDER))) { 575 DPRINTF("mountfs error=%d\n", error); 576 vfs_unbusy(mp, false, NULL); 577 vfs_destroy(mp); 578 return error; 579 } 580 581 mountlist_append(mp); 582 583 vfs_unbusy(mp, false, NULL); 584 585 return 0; 586 } 587 588 /* Reload disk inode information */ 589 int 590 v7fs_vnode_reload(struct mount *mp, struct vnode *vp) 591 { 592 struct v7fs_mount *v7fsmount = mp->mnt_data; 593 struct v7fs_self *fs = v7fsmount->core; 594 struct v7fs_node *v7fs_node; 595 struct v7fs_inode *inode = &((struct v7fs_node *)vp->v_data)->inode; 596 int target_ino = inode->inode_number; 597 int error = 0; 598 599 DPRINTF("#%d\n", target_ino); 600 mutex_enter(&mntvnode_lock); 601 for (v7fs_node = LIST_FIRST(&v7fsmount->v7fs_node_head); 602 v7fs_node != NULL; v7fs_node = LIST_NEXT(v7fs_node, link)) { 603 inode = &v7fs_node->inode; 604 if (!v7fs_inode_allocated(inode)) { 605 continue; 606 } 607 if (inode->inode_number == target_ino) { 608 error = v7fs_inode_load(fs, &v7fs_node->inode, 609 target_ino); 610 DPRINTF("sync #%d error=%d\n", target_ino, error); 611 break; 612 } 613 } 614 mutex_exit(&mntvnode_lock); 615 616 return error; 617 } 618