Index: sys/kern/vfs_mount.c =================================================================== --- sys/kern/vfs_mount.c (wersja 211930) +++ sys/kern/vfs_mount.c (kopia robocza) @@ -704,9 +704,7 @@ goto bail; } - mtx_lock(&Giant); error = vfs_domount(td, fstype, fspath, fsflags, optlist); - mtx_unlock(&Giant); bail: /* copyout the errmsg */ if (errmsg_pos != -1 && ((2 * errmsg_pos + 1) < fsoptions->uio_iovcnt) @@ -795,191 +793,189 @@ return (error); } - /* - * vfs_domount(): actually attempt a filesystem mount. + * vfs_domount_first(): first file system mount (not update) */ static int -vfs_domount( +vfs_domount_first( struct thread *td, /* Calling thread. */ const char *fstype, /* Filesystem type. */ char *fspath, /* Mount path. */ + struct vnode *vp, /* Vnode to be covered. */ int fsflags, /* Flags common to all filesystems. */ void *fsdata /* Options local to the filesystem. */ ) { - struct vnode *vp; + struct vattr va; + struct vfsconf *vfsp; struct mount *mp; - struct vfsconf *vfsp; - struct oexport_args oexport; - struct export_args export; - int error, flag = 0; - struct vattr va; - struct nameidata nd; + struct vnode *newdp; + int error; mtx_assert(&Giant, MA_OWNED); - /* - * Be ultra-paranoid about making sure the type and fspath - * variables will fit in our mp buffers, including the - * terminating NUL. - */ - if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN) - return (ENAMETOOLONG); + ASSERT_VOP_ELOCKED(vp, __func__); + KASSERT((fsflags & MNT_UPDATE) == 0, ("MNT_UPDATE shouldn't be here")); - if (jailed(td->td_ucred) || usermount == 0) { - if ((error = priv_check(td, PRIV_VFS_MOUNT)) != 0) - return (error); - } + /* Load KLDs before we lock the covered vnode to avoid reversals. */ + /* Don't try to load KLDs if we're mounting the root. */ + if (fsflags & MNT_ROOTFS) + vfsp = vfs_byname(fstype); + else + vfsp = vfs_byname_kld(fstype, td, &error); + if (vfsp == NULL) + return (ENODEV); + if (jailed(td->td_ucred) && !(vfsp->vfc_flags & VFCF_JAIL)) + return (EPERM); /* - * Do not allow NFS export or MNT_SUIDDIR by unprivileged users. + * If the user is not root, ensure that they own the directory + * onto which we are attempting to mount. */ - if (fsflags & MNT_EXPORTED) { - error = priv_check(td, PRIV_VFS_MOUNT_EXPORTED); - if (error) + error = VOP_GETATTR(vp, &va, td->td_ucred); + if (error != 0) + return (error); + if (va.va_uid != td->td_ucred->cr_uid) { + error = priv_check_cred(td->td_ucred, PRIV_VFS_ADMIN, 0); + if (error != 0) return (error); } - if (fsflags & MNT_SUIDDIR) { - error = priv_check(td, PRIV_VFS_MOUNT_SUIDDIR); - if (error) - return (error); + error = vinvalbuf(vp, V_SAVE, 0, 0); + if (error != 0) + return (error); + if (vp->v_type != VDIR) + return (ENOTDIR); + VI_LOCK(vp); + if ((vp->v_iflag & VI_MOUNT) != 0 || vp->v_mountedhere != NULL) { + VI_UNLOCK(vp); + return (EBUSY); } + vp->v_iflag |= VI_MOUNT; + VI_UNLOCK(vp); + VOP_UNLOCK(vp, 0); + + /* Allocate and initialize the filesystem. */ + mp = vfs_mount_alloc(vp, vfsp, fspath, td->td_ucred); + /* XXXMAC: pass to vfs_mount_alloc? */ + mp->mnt_optnew = fsdata; + /* Set the mount level flags. */ + mp->mnt_flag = (fsflags & (MNT_UPDATEMASK | MNT_ROOTFS | MNT_RDONLY)); + /* - * Silently enforce MNT_NOSUID and MNT_USER for unprivileged users. + * Mount the filesystem. + * XXX The final recipients of VFS_MOUNT just overwrite the ndp they + * get. No freeing of cn_pnbuf. */ - if ((fsflags & (MNT_NOSUID | MNT_USER)) != (MNT_NOSUID | MNT_USER)) { - if (priv_check(td, PRIV_VFS_MOUNT_NONUSER) != 0) - fsflags |= MNT_NOSUID | MNT_USER; + error = VFS_MOUNT(mp); + if (error != 0) { + vfs_unbusy(mp); + vfs_mount_destroy(mp); + return (error); } - /* Load KLDs before we lock the covered vnode to avoid reversals. */ - vfsp = NULL; - if ((fsflags & MNT_UPDATE) == 0) { - /* Don't try to load KLDs if we're mounting the root. */ - if (fsflags & MNT_ROOTFS) - vfsp = vfs_byname(fstype); - else - vfsp = vfs_byname_kld(fstype, td, &error); - if (vfsp == NULL) - return (ENODEV); - if (jailed(td->td_ucred) && !(vfsp->vfc_flags & VFCF_JAIL)) - return (EPERM); + if (mp->mnt_opt != NULL) + vfs_freeopts(mp->mnt_opt); + mp->mnt_opt = mp->mnt_optnew; + (void)VFS_STATFS(mp, &mp->mnt_stat); + + /* + * Prevent external consumers of mount options from reading mnt_optnew. + */ + mp->mnt_optnew = NULL; + + if ((mp->mnt_flag & MNT_ASYNC) != 0 && mp->mnt_noasync == 0) + mp->mnt_kern_flag |= MNTK_ASYNC; + else + mp->mnt_kern_flag &= ~MNTK_ASYNC; + + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + cache_purge(vp); + VI_LOCK(vp); + vp->v_iflag &= ~VI_MOUNT; + VI_UNLOCK(vp); + /* Grab an extra reference of the vnode we cover. */ + vref(vp); + /* Place the new filesystem at the end of the mount list. */ + vp->v_mountedhere = mp; + mtx_lock(&mountlist_mtx); + TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); + mtx_unlock(&mountlist_mtx); + vfs_event_signal(NULL, VQ_MOUNT, 0); + if (VFS_ROOT(mp, LK_EXCLUSIVE, &newdp)) + panic("mount: lost mount"); + VOP_UNLOCK(newdp, 0); + VOP_UNLOCK(vp, 0); + mountcheckdirs(vp, newdp); + vrele(newdp); + if ((mp->mnt_flag & MNT_RDONLY) == 0) + vfs_allocate_syncvnode(mp); + vfs_unbusy(mp); + return (0); +} + +/* + * vfs_domount_update(): update of mounted file system + */ +static int +vfs_domount_update( + struct thread *td, /* Calling thread. */ + struct vnode *vp, /* Mount point vnode. */ + int fsflags, /* Flags common to all filesystems. */ + void *fsdata /* Options local to the filesystem. */ + ) +{ + struct oexport_args oexport; + struct export_args export; + struct mount *mp; + int error, flag; + + mtx_assert(&Giant, MA_OWNED); + ASSERT_VOP_ELOCKED(vp, __func__); + KASSERT((fsflags & MNT_UPDATE) != 0, ("MNT_UPDATE should be here")); + + if ((vp->v_vflag & VV_ROOT) == 0) + return (EINVAL); + mp = vp->v_mount; + MNT_ILOCK(mp); + /* + * We only allow the filesystem to be reloaded if it + * is currently mounted read-only. + */ + if ((fsflags & MNT_RELOAD) != 0 && (mp->mnt_flag & MNT_RDONLY) == 0) { + MNT_IUNLOCK(mp); + return (EOPNOTSUPP); /* Needs translation */ } + flag = mp->mnt_flag; + MNT_IUNLOCK(mp); /* - * Get vnode to be covered + * Only privileged root, or (if MNT_USER is set) the user that + * did the original mount is permitted to update it. */ - NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, UIO_SYSSPACE, - fspath, td); - if ((error = namei(&nd)) != 0) + error = vfs_suser(mp, td); + if (error != 0) return (error); - NDFREE(&nd, NDF_ONLY_PNBUF); - vp = nd.ni_vp; - if (fsflags & MNT_UPDATE) { - if ((vp->v_vflag & VV_ROOT) == 0) { - vput(vp); - return (EINVAL); - } - mp = vp->v_mount; - MNT_ILOCK(mp); - flag = mp->mnt_flag; - /* - * We only allow the filesystem to be reloaded if it - * is currently mounted read-only. - */ - if ((fsflags & MNT_RELOAD) && - ((mp->mnt_flag & MNT_RDONLY) == 0)) { - MNT_IUNLOCK(mp); - vput(vp); - return (EOPNOTSUPP); /* Needs translation */ - } - MNT_IUNLOCK(mp); - /* - * Only privileged root, or (if MNT_USER is set) the user that - * did the original mount is permitted to update it. - */ - error = vfs_suser(mp, td); - if (error) { - vput(vp); - return (error); - } - if (vfs_busy(mp, MBF_NOWAIT)) { - vput(vp); - return (EBUSY); - } - VI_LOCK(vp); - if ((vp->v_iflag & VI_MOUNT) != 0 || - vp->v_mountedhere != NULL) { - VI_UNLOCK(vp); - vfs_unbusy(mp); - vput(vp); - return (EBUSY); - } - vp->v_iflag |= VI_MOUNT; + if (vfs_busy(mp, MBF_NOWAIT)) + return (EBUSY); + VI_LOCK(vp); + if ((vp->v_iflag & VI_MOUNT) != 0 || vp->v_mountedhere != NULL) { VI_UNLOCK(vp); - MNT_ILOCK(mp); - mp->mnt_flag |= fsflags & - (MNT_RELOAD | MNT_FORCE | MNT_UPDATE | MNT_SNAPSHOT | MNT_ROOTFS); - MNT_IUNLOCK(mp); - VOP_UNLOCK(vp, 0); - mp->mnt_optnew = fsdata; - vfs_mergeopts(mp->mnt_optnew, mp->mnt_opt); - } else { - /* - * If the user is not root, ensure that they own the directory - * onto which we are attempting to mount. - */ - error = VOP_GETATTR(vp, &va, td->td_ucred); - if (error) { - vput(vp); - return (error); - } - if (va.va_uid != td->td_ucred->cr_uid) { - error = priv_check_cred(td->td_ucred, PRIV_VFS_ADMIN, - 0); - if (error) { - vput(vp); - return (error); - } - } - error = vinvalbuf(vp, V_SAVE, 0, 0); - if (error != 0) { - vput(vp); - return (error); - } - if (vp->v_type != VDIR) { - vput(vp); - return (ENOTDIR); - } - VI_LOCK(vp); - if ((vp->v_iflag & VI_MOUNT) != 0 || - vp->v_mountedhere != NULL) { - VI_UNLOCK(vp); - vput(vp); - return (EBUSY); - } - vp->v_iflag |= VI_MOUNT; - VI_UNLOCK(vp); - VOP_UNLOCK(vp, 0); - - /* - * Allocate and initialize the filesystem. - */ - mp = vfs_mount_alloc(vp, vfsp, fspath, td->td_ucred); - - /* XXXMAC: pass to vfs_mount_alloc? */ - mp->mnt_optnew = fsdata; + vfs_unbusy(mp); + return (EBUSY); } + vp->v_iflag |= VI_MOUNT; + VI_UNLOCK(vp); + VOP_UNLOCK(vp, 0); - /* - * Set the mount level flags. - */ MNT_ILOCK(mp); - mp->mnt_flag = (mp->mnt_flag & ~MNT_UPDATEMASK) | - (fsflags & (MNT_UPDATEMASK | MNT_FORCE | MNT_ROOTFS | - MNT_RDONLY)); + mp->mnt_flag |= fsflags & + (MNT_RELOAD | MNT_FORCE | MNT_UPDATE | MNT_SNAPSHOT | MNT_ROOTFS); + mp->mnt_flag &= ~MNT_UPDATEMASK; + mp->mnt_flag |= fsflags & (MNT_UPDATEMASK | MNT_RDONLY); if ((mp->mnt_flag & MNT_ASYNC) == 0) mp->mnt_kern_flag &= ~MNTK_ASYNC; MNT_IUNLOCK(mp); + mp->mnt_optnew = fsdata; + vfs_mergeopts(mp->mnt_optnew, mp->mnt_opt); + /* * Mount the filesystem. * XXX The final recipients of VFS_MOUNT just overwrite the ndp they @@ -987,11 +983,8 @@ */ error = VFS_MOUNT(mp); - /* - * Process the export option only if we are - * updating mount options. - */ - if (!error && (fsflags & MNT_UPDATE)) { + /* Process the export option only if we are updating mount options. */ + if (error == 0) { if (vfs_copyopt(mp->mnt_optnew, "export", &export, sizeof(export)) == 0) error = vfs_export(mp, &export); @@ -1010,81 +1003,138 @@ } } - if (!error) { - if (mp->mnt_opt != NULL) - vfs_freeopts(mp->mnt_opt); - mp->mnt_opt = mp->mnt_optnew; - (void)VFS_STATFS(mp, &mp->mnt_stat); + MNT_ILOCK(mp); + if (error == 0) { + mp->mnt_flag &= ~(MNT_UPDATE | MNT_RELOAD | MNT_FORCE | + MNT_SNAPSHOT); + } else { + /* + * If we fail, restore old mount flags. MNT_QUOTA is special, + * because it is not part of MNT_UPDATEMASK, but it could have + * changed in the meantime if quotactl(2) was called. + * All in all we want current value of MNT_QUOTA, not the old + * one. + */ + mp->mnt_flag = (mp->mnt_flag & MNT_QUOTA) | (flag & ~MNT_QUOTA); } - /* - * Prevent external consumers of mount options from reading - * mnt_optnew. - */ - mp->mnt_optnew = NULL; - if (mp->mnt_flag & MNT_UPDATE) { - MNT_ILOCK(mp); - if (error) - mp->mnt_flag = (mp->mnt_flag & MNT_QUOTA) | - (flag & ~MNT_QUOTA); - else - mp->mnt_flag &= ~(MNT_UPDATE | MNT_RELOAD | - MNT_FORCE | MNT_SNAPSHOT); - if ((mp->mnt_flag & MNT_ASYNC) != 0 && mp->mnt_noasync == 0) - mp->mnt_kern_flag |= MNTK_ASYNC; - else - mp->mnt_kern_flag &= ~MNTK_ASYNC; - MNT_IUNLOCK(mp); - if ((mp->mnt_flag & MNT_RDONLY) == 0) { - if (mp->mnt_syncer == NULL) - vfs_allocate_syncvnode(mp); - } else { - if (mp->mnt_syncer != NULL) - vrele(mp->mnt_syncer); - mp->mnt_syncer = NULL; - } + if ((mp->mnt_flag & MNT_ASYNC) != 0 && mp->mnt_noasync == 0) + mp->mnt_kern_flag |= MNTK_ASYNC; + else + mp->mnt_kern_flag &= ~MNTK_ASYNC; + MNT_IUNLOCK(mp); + + if (error != 0) { vfs_unbusy(mp); VI_LOCK(vp); vp->v_iflag &= ~VI_MOUNT; VI_UNLOCK(vp); - vrele(vp); return (error); } - MNT_ILOCK(mp); - if ((mp->mnt_flag & MNT_ASYNC) != 0 && mp->mnt_noasync == 0) - mp->mnt_kern_flag |= MNTK_ASYNC; - else - mp->mnt_kern_flag &= ~MNTK_ASYNC; - MNT_IUNLOCK(mp); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + + if (mp->mnt_opt != NULL) + vfs_freeopts(mp->mnt_opt); + mp->mnt_opt = mp->mnt_optnew; + (void)VFS_STATFS(mp, &mp->mnt_stat); /* - * Put the new filesystem on the mount list after root. + * Prevent external consumers of mount options from reading + * mnt_optnew. */ - cache_purge(vp); + mp->mnt_optnew = NULL; + + if ((mp->mnt_flag & MNT_RDONLY) == 0) { + if (mp->mnt_syncer == NULL) + vfs_allocate_syncvnode(mp); + } else { + if (mp->mnt_syncer != NULL) + vrele(mp->mnt_syncer); + mp->mnt_syncer = NULL; + } + vfs_unbusy(mp); VI_LOCK(vp); vp->v_iflag &= ~VI_MOUNT; VI_UNLOCK(vp); - if (!error) { - struct vnode *newdp; + return (0); +} - vp->v_mountedhere = mp; - mtx_lock(&mountlist_mtx); - TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); - mtx_unlock(&mountlist_mtx); - vfs_event_signal(NULL, VQ_MOUNT, 0); - if (VFS_ROOT(mp, LK_EXCLUSIVE, &newdp)) - panic("mount: lost mount"); - VOP_UNLOCK(newdp, 0); - VOP_UNLOCK(vp, 0); - mountcheckdirs(vp, newdp); - vrele(newdp); - if ((mp->mnt_flag & MNT_RDONLY) == 0) - vfs_allocate_syncvnode(mp); - vfs_unbusy(mp); +/* + * vfs_domount(): actually attempt a filesystem mount. + */ +static int +vfs_domount( + struct thread *td, /* Calling thread. */ + const char *fstype, /* Filesystem type. */ + char *fspath, /* Mount path. */ + int fsflags, /* Flags common to all filesystems. */ + void *fsdata /* Options local to the filesystem. */ + ) +{ + struct nameidata nd; + struct vnode *vp; + int error; + + /* + * Be ultra-paranoid about making sure the type and fspath + * variables will fit in our mp buffers, including the + * terminating NUL. + */ + if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN) + return (ENAMETOOLONG); + + if (jailed(td->td_ucred) || usermount == 0) { + if ((error = priv_check(td, PRIV_VFS_MOUNT)) != 0) + return (error); + } + + /* + * Do not allow NFS export or MNT_SUIDDIR by unprivileged users. + */ + if (fsflags & MNT_EXPORTED) { + error = priv_check(td, PRIV_VFS_MOUNT_EXPORTED); + if (error) + return (error); + } + if (fsflags & MNT_SUIDDIR) { + error = priv_check(td, PRIV_VFS_MOUNT_SUIDDIR); + if (error) + return (error); + } + /* + * Silently enforce MNT_NOSUID and MNT_USER for unprivileged users. + */ + if ((fsflags & (MNT_NOSUID | MNT_USER)) != (MNT_NOSUID | MNT_USER)) { + if (priv_check(td, PRIV_VFS_MOUNT_NONUSER) != 0) + fsflags |= MNT_NOSUID | MNT_USER; + } + + /* + * Get vnode to be covered or mount point's vnode in case of MNT_UPDATE. + */ + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1, + UIO_SYSSPACE, fspath, td); + error = namei(&nd); + if (error != 0) + return (error); + NDFREE(&nd, NDF_ONLY_PNBUF); + vp = nd.ni_vp; + + mtx_lock(&Giant); + if ((fsflags & MNT_UPDATE) == 0) { + error = vfs_domount_first(td, fstype, fspath, vp, fsflags, + fsdata); } else { - vfs_unbusy(mp); - vfs_mount_destroy(mp); + error = vfs_domount_update(td, vp, fsflags, fsdata); + } + mtx_unlock(&Giant); + + ASSERT_VI_UNLOCKED(vp, __func__); + if (error == 0) + ASSERT_VOP_UNLOCKED(vp, __func__); + + if (VOP_ISLOCKED(vp)) vput(vp); - } + else + vrele(vp); + return (error); }