-/* $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 $ */
/*
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
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.
*/
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;
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);
mp->mnt_vnodecovered->v_mountedhere = NULL;
vfs_unbusy(mp);
free(mp, M_MOUNT, sizeof(*mp));
+ vfs_unbusy(vp->v_mount);
vput(vp);
}
return (error);
*/
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;