/* Create the VM system backing object for this vnode */ int vnode_create_vobject(struct vnode *vp, off_t isize, struct thread *td) { vm_object_t object; vm_ooffset_t size = isize; struct vattr va; if (!vn_isdisk(vp, NULL) && vn_canvmio(vp) == FALSE) return (0); while ((object = vp->v_object) != NULL) { VM_OBJECT_WLOCK(object); if (!(object->flags & OBJ_DEAD)) { VM_OBJECT_WUNLOCK(object); return (0); } VOP_UNLOCK(vp, 0); vm_object_set_flag(object, OBJ_DISCONNECTWNT); VM_OBJECT_SLEEP(object, object, PDROP | PVM, "vodead", 0); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); } if (size == 0) { if (vn_isdisk(vp, NULL)) { size = IDX_TO_OFF(INT_MAX); } else { if (VOP_GETATTR(vp, &va, td->td_ucred)) return (0); size = va.va_size; } } object = vnode_pager_alloc(vp, size, 0, 0, td->td_ucred); /* * Dereference the reference we just created. This assumes * that the object is associated with the vp. */ VM_OBJECT_WLOCK(object); object->ref_count--; VM_OBJECT_WUNLOCK(object); vrele(vp); KASSERT(vp->v_object != NULL, ("vnode_create_vobject: NULL object")); return (0); }
static int devfs_spec_fsync(struct vop_fsync_args *ap) { struct vnode *vp = ap->a_vp; int error; if (!vn_isdisk(vp, NULL)) return (0); /* * Flush all dirty buffers associated with a block device. */ error = vfsync(vp, ap->a_waitfor, 10000, NULL, NULL); return (error); }
static int hammer_setup_device(struct vnode **devvpp, const char *dev_path, int ronly) { int error; struct nlookupdata nd; /* * Get the device vnode */ if (*devvpp == NULL) { error = nlookup_init(&nd, dev_path, UIO_SYSSPACE, NLC_FOLLOW); if (error == 0) error = nlookup(&nd); if (error == 0) error = cache_vref(&nd.nl_nch, nd.nl_cred, devvpp); nlookup_done(&nd); } else { error = 0; } if (error == 0) { if (vn_isdisk(*devvpp, &error)) { error = vfs_mountedon(*devvpp); } } if (error == 0 && vcount(*devvpp) > 0) error = EBUSY; if (error == 0) { vn_lock(*devvpp, LK_EXCLUSIVE | LK_RETRY); error = vinvalbuf(*devvpp, V_SAVE, 0, 0); if (error == 0) { error = VOP_OPEN(*devvpp, (ronly ? FREAD : FREAD|FWRITE), FSCRED, NULL); } vn_unlock(*devvpp); } if (error && *devvpp) { vrele(*devvpp); *devvpp = NULL; } return (error); }
/* ARGSUSED */ static int ffs_fsync(struct vop_fsync_args *ap) { struct vnode *vp = ap->a_vp; int error; if (vn_isdisk(vp, NULL)) if (vp->v_rdev && vp->v_rdev->si_mountpoint != NULL && (vp->v_rdev->si_mountpoint->mnt_flag & MNT_SOFTDEP)) softdep_fsync_mountdev(vp); /* * Flush all dirty buffers associated with a vnode. */ error = vfsync(vp, ap->a_waitfor, NIADDR + 1, ffs_checkdeferred, softdep_sync_metadata); if (error == 0) error = ffs_update(vp, (ap->a_waitfor == MNT_WAIT)); return (error); }
static int cd9660_mount(struct mount *mp, struct thread *td) { struct vnode *devvp; char *fspec; int error; mode_t accessmode; struct nameidata ndp; struct iso_mnt *imp = 0; /* * Unconditionally mount as read-only. */ MNT_ILOCK(mp); mp->mnt_flag |= MNT_RDONLY; MNT_IUNLOCK(mp); fspec = vfs_getopts(mp->mnt_optnew, "from", &error); if (error) return (error); imp = VFSTOISOFS(mp); if (mp->mnt_flag & MNT_UPDATE) { if (vfs_flagopt(mp->mnt_optnew, "export", NULL, 0)) return (0); } /* * Not an update, or updating the name: look up the name * and verify that it refers to a sensible block device. */ NDINIT(&ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspec, td); if ((error = namei(&ndp))) return (error); NDFREE(&ndp, NDF_ONLY_PNBUF); devvp = ndp.ni_vp; if (!vn_isdisk(devvp, &error)) { vput(devvp); return (error); } /* * Verify that user has necessary permissions on the device, * or has superuser abilities */ accessmode = VREAD; error = VOP_ACCESS(devvp, accessmode, td->td_ucred, td); if (error) error = priv_check(td, PRIV_VFS_MOUNT_PERM); if (error) { vput(devvp); return (error); } if ((mp->mnt_flag & MNT_UPDATE) == 0) { error = iso_mountfs(devvp, mp, td); if (error) vrele(devvp); } else { if (devvp != imp->im_devvp) error = EINVAL; /* needs translation */ vput(devvp); } if (error) return (error); vfs_mountedfrom(mp, fspec); return (0); }
static int udf_mount(struct mount *mp) { struct vnode *devvp; /* vnode of the mount device */ struct thread *td; struct udf_mnt *imp = NULL; struct vfsoptlist *opts; char *fspec, *cs_disk, *cs_local; int error, len, *udf_flags; struct nameidata nd, *ndp = &nd; td = curthread; opts = mp->mnt_optnew; /* * Unconditionally mount as read-only. */ MNT_ILOCK(mp); mp->mnt_flag |= MNT_RDONLY; MNT_IUNLOCK(mp); /* * No root filesystem support. Probably not a big deal, since the * bootloader doesn't understand UDF. */ if (mp->mnt_flag & MNT_ROOTFS) return (ENOTSUP); fspec = NULL; error = vfs_getopt(opts, "from", (void **)&fspec, &len); if (!error && fspec[len - 1] != '\0') return (EINVAL); if (mp->mnt_flag & MNT_UPDATE) { return (0); } /* Check that the mount device exists */ if (fspec == NULL) return (EINVAL); NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspec, td); if ((error = namei(ndp))) return (error); NDFREE(ndp, NDF_ONLY_PNBUF); devvp = ndp->ni_vp; if (vn_isdisk(devvp, &error) == 0) { vput(devvp); return (error); } /* Check the access rights on the mount device */ error = VOP_ACCESS(devvp, VREAD, td->td_ucred, td); if (error) error = priv_check(td, PRIV_VFS_MOUNT_PERM); if (error) { vput(devvp); return (error); } if ((error = udf_mountfs(devvp, mp))) { vrele(devvp); return (error); } imp = VFSTOUDFFS(mp); udf_flags = NULL; error = vfs_getopt(opts, "flags", (void **)&udf_flags, &len); if (error || len != sizeof(int)) return (EINVAL); imp->im_flags = *udf_flags; if (imp->im_flags & UDFMNT_KICONV && udf_iconv) { cs_disk = NULL; error = vfs_getopt(opts, "cs_disk", (void **)&cs_disk, &len); if (!error && cs_disk[len - 1] != '\0') return (EINVAL); cs_local = NULL; error = vfs_getopt(opts, "cs_local", (void **)&cs_local, &len); if (!error && cs_local[len - 1] != '\0') return (EINVAL); udf_iconv->open(cs_local, cs_disk, &imp->im_d2l); #if 0 udf_iconv->open(cs_disk, cs_local, &imp->im_l2d); #endif } vfs_mountedfrom(mp, fspec); return 0; };
/* * mp - path - addr in user space of mount point (ie /usr or whatever) * data - addr in user space of mount params including the name of the block * special file to treat as a filesystem. */ static int msdosfs_mount(struct mount *mp) { struct vnode *devvp; /* vnode for blk device to mount */ struct thread *td; /* msdosfs specific mount control block */ struct msdosfsmount *pmp = NULL; struct nameidata ndp; int error, flags; accmode_t accmode; char *from; td = curthread; if (vfs_filteropt(mp->mnt_optnew, msdosfs_opts)) return (EINVAL); /* * If updating, check whether changing from read-only to * read/write; if there is no device name, that's all we do. */ if (mp->mnt_flag & MNT_UPDATE) { pmp = VFSTOMSDOSFS(mp); if (vfs_flagopt(mp->mnt_optnew, "export", NULL, 0)) { /* * Forbid export requests if filesystem has * MSDOSFS_LARGEFS flag set. */ if ((pmp->pm_flags & MSDOSFS_LARGEFS) != 0) { vfs_mount_error(mp, "MSDOSFS_LARGEFS flag set, cannot export"); return (EOPNOTSUPP); } } if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) && vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) { error = VFS_SYNC(mp, MNT_WAIT); if (error) return (error); flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; error = vflush(mp, 0, flags, td); if (error) return (error); /* * Now the volume is clean. Mark it so while the * device is still rw. */ error = markvoldirty(pmp, 0); if (error) { (void)markvoldirty(pmp, 1); return (error); } /* Downgrade the device from rw to ro. */ DROP_GIANT(); g_topology_lock(); error = g_access(pmp->pm_cp, 0, -1, 0); g_topology_unlock(); PICKUP_GIANT(); if (error) { (void)markvoldirty(pmp, 1); return (error); } /* * Backing out after an error was painful in the * above. Now we are committed to succeeding. */ pmp->pm_fmod = 0; pmp->pm_flags |= MSDOSFSMNT_RONLY; MNT_ILOCK(mp); mp->mnt_flag |= MNT_RDONLY; MNT_IUNLOCK(mp); } else if ((pmp->pm_flags & MSDOSFSMNT_RONLY) && !vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) { /* * If upgrade to read-write by non-root, then verify * that user has necessary permissions on the device. */ devvp = pmp->pm_devvp; vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); error = VOP_ACCESS(devvp, VREAD | VWRITE, td->td_ucred, td); if (error) error = priv_check(td, PRIV_VFS_MOUNT_PERM); if (error) { VOP_UNLOCK(devvp, 0); return (error); } VOP_UNLOCK(devvp, 0); DROP_GIANT(); g_topology_lock(); error = g_access(pmp->pm_cp, 0, 1, 0); g_topology_unlock(); PICKUP_GIANT(); if (error) return (error); pmp->pm_fmod = 1; pmp->pm_flags &= ~MSDOSFSMNT_RONLY; MNT_ILOCK(mp); mp->mnt_flag &= ~MNT_RDONLY; MNT_IUNLOCK(mp); /* Now that the volume is modifiable, mark it dirty. */ error = markvoldirty(pmp, 1); if (error) return (error); } } /* * Not an update, or updating the name: look up the name * and verify that it refers to a sensible disk device. */ if (vfs_getopt(mp->mnt_optnew, "from", (void **)&from, NULL)) return (EINVAL); NDINIT(&ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, from, td); error = namei(&ndp); if (error) return (error); devvp = ndp.ni_vp; NDFREE(&ndp, NDF_ONLY_PNBUF); if (!vn_isdisk(devvp, &error)) { vput(devvp); return (error); } /* * If mount by non-root, then verify that user has necessary * permissions on the device. */ accmode = VREAD; if ((mp->mnt_flag & MNT_RDONLY) == 0) accmode |= VWRITE; error = VOP_ACCESS(devvp, accmode, td->td_ucred, td); if (error) error = priv_check(td, PRIV_VFS_MOUNT_PERM); if (error) { vput(devvp); return (error); } if ((mp->mnt_flag & MNT_UPDATE) == 0) { error = mountmsdosfs(devvp, mp); #ifdef MSDOSFS_DEBUG /* only needed for the printf below */ pmp = VFSTOMSDOSFS(mp); #endif } else { vput(devvp); if (devvp != pmp->pm_devvp) return (EINVAL); /* XXX needs translation */ } if (error) { vrele(devvp); return (error); } error = update_mp(mp, td); if (error) { if ((mp->mnt_flag & MNT_UPDATE) == 0) msdosfs_unmount(mp, MNT_FORCE); return error; } if (devvp->v_type == VCHR && devvp->v_rdev != NULL) devvp->v_rdev->si_mountpt = mp; vfs_mountedfrom(mp, from); #ifdef MSDOSFS_DEBUG printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap); #endif return (0); }
/* * VFS Operations. * * mount system call */ static int ext2_mount(struct mount *mp) { struct vfsoptlist *opts; struct vnode *devvp; struct thread *td; struct ext2mount *ump = NULL; struct m_ext2fs *fs; struct nameidata nd, *ndp = &nd; accmode_t accmode; char *path, *fspec; int error, flags, len; td = curthread; opts = mp->mnt_optnew; if (vfs_filteropt(opts, ext2_opts)) return (EINVAL); vfs_getopt(opts, "fspath", (void **)&path, NULL); /* Double-check the length of path.. */ if (strlen(path) >= MAXMNTLEN - 1) return (ENAMETOOLONG); fspec = NULL; error = vfs_getopt(opts, "from", (void **)&fspec, &len); if (!error && fspec[len - 1] != '\0') return (EINVAL); /* * If updating, check whether changing from read-only to * read/write; if there is no device name, that's all we do. */ if (mp->mnt_flag & MNT_UPDATE) { ump = VFSTOEXT2(mp); fs = ump->um_e2fs; error = 0; if (fs->e2fs_ronly == 0 && vfs_flagopt(opts, "ro", NULL, 0)) { error = VFS_SYNC(mp, MNT_WAIT); if (error) return (error); flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; error = ext2_flushfiles(mp, flags, td); if ( error == 0 && fs->e2fs_wasvalid && ext2_cgupdate(ump, MNT_WAIT) == 0) { fs->e2fs->e2fs_state |= E2FS_ISCLEAN; ext2_sbupdate(ump, MNT_WAIT); } fs->e2fs_ronly = 1; vfs_flagopt(opts, "ro", &mp->mnt_flag, MNT_RDONLY); DROP_GIANT(); g_topology_lock(); g_access(ump->um_cp, 0, -1, 0); g_topology_unlock(); PICKUP_GIANT(); } if (!error && (mp->mnt_flag & MNT_RELOAD)) error = ext2_reload(mp, td); if (error) return (error); devvp = ump->um_devvp; if (fs->e2fs_ronly && !vfs_flagopt(opts, "ro", NULL, 0)) { if (ext2_check_sb_compat(fs->e2fs, devvp->v_rdev, 0)) return (EPERM); /* * If upgrade to read-write by non-root, then verify * that user has necessary permissions on the device. */ vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); error = VOP_ACCESS(devvp, VREAD | VWRITE, td->td_ucred, td); if (error) error = priv_check(td, PRIV_VFS_MOUNT_PERM); if (error) { VOP_UNLOCK(devvp, 0); return (error); } VOP_UNLOCK(devvp, 0); DROP_GIANT(); g_topology_lock(); error = g_access(ump->um_cp, 0, 1, 0); g_topology_unlock(); PICKUP_GIANT(); if (error) return (error); if ((fs->e2fs->e2fs_state & E2FS_ISCLEAN) == 0 || (fs->e2fs->e2fs_state & E2FS_ERRORS)) { if (mp->mnt_flag & MNT_FORCE) { printf( "WARNING: %s was not properly dismounted\n", fs->e2fs_fsmnt); } else { printf( "WARNING: R/W mount of %s denied. Filesystem is not clean - run fsck\n", fs->e2fs_fsmnt); return (EPERM); } } fs->e2fs->e2fs_state &= ~E2FS_ISCLEAN; (void)ext2_cgupdate(ump, MNT_WAIT); fs->e2fs_ronly = 0; MNT_ILOCK(mp); mp->mnt_flag &= ~MNT_RDONLY; MNT_IUNLOCK(mp); } if (vfs_flagopt(opts, "export", NULL, 0)) { /* Process export requests in vfs_mount.c. */ return (error); } } /* * Not an update, or updating the name: look up the name * and verify that it refers to a sensible disk device. */ if (fspec == NULL) return (EINVAL); NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspec, td); if ((error = namei(ndp)) != 0) return (error); NDFREE(ndp, NDF_ONLY_PNBUF); devvp = ndp->ni_vp; if (!vn_isdisk(devvp, &error)) { vput(devvp); return (error); } /* * If mount by non-root, then verify that user has necessary * permissions on the device. * * XXXRW: VOP_ACCESS() enough? */ accmode = VREAD; if ((mp->mnt_flag & MNT_RDONLY) == 0) accmode |= VWRITE; error = VOP_ACCESS(devvp, accmode, td->td_ucred, td); if (error) error = priv_check(td, PRIV_VFS_MOUNT_PERM); if (error) { vput(devvp); return (error); } if ((mp->mnt_flag & MNT_UPDATE) == 0) { error = ext2_mountfs(devvp, mp); } else { if (devvp != ump->um_devvp) { vput(devvp); return (EINVAL); /* needs translation */ } else vput(devvp); } if (error) { vrele(devvp); return (error); } ump = VFSTOEXT2(mp); fs = ump->um_e2fs; /* * Note that this strncpy() is ok because of a check at the start * of ext2_mount(). */ strncpy(fs->e2fs_fsmnt, path, MAXMNTLEN); fs->e2fs_fsmnt[MAXMNTLEN - 1] = '\0'; vfs_mountedfrom(mp, fspec); return (0); }
static int ntfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred) { size_t size; int error; struct vnode *devvp; struct ntfs_args args; struct nlookupdata nd; struct vnode *rootvp; error = 0; /* * Use NULL path to flag a root mount */ if( path == NULL) { /* *** * Mounting root file system *** */ /* Get vnode for root device*/ if( bdevvp( rootdev, &rootvp)) panic("ffs_mountroot: can't setup bdevvp for root"); /* * FS specific handling */ mp->mnt_flag |= MNT_RDONLY; /* XXX globally applicable?*/ /* * Attempt mount */ if( ( error = ntfs_mountfs(rootvp, mp, &args, cred)) != 0) { /* fs specific cleanup (if any)*/ goto error_1; } goto dostatfs; /* success*/ } /* *** * Mounting non-root file system or updating a file system *** */ /* copy in user arguments*/ error = copyin(data, (caddr_t)&args, sizeof (struct ntfs_args)); if (error) goto error_1; /* can't get arguments*/ /* * If updating, check whether changing from read-only to * read/write; if there is no device name, that's all we do. */ if (mp->mnt_flag & MNT_UPDATE) { /* if not updating name...*/ if (args.fspec == NULL) { /* * Process export requests. Jumping to "success" * will return the vfs_export() error code. */ struct ntfsmount *ntm = VFSTONTFS(mp); error = vfs_export(mp, &ntm->ntm_export, &args.export); goto success; } kprintf("ntfs_mount(): MNT_UPDATE not supported\n"); error = EINVAL; goto error_1; } /* * Not an update, or updating the name: look up the name * and verify that it refers to a sensible block device. */ devvp = NULL; error = nlookup_init(&nd, args.fspec, UIO_USERSPACE, NLC_FOLLOW); if (error == 0) error = nlookup(&nd); if (error == 0) error = cache_vref(&nd.nl_nch, nd.nl_cred, &devvp); nlookup_done(&nd); if (error) goto error_1; if (!vn_isdisk(devvp, &error)) goto error_2; if (mp->mnt_flag & MNT_UPDATE) { #if 0 /* ******************** * UPDATE ******************** */ if (devvp != ntmp->um_devvp) error = EINVAL; /* needs translation */ else vrele(devvp); /* * Update device name only on success */ if( !error) { /* Save "mounted from" info for mount point (NULL pad)*/ copyinstr( args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero( mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); } #endif } else { /* ******************** * NEW MOUNT ******************** */ /* Save "mounted from" info for mount point (NULL pad)*/ copyinstr( args.fspec, /* device name*/ mp->mnt_stat.f_mntfromname, /* save area*/ MNAMELEN - 1, /* max size*/ &size); /* real size*/ bzero( mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); error = ntfs_mountfs(devvp, mp, &args, cred); } if (error) { goto error_2; } dostatfs: /* * Initialize FS stat information in mount struct; uses * mp->mnt_stat.f_mntfromname. * * This code is common to root and non-root mounts */ (void)VFS_STATFS(mp, &mp->mnt_stat, cred); goto success; error_2: /* error with devvp held*/ /* release devvp before failing*/ vrele(devvp); error_1: /* no state to back out*/ success: return(error); }
/************************************************************************ * VOLUMES * ************************************************************************ * * Load a HAMMER volume by name. Returns 0 on success or a positive error * code on failure. Volumes must be loaded at mount time, get_volume() will * not load a new volume. * * The passed devvp is vref()'d but not locked. This function consumes the * ref (typically by associating it with the volume structure). * * Calls made to hammer_load_volume() or single-threaded */ int hammer_install_volume(struct hammer_mount *hmp, const char *volname, struct vnode *devvp) { struct mount *mp; hammer_volume_t volume; struct hammer_volume_ondisk *ondisk; struct nlookupdata nd; struct buf *bp = NULL; int error; int ronly; int setmp = 0; mp = hmp->mp; ronly = ((mp->mnt_flag & MNT_RDONLY) ? 1 : 0); /* * Allocate a volume structure */ ++hammer_count_volumes; volume = kmalloc(sizeof(*volume), hmp->m_misc, M_WAITOK|M_ZERO); volume->vol_name = kstrdup(volname, hmp->m_misc); volume->io.hmp = hmp; /* bootstrap */ hammer_io_init(&volume->io, volume, HAMMER_STRUCTURE_VOLUME); volume->io.offset = 0LL; volume->io.bytes = HAMMER_BUFSIZE; /* * Get the device vnode */ if (devvp == NULL) { error = nlookup_init(&nd, volume->vol_name, UIO_SYSSPACE, NLC_FOLLOW); if (error == 0) error = nlookup(&nd); if (error == 0) error = cache_vref(&nd.nl_nch, nd.nl_cred, &volume->devvp); nlookup_done(&nd); } else { error = 0; volume->devvp = devvp; } if (error == 0) { if (vn_isdisk(volume->devvp, &error)) { error = vfs_mountedon(volume->devvp); } } if (error == 0 && vcount(volume->devvp) > 0) error = EBUSY; if (error == 0) { vn_lock(volume->devvp, LK_EXCLUSIVE | LK_RETRY); error = vinvalbuf(volume->devvp, V_SAVE, 0, 0); if (error == 0) { error = VOP_OPEN(volume->devvp, (ronly ? FREAD : FREAD|FWRITE), FSCRED, NULL); } vn_unlock(volume->devvp); } if (error) { hammer_free_volume(volume); return(error); } volume->devvp->v_rdev->si_mountpoint = mp; setmp = 1; /* * Extract the volume number from the volume header and do various * sanity checks. */ error = bread(volume->devvp, 0LL, HAMMER_BUFSIZE, &bp); if (error) goto late_failure; ondisk = (void *)bp->b_data; if (ondisk->vol_signature != HAMMER_FSBUF_VOLUME) { kprintf("hammer_mount: volume %s has an invalid header\n", volume->vol_name); error = EFTYPE; goto late_failure; } volume->vol_no = ondisk->vol_no; volume->buffer_base = ondisk->vol_buf_beg; volume->vol_flags = ondisk->vol_flags; volume->nblocks = ondisk->vol_nblocks; volume->maxbuf_off = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no, ondisk->vol_buf_end - ondisk->vol_buf_beg); volume->maxraw_off = ondisk->vol_buf_end; if (RB_EMPTY(&hmp->rb_vols_root)) { hmp->fsid = ondisk->vol_fsid; } else if (bcmp(&hmp->fsid, &ondisk->vol_fsid, sizeof(uuid_t))) { kprintf("hammer_mount: volume %s's fsid does not match " "other volumes\n", volume->vol_name); error = EFTYPE; goto late_failure; } /* * Insert the volume structure into the red-black tree. */ if (RB_INSERT(hammer_vol_rb_tree, &hmp->rb_vols_root, volume)) { kprintf("hammer_mount: volume %s has a duplicate vol_no %d\n", volume->vol_name, volume->vol_no); error = EEXIST; } /* * Set the root volume . HAMMER special cases rootvol the structure. * We do not hold a ref because this would prevent related I/O * from being flushed. */ if (error == 0 && ondisk->vol_rootvol == ondisk->vol_no) { hmp->rootvol = volume; hmp->nvolumes = ondisk->vol_count; if (bp) { brelse(bp); bp = NULL; } hmp->mp->mnt_stat.f_blocks += ondisk->vol0_stat_bigblocks * (HAMMER_LARGEBLOCK_SIZE / HAMMER_BUFSIZE); hmp->mp->mnt_vstat.f_blocks += ondisk->vol0_stat_bigblocks * (HAMMER_LARGEBLOCK_SIZE / HAMMER_BUFSIZE); } late_failure: if (bp) brelse(bp); if (error) { /*vinvalbuf(volume->devvp, V_SAVE, 0, 0);*/ if (setmp) volume->devvp->v_rdev->si_mountpoint = NULL; vn_lock(volume->devvp, LK_EXCLUSIVE | LK_RETRY); VOP_CLOSE(volume->devvp, ronly ? FREAD : FREAD|FWRITE); vn_unlock(volume->devvp); hammer_free_volume(volume); } return (error); }
static int ntfs_mount(struct mount *mp) { int err = 0, error; struct vnode *devvp; struct nameidata ndp; struct thread *td; char *from; td = curthread; if (vfs_filteropt(mp->mnt_optnew, ntfs_opts)) return (EINVAL); /* Force mount as read-only. */ MNT_ILOCK(mp); mp->mnt_flag |= MNT_RDONLY; MNT_IUNLOCK(mp); from = vfs_getopts(mp->mnt_optnew, "from", &error); if (error) return (error); /* * If updating, check whether changing from read-only to * read/write. */ if (mp->mnt_flag & MNT_UPDATE) { if (vfs_flagopt(mp->mnt_optnew, "export", NULL, 0)) { /* Process export requests in vfs_mount.c */ return (0); } else { printf("ntfs_mount(): MNT_UPDATE not supported\n"); return (EINVAL); } } /* * Not an update, or updating the name: look up the name * and verify that it refers to a sensible block device. */ NDINIT(&ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, from, td); err = namei(&ndp); if (err) return (err); NDFREE(&ndp, NDF_ONLY_PNBUF); devvp = ndp.ni_vp; if (!vn_isdisk(devvp, &err)) { vput(devvp); return (err); } /* * If mount by non-root, then verify that user has necessary * permissions on the device. */ err = VOP_ACCESS(devvp, VREAD, td->td_ucred, td); if (err) err = priv_check(td, PRIV_VFS_MOUNT_PERM); if (err) { vput(devvp); return (err); } /* * Since this is a new mount, we want the names for the device and * the mount point copied in. If an error occurs, the mountpoint is * discarded by the upper level code. Note that vfs_mount() handles * copying the mountpoint f_mntonname for us, so we don't have to do * it here unless we want to set it to something other than "path" * for some rason. */ err = ntfs_mountfs(devvp, mp, td); if (err == 0) { /* Save "mounted from" info for mount point. */ vfs_mountedfrom(mp, from); } else vrele(devvp); return (err); }
/* * Mount system call */ static int reiserfs_mount(struct mount *mp) { size_t size; int error, len; accmode_t accmode; char *path, *fspec; struct vnode *devvp; struct vfsoptlist *opts; struct reiserfs_mount *rmp; struct reiserfs_sb_info *sbi; struct nameidata nd, *ndp = &nd; struct thread *td; td = curthread; if (!(mp->mnt_flag & MNT_RDONLY)) return EROFS; /* Get the new options passed to mount */ opts = mp->mnt_optnew; /* `fspath' contains the mount point (eg. /mnt/linux); REQUIRED */ vfs_getopt(opts, "fspath", (void **)&path, NULL); reiserfs_log(LOG_INFO, "mount point is `%s'\n", path); /* `from' contains the device name (eg. /dev/ad0s1); REQUIRED */ fspec = NULL; error = vfs_getopt(opts, "from", (void **)&fspec, &len); if (!error && fspec[len - 1] != '\0') return (EINVAL); reiserfs_log(LOG_INFO, "device is `%s'\n", fspec); /* Handle MNT_UPDATE (mp->mnt_flag) */ if (mp->mnt_flag & MNT_UPDATE) { /* For now, only NFS export is supported. */ if (vfs_flagopt(opts, "export", NULL, 0)) return (0); } /* Not an update, or updating the name: look up the name * and verify that it refers to a sensible disk device. */ if (fspec == NULL) return (EINVAL); NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspec, td); if ((error = namei(ndp)) != 0) return (error); NDFREE(ndp, NDF_ONLY_PNBUF); devvp = ndp->ni_vp; if (!vn_isdisk(devvp, &error)) { vput(devvp); return (error); } /* If mount by non-root, then verify that user has necessary * permissions on the device. */ accmode = VREAD; if ((mp->mnt_flag & MNT_RDONLY) == 0) accmode |= VWRITE; error = VOP_ACCESS(devvp, accmode, td->td_ucred, td); if (error) error = priv_check(td, PRIV_VFS_MOUNT_PERM); if (error) { vput(devvp); return (error); } if ((mp->mnt_flag & MNT_UPDATE) == 0) { error = reiserfs_mountfs(devvp, mp, td); } else { /* TODO Handle MNT_UPDATE */ vput(devvp); return (EOPNOTSUPP); } if (error) { vrele(devvp); return (error); } rmp = VFSTOREISERFS(mp); sbi = rmp->rm_reiserfs; /* * Note that this strncpy() is ok because of a check at the start * of reiserfs_mount(). */ reiserfs_log(LOG_DEBUG, "prepare statfs data\n"); (void)copystr(fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); (void)reiserfs_statfs(mp, &mp->mnt_stat); reiserfs_log(LOG_DEBUG, "done\n"); return (0); }
/* * MPSAFE */ int vn_stat(struct vnode *vp, struct stat *sb, struct ucred *cred) { struct vattr vattr; struct vattr *vap; int error; u_short mode; cdev_t dev; vap = &vattr; error = VOP_GETATTR(vp, vap); if (error) return (error); /* * Zero the spare stat fields */ sb->st_lspare = 0; sb->st_qspare1 = 0; sb->st_qspare2 = 0; /* * Copy from vattr table */ if (vap->va_fsid != VNOVAL) sb->st_dev = vap->va_fsid; else sb->st_dev = vp->v_mount->mnt_stat.f_fsid.val[0]; sb->st_ino = vap->va_fileid; mode = vap->va_mode; switch (vap->va_type) { case VREG: mode |= S_IFREG; break; case VDATABASE: mode |= S_IFDB; break; case VDIR: mode |= S_IFDIR; break; case VBLK: mode |= S_IFBLK; break; case VCHR: mode |= S_IFCHR; break; case VLNK: mode |= S_IFLNK; /* This is a cosmetic change, symlinks do not have a mode. */ if (vp->v_mount->mnt_flag & MNT_NOSYMFOLLOW) sb->st_mode &= ~ACCESSPERMS; /* 0000 */ else sb->st_mode |= ACCESSPERMS; /* 0777 */ break; case VSOCK: mode |= S_IFSOCK; break; case VFIFO: mode |= S_IFIFO; break; default: return (EBADF); } sb->st_mode = mode; if (vap->va_nlink > (nlink_t)-1) sb->st_nlink = (nlink_t)-1; else sb->st_nlink = vap->va_nlink; sb->st_uid = vap->va_uid; sb->st_gid = vap->va_gid; sb->st_rdev = dev2udev(vp->v_rdev); sb->st_size = vap->va_size; sb->st_atimespec = vap->va_atime; sb->st_mtimespec = vap->va_mtime; sb->st_ctimespec = vap->va_ctime; /* * A VCHR and VBLK device may track the last access and last modified * time independantly of the filesystem. This is particularly true * because device read and write calls may bypass the filesystem. */ if (vp->v_type == VCHR || vp->v_type == VBLK) { dev = vp->v_rdev; if (dev != NULL) { if (dev->si_lastread) { sb->st_atimespec.tv_sec = dev->si_lastread; sb->st_atimespec.tv_nsec = 0; } if (dev->si_lastwrite) { sb->st_atimespec.tv_sec = dev->si_lastwrite; sb->st_atimespec.tv_nsec = 0; } } } /* * According to www.opengroup.org, the meaning of st_blksize is * "a filesystem-specific preferred I/O block size for this * object. In some filesystem types, this may vary from file * to file" * Default to PAGE_SIZE after much discussion. */ if (vap->va_type == VREG) { sb->st_blksize = vap->va_blocksize; } else if (vn_isdisk(vp, NULL)) { /* * XXX this is broken. If the device is not yet open (aka * stat() call, aka v_rdev == NULL), how are we supposed * to get a valid block size out of it? */ dev = vp->v_rdev; sb->st_blksize = dev->si_bsize_best; if (sb->st_blksize < dev->si_bsize_phys) sb->st_blksize = dev->si_bsize_phys; if (sb->st_blksize < BLKDEV_IOSIZE) sb->st_blksize = BLKDEV_IOSIZE; } else { sb->st_blksize = PAGE_SIZE; } sb->st_flags = vap->va_flags; error = priv_check_cred(cred, PRIV_VFS_GENERATION, 0); if (error) sb->st_gen = 0; else sb->st_gen = (u_int32_t)vap->va_gen; sb->st_blocks = vap->va_bytes / S_BLKSIZE; return (0); }
static int devfs_spec_open(struct vop_open_args *ap) { struct vnode *vp = ap->a_vp; struct vnode *orig_vp = NULL; struct devfs_node *node = DEVFS_NODE(vp); struct devfs_node *newnode; cdev_t dev, ndev = NULL; int error = 0; if (node) { if (node->d_dev == NULL) return ENXIO; if (!devfs_node_is_accessible(node)) return ENOENT; } if ((dev = vp->v_rdev) == NULL) return ENXIO; vn_lock(vp, LK_UPGRADE | LK_RETRY); if (node && ap->a_fp) { devfs_debug(DEVFS_DEBUG_DEBUG, "devfs_spec_open: -1.1-\n"); lockmgr(&devfs_lock, LK_EXCLUSIVE); ndev = devfs_clone(dev, node->d_dir.d_name, node->d_dir.d_namlen, ap->a_mode, ap->a_cred); if (ndev != NULL) { newnode = devfs_create_device_node( DEVFS_MNTDATA(vp->v_mount)->root_node, ndev, NULL, NULL); /* XXX: possibly destroy device if this happens */ if (newnode != NULL) { dev = ndev; devfs_link_dev(dev); devfs_debug(DEVFS_DEBUG_DEBUG, "parent here is: %s, node is: |%s|\n", ((node->parent->node_type == Nroot) ? "ROOT!" : node->parent->d_dir.d_name), newnode->d_dir.d_name); devfs_debug(DEVFS_DEBUG_DEBUG, "test: %s\n", ((struct devfs_node *)(TAILQ_LAST(DEVFS_DENODE_HEAD(node->parent), devfs_node_head)))->d_dir.d_name); /* * orig_vp is set to the original vp if we cloned. */ /* node->flags |= DEVFS_CLONED; */ devfs_allocv(&vp, newnode); orig_vp = ap->a_vp; ap->a_vp = vp; } } lockmgr(&devfs_lock, LK_RELEASE); } devfs_debug(DEVFS_DEBUG_DEBUG, "devfs_spec_open() called on %s! \n", dev->si_name); /* * Make this field valid before any I/O in ->d_open */ if (!dev->si_iosize_max) /* XXX: old DFLTPHYS == 64KB dependency */ dev->si_iosize_max = min(MAXPHYS,64*1024); if (dev_dflags(dev) & D_TTY) vsetflags(vp, VISTTY); /* * Open underlying device */ vn_unlock(vp); error = dev_dopen(dev, ap->a_mode, S_IFCHR, ap->a_cred, ap->a_fp); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); /* * Clean up any cloned vp if we error out. */ if (error) { if (orig_vp) { vput(vp); ap->a_vp = orig_vp; /* orig_vp = NULL; */ } return error; } /* * This checks if the disk device is going to be opened for writing. * It will be only allowed in the cases where securelevel permits it * and it's not mounted R/W. */ if ((dev_dflags(dev) & D_DISK) && (ap->a_mode & FWRITE) && (ap->a_cred != FSCRED)) { /* Very secure mode. No open for writing allowed */ if (securelevel >= 2) return EPERM; /* * If it is mounted R/W, do not allow to open for writing. * In the case it's mounted read-only but securelevel * is >= 1, then do not allow opening for writing either. */ if (vfs_mountedon(vp)) { if (!(dev->si_mountpoint->mnt_flag & MNT_RDONLY)) return EBUSY; else if (securelevel >= 1) return EPERM; } } if (dev_dflags(dev) & D_TTY) { if (dev->si_tty) { struct tty *tp; tp = dev->si_tty; if (!tp->t_stop) { devfs_debug(DEVFS_DEBUG_DEBUG, "devfs: no t_stop\n"); tp->t_stop = nottystop; } } } if (vn_isdisk(vp, NULL)) { if (!dev->si_bsize_phys) dev->si_bsize_phys = DEV_BSIZE; vinitvmio(vp, IDX_TO_OFF(INT_MAX), PAGE_SIZE, -1); } vop_stdopen(ap); #if 0 if (node) nanotime(&node->atime); #endif /* * If we replaced the vp the vop_stdopen() call will have loaded * it into fp->f_data and vref()d the vp, giving us two refs. So * instead of just unlocking it here we have to vput() it. */ if (orig_vp) vput(vp); /* Ugly pty magic, to make pty devices appear once they are opened */ if (node && (node->flags & DEVFS_PTY) == DEVFS_PTY) node->flags &= ~DEVFS_INVISIBLE; if (ap->a_fp) { KKASSERT(ap->a_fp->f_type == DTYPE_VNODE); KKASSERT((ap->a_fp->f_flag & FMASK) == (ap->a_mode & FMASK)); ap->a_fp->f_ops = &devfs_dev_fileops; KKASSERT(ap->a_fp->f_data == (void *)vp); } return 0; }
/* * spec_getpages() - get pages associated with device vnode. * * Note that spec_read and spec_write do not use the buffer cache, so we * must fully implement getpages here. */ static int devfs_spec_getpages(struct vop_getpages_args *ap) { vm_offset_t kva; int error; int i, pcount, size; struct buf *bp; vm_page_t m; vm_ooffset_t offset; int toff, nextoff, nread; struct vnode *vp = ap->a_vp; int blksiz; int gotreqpage; error = 0; pcount = round_page(ap->a_count) / PAGE_SIZE; /* * Calculate the offset of the transfer and do sanity check. */ offset = IDX_TO_OFF(ap->a_m[0]->pindex) + ap->a_offset; /* * Round up physical size for real devices. We cannot round using * v_mount's block size data because v_mount has nothing to do with * the device. i.e. it's usually '/dev'. We need the physical block * size for the device itself. * * We can't use v_rdev->si_mountpoint because it only exists when the * block device is mounted. However, we can use v_rdev. */ if (vn_isdisk(vp, NULL)) blksiz = vp->v_rdev->si_bsize_phys; else blksiz = DEV_BSIZE; size = (ap->a_count + blksiz - 1) & ~(blksiz - 1); bp = getpbuf_kva(NULL); kva = (vm_offset_t)bp->b_data; /* * Map the pages to be read into the kva. */ pmap_qenter(kva, ap->a_m, pcount); /* Build a minimal buffer header. */ bp->b_cmd = BUF_CMD_READ; bp->b_bcount = size; bp->b_resid = 0; bsetrunningbufspace(bp, size); bp->b_bio1.bio_offset = offset; bp->b_bio1.bio_done = devfs_spec_getpages_iodone; mycpu->gd_cnt.v_vnodein++; mycpu->gd_cnt.v_vnodepgsin += pcount; /* Do the input. */ vn_strategy(ap->a_vp, &bp->b_bio1); crit_enter(); /* We definitely need to be at splbio here. */ while (bp->b_cmd != BUF_CMD_DONE) tsleep(bp, 0, "spread", 0); crit_exit(); if (bp->b_flags & B_ERROR) { if (bp->b_error) error = bp->b_error; else error = EIO; } /* * If EOF is encountered we must zero-extend the result in order * to ensure that the page does not contain garabge. When no * error occurs, an early EOF is indicated if b_bcount got truncated. * b_resid is relative to b_bcount and should be 0, but some devices * might indicate an EOF with b_resid instead of truncating b_bcount. */ nread = bp->b_bcount - bp->b_resid; if (nread < ap->a_count) bzero((caddr_t)kva + nread, ap->a_count - nread); pmap_qremove(kva, pcount); gotreqpage = 0; for (i = 0, toff = 0; i < pcount; i++, toff = nextoff) { nextoff = toff + PAGE_SIZE; m = ap->a_m[i]; m->flags &= ~PG_ZERO; /* * NOTE: vm_page_undirty/clear_dirty etc do not clear the * pmap modified bit. pmap modified bit should have * already been cleared. */ if (nextoff <= nread) { m->valid = VM_PAGE_BITS_ALL; vm_page_undirty(m); } else if (toff < nread) { /* * Since this is a VM request, we have to supply the * unaligned offset to allow vm_page_set_valid() * to zero sub-DEV_BSIZE'd portions of the page. */ vm_page_set_valid(m, 0, nread - toff); vm_page_clear_dirty_end_nonincl(m, 0, nread - toff); } else { m->valid = 0; vm_page_undirty(m); } if (i != ap->a_reqpage) { /* * Just in case someone was asking for this page we * now tell them that it is ok to use. */ if (!error || (m->valid == VM_PAGE_BITS_ALL)) { if (m->valid) { if (m->flags & PG_REFERENCED) { vm_page_activate(m); } else { vm_page_deactivate(m); } vm_page_wakeup(m); } else { vm_page_free(m); } } else { vm_page_free(m); } } else if (m->valid) { gotreqpage = 1; /* * Since this is a VM request, we need to make the * entire page presentable by zeroing invalid sections. */ if (m->valid != VM_PAGE_BITS_ALL) vm_page_zero_invalid(m, FALSE); } } if (!gotreqpage) { m = ap->a_m[ap->a_reqpage]; devfs_debug(DEVFS_DEBUG_WARNING, "spec_getpages:(%s) I/O read failure: (error=%d) bp %p vp %p\n", devtoname(vp->v_rdev), error, bp, bp->b_vp); devfs_debug(DEVFS_DEBUG_WARNING, " size: %d, resid: %d, a_count: %d, valid: 0x%x\n", size, bp->b_resid, ap->a_count, m->valid); devfs_debug(DEVFS_DEBUG_WARNING, " nread: %d, reqpage: %d, pindex: %lu, pcount: %d\n", nread, ap->a_reqpage, (u_long)m->pindex, pcount); /* * Free the buffer header back to the swap buffer pool. */ relpbuf(bp, NULL); return VM_PAGER_ERROR; } /* * Free the buffer header back to the swap buffer pool. */ relpbuf(bp, NULL); if (DEVFS_NODE(ap->a_vp)) nanotime(&DEVFS_NODE(ap->a_vp)->mtime); return VM_PAGER_OK; }
/* * Convert a vnode strategy call into a device strategy call. Vnode strategy * calls are not limited to device DMA limits so we have to deal with the * case. * * spec_strategy(struct vnode *a_vp, struct bio *a_bio) */ static int devfs_spec_strategy(struct vop_strategy_args *ap) { struct bio *bio = ap->a_bio; struct buf *bp = bio->bio_buf; struct buf *nbp; struct vnode *vp; struct mount *mp; int chunksize; int maxiosize; if (bp->b_cmd != BUF_CMD_READ && LIST_FIRST(&bp->b_dep) != NULL) buf_start(bp); /* * Collect statistics on synchronous and asynchronous read * and write counts for disks that have associated filesystems. */ vp = ap->a_vp; KKASSERT(vp->v_rdev != NULL); /* XXX */ if (vn_isdisk(vp, NULL) && (mp = vp->v_rdev->si_mountpoint) != NULL) { if (bp->b_cmd == BUF_CMD_READ) { if (bp->b_flags & BIO_SYNC) mp->mnt_stat.f_syncreads++; else mp->mnt_stat.f_asyncreads++; } else { if (bp->b_flags & BIO_SYNC) mp->mnt_stat.f_syncwrites++; else mp->mnt_stat.f_asyncwrites++; } } /* * Device iosize limitations only apply to read and write. Shortcut * the I/O if it fits. */ if ((maxiosize = vp->v_rdev->si_iosize_max) == 0) { devfs_debug(DEVFS_DEBUG_DEBUG, "%s: si_iosize_max not set!\n", dev_dname(vp->v_rdev)); maxiosize = MAXPHYS; } #if SPEC_CHAIN_DEBUG & 2 maxiosize = 4096; #endif if (bp->b_bcount <= maxiosize || (bp->b_cmd != BUF_CMD_READ && bp->b_cmd != BUF_CMD_WRITE)) { dev_dstrategy_chain(vp->v_rdev, bio); return (0); } /* * Clone the buffer and set up an I/O chain to chunk up the I/O. */ nbp = kmalloc(sizeof(*bp), M_DEVBUF, M_INTWAIT|M_ZERO); initbufbio(nbp); buf_dep_init(nbp); BUF_LOCK(nbp, LK_EXCLUSIVE); BUF_KERNPROC(nbp); nbp->b_vp = vp; nbp->b_flags = B_PAGING | (bp->b_flags & B_BNOCLIP); nbp->b_data = bp->b_data; nbp->b_bio1.bio_done = devfs_spec_strategy_done; nbp->b_bio1.bio_offset = bio->bio_offset; nbp->b_bio1.bio_caller_info1.ptr = bio; /* * Start the first transfer */ if (vn_isdisk(vp, NULL)) chunksize = vp->v_rdev->si_bsize_phys; else chunksize = DEV_BSIZE; chunksize = maxiosize / chunksize * chunksize; #if SPEC_CHAIN_DEBUG & 1 devfs_debug(DEVFS_DEBUG_DEBUG, "spec_strategy chained I/O chunksize=%d\n", chunksize); #endif nbp->b_cmd = bp->b_cmd; nbp->b_bcount = chunksize; nbp->b_bufsize = chunksize; /* used to detect a short I/O */ nbp->b_bio1.bio_caller_info2.index = chunksize; #if SPEC_CHAIN_DEBUG & 1 devfs_debug(DEVFS_DEBUG_DEBUG, "spec_strategy: chain %p offset %d/%d bcount %d\n", bp, 0, bp->b_bcount, nbp->b_bcount); #endif dev_dstrategy(vp->v_rdev, &nbp->b_bio1); if (DEVFS_NODE(vp)) { nanotime(&DEVFS_NODE(vp)->atime); nanotime(&DEVFS_NODE(vp)->mtime); } return (0); }