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