autofs-5.0.3 - fix multi mount race. From: Ian Kent 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 :/ /om2 :/ /om2/om21 :/ /om2/om22 :/ when a path walk hits mm1/om2 :/ 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 :/ mount. So the walk follows the stack up to the mount at :/, 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. --- CHANGELOG | 1 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 | 408 ++++++++++++++++++++++++++++++++++++----------- 18 files changed, 517 insertions(+), 344 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ff44cc7..a7b41ec 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -29,6 +29,7 @@ - fix submount shutdown recovery handling. - avoid stat of possibly dead mount points and limit time to wait for umount during expire. +- make mount of multi-mounts wuth a root offset atomic. 14/01/2008 autofs-5.0.3 ----------------------- diff --git a/daemon/automount.c b/daemon/automount.c index 5bd5f6d..5b6a561 100644 --- a/daemon/automount.c +++ b/daemon/automount.c @@ -489,7 +489,7 @@ static int umount_subtree_mounts(struct autofs_point *ap, const char *path, unsi 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++; @@ -572,7 +572,7 @@ static int umount_all(struct autofs_point *ap, int force) 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; @@ -589,7 +589,7 @@ int umount_autofs(struct autofs_point *ap, int force) 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); @@ -754,7 +754,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; @@ -1056,7 +1056,7 @@ static int autofs_init_ap(struct autofs_point *ap) return 0; } -static int mount_autofs(struct autofs_point *ap) +static int mount_autofs(struct autofs_point *ap, const char *root) { int status = 0; @@ -1066,7 +1066,7 @@ static int mount_autofs(struct autofs_point *ap) 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; @@ -1531,10 +1531,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); @@ -1545,14 +1547,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"); @@ -1615,7 +1627,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; } @@ -1625,7 +1637,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; diff --git a/daemon/direct.c b/daemon/direct.c index 7fb78a3..34e882b 100644 --- a/daemon/direct.c +++ b/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_point *ap, struct mapent *me) 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_point *ap, struct mapent *me) 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_point *ap, struct mapent *me) * 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_point *ap, struct mapent *me) 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_point *ap, struct mapent *me) 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_point *ap, struct mapent *me) } 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_point *ap, struct mapent *me) 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_point *ap, struct mapent *me) 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; } diff --git a/daemon/indirect.c b/daemon/indirect.c index e832cd4..168d915 100644 --- a/daemon/indirect.c +++ b/daemon/indirect.c @@ -83,7 +83,7 @@ static int unlink_mount_tree(struct autofs_point *ap, struct mnt_list *mnts) 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(struct autofs_point *ap) 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(struct autofs_point *ap) } 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(struct autofs_point *ap) 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(struct autofs_point *ap) 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(struct autofs_point *ap) 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_point *ap) 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_point *ap) 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 autofs_point *ap) 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_point *ap) 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_point *ap) 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_point *ap) 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_point *ap) 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; diff --git a/daemon/lookup.c b/daemon/lookup.c index 85ac519..db3b636 100644 --- a/daemon/lookup.c +++ b/daemon/lookup.c @@ -565,7 +565,7 @@ int lookup_nss_read_map(struct autofs_point *ap, struct map_source *source, time 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; @@ -611,12 +611,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) { diff --git a/daemon/state.c b/daemon/state.c index 122177c..0042a74 100644 --- a/daemon/state.c +++ b/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, "/"); diff --git a/include/automount.h b/include/automount.h index 8ff24a7..61cdac6 100644 --- a/include/automount.h +++ b/include/automount.h @@ -230,7 +230,7 @@ int lookup_nss_read_master(struct master *master, time_t age); 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, const char *name, int namelen); 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); diff --git a/include/master.h b/include/master.h index a397a75..e62c67b 100644 --- a/include/master.h +++ b/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 *); diff --git a/include/mounts.h b/include/mounts.h index 7120351..ca4f9f3 100644 --- a/include/mounts.h +++ b/include/mounts.h @@ -85,7 +85,7 @@ int tree_find_mnt_ents(struct mnt_list *mnts, struct list_head *list, const char 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 diff --git a/lib/master.c b/lib/master.c index 71ba04a..d971ad6 100644 --- a/lib/master.c +++ b/lib/master.c @@ -602,27 +602,32 @@ struct master_mapent *master_find_mapent(struct master *master, const char *path 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_mapent *entry) } suc.ap = ap; + suc.root = ap->path; suc.done = 0; suc.status = 0; diff --git a/lib/mounts.c b/lib/mounts.c index d77a6b0..f6fc389 100644 --- a/lib/mounts.c +++ b/lib/mounts.c @@ -1105,7 +1105,8 @@ int umount_ent(struct autofs_point *ap, const char *path) 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; @@ -1113,17 +1114,13 @@ int mount_multi_triggers(struct autofs_point *ap, char *root, struct mapent *me, 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); @@ -1137,9 +1134,9 @@ int mount_multi_triggers(struct autofs_point *ap, char *root, struct mapent *me, 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 { @@ -1161,7 +1158,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; @@ -1198,7 +1195,7 @@ int umount_multi_triggers(struct autofs_point *ap, char *root, struct mapent *me * 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++; @@ -1238,7 +1235,7 @@ int umount_multi_triggers(struct autofs_point *ap, char *root, struct mapent *me 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++; diff --git a/modules/mount_autofs.c b/modules/mount_autofs.c index d6cbda8..8a7dd78 100644 --- a/modules/mount_autofs.c +++ b/modules/mount_autofs.c @@ -48,7 +48,7 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, { 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, const char *root, const char *name, 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, const char *root, const char *name, } 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, const char *root, const char *name, } suc.ap = nap; + suc.root = mountpoint; suc.done = 0; suc.status = 0; @@ -235,7 +236,7 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, 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, const char *root, const char *name, 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); diff --git a/modules/mount_bind.c b/modules/mount_bind.c index e4a04d0..396a3ca 100644 --- a/modules/mount_bind.c +++ b/modules/mount_bind.c @@ -74,34 +74,24 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, int 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, const char *root, const char *name, int 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", diff --git a/modules/mount_changer.c b/modules/mount_changer.c index 08d9147..e838fcf 100644 --- a/modules/mount_changer.c +++ b/modules/mount_changer.c @@ -49,34 +49,24 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, int 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); diff --git a/modules/mount_ext2.c b/modules/mount_ext2.c index 8cf9937..7589991 100644 --- a/modules/mount_ext2.c +++ b/modules/mount_ext2.c @@ -43,32 +43,22 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, int 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, const char *root, const char *name, int 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")) diff --git a/modules/mount_generic.c b/modules/mount_generic.c index 85b4391..4b1213e 100644 --- a/modules/mount_generic.c +++ b/modules/mount_generic.c @@ -42,32 +42,22 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, int 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, const char *root, const char *name, int 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", diff --git a/modules/mount_nfs.c b/modules/mount_nfs.c index d7f42a7..0b253d8 100644 --- a/modules/mount_nfs.c +++ b/modules/mount_nfs.c @@ -64,7 +64,7 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, int 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, const char *root, const char *name, int /* 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, const char *root, const char *name, int 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 diff --git a/modules/parse_sun.c b/modules/parse_sun.c index d839694..b548520 100644 --- a/modules/parse_sun.c +++ b/modules/parse_sun.c @@ -31,12 +31,18 @@ #include #include #include +#include +#include #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_context = { 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 */ @@ -756,8 +763,10 @@ add_offset_entry(struct autofs_point *ap, const char *name, 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"); @@ -961,53 +970,318 @@ static int parse_mapent(const char *ent, char *g_options, char **options, char * 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; - if (!mm) + 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; + } - cache_multi_lock(me->parent); + 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; + } - m_key = mm->key; + debug(ap->logopt, + "moved mount tree from %s to %s", mm_tmp_root, mm_root); - if (*m_key == '/') { - m_root = m_key; - start = strlen(m_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; + 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); } - strcpy(m_root, ap->path); - strcat(m_root, "/"); - strcat(m_root, m_key); } + return; +} - base = &me->key[start]; +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; + + pos = NULL; + + /* 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; + + 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; +} - 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; +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))); } - cache_multi_unlock(me->parent); + return found; +} - return ret; +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(mm_key) + 1; + mm_root = alloca(start + 3); + strcpy(mm_root, ap->path); + strcat(mm_root, "/"); + strcat(mm_root, mm_key); + } + + 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 = "/"; + + /* 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; } /* @@ -1029,7 +1303,7 @@ int parse_mount(struct autofs_point *ap, const char *name, 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; @@ -1154,7 +1428,7 @@ int parse_mount(struct autofs_point *ap, const char *name, 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 == '/') { @@ -1302,48 +1576,7 @@ int parse_mount(struct autofs_point *ap, const char *name, */ 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); @@ -1461,24 +1694,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); }