summaryrefslogtreecommitdiff
path: root/sys/kern/vfs_syscalls.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/vfs_syscalls.c')
-rw-r--r--sys/kern/vfs_syscalls.c85
1 files changed, 84 insertions, 1 deletions
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index 25d53b46afe..b41c4a24b96 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vfs_syscalls.c,v 1.271 2017/02/15 03:36:58 guenther Exp $ */
+/* $OpenBSD: vfs_syscalls.c,v 1.272 2017/04/15 13:56:43 bluhm Exp $ */
/* $NetBSD: vfs_syscalls.c,v 1.71 1996/04/23 10:29:02 mycroft Exp $ */
/*
@@ -88,6 +88,7 @@ int domkdirat(struct proc *, int, const char *, mode_t);
int doutimensat(struct proc *, int, const char *, struct timespec [2], int);
int dovutimens(struct proc *, struct vnode *, struct timespec [2]);
int dofutimens(struct proc *, int, struct timespec [2]);
+int dounmount_leaf(struct mount *, int, struct proc *);
/*
* Virtual File System System Calls
@@ -204,7 +205,22 @@ sys_mount(struct proc *p, void *v, register_t *retval)
strncpy(mp->mnt_stat.f_fstypename, vfsp->vfc_name, MFSNAMELEN);
mp->mnt_vnodecovered = vp;
mp->mnt_stat.f_owner = p->p_ucred->cr_uid;
+
update:
+ /* Ensure that the parent mountpoint does not get unmounted. */
+ error = vfs_busy(vp->v_mount, VB_READ|VB_NOWAIT);
+ if (error) {
+ if (mp->mnt_flag & MNT_UPDATE) {
+ mp->mnt_flag = mntflag;
+ vfs_unbusy(mp);
+ } else {
+ vfs_unbusy(mp);
+ free(mp, M_MOUNT, sizeof(*mp));
+ }
+ vput(vp);
+ return (error);
+ }
+
/*
* Set the mount level flags.
*/
@@ -226,6 +242,7 @@ update:
mp->mnt_stat.f_ctime = time_second;
}
if (mp->mnt_flag & MNT_UPDATE) {
+ vfs_unbusy(vp->v_mount);
vput(vp);
if (mp->mnt_flag & MNT_WANTRDWR)
mp->mnt_flag &= ~MNT_RDONLY;
@@ -257,6 +274,7 @@ update:
vfsp->vfc_refcount++;
TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
checkdirs(vp);
+ vfs_unbusy(vp->v_mount);
VOP_UNLOCK(vp, p);
if ((mp->mnt_flag & MNT_RDONLY) == 0)
error = vfs_allocate_syncvnode(mp);
@@ -268,6 +286,7 @@ update:
mp->mnt_vnodecovered->v_mountedhere = NULL;
vfs_unbusy(mp);
free(mp, M_MOUNT, sizeof(*mp));
+ vfs_unbusy(vp->v_mount);
vput(vp);
}
return (error);
@@ -374,6 +393,70 @@ sys_unmount(struct proc *p, void *v, register_t *retval)
int
dounmount(struct mount *mp, int flags, struct proc *p)
{
+ SLIST_HEAD(, mount) mplist;
+ struct mount *nmp;
+ int error;
+
+ SLIST_INIT(&mplist);
+ SLIST_INSERT_HEAD(&mplist, mp, mnt_dounmount);
+
+ /*
+ * Collect nested mount points. This takes advantage of the mount list
+ * being ordered - nested mount points come after their parent.
+ */
+ while ((mp = TAILQ_NEXT(mp, mnt_list)) != NULL) {
+ SLIST_FOREACH(nmp, &mplist, mnt_dounmount) {
+ if (mp->mnt_vnodecovered == NULLVP ||
+ mp->mnt_vnodecovered->v_mount != nmp)
+ continue;
+
+ if ((flags & MNT_FORCE) == 0) {
+ error = EBUSY;
+ goto err;
+ }
+ error = vfs_busy(mp, VB_WRITE|VB_WAIT);
+ if (error) {
+ if ((flags & MNT_DOOMED)) {
+ /*
+ * If the mount point was busy due to
+ * being unmounted, it has been removed
+ * from the mount list already.
+ * Restart the iteration from the last
+ * collected busy entry.
+ */
+ mp = SLIST_FIRST(&mplist);
+ break;
+ }
+ goto err;
+ }
+ SLIST_INSERT_HEAD(&mplist, mp, mnt_dounmount);
+ break;
+ }
+ }
+
+ /*
+ * Nested mount points cannot appear during this loop as mounting
+ * requires a read lock for the parent mount point.
+ */
+ while ((mp = SLIST_FIRST(&mplist)) != NULL) {
+ SLIST_REMOVE(&mplist, mp, mount, mnt_dounmount);
+ error = dounmount_leaf(mp, flags, p);
+ if (error)
+ goto err;
+ }
+ return (0);
+
+err:
+ while ((mp = SLIST_FIRST(&mplist)) != NULL) {
+ SLIST_REMOVE(&mplist, mp, mount, mnt_dounmount);
+ vfs_unbusy(mp);
+ }
+ return (error);
+}
+
+int
+dounmount_leaf(struct mount *mp, int flags, struct proc *p)
+{
struct vnode *coveredvp;
int error;
int hadsyncer = 0;