Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 130701790bf2d95e902edf16031ff596 > files > 68

autofs-5.0.1-0.rc2.164.el5_8.src.rpm

autofs-5.0.3 - fix multi mount race.

From: Ian Kent <raven@themaw.net>

When using multi-mounts it is possible for a path walk to walk into
a mount tree before it is completely setup which leads to autofs
incorrectly failing to perform mounts.

For example, for the multi-mount

mm1
     /om1       <server1>:/<path1>
     /om2       <server2>:/<path2>
     /om2/om21  <server3>:/<path3>
     /om2/om22  <server3>:/<path4>

when a path walk hits mm1/om2 <serverr2>:/<path2> is mounted on top of
mm1/om2. If a path walk comes along before the multi-mount offsets for
mm1/om2 are setup it doesn't see that mm1/om2 is pending. This happens
because the lookup gets to mm1/om2, which is within the mm1 file system,
and is covered by a mount trigger mounted at mm1/om2, and the the trigger
itself is covered by the <server3>:/<path3> mount. So the walk follows
the stack up to the mount at <server3>:/<path3>, never seeing that the
trigger mm1/om2 is currently pending.

In the example above mm1/om2 could also be a submount.

To resolve this the mount tree needs to be created under a temporary
directory and moved into place once setup in one operation.
---

 daemon/automount.c      |   32 ++-
 daemon/direct.c         |   40 ++--
 daemon/indirect.c       |   55 +++---
 daemon/lookup.c         |    6 
 daemon/state.c          |    2 
 include/automount.h     |   12 -
 include/master.h        |    1 
 include/mounts.h        |    4 
 lib/master.c            |   20 +-
 lib/mounts.c            |   21 +-
 modules/mount_autofs.c  |   61 +++----
 modules/mount_bind.c    |   43 +---
 modules/mount_changer.c |   34 +--
 modules/mount_ext2.c    |   40 +---
 modules/mount_generic.c |   40 +---
 modules/mount_nfs.c     |   41 +---
 modules/parse_sun.c     |  414 ++++++++++++++++++++++++++++++++++++------------
 17 files changed, 519 insertions(+), 347 deletions(-)


--- autofs-5.0.1.orig/daemon/automount.c
+++ autofs-5.0.1/daemon/automount.c
@@ -483,7 +483,7 @@ static int umount_subtree_mounts(struct 
 		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
 		/* Lock the closest parent nesting point for umount */
 		cache_multi_lock(me->parent);
-		if (umount_multi_triggers(ap, root, me, base)) {
+		if (umount_multi_triggers(ap, me, root, base)) {
 			warn(ap->logopt,
 			     "some offset mounts still present under %s", path);
 			left++;
@@ -566,7 +566,7 @@ static int umount_all(struct autofs_poin
 	return left;
 }
 
-int umount_autofs(struct autofs_point *ap, int force)
+int umount_autofs(struct autofs_point *ap, const char *root, int force)
 {
 	int ret = 0;
 
@@ -583,7 +583,7 @@ int umount_autofs(struct autofs_point *a
 	if (ap->type == LKP_INDIRECT) {
 		if (umount_all(ap, force) && !force)
 			return -1;
-		ret = umount_autofs_indirect(ap);
+		ret = umount_autofs_indirect(ap, root);
 	} else
 		ret = umount_autofs_direct(ap);
 
@@ -729,7 +729,7 @@ out_free:
 	return ret;
 }
 
-static int destroy_logpri_fifo(struct autofs_point *ap)
+int destroy_logpri_fifo(struct autofs_point *ap)
 {
 	int ret = -1;
 	int fd = ap->logpri_fifo;
@@ -1031,7 +1031,7 @@ static int autofs_init_ap(struct autofs_
 	return 0;
 }
 
-static int mount_autofs(struct autofs_point *ap)
+static int mount_autofs(struct autofs_point *ap, const char *root)
 {
 	int status = 0;
 
@@ -1041,7 +1041,7 @@ static int mount_autofs(struct autofs_po
 	if (ap->type == LKP_DIRECT)
 		status = mount_autofs_direct(ap);
 	else
-		status = mount_autofs_indirect(ap);
+		status = mount_autofs_indirect(ap, root);
 
 	if (status < 0)
 		return -1;
@@ -1493,10 +1493,12 @@ void *handle_mounts(void *arg)
 	struct startup_cond *suc;
 	struct autofs_point *ap;
 	int cancel_state, status = 0;
+	char *root;
 
 	suc = (struct startup_cond *) arg;
 
 	ap = suc->ap;
+	root = strdup(suc->root);
 
 	pthread_cleanup_push(return_start_status, suc);
 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
@@ -1507,14 +1509,24 @@ void *handle_mounts(void *arg)
 		fatal(status);
 	}
 
-	if (mount_autofs(ap) < 0) {
+	if (!root) {
+		crit(ap->logopt, "failed to alloc string root");
+		suc->status = 1;
+		pthread_setcancelstate(cancel_state, NULL);
+		pthread_exit(NULL);
+	}
+
+	if (mount_autofs(ap, root) < 0) {
 		crit(ap->logopt, "mount of %s failed!", ap->path);
 		suc->status = 1;
-		umount_autofs(ap, 1);
+		umount_autofs(ap, root, 1);
+		free(root);
 		pthread_setcancelstate(cancel_state, NULL);
 		pthread_exit(NULL);
 	}
 
+	free(root);
+
 	if (ap->ghost && ap->type != LKP_DIRECT)
 		info(ap->logopt, "ghosting enabled");
 
@@ -1577,7 +1589,7 @@ void *handle_mounts(void *arg)
 			 * to check for possible recovery.
 			 */
 			if (ap->type == LKP_DIRECT) {
-				umount_autofs(ap, 1);
+				umount_autofs(ap, NULL, 1);
 				break;
 			}
 
@@ -1587,7 +1599,7 @@ void *handle_mounts(void *arg)
 			 * so we can continue. This can happen if a lookup
 			 * occurs while we're trying to umount.
 			 */
-			ret = umount_autofs(ap, 1);
+			ret = umount_autofs(ap, NULL, 1);
 			if (!ret)
 				break;
 
--- autofs-5.0.1.orig/daemon/direct.c
+++ autofs-5.0.1/daemon/direct.c
@@ -646,7 +646,7 @@ force_umount:
 	return rv;
 }
 
-int mount_autofs_offset(struct autofs_point *ap, struct mapent *me)
+int mount_autofs_offset(struct autofs_point *ap, struct mapent *me, const char *root, const char *offset)
 {
 	char buf[MAX_ERR_BUF];
 	struct mnt_params *mp;
@@ -654,6 +654,7 @@ int mount_autofs_offset(struct autofs_po
 	struct stat st;
 	int ioctlfd, cl_flags, status, ret;
 	const char *type, *map_name = NULL;
+	char mountpoint[PATH_MAX];
 
 	if (is_mounted(_PROC_MOUNTS, me->key, MNTS_AUTOFS)) {
 		if (ap->state != ST_READMAP)
@@ -695,8 +696,11 @@ int mount_autofs_offset(struct autofs_po
 			return MOUNT_OFFSET_OK;
 	}
 
+	strcpy(mountpoint, root);
+	strcat(mountpoint, offset);
+
 	/* In case the directory doesn't exist, try to mkdir it */
-	if (mkdir_path(me->key, 0555) < 0) {
+	if (mkdir_path(mountpoint, 0555) < 0) {
 		if (errno == EEXIST) {
 			/*
 			 * If the mount point directory is a real mount
@@ -705,7 +709,7 @@ int mount_autofs_offset(struct autofs_po
 			 * the kernel NFS client.
 			 */
 			if (me->multi != me &&
-			    is_mounted(_PATH_MOUNTED, me->key, MNTS_REAL))
+			    is_mounted(_PATH_MOUNTED, mountpoint, MNTS_REAL))
 				return MOUNT_OFFSET_IGNORE;
 
 			/* 
@@ -725,13 +729,13 @@ int mount_autofs_offset(struct autofs_po
 			char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
 			debug(ap->logopt,
 			     "can't create mount directory: %s, %s",
-			     me->key, estr);
+			     mountpoint, estr);
 			return MOUNT_OFFSET_FAIL;
 		} else {
 			char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
 			crit(ap->logopt,
 			     "failed to create mount directory: %s, %s",
-			     me->key, estr);
+			     mountpoint, estr);
 			return MOUNT_OFFSET_FAIL;
 		}
 	} else {
@@ -741,7 +745,7 @@ int mount_autofs_offset(struct autofs_po
 
 	debug(ap->logopt,
 	      "calling mount -t autofs " SLOPPY " -o %s automount %s",
-	      mp->options, me->key);
+	      mp->options, mountpoint);
 
 	type = ap->entry->maps->type;
 	if (type && !strcmp(ap->entry->maps->type, "hosts")) {
@@ -753,22 +757,18 @@ int mount_autofs_offset(struct autofs_po
 	} else
 		map_name = me->mc->map->argv[0];
 
-	ret = mount(map_name, me->key, "autofs", MS_MGC_VAL, mp->options);
+	ret = mount(map_name, mountpoint, "autofs", MS_MGC_VAL, mp->options);
 	if (ret) {
-		crit(ap->logopt, "failed to mount autofs path %s", me->key);
-		goto out_err;
-	}
-
-	if (ret != 0) {
 		crit(ap->logopt,
-		     "failed to mount autofs offset trigger %s", me->key);
+		     "failed to mount offset trigger %s at %s",
+		     me->key, mountpoint);
 		goto out_err;
 	}
 
 	/* Root directory for ioctl()'s */
-	ioctlfd = open(me->key, O_RDONLY);
+	ioctlfd = open(mountpoint, O_RDONLY);
 	if (ioctlfd < 0) {
-		crit(ap->logopt, "failed to create ioctl fd for %s", me->key);
+		crit(ap->logopt, "failed to create ioctl fd for %s", mountpoint);
 		goto out_umount;
 	}
 
@@ -782,7 +782,7 @@ int mount_autofs_offset(struct autofs_po
 	ret = fstat(ioctlfd, &st);
 	if (ret == -1) {
 		error(ap->logopt,
-		     "failed to stat direct mount trigger %s", me->key);
+		     "failed to stat direct mount trigger %s", mountpoint);
 		goto out_close;
 	}
 
@@ -790,17 +790,17 @@ int mount_autofs_offset(struct autofs_po
 
 	close(ioctlfd);
 
-	debug(ap->logopt, "mounted trigger %s", me->key);
+	debug(ap->logopt, "mounted trigger %s at %s", me->key, mountpoint);
 
 	return MOUNT_OFFSET_OK;
 
 out_close:
 	close(ioctlfd);
 out_umount:
-	umount(me->key);
+	umount(mountpoint);
 out_err:
-	if (stat(me->key, &st) == 0 && me->dir_created)
-		 rmdir_path(ap, me->key, st.st_dev);
+	if (stat(mountpoint, &st) == 0 && me->dir_created)
+		 rmdir_path(ap, mountpoint, st.st_dev);
 
 	return MOUNT_OFFSET_FAIL;
 }
--- autofs-5.0.1.orig/daemon/indirect.c
+++ autofs-5.0.1/daemon/indirect.c
@@ -83,7 +83,7 @@ static int unlink_mount_tree(struct auto
 	return ret;
 }
 
-static int do_mount_autofs_indirect(struct autofs_point *ap)
+static int do_mount_autofs_indirect(struct autofs_point *ap, const char *root)
 {
 	time_t timeout = ap->exp_timeout;
 	char *options = NULL;
@@ -109,11 +109,11 @@ static int do_mount_autofs_indirect(stru
 		goto out_err;
 
 	/* In case the directory doesn't exist, try to mkdir it */
-	if (mkdir_path(ap->path, 0555) < 0) {
+	if (mkdir_path(root, 0555) < 0) {
 		if (errno != EEXIST && errno != EROFS) {
 			crit(ap->logopt,
 			     "failed to create autofs directory %s",
-			     ap->path);
+			     root);
 			goto out_err;
 		}
 		/* If we recieve an error, and it's EEXIST or EROFS we know
@@ -134,9 +134,10 @@ static int do_mount_autofs_indirect(stru
 	} else
 		map_name = ap->entry->maps->argv[0];
 
-	ret = mount(map_name, ap->path, "autofs", MS_MGC_VAL, options);
+	ret = mount(map_name, root, "autofs", MS_MGC_VAL, options);
 	if (ret) {
-		crit(ap->logopt, "failed to mount autofs path %s", ap->path);
+		crit(ap->logopt,
+		     "failed to mount autofs path %s at %s", ap->path, root);
 		goto out_rmdir;
 	}
 
@@ -145,7 +146,7 @@ static int do_mount_autofs_indirect(stru
 	options = NULL;
 
 	/* Root directory for ioctl()'s */
-	ap->ioctlfd = open(ap->path, O_RDONLY);
+	ap->ioctlfd = open(root, O_RDONLY);
 	if (ap->ioctlfd < 0) {
 		crit(ap->logopt,
 		     "failed to create ioctl fd for autofs path %s", ap->path);
@@ -163,13 +164,13 @@ static int do_mount_autofs_indirect(stru
 
 	if (ap->exp_timeout)
 		info(ap->logopt,
-		    "mounted indirect mount on %s "
+		    "mounted indirect mount for %s "
 		    "with timeout %u, freq %u seconds", ap->path,
 	 	    (unsigned int) ap->exp_timeout,
 		    (unsigned int) ap->exp_runfreq);
 	else
 		info(ap->logopt,
-		    "mounted indirect mount on %s with timeouts disabled",
+		    "mounted indirect mount for %s with timeouts disabled",
 		    ap->path);
 
 	fstat(ap->ioctlfd, &st);
@@ -178,10 +179,10 @@ static int do_mount_autofs_indirect(stru
 	return 0;
 
 out_umount:
-	umount(ap->path);
+	umount(root);
 out_rmdir:
 	if (ap->dir_created)
-		rmdir_path(ap, ap->path, ap->dev);
+		rmdir(root);
 out_err:
 	if (options)
 		free(options);
@@ -193,7 +194,7 @@ out_err:
 	return -1;
 }
 
-int mount_autofs_indirect(struct autofs_point *ap)
+int mount_autofs_indirect(struct autofs_point *ap, const char *root)
 {
 	time_t now = time(NULL);
 	int status;
@@ -207,11 +208,11 @@ int mount_autofs_indirect(struct autofs_
 		return -1;
 	}
 
-	status = do_mount_autofs_indirect(ap);
+	status = do_mount_autofs_indirect(ap, root);
 	if (status < 0)
 		return -1;
 
-	map = lookup_ghost(ap);
+	map = lookup_ghost(ap, root);
 	if (map & LKP_FAIL) {
 		if (map & LKP_DIRECT) {
 			error(ap->logopt,
@@ -230,7 +231,7 @@ int mount_autofs_indirect(struct autofs_
 	return 0;
 }
 
-static void close_mount_fds(struct autofs_point *ap)
+void close_mount_fds(struct autofs_point *ap)
 {
 	/*
 	 * Since submounts look after themselves the parent never knows
@@ -255,11 +256,17 @@ static void close_mount_fds(struct autof
 	return;
 }
 
-int umount_autofs_indirect(struct autofs_point *ap)
+int umount_autofs_indirect(struct autofs_point *ap, const char *root)
 {
 	char buf[MAX_ERR_BUF];
+	char mountpoint[PATH_MAX + 1];
 	int ret, rv, retries;
 
+	if (root)
+		strcpy(mountpoint, root);
+	else
+		strcpy(mountpoint, ap->path);
+
 	/* If we are trying to shutdown make sure we can umount */
 	rv = ioctl(ap->ioctlfd, AUTOFS_IOC_ASKUMOUNT, &ret);
 	if (rv == -1) {
@@ -284,7 +291,7 @@ int umount_autofs_indirect(struct autofs
 	sched_yield();
 
 	retries = UMOUNT_RETRIES;
-	while ((rv = umount(ap->path)) == -1 && retries--) {
+	while ((rv = umount(mountpoint)) == -1 && retries--) {
 		struct timespec tm = {0, 200000000};
 		if (errno != EBUSY)
 			break;
@@ -296,13 +303,13 @@ int umount_autofs_indirect(struct autofs
 		case ENOENT:
 		case EINVAL:
 			error(ap->logopt,
-			      "mount point %s does not exist", ap->path);
+			      "mount point %s does not exist", mountpoint);
 			close_mount_fds(ap);
 			return 0;
 			break;
 		case EBUSY:
 			debug(ap->logopt,
-			      "mount point %s is in use", ap->path);
+			      "mount point %s is in use", mountpoint);
 			if (ap->state == ST_SHUTDOWN_FORCE) {
 				close_mount_fds(ap);
 				goto force_umount;
@@ -321,11 +328,11 @@ int umount_autofs_indirect(struct autofs
 					return 0;
 				}
 #endif
-				ap->ioctlfd = open(ap->path, O_RDONLY);
+				ap->ioctlfd = open(mountpoint, O_RDONLY);
 				if (ap->ioctlfd < 0) {
 					warn(ap->logopt,
 					     "could not recover autofs path %s",
-					     ap->path);
+					     mountpoint);
 					close_mount_fds(ap);
 					return 0;
 				}
@@ -355,12 +362,12 @@ int umount_autofs_indirect(struct autofs
 force_umount:
 	if (rv != 0) {
 		warn(ap->logopt,
-		     "forcing umount of indirect mount %s", ap->path);
-		rv = umount2(ap->path, MNT_DETACH);
+		     "forcing umount of indirect mount %s", mountpoint);
+		rv = umount2(mountpoint, MNT_DETACH);
 	} else {
-		info(ap->logopt, "umounted indirect mount %s", ap->path);
+		info(ap->logopt, "umounted indirect mount %s", mountpoint);
 		if (ap->submount)
-			rm_unwanted(ap->logopt, ap->path, 1, ap->dev);
+			rm_unwanted(ap->logopt, mountpoint, 1, ap->dev);
 	}
 
 	return rv;
--- autofs-5.0.1.orig/daemon/lookup.c
+++ autofs-5.0.1/daemon/lookup.c
@@ -566,7 +566,7 @@ int lookup_nss_read_map(struct autofs_po
 	return 0;
 }
 
-int lookup_ghost(struct autofs_point *ap)
+int lookup_ghost(struct autofs_point *ap, const char *root)
 {
 	struct master_mapent *entry = ap->entry;
 	struct map_source *map;
@@ -612,12 +612,12 @@ int lookup_ghost(struct autofs_point *ap
 				goto next;
 			}
 
-			fullpath = alloca(strlen(me->key) + strlen(ap->path) + 3);
+			fullpath = alloca(strlen(me->key) + strlen(root) + 3);
 			if (!fullpath) {
 				warn(ap->logopt, "failed to allocate full path");
 				goto next;
 			}
-			sprintf(fullpath, "%s/%s", ap->path, me->key);
+			sprintf(fullpath, "%s/%s", root, me->key);
 
 			ret = stat(fullpath, &st);
 			if (ret == -1 && errno != ENOENT) {
--- autofs-5.0.1.orig/daemon/state.c
+++ autofs-5.0.1/daemon/state.c
@@ -393,7 +393,7 @@ static void *do_readmap(void *arg)
 
 	if (ap->type == LKP_INDIRECT) {
 		lookup_prune_cache(ap, now);
-		status = lookup_ghost(ap);
+		status = lookup_ghost(ap, ap->path);
 	} else {
 		struct mapent *me, *ne, *nested;
 		mnts = tree_make_mnt_tree(_PROC_MOUNTS, "/");
--- autofs-5.0.1.orig/include/automount.h
+++ autofs-5.0.1/include/automount.h
@@ -230,7 +230,7 @@ int lookup_nss_read_master(struct master
 int lookup_nss_read_map(struct autofs_point *ap, struct map_source *source, time_t age);
 int lookup_enumerate(struct autofs_point *ap,
 	int (*fn)(struct autofs_point *,struct mapent *, int), time_t now);
-int lookup_ghost(struct autofs_point *ap);
+int lookup_ghost(struct autofs_point *ap, const char *root);
 int lookup_nss_mount(struct autofs_point *ap, struct map_source *source, const char *name, int name_len);
 void lookup_close_lookup(struct autofs_point *ap);
 int lookup_prune_cache(struct autofs_point *ap, time_t age);
@@ -332,6 +332,7 @@ struct startup_cond {
 	pthread_mutex_t mutex;
 	pthread_cond_t  cond;
 	struct autofs_point *ap;
+	char *root;
 	unsigned int done;
 	unsigned int status;
 };
@@ -427,12 +428,13 @@ int do_expire(struct autofs_point *ap, c
 void *expire_proc_indirect(void *);
 void *expire_proc_direct(void *);
 int expire_offsets_direct(struct autofs_point *ap, struct mapent *me, int now);
-int mount_autofs_indirect(struct autofs_point *ap);
+int mount_autofs_indirect(struct autofs_point *ap, const char *root);
 int mount_autofs_direct(struct autofs_point *ap);
-int mount_autofs_offset(struct autofs_point *ap, struct mapent *me);
+int mount_autofs_offset(struct autofs_point *ap, struct mapent *me, const char *root, const char *offset);
 void submount_signal_parent(struct autofs_point *ap, unsigned int success);
-int umount_autofs(struct autofs_point *ap, int force);
-int umount_autofs_indirect(struct autofs_point *ap);
+void close_mount_fds(struct autofs_point *ap);
+int umount_autofs(struct autofs_point *ap, const char *root, int force);
+int umount_autofs_indirect(struct autofs_point *ap, const char *root);
 int do_umount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, struct mapent *me);
 int umount_autofs_direct(struct autofs_point *ap);
 int umount_autofs_offset(struct autofs_point *ap, struct mapent *me);
--- autofs-5.0.1.orig/include/master.h
+++ autofs-5.0.1/include/master.h
@@ -91,6 +91,7 @@ void master_source_lock_cleanup(void *);
 void master_source_current_wait(struct master_mapent *);
 void master_source_current_signal(struct master_mapent *);
 struct master_mapent *master_find_mapent(struct master *, const char *);
+struct autofs_point *__master_find_submount(struct autofs_point *, const char *);
 struct autofs_point *master_find_submount(struct autofs_point *, const char *);
 struct master_mapent *master_new_mapent(struct master *, const char *, time_t);
 void master_add_mapent(struct master *, struct master_mapent *);
--- autofs-5.0.1.orig/include/mounts.h
+++ autofs-5.0.1/include/mounts.h
@@ -85,7 +85,7 @@ int tree_find_mnt_ents(struct mnt_list *
 int tree_is_mounted(struct mnt_list *mnts, const char *path, unsigned int type);
 void set_tsd_user_vars(unsigned int, uid_t, gid_t);
 int umount_ent(struct autofs_point *, const char *);
-int mount_multi_triggers(struct autofs_point *, char *, struct mapent *, const char *);
-int umount_multi_triggers(struct autofs_point *, char *, struct mapent *, const char *);
+int mount_multi_triggers(struct autofs_point *, struct mapent *, const char *, unsigned int, const char *);
+int umount_multi_triggers(struct autofs_point *, struct mapent *, char *, const char *);
 
 #endif
--- autofs-5.0.1.orig/lib/master.c
+++ autofs-5.0.1/lib/master.c
@@ -602,27 +602,32 @@ struct master_mapent *master_find_mapent
 	return NULL;
 }
 
-struct autofs_point *master_find_submount(struct autofs_point *ap, const char *path)
+struct autofs_point *__master_find_submount(struct autofs_point *ap, const char *path)
 {
 	struct list_head *head, *p;
 
-	mounts_mutex_lock(ap);
-
 	head = &ap->submounts;
 	list_for_each(p, head) {
 		struct autofs_point *submount;
 
 		submount = list_entry(p, struct autofs_point, mounts);
 
-		if (!strcmp(submount->path, path)) {
-			mounts_mutex_unlock(ap);
+		if (!strcmp(submount->path, path))
 			return submount;
-		}
 	}
 
+	return NULL;
+}
+
+struct autofs_point *master_find_submount(struct autofs_point *ap, const char *path)
+{
+	struct autofs_point *submount;
+
+	mounts_mutex_lock(ap);
+	submount = __master_find_submount(ap, path);
 	mounts_mutex_unlock(ap);
 
-	return NULL;
+	return submount;
 }
 
 struct master_mapent *master_new_mapent(struct master *master, const char *path, time_t age)
@@ -955,6 +960,7 @@ static int master_do_mount(struct master
 	}
 
 	suc.ap = ap;
+	suc.root = ap->path;
 	suc.done = 0;
 	suc.status = 0;
 
--- autofs-5.0.1.orig/lib/mounts.c
+++ autofs-5.0.1/lib/mounts.c
@@ -1097,7 +1097,8 @@ int umount_ent(struct autofs_point *ap, 
 	return rv;
 }
 
-int mount_multi_triggers(struct autofs_point *ap, char *root, struct mapent *me, const char *base)
+int mount_multi_triggers(struct autofs_point *ap, struct mapent *me,
+			 const char *root, unsigned int start, const char *base)
 {
 	char path[PATH_MAX + 1];
 	char *offset = path;
@@ -1105,17 +1106,13 @@ int mount_multi_triggers(struct autofs_p
 	struct list_head *pos = NULL;
 	unsigned int fs_path_len;
 	unsigned int mounted;
-	int ret, start;
+	int ret;
 
-	fs_path_len = strlen(root) + strlen(base);
+	fs_path_len = start + strlen(base);
 	if (fs_path_len > PATH_MAX)
 		return -1;
 
-	strcpy(path, root);
-	strcat(path, base);
-
 	mounted = 0;
-	start = strlen(root);
 	offset = cache_get_offset(base, offset, start, &me->multi_list, &pos);
 	while (offset) {
 		int plen = fs_path_len + strlen(offset);
@@ -1129,9 +1126,9 @@ int mount_multi_triggers(struct autofs_p
 		if (!oe || !oe->mapent)
 			goto cont;
 
-		debug(ap->logopt, "mount offset %s", oe->key);
+		debug(ap->logopt, "mount offset %s at %s", oe->key, root);
 
-		ret = mount_autofs_offset(ap, oe);
+		ret = mount_autofs_offset(ap, oe, root, offset);
 		if (ret >= MOUNT_OFFSET_OK)
 			mounted++;
 		else {
@@ -1153,7 +1150,7 @@ cont:
 	return mounted;
 }
 
-int umount_multi_triggers(struct autofs_point *ap, char *root, struct mapent *me, const char *base)
+int umount_multi_triggers(struct autofs_point *ap, struct mapent *me, char *root, const char *base)
 {
 	char path[PATH_MAX + 1];
 	char *offset;
@@ -1190,7 +1187,7 @@ int umount_multi_triggers(struct autofs_
 		 * nonstrict mount fail.
 		 */
 		oe_base = oe->key + strlen(root);
-		left += umount_multi_triggers(ap, root, oe, oe_base);
+		left += umount_multi_triggers(ap, oe, root, oe_base);
 
 		if (oe->ioctlfd != -1)
 			left++;
@@ -1230,7 +1227,7 @@ int umount_multi_triggers(struct autofs_
 		if (is_mounted(_PATH_MOUNTED, root, MNTS_REAL)) {
 			info(ap->logopt, "unmounting dir = %s", root);
 			if (umount_ent(ap, root)) {
-				if (mount_multi_triggers(ap, root, me, "/") < 0)
+				if (mount_multi_triggers(ap, me, root, strlen(root), "/") < 0)
 					warn(ap->logopt,
 					     "failed to remount offset triggers");
 				return left++;
--- autofs-5.0.1.orig/modules/mount_autofs.c
+++ autofs-5.0.1/modules/mount_autofs.c
@@ -48,7 +48,7 @@ int mount_mount(struct autofs_point *ap,
 {
 	struct startup_cond suc;
 	pthread_t thid;
-	char *fullpath;
+	char *realpath, *mountpoint;
 	const char **argv;
 	int argc, status, ghost = ap->ghost;
 	time_t timeout = ap->exp_timeout;
@@ -60,32 +60,32 @@ int mount_mount(struct autofs_point *ap,
 	struct autofs_point *nap;
 	char buf[MAX_ERR_BUF];
 	char *options, *p;
-	int ret;
-
-	fullpath = alloca(strlen(root) + name_len + 2);
-	if (!fullpath) {
-		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
-		logerr(MODPREFIX "alloca: %s", estr);
-		return 1;
-	}
+	int len, ret;
 
 	/* Root offset of multi-mount */
-	if (*name == '/' && name_len == 1)
-		strcpy(fullpath, root);
-	else if (*name == '/')
-		strcpy(fullpath, name);
-	else {
-		strcpy(fullpath, root);
-		strcat(fullpath, "/");
-		strcat(fullpath, name);
-	}
-
-	if (is_mounted(_PATH_MOUNTED, fullpath, MNTS_REAL)) {
-		error(ap->logopt,
-		      MODPREFIX 
-		      "warning: about to mount over %s, continuing",
-		      fullpath);
-		return 0;
+	len = strlen(root);
+	if (root[len - 1] == '/') {
+		realpath = alloca(strlen(ap->path) + name_len + 2);
+		mountpoint = alloca(len + 1);
+		strcpy(realpath, ap->path);
+		strcat(realpath, "/");
+		strcat(realpath, name);
+		len--;
+		strncpy(mountpoint, root, len);
+		mountpoint[len] = '\0';
+	} else if (*name == '/') {
+		realpath = alloca(name_len + 1);
+		mountpoint = alloca(len + 1);
+		strcpy(mountpoint, root);
+		strcpy(realpath, name);
+	} else {
+		realpath = alloca(len + name_len + 2);
+		mountpoint = alloca(len + name_len + 2);
+		strcpy(mountpoint, root);
+		strcat(mountpoint, "/");
+		strcpy(realpath, mountpoint);
+		strcat(mountpoint, name);
+		strcat(realpath, name);
 	}
 
 	options = NULL;
@@ -136,12 +136,12 @@ int mount_mount(struct autofs_point *ap,
 	}
 
 	debug(ap->logopt,
-	      MODPREFIX "fullpath=%s what=%s options=%s",
-	      fullpath, what, options);
+	      MODPREFIX "mountpoint=%s what=%s options=%s",
+	      mountpoint, what, options);
 
 	master = ap->entry->master;
 
-	entry = master_new_mapent(master, fullpath, ap->entry->age);
+	entry = master_new_mapent(master, realpath, ap->entry->age);
 	if (!entry) {
 		error(ap->logopt,
 		      MODPREFIX "failed to malloc master_mapent struct");
@@ -228,6 +228,7 @@ int mount_mount(struct autofs_point *ap,
 	}
 
 	suc.ap = nap;
+	suc.root = mountpoint;
 	suc.done = 0;
 	suc.status = 0;
 
@@ -235,7 +236,7 @@ int mount_mount(struct autofs_point *ap,
 		crit(ap->logopt,
 		     MODPREFIX
 		     "failed to create mount handler thread for %s",
-		     fullpath);
+		     realpath);
 		handle_mounts_startup_cond_destroy(&suc);
 		mounts_mutex_unlock(ap);
 		master_free_map_source(source, 1);
@@ -256,7 +257,7 @@ int mount_mount(struct autofs_point *ap,
 
 	if (suc.status) {
 		crit(ap->logopt,
-		     MODPREFIX "failed to create submount for %s", fullpath);
+		     MODPREFIX "failed to create submount for %s", realpath);
 		handle_mounts_startup_cond_destroy(&suc);
 		mounts_mutex_unlock(ap);
 		master_free_map_source(source, 1);
--- autofs-5.0.1.orig/modules/mount_bind.c
+++ autofs-5.0.1/modules/mount_bind.c
@@ -74,34 +74,24 @@ int mount_mount(struct autofs_point *ap,
 	char *fullpath;
 	char buf[MAX_ERR_BUF];
 	int err;
-	int i, rlen;
+	int i, len;
 
 	/* Root offset of multi-mount */
-	if (*name == '/' && name_len == 1) {
-		rlen = strlen(root);
-		name_len = 0;
+	len = strlen(root);
+	if (root[len - 1] == '/') {
+		fullpath = alloca(len);
+		len = snprintf(fullpath, len, "%s", root);
 	/* Direct mount name is absolute path so don't use root */
-	} else if (*name == '/')
-		rlen = 0;
-	else
-		rlen = strlen(root);
-
-	fullpath = alloca(rlen + name_len + 2);
-	if (!fullpath) {
-		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
-		logerr(MODPREFIX "alloca: %s", estr);
-		return 1;
+	} else if (*name == '/') {
+		fullpath = alloca(len + 1);
+		len = sprintf(fullpath, "%s", root);
+	} else {
+		fullpath = alloca(len + name_len + 2);
+		len = sprintf(fullpath, "%s/%s", root, name);
 	}
+	fullpath[len] = '\0';
 
-	if (name_len) {
-		if (rlen)
-			sprintf(fullpath, "%s/%s", root, name);
-		else
-			sprintf(fullpath, "%s", name);
-	} else
-		sprintf(fullpath, "%s", root);
-
-	i = strlen(fullpath);
+	i = len;
 	while (--i > 0 && fullpath[i] == '/')
 		fullpath[i] = '\0';
 
@@ -125,13 +115,6 @@ int mount_mount(struct autofs_point *ap,
 		if (!status)
 			existed = 0;
 
-		if (is_mounted(_PATH_MOUNTED, fullpath, MNTS_REAL)) {
-			error(ap->logopt,
-			      MODPREFIX "warning: %s is already mounted",
-			      fullpath);
-			return 0;
-		}
-
 		debug(ap->logopt,
 		      MODPREFIX
 		      "calling mount --bind " SLOPPY " -o %s %s %s",
--- autofs-5.0.1.orig/modules/mount_changer.c
+++ autofs-5.0.1/modules/mount_changer.c
@@ -49,34 +49,24 @@ int mount_mount(struct autofs_point *ap,
 	char *fullpath;
 	char buf[MAX_ERR_BUF];
 	int err;
-	int rlen, status, existed = 1;
+	int len, status, existed = 1;
 
 	fstype = "iso9660";
 
 	/* Root offset of multi-mount */
-	if (*name == '/' && name_len == 1) {
-		rlen = strlen(root);
-		 name_len = 0;
+	len = strlen(root);
+	if (root[len - 1] == '/') {
+		fullpath = alloca(len);
+		len = snprintf(fullpath, len, "%s", root);
 	/* Direct mount name is absolute path so don't use root */
-	} else if (*name == '/')
-		rlen = 0;
-	else
-		rlen = strlen(root);
-
-	fullpath = alloca(rlen + name_len + 2);
-	if (!fullpath) {
-		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
-		logerr(MODPREFIX "alloca: %s", estr);
-		return 1;
+	} else if (*name == '/') {
+		fullpath = alloca(len + 1);
+		len = sprintf(fullpath, "%s", root);
+	} else {
+		fullpath = alloca(len + name_len + 2);
+		len = sprintf(fullpath, "%s/%s", root, name);
 	}
-
-	if (name_len) {
-		if (rlen)
-			sprintf(fullpath, "%s/%s", root, name);
-		else
-			sprintf(fullpath, "%s", name);
-	} else
-		sprintf(fullpath, "%s", root);
+	fullpath[len] = '\0';
 
 	debug(ap->logopt, MODPREFIX "calling umount %s", what);
 
--- autofs-5.0.1.orig/modules/mount_ext2.c
+++ autofs-5.0.1/modules/mount_ext2.c
@@ -43,32 +43,22 @@ int mount_mount(struct autofs_point *ap,
 	const char *p, *p1;
 	int err, ro = 0;
 	const char *fsck_prog;
-	int rlen, status, existed = 1;
+	int len, status, existed = 1;
 
 	/* Root offset of multi-mount */
-	if (*name == '/' && name_len == 1) {
-		rlen = strlen(root);
-		name_len = 0;
+	len = strlen(root);
+	if (root[len - 1] == '/') {
+		fullpath = alloca(len);
+		len = snprintf(fullpath, len, "%s", root);
 	/* Direct mount name is absolute path so don't use root */
-	} else if (*name == '/')
-		rlen = 0;
-	else
-		rlen = strlen(root);
-
-	fullpath = alloca(rlen + name_len + 2);
-	if (!fullpath) {
-		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
-		logerr(MODPREFIX "alloca: %s", estr);
-		return 1;
+	} else if (*name == '/') {
+		fullpath = alloca(len + 1);
+		len = sprintf(fullpath, "%s", root);
+	} else {
+		fullpath = alloca(len + name_len + 2);
+		len = sprintf(fullpath, "%s/%s", root, name);
 	}
-
-	if (name_len) {
-		if (rlen)
-			sprintf(fullpath, "%s/%s", root, name);
-		else
-			sprintf(fullpath, "%s", name);
-	} else
-		sprintf(fullpath, "%s", root);
+	fullpath[len] = '\0';
 
 	debug(ap->logopt, MODPREFIX "calling mkdir_path %s", fullpath);
 
@@ -83,12 +73,6 @@ int mount_mount(struct autofs_point *ap,
 	if (!status)
 		existed = 0;
 
-	if (is_mounted(_PATH_MOUNTED, fullpath, MNTS_REAL)) {
-		error(ap->logopt,
-		      MODPREFIX "warning: %s is already mounted", fullpath);
-		return 0;
-	}
-
 	if (options && options[0]) {
 		for (p = options; (p1 = strchr(p, ',')); p = p1)
 			if (!strncmp(p, "ro", p1 - p) && ++p1 - p == sizeof("ro"))
--- autofs-5.0.1.orig/modules/mount_generic.c
+++ autofs-5.0.1/modules/mount_generic.c
@@ -42,32 +42,22 @@ int mount_mount(struct autofs_point *ap,
 	char *fullpath;
 	char buf[MAX_ERR_BUF];
 	int err;
-	int rlen, status, existed = 1;
+	int len, status, existed = 1;
 
 	/* Root offset of multi-mount */
-	if (*name == '/' && name_len == 1) {
-		rlen = strlen(root);
-		name_len = 0;
+	len = strlen(root);
+	if (root[len - 1] == '/') {
+		fullpath = alloca(len);
+		len = snprintf(fullpath, len, "%s", root);
 	/* Direct mount name is absolute path so don't use root */
-	} else if (*name == '/')
-		rlen = 0;
-	else
-		rlen = strlen(root);
-
-	fullpath = alloca(rlen + name_len + 2);
-	if (!fullpath) {
-		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
-		logerr(MODPREFIX "alloca: %s", estr);
-		return 1;
+	} else if (*name == '/') {
+		fullpath = alloca(len + 1);
+		len = sprintf(fullpath, "%s", root);
+	} else {
+		fullpath = alloca(len + name_len + 2);
+		len = sprintf(fullpath, "%s/%s", root, name);
 	}
-
-	if (name_len) {
-		if (rlen)
-			sprintf(fullpath, "%s/%s", root, name);
-		else
-			sprintf(fullpath, "%s", name);
-	} else
-		sprintf(fullpath, "%s", root);
+	fullpath[len] = '\0';
 
 	debug(ap->logopt, MODPREFIX "calling mkdir_path %s", fullpath);
 
@@ -82,12 +72,6 @@ int mount_mount(struct autofs_point *ap,
 	if (!status)
 		existed = 0;
 
-	if (is_mounted(_PATH_MOUNTED, fullpath, MNTS_REAL)) {
-		error(ap->logopt,
-		      MODPREFIX "warning: %s is already mounted", fullpath);
-		return 0;
-	}
-
 	if (options && options[0]) {
 		debug(ap->logopt,
 		      MODPREFIX "calling mount -t %s " SLOPPY "-o %s %s %s",
--- autofs-5.0.1.orig/modules/mount_nfs.c
+++ autofs-5.0.1/modules/mount_nfs.c
@@ -64,7 +64,7 @@ int mount_mount(struct autofs_point *ap,
 	struct host *this, *hosts = NULL;
 	unsigned int vers;
 	char *nfsoptions = NULL;
-	int len, rlen, status, err, existed = 1;
+	int len, status, err, existed = 1;
 	int nosymlink = 0;
 	int ro = 0;            /* Set if mount bind should be read-only */
 
@@ -146,30 +146,18 @@ int mount_mount(struct autofs_point *ap,
 	/* Construct and perhaps create mount point directory */
 
 	/* Root offset of multi-mount */
-	if (*name == '/' && name_len == 1) {
-		rlen = strlen(root);
-		name_len = 0;
+	len = strlen(root);
+	if (root[len - 1] == '/') {
+		fullpath = alloca(len);
+		len = snprintf(fullpath, len, "%s", root);
 	/* Direct mount name is absolute path so don't use root */
-	} else if (*name == '/')
-		rlen = 0;
-	else
-		rlen = strlen(root);
-
-	fullpath = alloca(rlen + name_len + 2);
-	if (!fullpath) {
-		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
-		logerr(MODPREFIX "alloca: %s", estr);
-		free_host_list(&hosts);
-		return 1;
-	}
-
-	if (name_len) {
-		if (rlen)
-			len = sprintf(fullpath, "%s/%s", root, name);
-		else
-			len = sprintf(fullpath, "%s", name);
-	} else
+	} else if (*name == '/') {
+		fullpath = alloca(len + 1);
 		len = sprintf(fullpath, "%s", root);
+	} else {
+		fullpath = alloca(len + name_len + 2);
+		len = sprintf(fullpath, "%s/%s", root, name);
+	}
 	fullpath[len] = '\0';
 
 	debug(ap->logopt, MODPREFIX "calling mkdir_path %s", fullpath);
@@ -189,13 +177,6 @@ int mount_mount(struct autofs_point *ap,
 	while (this) {
 		char *loc, *port_opt = NULL;
 
-		if (is_mounted(_PATH_MOUNTED, fullpath, MNTS_REAL)) {
-			error(ap->logopt,
-			      MODPREFIX
-			      "warning: %s is already mounted", fullpath);
-			break;
-		}
-
 		/*
 		 * If the "port" option is specified, then we don't want
 		 * a bind mount. Use the "port" option if you want to
--- autofs-5.0.1.orig/modules/parse_sun.c
+++ autofs-5.0.1/modules/parse_sun.c
@@ -31,12 +31,18 @@
 #include <sys/vfs.h>
 #include <sys/utsname.h>
 #include <netinet/in.h>
+#include <sys/mount.h>
+#include <linux/fs.h>
 
 #define MODULE_PARSE
 #include "automount.h"
 
 #define MODPREFIX "parse(sun): "
 
+#define MOUNT_MOVE_NONE		0x00
+#define MOUNT_MOVE_AUTOFS	0x01
+#define MOUNT_MOVE_OTHER	0x02
+
 int parse_version = AUTOFS_PARSE_VERSION;	/* Required by protocol */
 
 static struct mount_mod *mount_nfs = NULL;
@@ -67,6 +73,7 @@ static struct parse_context default_cont
 	1			/* Do slashify_colons */
 };
 
+int destroy_logpri_fifo(struct autofs_point *ap);
 static char *concat_options(char *left, char *right);
 
 /* Free all storage associated with this context */
@@ -750,8 +757,10 @@ add_offset_entry(struct autofs_point *ap
 
 	p_len = strlen(path);
 	/* Trailing '/' causes us pain */
-	if (p_len > 1 && path[p_len - 1] == '/')
-		p_len--;
+	if (p_len > 1) {
+		while (p_len > 1 && path[p_len - 1] == '/')
+			p_len--;
+	}
 	m_key_len = m_root_len + p_len;
 	if (m_key_len > PATH_MAX) {
 		error(ap->logopt, MODPREFIX "multi mount key too long");
@@ -949,53 +958,318 @@ static int parse_mapent(const char *ent,
 	return (p - ent);
 }
 
-static int mount_subtree_offsets(struct autofs_point *ap, struct mapent_cache *mc, struct mapent *me)
+static int move_mount(struct autofs_point *ap,
+		      const char *mm_tmp_root, const char *mm_root,
+		      unsigned int move)
 {
-	struct mapent *mm;
-	char *m_key;
-	int ret, start;
-	char *base, *m_root;
 	char buf[MAX_ERR_BUF];
+	int err;
 
-	mm = me->multi;
+	if (move == MOUNT_MOVE_NONE)
+		return 1;
+
+	err = mkdir_path(mm_root, 0555);
+	if (err < 0 && errno != EEXIST) {
+		error(ap->logopt,
+		      "failed to create move target mount point %s", mm_root);
+		return 0;
+	}
 
-	if (!mm)
+	if (move == MOUNT_MOVE_AUTOFS)
+		err = mount(mm_tmp_root, mm_root, NULL, MS_MOVE, NULL);
+	else
+		err = spawn_mount(ap->logopt,
+				  "--move", mm_tmp_root, mm_root, NULL);
+	if (err) {
+		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
+		error(ap->logopt,
+		      "failed to move mount from %s to %s: %s",
+		      mm_tmp_root, mm_root, estr);
 		return 0;
+	}
+
+	debug(ap->logopt,
+	      "moved mount tree from %s to %s", mm_tmp_root, mm_root);
+
+	return 1;
+}
+
+static void cleanup_multi_root(struct autofs_point *ap, const char *root,
+					 const char *path, unsigned int move)
+{
+	if (move == MOUNT_MOVE_NONE)
+		return;
+
+	if (move == MOUNT_MOVE_OTHER)
+		spawn_umount(ap->logopt, root, NULL);
+	else {
+		struct autofs_point *submount;
+
+		mounts_mutex_lock(ap);
+		submount = __master_find_submount(ap, path);
+		if (!submount) {
+			mounts_mutex_unlock(ap);
+			return;
+		}
+
+		alarm_delete(submount);
+		st_remove_tasks(submount);
+		st_wait_state(submount, ST_READY);
+
+		submount->parent->submnt_count--;
+		list_del_init(&submount->mounts);
+
+		ioctl(submount->ioctlfd, AUTOFS_IOC_CATATONIC, 0);
+
+		mounts_mutex_unlock(ap);
+
+		if (submount->thid) {
+			pthread_cancel(submount->thid);
+			close_mount_fds(submount);
+			umount(root);
+			destroy_logpri_fifo(submount);
+			master_free_mapent_sources(submount->entry, 1);
+			master_free_mapent(ap->entry);
+		}
+	}
+	return;
+}
+
+static void cleanup_multi_triggers(struct autofs_point *ap,
+			    struct mapent *me, const char *root, int start,
+			    const char *base)
+{
+	char path[PATH_MAX + 1];
+	char offset[PATH_MAX + 1];
+	char *poffset = offset;
+	struct mapent *oe;
+	struct list_head *mm_root, *pos;
+	const char o_root[] = "/";
+	const char *mm_base;
+
+	mm_root = &me->multi->multi_list;
+
+	if (!base)
+		mm_base = o_root;
+	else
+		mm_base = base;
 
-	cache_multi_lock(me->parent);
+	pos = NULL;
 
-	m_key = mm->key;
+	/* Make sure "none" of the offsets have an active mount. */
+	while ((poffset = cache_get_offset(mm_base, poffset, start, mm_root, &pos))) {
+		oe = cache_lookup_offset(mm_base, poffset, start, &me->multi_list);
+		/* root offset is a special case */
+		if (!oe || !oe->mapent || (strlen(oe->key) - start) == 1)
+			continue;
 
-	if (*m_key == '/') {
-		m_root = m_key;
-		start = strlen(m_key);
+		strcpy(path, root);
+		strcat(path, poffset);
+		if (umount(path)) {
+			error(ap->logopt, "error recovering from mount fail");
+			error(ap->logopt, "cannot umount offset %s", path);
+		}
+	}
+
+	return;
+}
+
+static int check_fstype_autofs_option(const char *options)
+{
+	char *tok, *tokbuf;
+	int found;
+
+	/*
+	 * Look for fstype= in options and return true if
+	 * the last occurrence is fstype=autofs.
+	 */
+	found = 0;
+	tokbuf = alloca(strlen(options) + 2);
+	strcpy(tokbuf, options);
+	tok = strtok_r(tokbuf, ",", &tokbuf);
+	if (tok) {
+		do {
+			if (strstr(tok, "fstype=")) {
+				if (strstr(tok, "autofs"))
+					found = 1;
+				else
+					found = 0;
+			}
+		} while ((tok = strtok_r(NULL, ",", &tokbuf)));
+	}
+
+	return found;
+}
+
+static int mount_subtree(struct autofs_point *ap, struct mapent *me,
+			 const char *name, char *loc, char *options, void *ctxt)
+{
+	struct mapent *mm;
+	struct mapent *ro;
+	char t_dir[] = "/tmp/autoXXXXXX";
+	char *mm_root, *mm_base, *mm_key;
+	const char *mm_tmp_root, *target;
+	unsigned int mm_tmp_root_len;
+	int start, ret = 0, rv;
+	unsigned int move;
+
+	rv = 0;
+
+	if (!me || !me->multi) {
+		int loclen = strlen(loc);
+		int namelen = strlen(name);
+		const char *root = ap->path;
+
+		if (!strcmp(ap->path, "/-"))
+			root = name;
+
+		rv = sun_mount(ap, root, name, namelen, loc, loclen, options, ctxt);
+
+		goto done;
+	}
+
+	mm = me->multi;
+	mm_key = mm->key;
+	move = MOUNT_MOVE_NONE;
+
+	if (*mm_key == '/') {
+		mm_root = mm_key;
+		start = strlen(mm_key);
 	} else {
-		start = strlen(ap->path) + strlen(m_key) + 1;
-		m_root = alloca(start + 1);
-		if (!m_root) {
-			char *estr;
-			cache_multi_unlock(me->parent);
-			estr = strerror_r(errno, buf, MAX_ERR_BUF);
-			error(ap->logopt, MODPREFIX "alloca: %s", estr);
-			return -1;
-		}
-		strcpy(m_root, ap->path);
-		strcat(m_root, "/");
-		strcat(m_root, m_key);
-	}
-
-	base = &me->key[start];
-
-	ret = mount_multi_triggers(ap, m_root, me->multi, base);
-	if (ret == -1) {
-		cache_multi_unlock(me->parent);
-		error(ap->logopt, MODPREFIX "failed to mount offset triggers");
-		return -1;
+		start = strlen(ap->path) + strlen(mm_key) + 1;
+		mm_root = alloca(start + 3);
+		strcpy(mm_root, ap->path);
+		strcat(mm_root, "/");
+		strcat(mm_root, mm_key);
 	}
 
-	cache_multi_unlock(me->parent);
+	mm_tmp_root = mkdtemp(t_dir);
+	if (!mm_tmp_root)
+		return 1;
+	mm_tmp_root_len = strlen(mm_tmp_root);
+
+	if (me == me->multi) {
+		/* name = NULL */
+		/* destination = mm_root */
+		target = mm_root;
+		mm_base = "/";
 
-	return ret;
+		/* Mount root offset if it exists */
+		ro = cache_lookup_offset(mm_base, mm_base, strlen(mm_root), &me->multi_list);
+		if (ro) {
+			char *myoptions, *ro_loc, *tmp;
+			int namelen = name ? strlen(name) : 0;
+			const char *root;
+			int ro_len;
+
+			rv = parse_mapent(ro->mapent,
+				options, &myoptions, &ro_loc, ap->logopt);
+			if (!rv) {
+				warn(ap->logopt,
+				      MODPREFIX "failed to parse root offset");
+				cache_delete_offset_list(me->mc, name);
+				rmdir(mm_tmp_root);
+				return 1;
+			}
+			ro_len = strlen(ro_loc);
+
+			move = MOUNT_MOVE_OTHER;
+			if (check_fstype_autofs_option(myoptions))
+				move = MOUNT_MOVE_AUTOFS;
+
+			root = mm_tmp_root;
+			tmp = alloca(mm_tmp_root_len + 1);
+			strcpy(tmp, mm_tmp_root);
+			tmp[mm_tmp_root_len] = '/';
+			tmp[mm_tmp_root_len + 1] = '\0';
+			root = tmp;
+
+			rv = sun_mount(ap, root, name, namelen, ro_loc, ro_len, myoptions, ctxt);
+
+			free(myoptions);
+			free(ro_loc);
+		}
+
+		if (ro && rv == 0) {
+			ret = mount_multi_triggers(ap, me, mm_tmp_root, start, mm_base);
+			if (ret == -1) {
+				error(ap->logopt, MODPREFIX
+					 "failed to mount offset triggers");
+				cleanup_multi_triggers(ap, me, mm_tmp_root, start, mm_base);
+				cleanup_multi_root(ap, mm_tmp_root, mm_root, move);
+				rmdir(mm_tmp_root);
+				return 1;
+			}
+		} else if (rv <= 0) {
+			move = MOUNT_MOVE_NONE;
+			ret = mount_multi_triggers(ap, me, mm_root, start, mm_base);
+			if (ret == -1) {
+				error(ap->logopt, MODPREFIX
+					 "failed to mount offset triggers");
+				cleanup_multi_triggers(ap, me, mm_tmp_root, start, mm_base);
+				rmdir(mm_tmp_root);
+				return 1;
+			}
+		}
+	} else {
+		int loclen = strlen(loc);
+		int namelen = strlen(name);
+
+		move = MOUNT_MOVE_OTHER;
+		if (check_fstype_autofs_option(options))
+			move = MOUNT_MOVE_AUTOFS;
+
+		/* name = mm_root + mm_base */
+		/* destination = mm_root + mm_base = name */
+		target = name;
+		mm_base = &me->key[start];
+
+		rv = sun_mount(ap, mm_tmp_root, name, namelen, loc, loclen, options, ctxt);
+		if (rv == 0) {
+			ret = mount_multi_triggers(ap, me->multi, mm_tmp_root, start, mm_base);
+			if (ret == -1) {
+				error(ap->logopt, MODPREFIX
+					 "failed to mount offset triggers");
+				cleanup_multi_triggers(ap, me, mm_tmp_root, start, mm_base);
+				cleanup_multi_root(ap, mm_tmp_root, mm_root, move);
+				rmdir(mm_tmp_root);
+				return 1;
+			}
+		} else if (rv < 0) {
+			move = MOUNT_MOVE_NONE;
+			ret = mount_multi_triggers(ap, me->multi, mm_root, start, mm_base);
+			if (ret == -1) {
+				error(ap->logopt, MODPREFIX
+					 "failed to mount offset triggers");
+				cleanup_multi_triggers(ap, me, mm_tmp_root, start, mm_base);
+				rmdir(mm_tmp_root);
+				return 1;
+			}
+		}
+	}
+
+	if (!move_mount(ap, mm_tmp_root, target, move)) {
+		cleanup_multi_triggers(ap, me, mm_tmp_root, start, mm_base);
+		cleanup_multi_root(ap, mm_tmp_root, mm_root, move);
+		rmdir(mm_tmp_root);
+		return 1;
+	}
+
+	rmdir(mm_tmp_root);
+
+	/* Mount for base of tree failed */
+	if (rv > 0)
+		return rv;
+
+done:
+	/*
+	 * Convert fail on nonstrict, non-empty multi-mount
+	 * to success
+	 */
+	if (rv < 0 && ret > 0)
+		rv = 0;
+
+	return rv;
 }
 
 /*
@@ -1017,7 +1291,7 @@ int parse_mount(struct autofs_point *ap,
 	char buf[MAX_ERR_BUF];
 	struct map_source *source;
 	struct mapent_cache *mc;
-	struct mapent *me, *ro;
+	struct mapent *me = NULL;
 	char *pmapent, *options;
 	const char *p;
 	int mapent_len, rv = 0;
@@ -1142,7 +1416,7 @@ int parse_mount(struct autofs_point *ap,
 		char *m_root = NULL;
 		int m_root_len;
 		time_t age = time(NULL);
-		int l, ret;
+		int l;
 
 		/* If name starts with "/" it's a direct mount */
 		if (*name == '/') {
@@ -1290,48 +1564,7 @@ int parse_mount(struct autofs_point *ap,
 		 */
 		cache_set_parents(me);
 
-		/* Mount root offset if it exists */
-		ro = cache_lookup_offset("/", "/", strlen(m_root), &me->multi_list);
-		if (ro) {
-			char *myoptions, *loc;
-
-			rv = parse_mapent(ro->mapent,
-				options, &myoptions, &loc, ap->logopt);
-			if (!rv) {
-				warn(ap->logopt,
-				      MODPREFIX "failed to mount root offset");
-				cache_delete_offset_list(mc, name);
-				cache_multi_unlock(me);
-				cache_unlock(mc);
-				free(options);
-				pthread_setcancelstate(cur_state, NULL);
-				return 1;
-			}
-
-			rv = sun_mount(ap, m_root,
-				"/", 1, loc, strlen(loc), myoptions, ctxt);
-
-			free(myoptions);
-			free(loc);
-		}
-
-		ret = mount_multi_triggers(ap, m_root, me, "/");
-		if (ret == -1) {
-			warn(ap->logopt,
-			      MODPREFIX "failed to mount offset triggers");
-			cache_multi_unlock(me);
-			cache_unlock(mc);
-			free(options);
-			pthread_setcancelstate(cur_state, NULL);
-			return 1;
-		}
-
-		/*
-		 * Convert fail on nonstrict, non-empty multi-mount
-		 * to success
-		 */
-		if (rv < 0 && ret > 0)
-			rv = 0;
+		rv = mount_subtree(ap, me, name, NULL, options, ctxt);
 
 		cache_multi_unlock(me);
 		cache_unlock(mc);
@@ -1449,24 +1682,15 @@ mount_it:
 		      MODPREFIX "core of entry: options=%s, loc=%.*s",
 		      options, loclen, loc);
 
-		rv = sun_mount(ap, ap->path, name, name_len, loc, loclen, options, ctxt);
+		cache_readlock(mc);
+		cache_multi_lock(me);
+
+		rv = mount_subtree(ap, me, name, loc, options, ctxt);
 
 		free(loc);
 		free(options);
 
-		/*
-		 * If it's a multi-mount insert the triggers
-		 * These are always direct mount triggers so root = ""
-		 */
-		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
-		cache_readlock(mc);
-		me = cache_lookup_distinct(mc, name);
-		if (me) {
-			int ret = mount_subtree_offsets(ap, mc, me);
-			/* Convert fail on nonstrict, non-empty multi-mount to success */
-			if (rv < 0 && ret > 0)
-				rv = 0;
-		}
+		cache_multi_unlock(me);
 		cache_unlock(mc);
 		pthread_setcancelstate(cur_state, NULL);
 	}