1 /* $NetBSD: v7fs_vfsops.c,v 1.12 2014/12/29 15:29:38 hannken 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.12 2014/12/29 15:29:38 hannken 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 78 int 79 v7fs_mount(struct mount *mp, const char *path, void *data, size_t *data_len) 80 { 81 struct lwp *l = curlwp; 82 struct v7fs_args *args = data; 83 struct v7fs_mount *v7fsmount = (void *)mp->mnt_data; 84 struct vnode *devvp = NULL; 85 int error = 0; 86 bool update = mp->mnt_flag & MNT_UPDATE; 87 88 DPRINTF("mnt_flag=%x %s\n", mp->mnt_flag, update ? "update" : ""); 89 90 if (args == NULL) 91 return EINVAL; 92 if (*data_len < sizeof(*args)) 93 return EINVAL; 94 95 if (mp->mnt_flag & MNT_GETARGS) { 96 if (!v7fsmount) 97 return EIO; 98 args->fspec = NULL; 99 args->endian = v7fsmount->core->endian; 100 *data_len = sizeof(*args); 101 return 0; 102 } 103 104 DPRINTF("args->fspec=%s endian=%d\n", args->fspec, args->endian); 105 if (args->fspec == NULL) { 106 /* nothing to do. */ 107 return EINVAL; 108 } 109 110 if (args->fspec != NULL) { 111 /* Look up the name and verify that it's sane. */ 112 error = namei_simple_user(args->fspec, 113 NSM_FOLLOW_NOEMULROOT, &devvp); 114 if (error != 0) 115 return (error); 116 DPRINTF("mount device=%lx\n", (long)devvp->v_rdev); 117 118 if (!update) { 119 /* 120 * Be sure this is a valid block device 121 */ 122 if (devvp->v_type != VBLK) 123 error = ENOTBLK; 124 else if (bdevsw_lookup(devvp->v_rdev) == NULL) 125 error = ENXIO; 126 } else { 127 KDASSERT(v7fsmount); 128 /* 129 * Be sure we're still naming the same device 130 * used for our initial mount 131 */ 132 if (devvp != v7fsmount->devvp) { 133 DPRINTF("devvp %p != %p rootvp=%p\n", devvp, 134 v7fsmount->devvp, rootvp); 135 if (rootvp == v7fsmount->devvp) { 136 vrele(devvp); 137 devvp = rootvp; 138 vref(devvp); 139 } else { 140 error = EINVAL; 141 } 142 } 143 } 144 } 145 146 /* 147 * If mount by non-root, then verify that user has necessary 148 * permissions on the device. 149 * 150 * Permission to update a mount is checked higher, so here we presume 151 * updating the mount is okay (for example, as far as securelevel goes) 152 * which leaves us with the normal check. 153 */ 154 if (error == 0) { 155 int accessmode = VREAD; 156 if (update ? 157 (mp->mnt_iflag & IMNT_WANTRDWR) != 0 : 158 (mp->mnt_flag & MNT_RDONLY) == 0) 159 accessmode |= VWRITE; 160 error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT, 161 KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp, 162 KAUTH_ARG(accessmode)); 163 } 164 165 if (error) { 166 vrele(devvp); 167 return error; 168 } 169 170 if (!update) { 171 if ((error = v7fs_openfs(devvp, mp, l))) { 172 vrele(devvp); 173 return error; 174 } 175 176 if ((error = v7fs_mountfs(devvp, mp, args->endian))) { 177 v7fs_closefs(devvp, mp); 178 VOP_UNLOCK(devvp); 179 vrele(devvp); 180 return error; 181 } 182 VOP_UNLOCK(devvp); 183 } else if (mp->mnt_flag & MNT_RDONLY) { 184 /* XXX: r/w -> read only */ 185 } 186 187 return set_statvfs_info(path, UIO_USERSPACE, args->fspec, UIO_USERSPACE, 188 mp->mnt_op->vfs_name, mp, l); 189 } 190 191 static int 192 is_v7fs_partition(struct vnode *devvp) 193 { 194 struct dkwedge_info dkw; 195 int error; 196 197 if ((error = getdiskinfo(devvp, &dkw)) != 0) { 198 DPRINTF("getdiskinfo=%d\n", error); 199 return error; 200 } 201 DPRINTF("ptype=%s size=%" PRIu64 "\n", dkw.dkw_ptype, dkw->dkw_size); 202 203 return strcmp(dkw.dkw_ptype, DKW_PTYPE_V7) == 0 ? 0 : EINVAL; 204 } 205 206 static int 207 v7fs_openfs(struct vnode *devvp, struct mount *mp, struct lwp *l) 208 { 209 kauth_cred_t cred = l->l_cred; 210 int oflags; 211 int error; 212 213 /* Flush buffer */ 214 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 215 if ((error = vinvalbuf(devvp, V_SAVE, cred, l, 0, 0))) 216 goto unlock_exit; 217 218 /* Open block device */ 219 oflags = FREAD; 220 if ((mp->mnt_flag & MNT_RDONLY) == 0) 221 oflags |= FWRITE; 222 223 if ((error = VOP_OPEN(devvp, oflags, NOCRED)) != 0) { 224 DPRINTF("VOP_OPEN=%d\n", error); 225 goto unlock_exit; 226 } 227 228 return 0; /* lock held */ 229 230 unlock_exit: 231 VOP_UNLOCK(devvp); 232 233 return error; 234 } 235 236 static void 237 v7fs_closefs(struct vnode *devvp, struct mount *mp) 238 { 239 int oflags = FREAD; 240 241 if ((mp->mnt_flag & MNT_RDONLY) == 0) 242 oflags |= FWRITE; 243 244 VOP_CLOSE(devvp, oflags, NOCRED); 245 } 246 247 static int 248 v7fs_mountfs(struct vnode *devvp, struct mount *mp, int endian) 249 { 250 struct v7fs_mount *v7fsmount; 251 int error; 252 struct v7fs_mount_device mount; 253 254 DPRINTF("%d\n",endian); 255 256 v7fsmount = kmem_zalloc(sizeof(*v7fsmount), KM_SLEEP); 257 if (v7fsmount == NULL) { 258 return ENOMEM; 259 } 260 v7fsmount->devvp = devvp; 261 v7fsmount->mountp = mp; 262 263 mount.device.vnode = devvp; 264 mount.endian = endian; 265 266 if ((error = v7fs_io_init(&v7fsmount->core, &mount, V7FS_BSIZE))) { 267 goto err_exit; 268 } 269 struct v7fs_self *fs = v7fsmount->core; 270 271 if ((error = v7fs_superblock_load(fs))) { 272 v7fs_io_fini(fs); 273 goto err_exit; 274 } 275 276 mp->mnt_data = v7fsmount; 277 mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)devvp->v_rdev; 278 mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_V7FS); 279 mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0]; 280 mp->mnt_stat.f_namemax = V7FS_NAME_MAX; 281 mp->mnt_flag |= MNT_LOCAL; 282 mp->mnt_dev_bshift = V7FS_BSHIFT; 283 mp->mnt_fs_bshift = V7FS_BSHIFT; 284 285 return 0; 286 287 err_exit: 288 kmem_free(v7fsmount, sizeof(*v7fsmount)); 289 return error; 290 } 291 292 int 293 v7fs_start(struct mount *mp, int flags) 294 { 295 296 DPRINTF("\n"); 297 /* Nothing to do. */ 298 return 0; 299 } 300 301 int 302 v7fs_unmount(struct mount *mp, int mntflags) 303 { 304 struct v7fs_mount *v7fsmount = (void *)mp->mnt_data; 305 int error; 306 307 DPRINTF("%p\n", v7fsmount); 308 309 if ((error = vflush(mp, NULLVP, 310 mntflags & MNT_FORCE ? FORCECLOSE : 0)) != 0) 311 return error; 312 313 vn_lock(v7fsmount->devvp, LK_EXCLUSIVE | LK_RETRY); 314 error = VOP_CLOSE(v7fsmount->devvp, FREAD, NOCRED); 315 vput(v7fsmount->devvp); 316 317 v7fs_io_fini(v7fsmount->core); 318 319 kmem_free(v7fsmount, sizeof(*v7fsmount)); 320 mp->mnt_data = NULL; 321 mp->mnt_flag &= ~MNT_LOCAL; 322 323 return 0; 324 } 325 326 int 327 v7fs_root(struct mount *mp, struct vnode **vpp) 328 { 329 struct vnode *vp; 330 int error; 331 332 DPRINTF("\n"); 333 if ((error = VFS_VGET(mp, V7FS_ROOT_INODE, &vp)) != 0) { 334 DPRINTF("error=%d\n", error); 335 return error; 336 } 337 *vpp = vp; 338 DPRINTF("done.\n"); 339 340 return 0; 341 } 342 343 int 344 v7fs_statvfs(struct mount *mp, struct statvfs *f) 345 { 346 struct v7fs_mount *v7fsmount = mp->mnt_data; 347 struct v7fs_self *fs = v7fsmount->core; 348 349 DPRINTF("scratch remain=%d\n", fs->scratch_remain); 350 351 v7fs_superblock_status(fs); 352 353 f->f_bsize = V7FS_BSIZE; 354 f->f_frsize = V7FS_BSIZE; 355 f->f_iosize = V7FS_BSIZE; 356 f->f_blocks = fs->stat.total_blocks; 357 f->f_bfree = fs->stat.free_blocks; 358 f->f_bavail = fs->stat.free_blocks; 359 f->f_bresvd = 0; 360 f->f_files = fs->stat.total_files; 361 f->f_ffree = fs->stat.free_inode; 362 f->f_favail = f->f_ffree; 363 f->f_fresvd = 0; 364 copy_statvfs_info(f, mp); 365 366 return 0; 367 } 368 369 static bool 370 v7fs_sync_selector(void *cl, struct vnode *vp) 371 { 372 struct v7fs_node *v7fs_node = vp->v_data; 373 374 if (v7fs_node == NULL) 375 return false; 376 if (!v7fs_inode_allocated(&v7fs_node->inode)) 377 return false; 378 379 return true; 380 } 381 382 int 383 v7fs_sync(struct mount *mp, int waitfor, kauth_cred_t cred) 384 { 385 struct v7fs_mount *v7fsmount = mp->mnt_data; 386 struct v7fs_self *fs = v7fsmount->core; 387 struct vnode_iterator *marker; 388 struct vnode *vp; 389 int err, error; 390 391 DPRINTF("\n"); 392 393 v7fs_superblock_writeback(fs); 394 error = 0; 395 vfs_vnode_iterator_init(mp, &marker); 396 while ((vp = vfs_vnode_iterator_next(marker, 397 v7fs_sync_selector, NULL)) != NULL) { 398 err = vn_lock(vp, LK_EXCLUSIVE); 399 if (err) { 400 vrele(vp); 401 continue; 402 } 403 err = VOP_FSYNC(vp, cred, FSYNC_WAIT, 0, 0); 404 vput(vp); 405 if (err != 0) 406 error = err; 407 } 408 vfs_vnode_iterator_destroy(marker); 409 410 return error; 411 } 412 413 static enum vtype 414 v7fs_mode_to_vtype (v7fs_mode_t mode) 415 { 416 enum vtype table[] = { VCHR, VDIR, VBLK, VREG, VLNK, VSOCK }; 417 418 if ((mode & V7FS_IFMT) == V7FSBSD_IFFIFO) 419 return VFIFO; 420 421 return table[((mode >> 13) & 7) - 1]; 422 } 423 424 int 425 v7fs_loadvnode(struct mount *mp, struct vnode *vp, 426 const void *key, size_t key_len, const void **new_key) 427 { 428 struct v7fs_mount *v7fsmount; 429 struct v7fs_self *fs; 430 struct v7fs_node *v7fs_node; 431 struct v7fs_inode inode; 432 v7fs_ino_t number; 433 int error; 434 435 KASSERT(key_len == sizeof(number)); 436 memcpy(&number, key, key_len); 437 438 v7fsmount = mp->mnt_data; 439 fs = v7fsmount->core; 440 441 /* Lookup requested i-node */ 442 if ((error = v7fs_inode_load(fs, &inode, number))) { 443 DPRINTF("v7fs_inode_load failed.\n"); 444 return error; 445 } 446 447 v7fs_node = pool_get(&v7fs_node_pool, PR_WAITOK); 448 memset(v7fs_node, 0, sizeof(*v7fs_node)); 449 450 vp->v_tag = VT_V7FS; 451 vp->v_data = v7fs_node; 452 v7fs_node->vnode = vp; 453 v7fs_node->v7fsmount = v7fsmount; 454 v7fs_node->inode = inode;/*structure copy */ 455 v7fs_node->lockf = NULL; /* advlock */ 456 457 genfs_node_init(vp, &v7fs_genfsops); 458 uvm_vnp_setsize(vp, v7fs_inode_filesize(&inode)); 459 460 if (number == V7FS_ROOT_INODE) { 461 vp->v_type = VDIR; 462 vp->v_vflag |= VV_ROOT; 463 vp->v_op = v7fs_vnodeop_p; 464 } else { 465 vp->v_type = v7fs_mode_to_vtype(inode.mode); 466 467 if (vp->v_type == VBLK || vp->v_type == VCHR) { 468 dev_t rdev = inode.device; 469 vp->v_op = v7fs_specop_p; 470 spec_node_init(vp, rdev); 471 } else if (vp->v_type == VFIFO) { 472 vp->v_op = v7fs_fifoop_p; 473 } else { 474 vp->v_op = v7fs_vnodeop_p; 475 } 476 } 477 478 *new_key = &v7fs_node->inode.inode_number; 479 480 return 0; 481 } 482 483 484 int 485 v7fs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) 486 { 487 int error; 488 v7fs_ino_t number; 489 struct vnode *vp; 490 491 KASSERT(ino <= UINT16_MAX); 492 number = ino; 493 494 error = vcache_get(mp, &number, sizeof(number), &vp); 495 if (error) 496 return error; 497 error = vn_lock(vp, LK_EXCLUSIVE); 498 if (error) { 499 vrele(vp); 500 return error; 501 } 502 503 *vpp = vp; 504 505 return 0; 506 } 507 508 int 509 v7fs_fhtovp(struct mount *mp, struct fid *fid, struct vnode **vpp) 510 { 511 512 DPRINTF("\n"); 513 /* notyet */ 514 return EOPNOTSUPP; 515 } 516 517 int 518 v7fs_vptofh(struct vnode *vpp, struct fid *fid, size_t *fh_size) 519 { 520 521 DPRINTF("\n"); 522 /* notyet */ 523 return EOPNOTSUPP; 524 } 525 526 void 527 v7fs_init(void) 528 { 529 530 DPRINTF("\n"); 531 pool_init(&v7fs_node_pool, sizeof(struct v7fs_node), 0, 0, 0, 532 "v7fs_node_pool", &pool_allocator_nointr, IPL_NONE); 533 } 534 535 void 536 v7fs_reinit(void) 537 { 538 539 /* Nothing to do. */ 540 DPRINTF("\n"); 541 } 542 543 void 544 v7fs_done(void) 545 { 546 547 DPRINTF("\n"); 548 pool_destroy(&v7fs_node_pool); 549 } 550 551 int 552 v7fs_gop_alloc(struct vnode *vp, off_t off, off_t len, int flags, 553 kauth_cred_t cred) 554 { 555 556 DPRINTF("\n"); 557 return 0; 558 } 559 560 int 561 v7fs_mountroot(void) 562 { 563 struct mount *mp; 564 int error; 565 566 DPRINTF(""); 567 /* On mountroot, devvp (rootdev) is opened by vfs_mountroot */ 568 if ((error = is_v7fs_partition (rootvp))) 569 return error; 570 571 if ((error = vfs_rootmountalloc(MOUNT_V7FS, "root_device", &mp))) { 572 DPRINTF("mountalloc error=%d\n", error); 573 vrele(rootvp); 574 return error; 575 } 576 577 if ((error = v7fs_mountfs(rootvp, mp, _BYTE_ORDER))) { 578 DPRINTF("mountfs error=%d\n", error); 579 vfs_unbusy(mp, false, NULL); 580 vfs_destroy(mp); 581 return error; 582 } 583 584 mountlist_append(mp); 585 586 vfs_unbusy(mp, false, NULL); 587 588 return 0; 589 } 590