autofs-5.0.3 - use miscellaneous device node, if available, for active restart From: Ian Kent There is a problem with active restarts in autofs (that is to say restarting autofs when there are busy mounts). Currently autofs uses "umount -l" to clear active mounts at restart. While using lazy umount works for most cases, anything that needs to walk back up the mount tree to construct a path, such as getcwd(2) and the proc file system /proc//cwd, no longer works because the point from which the path is constructed has been detached from the mount tree. The actual problem with autofs is that it can't reconnect to existing mounts. Immediately one things of just adding the ability to remount autofs file systems would solve it, but alas, that can't work. This is because autofs direct mounts and the implementation of "on demand mount and expire" of nested mount trees have the file system mounted on top of the mount trigger dentry. To resolve this a miscellaneous device node for routing ioctl commands to these mount points has been implemented in the autofs4 kernel module and a library added to autofs. This provides the ability to open a file descriptor for these over mounted autofs mount points. This patch adds the functionaility needed to utilize these new features and re-construct a mount tree from existing mounts and then re-connect them. Clrearly, the maps used must correspond to the existing mounts for this to work, so changing maps between stopping and starting autofs again may lead to inconsistencies. --- CHANGELOG | 1 README.active-restart | 114 +++++++++++++++++++ daemon/automount.c | 15 +- daemon/direct.c | 146 ++++++++++++++---------- daemon/indirect.c | 62 ++++++---- daemon/lookup.c | 12 +- daemon/mount.c | 7 + daemon/state.c | 2 include/automount.h | 28 +++-- include/mounts.h | 10 +- lib/cache.c | 28 ++++- lib/master.c | 7 + lib/master_parse.y | 2 lib/mounts.c | 289 +++++++++++++++++++++++++++++++++++++++++++++++ modules/lookup_hosts.c | 2 modules/mount_afs.c | 3 modules/mount_autofs.c | 11 +- modules/mount_bind.c | 8 + modules/mount_changer.c | 5 + modules/mount_ext2.c | 5 + modules/mount_generic.c | 5 + modules/mount_nfs.c | 8 + modules/parse_sun.c | 104 +++++++++-------- 23 files changed, 701 insertions(+), 173 deletions(-) create mode 100644 README.active-restart diff --git a/CHANGELOG b/CHANGELOG index 0d0a7c4..083c5b6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -50,6 +50,7 @@ - fix master map lexer eval order. - fix bad alloca usage. - add miscellaneous device node interface library. +- use miscellaneous device node, if available, for active restart. 14/01/2008 autofs-5.0.3 ----------------------- diff --git a/README.active-restart b/README.active-restart new file mode 100644 index 0000000..95e9862 --- /dev/null +++ b/README.active-restart @@ -0,0 +1,114 @@ + +The problem +----------- + +The initial release of autofs version 5 used the "umount -l" (lazy +umount) to clear stale mounts at startup so that new mounts could +be made with a clean slate. This proved to be a bad choice because +the lazy umount removes the mount from the kernel mount tree. While +the mount itself persists (invisibe to further VFS lookups) until +the mount isn't busy anymore the kernel function d_path(), used to +calculate the path from the mount point to the root, isn't able to +perform its function any more. This leads to things that use d_path(), +such as /bin/pwd, the contents of the link /proc//cwd, and so on. + +The actual problem with autofs is that it can't re-connect to existing +mounts. Immediately one thinks of just adding the ability to remount +the autofs file system would solve it, but that can't work. This is +because autofs direct mounts and the implementation of "on demand mount +and expire" of nested mount trees i(multi-mounts) have the file system +mounted directly on top of the mount trigger directory. + +For example, there are two types of automount maps, direct (in the kernel +module source you will see a third type called an offset, which is just +a direct mount in disguise) and indirect. + +Here is a master map with direct and indirect map entries: + +/- /etc/auto.direct +/test /etc/auto.indirect + +and the corresponding map files: + +/etc/auto.direct: + +/automount/dparse/g6 budgie:/autofs/export1 +/automount/dparse/g1 shark:/autofs/export1 +and so on. + +/etc/auto.indirect: + +g1 shark:/autofs/export1 +g6 budgie:/autofs/export1 +and so on. + +For the above indirect map an autofs file system is mounted on /test and +mounts are triggered for each sub-directory key. So we see a mount of +shark:/autofs/export1 on /test/g1, for example. + +The way that direct mounts are handled is by making an autofs mount on +each full path, such as /automount/dparse/g1, and using it as a mount +trigger. So when we walk on the path we mount shark:/autofs/export1 "on +top of this mount point". + +But, each entry in direct and indirect maps can have offsets (making +them multi-mount map entries). + +For example, an indirect mount map entry could also be: + +g1 \ + / shark:/autofs/export5/testing/test \ + /s1 shark:/autofs/export/testing/test/s1 \ + /s2 shark:/autofs/export5/testing/test/s2 \ + /s1/ss1 shark:/autofs/export1 \ + /s2/ss2 shark:/autofs/export2 + +and a similarly a direct mount map entry could also be: + +/automount/dparse/g1 \ + / shark:/autofs/export5/testing/test \ + /s1 shark:/autofs/export/testing/test/s1 \ + /s2 shark:/autofs/export5/testing/test/s2 \ + /s1/ss1 shark:/autofs/export2 \ + /s2/ss2 shark:/autofs/export2 + +One of the issues with version 4 of autofs was that, when mounting an +entry with a large number of offsets, possibly with nesting, we needed +to mount and umount all of the offsets as a single unit. Not really a +problem, except for people with a large number of offsets in map entries. +This mechanism is used for the well known "hosts" map and we have seen +cases (in 2.4) where the available number of mounts are exhausted or +where the number of privileged ports available is exhausted. + +In version 5 we mount only as we go down the tree of offsets and +similarly for expiring them which resolves the above problem. There is +somewhat more detail to the implementation but it isn't needed for the +sake of the problem explanation. The one important detail is that these +offsets are implemented using the same mechanism as the direct mounts +above and so the autofs mount points can be covered by another mount. + +To be able to restart autofs leaving existing direct, indirect and +offset mounts in place we need to be able to obtain a file handle +for these potentially covered autofs mount points. To do this the +autofs ioctl control implementation has been re-implemented, +providing the same functions as the existing ioctl interface and +new operations to the ability to open a file handle on a covered +autofs mount point. + +In order to utilize the new interface both kernel and user space +changes (included in this release) are needed. The default install +of autofs won't use the new interface unless it is enabled in the +configuration. This can be done by setting "USE_MISC_DEVICE" to "yes" +in the autofs configuration. In addition, if selinux is being used, +it will likely need to allow operations on the autofs device file +or be set to permissive mode. + +Patches for several recent kernel that don't have the implementation +can be found in the patches directory. They have "dev-ioctl" in their +name. Note that, to use these patches, you should be using a kernel +patched with all the current autofs bug fixes since, apart from porobably +not working, the patch probably won't apply either. The bug fix patches +can also be found in the patches directory and they have "v5-update" in +their names. + +Ian diff --git a/daemon/automount.c b/daemon/automount.c index 03e4851..863aac5 100644 --- a/daemon/automount.c +++ b/daemon/automount.c @@ -391,12 +391,12 @@ static void check_rm_dirs(struct autofs_point *ap, const char *path, int incl) if (!incl && ap->submount) return; - if ((!ap->ghost) || + if ((!(ap->flags & MOUNT_FLAG_GHOST)) || (ap->state == ST_SHUTDOWN_PENDING || ap->state == ST_SHUTDOWN_FORCE || ap->state == ST_SHUTDOWN)) rm_unwanted(ap->logopt, path, incl, ap->dev); - else if (ap->ghost && (ap->type == LKP_INDIRECT)) + else if ((ap->flags & MOUNT_FLAG_GHOST) && (ap->type == LKP_INDIRECT)) rm_unwanted(ap->logopt, path, 0, ap->dev); } @@ -487,7 +487,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); + cache_multi_writelock(me->parent); if (umount_multi_triggers(ap, me, root, base)) { warn(ap->logopt, "some offset mounts still present under %s", path); @@ -1452,7 +1452,8 @@ static void handle_mounts_cleanup(void *arg) submount = ap->submount; strcpy(path, ap->path); - if (!submount && strcmp(ap->path, "/-") && ap->dir_created) + if (!submount && strcmp(ap->path, "/-") && + ap->flags & MOUNT_FLAG_DIR_CREATED) clean = 1; if (submount) { @@ -1530,7 +1531,7 @@ void *handle_mounts(void *arg) free(root); - if (ap->ghost && ap->type != LKP_DIRECT) + if (ap->flags & MOUNT_FLAG_GHOST && ap->type != LKP_DIRECT) info(ap->logopt, "ghosting enabled"); suc->status = 0; @@ -1955,7 +1956,9 @@ int main(int argc, char *argv[]) if (!query_kproto_ver() || get_kver_major() < 5) { fprintf(stderr, - "%s: kernel protocol version 5.00 or above required.\n", + "%s: test mount forbidden or " + "incorrect kernel protocol version, " + "kernel protocol version 5.00 or above required.\n", program); exit(1); } diff --git a/daemon/direct.c b/daemon/direct.c index 99d6d1f..98fcc07 100644 --- a/daemon/direct.c +++ b/daemon/direct.c @@ -20,6 +20,7 @@ * ----------------------------------------------------------------------- */ #include +#include #include #include #include @@ -177,7 +178,7 @@ force_umount: } else info(ap->logopt, "umounted direct mount %s", me->key); - if (!rv && me->dir_created) { + if (!rv && me->flags & MOUNT_FLAG_DIR_CREATED) { if (rmdir(me->key) == -1) { char *estr = strerror_r(errno, buf, MAX_ERR_BUF); warn(ap->logopt, "failed to remove dir %s: %s", @@ -278,15 +279,10 @@ static int unlink_mount_tree(struct autofs_point *ap, struct list_head *list) return ret; } -int do_mount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, struct mapent *me) +static int unlink_active_mounts(struct autofs_point *ap, struct mnt_list *mnts, struct mapent *me) { struct ioctl_ops *ops = get_ioctl_ops(); - struct mnt_params *mp; - time_t timeout = ap->exp_timeout; - struct stat st; - int status, ret, ioctlfd; struct list_head list; - const char *map_name; INIT_LIST_HEAD(&list); @@ -315,18 +311,55 @@ int do_mount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, struc return 0; } + } - if (!unlink_mount_tree(ap, &list)) { - debug(ap->logopt, - "already mounted as other than autofs " - "or failed to unlink entry in tree"); - return -1; - } + if (!unlink_mount_tree(ap, &list)) { + debug(ap->logopt, + "already mounted as other than autofs " + "or failed to unlink entry in tree"); + return 0; } - if (me->ioctlfd != -1) { - error(ap->logopt, "active direct mount %s", me->key); - return -1; + return 1; +} + +int do_mount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, struct mapent *me) +{ + const char *str_direct = mount_type_str(t_direct); + struct ioctl_ops *ops = get_ioctl_ops(); + struct mnt_params *mp; + time_t timeout = ap->exp_timeout; + struct stat st; + int status, ret, ioctlfd; + const char *map_name; + + /* Calculate the timeouts */ + ap->exp_runfreq = (timeout + CHECK_RATIO - 1) / CHECK_RATIO; + + if (ops->version) { + ap->flags |= MOUNT_FLAG_REMOUNT; + ret = try_remount(ap, me, t_direct); + ap->flags &= ~MOUNT_FLAG_REMOUNT; + if (ret == 1) + return 0; + if (ret == 0) + return -1; + } else { + /* + * A return of 0 indicates we're re-reading the map. + * A return of 1 indicates we successfully unlinked + * the mount tree if there was one. A return of -1 + * inducates we failed to unlink the mount tree so + * we have to return a failure. + */ + ret = unlink_active_mounts(ap, mnts, me); + if (ret == -1 || ret == 0) + return ret; + + if (me->ioctlfd != -1) { + error(ap->logopt, "active direct mount %s", me->key); + return -1; + } } status = pthread_once(&key_mnt_params_once, key_mnt_params_init); @@ -352,7 +385,7 @@ int do_mount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, struc } if (!mp->options) { - mp->options = make_options_string(ap->path, ap->kpipefd, "direct"); + mp->options = make_options_string(ap->path, ap->kpipefd, str_direct); if (!mp->options) return 0; } @@ -366,10 +399,10 @@ int do_mount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, struc } /* If we recieve an error, and it's EEXIST or EROFS we know the directory was not created. */ - me->dir_created = 0; + me->flags &= ~MOUNT_FLAG_DIR_CREATED; } else { /* No errors so the directory was successfully created */ - me->dir_created = 1; + me->flags |= MOUNT_FLAG_DIR_CREATED; } map_name = me->mc->map->argv[0]; @@ -393,24 +426,9 @@ int do_mount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, struc goto out_umount; } - /* Calculate the timeouts */ - ap->exp_runfreq = (timeout + CHECK_RATIO - 1) / CHECK_RATIO; - ops->timeout(ap->logopt, ioctlfd, &timeout); - - if (ap->exp_timeout) - info(ap->logopt, - "mounted direct mount on %s " - "with timeout %u, freq %u seconds", me->key, - (unsigned int) ap->exp_timeout, - (unsigned int) ap->exp_runfreq); - else - info(ap->logopt, - "mounted direct mount on %s with timeouts disabled", - me->key); - + notify_mount_result(ap, me->key, str_direct); cache_set_ino_index(me->mc, me->key, st.st_dev, st.st_ino); - ops->close(ap->logopt, ioctlfd); debug(ap->logopt, "mounted trigger %s", me->key); @@ -421,7 +439,7 @@ out_umount: /* TODO: maybe force umount (-l) */ umount(me->key); out_err: - if (me->dir_created) + if (me->flags & MOUNT_FLAG_DIR_CREATED) rmdir(me->key); return -1; @@ -453,8 +471,6 @@ int mount_autofs_direct(struct autofs_point *ap) pthread_cleanup_push(master_source_lock_cleanup, ap->entry); master_source_readlock(ap->entry); nc = ap->entry->master->nc; - cache_readlock(nc); - pthread_cleanup_push(cache_lock_cleanup, nc); map = ap->entry->maps; while (map) { /* @@ -467,8 +483,6 @@ int mount_autofs_direct(struct autofs_point *ap) } mc = map->mc; - pthread_cleanup_push(cache_lock_cleanup, mc); - cache_readlock(mc); me = cache_enumerate(mc, NULL); while (me) { ne = cache_lookup_distinct(nc, me->key); @@ -496,12 +510,10 @@ int mount_autofs_direct(struct autofs_point *ap) me = cache_enumerate(mc, me); } - pthread_cleanup_pop(1); map = map->next; } pthread_cleanup_pop(1); pthread_cleanup_pop(1); - pthread_cleanup_pop(1); return 0; } @@ -607,7 +619,7 @@ force_umount: } else info(ap->logopt, "umounted offset mount %s", me->key); - if (!rv && me->dir_created) { + if (!rv && me->flags & MOUNT_FLAG_DIR_CREATED) { if (rmdir(me->key) == -1) { char *estr = strerror_r(errno, buf, MAX_ERR_BUF); warn(ap->logopt, "failed to remove dir %s: %s", @@ -619,6 +631,7 @@ force_umount: int mount_autofs_offset(struct autofs_point *ap, struct mapent *me, const char *root, const char *offset) { + const char *str_offset = mount_type_str(t_offset); struct ioctl_ops *ops = get_ioctl_ops(); char buf[MAX_ERR_BUF]; struct mnt_params *mp; @@ -628,16 +641,24 @@ int mount_autofs_offset(struct autofs_point *ap, struct mapent *me, const char * const char *type, *map_name = NULL; char mountpoint[PATH_MAX]; - if (is_mounted(_PROC_MOUNTS, me->key, MNTS_AUTOFS)) { - if (ap->state != ST_READMAP) - warn(ap->logopt, - "trigger %s already mounted", me->key); - return MOUNT_OFFSET_OK; - } - - if (me->ioctlfd != -1) { - error(ap->logopt, "active offset mount %s", me->key); + if (ops->version && ap->flags & MOUNT_FLAG_REMOUNT) { + ret = try_remount(ap, me, t_offset); + if (ret == 1) + return MOUNT_OFFSET_OK; return MOUNT_OFFSET_FAIL; + } else { +/* + if (is_mounted(_PROC_MOUNTS, me->key, MNTS_AUTOFS)) { + if (ap->state != ST_READMAP) + warn(ap->logopt, + "trigger %s already mounted", me->key); + return MOUNT_OFFSET_OK; + } +*/ + if (me->ioctlfd != -1) { + error(ap->logopt, "active offset mount %s", me->key); + return MOUNT_OFFSET_FAIL; + } } status = pthread_once(&key_mnt_params_once, key_mnt_params_init); @@ -663,7 +684,7 @@ int mount_autofs_offset(struct autofs_point *ap, struct mapent *me, const char * } if (!mp->options) { - mp->options = make_options_string(ap->path, ap->kpipefd, "offset"); + mp->options = make_options_string(ap->path, ap->kpipefd, str_offset); if (!mp->options) return MOUNT_OFFSET_OK; } @@ -688,7 +709,7 @@ int mount_autofs_offset(struct autofs_point *ap, struct mapent *me, const char * * If we recieve an error, and it's EEXIST * we know the directory was not created. */ - me->dir_created = 0; + me->flags &= ~MOUNT_FLAG_DIR_CREATED; } else if (errno == EACCES) { /* * We require the mount point directory to exist when @@ -712,7 +733,7 @@ int mount_autofs_offset(struct autofs_point *ap, struct mapent *me, const char * } } else { /* No errors so the directory was successfully created */ - me->dir_created = 1; + me->flags |= MOUNT_FLAG_DIR_CREATED; } debug(ap->logopt, @@ -751,6 +772,7 @@ int mount_autofs_offset(struct autofs_point *ap, struct mapent *me, const char * } ops->timeout(ap->logopt, ioctlfd, &timeout); + notify_mount_result(ap, mountpoint, str_offset); cache_set_ino_index(me->mc, me->key, st.st_dev, st.st_ino); ops->close(ap->logopt, ioctlfd); @@ -761,7 +783,7 @@ int mount_autofs_offset(struct autofs_point *ap, struct mapent *me, const char * out_umount: umount(mountpoint); out_err: - if (stat(mountpoint, &st) == 0 && me->dir_created) + if (stat(mountpoint, &st) == 0 && me->flags & MOUNT_FLAG_DIR_CREATED) rmdir_path(ap, mountpoint, st.st_dev); return MOUNT_OFFSET_FAIL; @@ -803,6 +825,7 @@ void *expire_proc_direct(void *arg) left = 0; + pthread_cleanup_push(mnts_cleanup, mnts); mnts = tree_make_mnt_tree(_PROC_MOUNTS, "/"); /* Get a list of mounts select real ones and expire them if possible */ @@ -811,7 +834,6 @@ void *expire_proc_direct(void *arg) ec.status = 0; return NULL; } - pthread_cleanup_push(mnts_cleanup, mnts); list_for_each(p, &list) { next = list_entry(p, struct mnt_list, list); @@ -1067,6 +1089,7 @@ int handle_packet_expire_direct(struct autofs_point *ap, autofs_packet_expire_di crit(ap->logopt, "can't find map entry for (%lu,%lu)", (unsigned long) pkt->dev, (unsigned long) pkt->ino); master_source_unlock(ap->entry); + master_mutex_unlock(); pthread_setcancelstate(state, NULL); return 1; } @@ -1304,6 +1327,8 @@ int handle_packet_missing_direct(struct autofs_point *ap, autofs_packet_missing_ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); + master_mutex_lock(); + /* * If our parent is a direct or offset mount that has been * covered by a mount and another lookup occurs after the @@ -1355,6 +1380,7 @@ int handle_packet_missing_direct(struct autofs_point *ap, autofs_packet_missing_ if (ioctlfd == -1) { cache_unlock(mc); master_source_unlock(ap->entry); + master_mutex_unlock(); pthread_setcancelstate(state, NULL); crit(ap->logopt, "failed to create ioctl fd for %s", me->key); /* TODO: how do we clear wait q in kernel ?? */ @@ -1371,6 +1397,7 @@ int handle_packet_missing_direct(struct autofs_point *ap, autofs_packet_missing_ ops->close(ap->logopt, ioctlfd); cache_unlock(mc); master_source_unlock(ap->entry); + master_mutex_unlock(); pthread_setcancelstate(state, NULL); return 1; } @@ -1383,6 +1410,7 @@ int handle_packet_missing_direct(struct autofs_point *ap, autofs_packet_missing_ ops->close(ap->logopt, ioctlfd); cache_unlock(mc); master_source_unlock(ap->entry); + master_mutex_unlock(); pthread_setcancelstate(state, NULL); return 1; } @@ -1396,6 +1424,7 @@ int handle_packet_missing_direct(struct autofs_point *ap, autofs_packet_missing_ ops->close(ap->logopt, ioctlfd); cache_unlock(mc); master_source_unlock(ap->entry); + master_mutex_unlock(); pthread_setcancelstate(state, NULL); return 1; } @@ -1432,6 +1461,7 @@ int handle_packet_missing_direct(struct autofs_point *ap, autofs_packet_missing_ ops->close(ap->logopt, ioctlfd); cache_unlock(mc); master_source_unlock(ap->entry); + master_mutex_unlock(); mount_mutex_unlock(mt); pending_cond_destroy(mt); pending_mutex_destroy(mt); @@ -1443,6 +1473,8 @@ int handle_packet_missing_direct(struct autofs_point *ap, autofs_packet_missing_ cache_unlock(mc); master_source_unlock(ap->entry); + master_mutex_unlock(); + pthread_cleanup_push(free_pending_args, mt); pthread_cleanup_push(pending_mutex_destroy, mt); pthread_cleanup_push(pending_cond_destroy, mt); diff --git a/daemon/indirect.c b/daemon/indirect.c index 4f75a4f..1232810 100644 --- a/daemon/indirect.c +++ b/daemon/indirect.c @@ -20,6 +20,7 @@ * ----------------------------------------------------------------------- */ #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include #include #include "automount.h" @@ -85,6 +87,7 @@ static int unlink_mount_tree(struct autofs_point *ap, struct mnt_list *mnts) static int do_mount_autofs_indirect(struct autofs_point *ap, const char *root) { + const char *str_indirect = mount_type_str(t_indirect); struct ioctl_ops *ops = get_ioctl_ops(); time_t timeout = ap->exp_timeout; char *options = NULL; @@ -93,19 +96,31 @@ static int do_mount_autofs_indirect(struct autofs_point *ap, const char *root) struct mnt_list *mnts; int ret; - mnts = get_mnt_list(_PROC_MOUNTS, ap->path, 1); - if (mnts) { - ret = unlink_mount_tree(ap, mnts); - free_mnt_list(mnts); - if (!ret) { - error(ap->logopt, - "already mounted as other than autofs " - "or failed to unlink entry in tree"); - goto out_err; + ap->exp_runfreq = (timeout + CHECK_RATIO - 1) / CHECK_RATIO; + + if (ops->version) { + ap->flags |= MOUNT_FLAG_REMOUNT; + ret = try_remount(ap, NULL, t_indirect); + ap->flags &= ~MOUNT_FLAG_REMOUNT; + if (ret == 1) + return 0; + if (ret == 0) + return -1; + } else { + mnts = get_mnt_list(_PROC_MOUNTS, ap->path, 1); + if (mnts) { + ret = unlink_mount_tree(ap, mnts); + free_mnt_list(mnts); + if (!ret) { + error(ap->logopt, + "already mounted as other than autofs " + "or failed to unlink entry in tree"); + goto out_err; + } } } - options = make_options_string(ap->path, ap->kpipefd, NULL); + options = make_options_string(ap->path, ap->kpipefd, str_indirect); if (!options) { error(ap->logopt, "options string error"); goto out_err; @@ -121,10 +136,10 @@ static int do_mount_autofs_indirect(struct autofs_point *ap, const char *root) } /* If we recieve an error, and it's EEXIST or EROFS we know the directory was not created. */ - ap->dir_created = 0; + ap->flags &= ~MOUNT_FLAG_DIR_CREATED; } else { /* No errors so the directory was successfully created */ - ap->dir_created = 1; + ap->flags |= MOUNT_FLAG_DIR_CREATED; } type = ap->entry->maps->type; @@ -165,24 +180,14 @@ static int do_mount_autofs_indirect(struct autofs_point *ap, const char *root) ap->exp_runfreq = (timeout + CHECK_RATIO - 1) / CHECK_RATIO; ops->timeout(ap->logopt, ap->ioctlfd, &timeout); - - if (ap->exp_timeout) - info(ap->logopt, - "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 for %s with timeouts disabled", - ap->path); + notify_mount_result(ap, root, str_indirect); return 0; out_umount: umount(root); out_rmdir: - if (ap->dir_created) + if (ap->flags & MOUNT_FLAG_DIR_CREATED) rmdir(root); out_err: if (options) @@ -227,7 +232,7 @@ int mount_autofs_indirect(struct autofs_point *ap, const char *root) } if (map & LKP_NOTSUP) - ap->ghost = 0; + ap->flags &= ~MOUNT_FLAG_GHOST; return 0; } @@ -785,6 +790,8 @@ int handle_packet_missing_indirect(struct autofs_point *ap, autofs_packet_missin pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); + master_mutex_lock(); + debug(ap->logopt, "token %ld, name %s, request pid %u", (unsigned long) pkt->wait_queue_token, pkt->name, pkt->pid); @@ -792,6 +799,7 @@ int handle_packet_missing_indirect(struct autofs_point *ap, autofs_packet_missin if (ap->shutdown || ap->state == ST_SHUTDOWN_FORCE) { ops->send_fail(ap->logopt, ap->ioctlfd, pkt->wait_queue_token, -ENOENT); + master_mutex_unlock(); pthread_setcancelstate(state, NULL); return 0; } @@ -802,6 +810,7 @@ int handle_packet_missing_indirect(struct autofs_point *ap, autofs_packet_missin logerr("malloc: %s", estr); ops->send_fail(ap->logopt, ap->ioctlfd, pkt->wait_queue_token, -ENOMEM); + master_mutex_unlock(); pthread_setcancelstate(state, NULL); return 1; } @@ -833,6 +842,7 @@ int handle_packet_missing_indirect(struct autofs_point *ap, autofs_packet_missin error(ap->logopt, "expire thread create failed"); ops->send_fail(ap->logopt, ap->ioctlfd, pkt->wait_queue_token, -status); + master_mutex_unlock(); mount_mutex_unlock(mt); pending_cond_destroy(mt); pending_mutex_destroy(mt); @@ -841,6 +851,8 @@ int handle_packet_missing_indirect(struct autofs_point *ap, autofs_packet_missin return 1; } + master_mutex_unlock(); + pthread_cleanup_push(free_pending_args, mt); pthread_cleanup_push(pending_mutex_destroy, mt); pthread_cleanup_push(pending_cond_destroy, mt); diff --git a/daemon/lookup.c b/daemon/lookup.c index 797640f..803df4f 100644 --- a/daemon/lookup.c +++ b/daemon/lookup.c @@ -287,7 +287,7 @@ static int do_read_map(struct autofs_point *ap, struct map_source *map, time_t a * We always need to whole map for direct mounts in order to * mount the triggers. */ - if (!ap->ghost && ap->type != LKP_DIRECT) + if (!(ap->flags & MOUNT_FLAG_GHOST) && ap->type != LKP_DIRECT) return NSS_STATUS_SUCCESS; if (!map->stale) @@ -576,7 +576,7 @@ int lookup_ghost(struct autofs_point *ap, const char *root) if (!strcmp(ap->path, "/-")) return LKP_FAIL | LKP_DIRECT; - if (!ap->ghost) + if (!(ap->flags & MOUNT_FLAG_GHOST)) return LKP_INDIRECT; pthread_cleanup_push(master_source_lock_cleanup, entry); @@ -903,7 +903,8 @@ int lookup_nss_mount(struct autofs_point *ap, struct map_source *source, const c map = map->next; } - send_map_update_request(ap); + if (ap->state != ST_INIT) + send_map_update_request(ap); pthread_cleanup_pop(1); if (result == NSS_STATUS_NOTFOUND) @@ -1042,7 +1043,7 @@ int lookup_prune_cache(struct autofs_point *ap, time_t age) status = cache_delete(mc, key); if (status != CHE_FAIL) { if (ap->type == LKP_INDIRECT) { - if (ap->ghost) + if (ap->flags & MOUNT_FLAG_GHOST) rmdir_path(ap, path, ap->dev); } else rmdir_path(ap, path, this->dev); @@ -1131,6 +1132,9 @@ struct mapent *lookup_source_mapent(struct autofs_point *ap, const char *key, un map = map->next; } + if (me && me->mc != mc) + error(LOGOPT_ANY, "mismatching mc in cache", me->key); + return me; } diff --git a/daemon/mount.c b/daemon/mount.c index 494ede1..4031eda 100644 --- a/daemon/mount.c +++ b/daemon/mount.c @@ -35,6 +35,7 @@ int do_mount(struct autofs_point *ap, const char *root, const char *name, int na { struct mount_mod *mod; const char *modstr; + size_t root_len = root ? strlen(root) : 0; char **ngp; int rv; @@ -61,10 +62,14 @@ int do_mount(struct autofs_point *ap, const char *root, const char *name, int na } } - if (ap->type == LKP_DIRECT) + if (*name == '/') debug(ap->logopt, "%s %s type %s options %s using module %s", what, name, fstype, options, modstr); + else if (root_len > 1 && root[root_len - 1] == '/') + debug(ap->logopt, + "%s %s type %s options %s using module %s", + what, root, fstype, options, modstr); else debug(ap->logopt, "%s %s/%s type %s options %s using module %s", diff --git a/daemon/state.c b/daemon/state.c index 3766649..87c16a6 100644 --- a/daemon/state.c +++ b/daemon/state.c @@ -51,8 +51,6 @@ void st_mutex_unlock(void) fatal(status); } -int do_mount_autofs_direct(struct autofs_point *, struct mnt_list *, struct mapent *); - void dump_state_queue(void) { struct list_head *head = &state_queue; diff --git a/include/automount.h b/include/automount.h index c9f6bc1..1ba0d3c 100644 --- a/include/automount.h +++ b/include/automount.h @@ -30,6 +30,7 @@ #include "rpc_subs.h" #include "mounts.h" #include "parse_subs.h" +#include "mounts.h" #include "dev-ioctl-lib.h" #ifdef WITH_DMALLOC @@ -143,7 +144,7 @@ struct mapent_cache { struct mapent { struct mapent *next; struct list_head ino_index; - pthread_mutex_t multi_mutex; + pthread_rwlock_t multi_rwlock; struct list_head multi_list; struct mapent_cache *mc; struct map_source *source; @@ -157,7 +158,7 @@ struct mapent { /* Time of last mount fail */ time_t status; /* For direct mounts per entry context is kept here */ - int dir_created; + int flags; /* File descriptor for ioctls */ int ioctlfd; dev_t dev; @@ -186,7 +187,8 @@ int cache_add_offset(struct mapent_cache *mc, const char *mkey, const char *key, int cache_set_parents(struct mapent *mm); int cache_update(struct mapent_cache *mc, struct map_source *ms, const char *key, const char *mapent, time_t age); int cache_delete(struct mapent_cache *mc, const char *key); -void cache_multi_lock(struct mapent *me); +void cache_multi_readlock(struct mapent *me); +void cache_multi_writelock(struct mapent *me); void cache_multi_unlock(struct mapent *me); int cache_delete_offset_list(struct mapent_cache *mc, const char *key); void cache_release(struct map_source *map); @@ -384,6 +386,18 @@ struct kernel_mod_version { unsigned int minor; }; +/* Enable/disable gohsted directories */ +#define MOUNT_FLAG_GHOST 0x0001 + +/* Directory created for this mount? */ +#define MOUNT_FLAG_DIR_CREATED 0x0002 + +/* Use random policy when selecting a host from which to mount */ +#define MOUNT_FLAG_RANDOM_SELECT 0x0004 + +/* Mount being re-mounted */ +#define MOUNT_FLAG_REMOUNT 0x0008 + struct autofs_point { pthread_t thid; char *path; /* Mount point name */ @@ -397,15 +411,12 @@ struct autofs_point { time_t exp_timeout; /* Timeout for expiring mounts */ time_t exp_runfreq; /* Frequency for polling for timeouts */ time_t negative_timeout; /* timeout in secs for failed mounts */ - unsigned ghost; /* Enable/disable gohsted directories */ - unsigned logopt; /* Per map logging */ + unsigned int flags; /* autofs mount flags */ + unsigned int logopt; /* Per map logging */ pthread_t exp_thread; /* Thread that is expiring */ pthread_t readmap_thread; /* Thread that is reading maps */ enum states state; /* Current state */ int state_pipe[2]; /* State change router pipe */ - unsigned dir_created; /* Directory created for this mount? */ - unsigned random_selection; /* Use random policy when selecting a - * host from which to mount */ struct autofs_point *parent; /* Owner of mounts list for submount */ pthread_mutex_t mounts_mutex; /* Protect mount lists */ struct list_head mounts; /* List of autofs mounts at current level */ @@ -428,6 +439,7 @@ 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, const char *root); +int do_mount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, struct mapent *me); int mount_autofs_direct(struct autofs_point *ap); 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); diff --git a/include/mounts.h b/include/mounts.h index 27e6089..4d932ca 100644 --- a/include/mounts.h +++ b/include/mounts.h @@ -34,9 +34,10 @@ #define MNTS_AUTOFS 0x0004 #define REMOUNT_SUCCESS 0x0000 -#define REMOUNT_OPEN_FAIL 0x0001 -#define REMOUNT_STAT_FAIL 0x0002 -#define REMOUNT_READ_MAP 0x0004 +#define REMOUNT_FAIL 0x0001 +#define REMOUNT_OPEN_FAIL 0x0002 +#define REMOUNT_STAT_FAIL 0x0004 +#define REMOUNT_READ_MAP 0x0008 extern const unsigned int t_indirect; extern const unsigned int t_direct; @@ -92,6 +93,9 @@ int tree_get_mnt_sublist(struct mnt_list *mnts, struct list_head *list, const ch int tree_find_mnt_ents(struct mnt_list *mnts, struct list_head *list, const char *path); 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); +const char *mount_type_str(unsigned int); +void notify_mount_result(struct autofs_point *, const char *, const char *); +int try_remount(struct autofs_point *, struct mapent *, unsigned int); int umount_ent(struct autofs_point *, 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 *); diff --git a/lib/cache.c b/lib/cache.c index d5abab0..ce47e04 100644 --- a/lib/cache.c +++ b/lib/cache.c @@ -111,14 +111,29 @@ void cache_lock_cleanup(void *arg) return; } -void cache_multi_lock(struct mapent *me) +void cache_multi_readlock(struct mapent *me) { int status; if (!me) return; - status = pthread_mutex_lock(&me->multi_mutex); + status = pthread_rwlock_rdlock(&me->multi_rwlock); + if (status) { + logmsg("mapent cache multi mutex lock failed"); + fatal(status); + } + return; +} + +void cache_multi_writelock(struct mapent *me) +{ + int status; + + if (!me) + return; + + status = pthread_rwlock_wrlock(&me->multi_rwlock); if (status) { logmsg("mapent cache multi mutex lock failed"); fatal(status); @@ -133,7 +148,7 @@ void cache_multi_unlock(struct mapent *me) if (!me) return; - status = pthread_mutex_unlock(&me->multi_mutex); + status = pthread_rwlock_unlock(&me->multi_rwlock); if (status) { logmsg("mapent cache multi mutex unlock failed"); fatal(status); @@ -553,8 +568,9 @@ int cache_add(struct mapent_cache *mc, struct map_source *ms, const char *key, c me->ioctlfd = -1; me->dev = (dev_t) -1; me->ino = (ino_t) -1; + me->flags = 0; - status = pthread_mutex_init(&me->multi_mutex, NULL); + status = pthread_rwlock_init(&me->multi_rwlock, NULL); if (status) fatal(status); @@ -762,7 +778,7 @@ int cache_delete(struct mapent_cache *mc, const char *key) goto done; } pred->next = me->next; - status = pthread_mutex_destroy(&me->multi_mutex); + status = pthread_rwlock_destroy(&me->multi_rwlock); if (status) fatal(status); ino_index_lock(mc); @@ -786,7 +802,7 @@ int cache_delete(struct mapent_cache *mc, const char *key) goto done; } mc->hash[hashval] = me->next; - status = pthread_mutex_destroy(&me->multi_mutex); + status = pthread_rwlock_destroy(&me->multi_rwlock); if (status) fatal(status); ino_index_lock(mc); diff --git a/lib/master.c b/lib/master.c index 8bc1248..a243e6a 100644 --- a/lib/master.c +++ b/lib/master.c @@ -99,14 +99,15 @@ int master_add_autofs_point(struct master_mapent *entry, else ap->negative_timeout = global_negative_timeout; ap->exp_runfreq = (timeout + CHECK_RATIO - 1) / CHECK_RATIO; - ap->ghost = ghost; + ap->flags = 0; + if (ghost) + ap->flags = MOUNT_FLAG_GHOST; if (ap->path[1] == '-') ap->type = LKP_DIRECT; else ap->type = LKP_INDIRECT; - ap->dir_created = 0; ap->logopt = logopt; ap->parent = NULL; @@ -479,7 +480,7 @@ void send_map_update_request(struct autofs_point *ap) struct map_source *map; int status, need_update = 0; - if (!ap->ghost) + if (!(ap->flags & MOUNT_FLAG_GHOST)) return; status = pthread_mutex_lock(&instance_mutex); diff --git a/lib/master_parse.y b/lib/master_parse.y index b1186c0..3e598d9 100644 --- a/lib/master_parse.y +++ b/lib/master_parse.y @@ -811,7 +811,7 @@ int master_parse_entry(const char *buffer, unsigned int default_timeout, unsigne ops->timeout(ap->logopt, ap->ioctlfd, &tout); } } - entry->ap->random_selection = random_selection; + entry->ap->flags |= MOUNT_FLAG_RANDOM_SELECT; if (negative_timeout) entry->ap->negative_timeout = negative_timeout; diff --git a/lib/mounts.c b/lib/mounts.c index a04a922..957448c 100644 --- a/lib/mounts.c +++ b/lib/mounts.c @@ -1080,6 +1080,295 @@ free_tsv: return; } +const char *mount_type_str(const unsigned int type) +{ + static const char *str_type[] = { + "indirect", + "direct", + "offset" + }; + unsigned int pos, i; + + for (pos = 0, i = type; pos < type_count; i >>= 1, pos++) + if (i & 0x1) + break; + + return (pos == type_count ? NULL : str_type[pos]); +} + +void notify_mount_result(struct autofs_point *ap, + const char *path, const char *type) +{ + if (ap->exp_timeout) + info(ap->logopt, + "mounted %s on %s with timeout %u, freq %u seconds", + type, path, + (unsigned int) ap->exp_timeout, + (unsigned int) ap->exp_runfreq); + else + info(ap->logopt, + "mounted %s on %s with timeouts disabled", + type, path); + + return; +} + +static int do_remount_direct(struct autofs_point *ap, int fd, const char *path) +{ + struct ioctl_ops *ops = get_ioctl_ops(); + int status = REMOUNT_SUCCESS; + uid_t uid; + gid_t gid; + int ret; + + ops->requestor(ap->logopt, fd, path, &uid, &gid); + if (uid != -1 && gid != -1) + set_tsd_user_vars(ap->logopt, uid, gid); + + ret = lookup_nss_mount(ap, NULL, path, strlen(path)); + if (ret) + info(ap->logopt, "re-connected to %s", path); + else { + status = REMOUNT_FAIL; + info(ap->logopt, "failed to re-connect %s", path); + } + + return status; +} + +static int do_remount_indirect(struct autofs_point *ap, int fd, const char *path) +{ + struct ioctl_ops *ops = get_ioctl_ops(); + int status = REMOUNT_SUCCESS; + struct dirent **de; + char buf[PATH_MAX + 1]; + uid_t uid; + gid_t gid; + unsigned int mounted; + int n, size; + + n = scandir(path, &de, 0, alphasort); + if (n < 0) + return -1; + + size = sizeof(buf); + + while (n--) { + int ret, len; + + if (strcmp(de[n]->d_name, ".") == 0 || + strcmp(de[n]->d_name, "..") == 0) { + free(de[n]); + continue; + } + + ret = cat_path(buf, size, path, de[n]->d_name); + if (!ret) { + do { + free(de[n]); + } while (n--); + free(de); + return -1; + } + + ops->ismountpoint(ap->logopt, -1, buf, &mounted); + if (!mounted) { + struct dirent **de2; + int i, j; + + i = j = scandir(buf, &de2, 0, alphasort); + while (i--) + free(de2[i]); + free(de2); + if (j <= 2) { + free(de[n]); + continue; + } + } + + ops->requestor(ap->logopt, fd, buf, &uid, &gid); + if (uid != -1 && gid != -1) + set_tsd_user_vars(ap->logopt, uid, gid); + + len = strlen(de[n]->d_name); + + ret = lookup_nss_mount(ap, NULL, de[n]->d_name, len); + if (ret) + info(ap->logopt, "re-connected to %s", buf); + else { + status = REMOUNT_FAIL; + info(ap->logopt, "failed to re-connect %s", buf); + } + free(de[n]); + } + free(de); + + return status; +} + +static int remount_active_mount(struct autofs_point *ap, + struct mapent_cache *mc, + const char *path, dev_t devid, + const unsigned int type, + int *ioctlfd) +{ + struct ioctl_ops *ops = get_ioctl_ops(); + time_t timeout = ap->exp_timeout; + const char *str_type = mount_type_str(type); + char buf[MAX_ERR_BUF]; + unsigned int mounted; + struct stat st; + int fd; + + *ioctlfd = -1; + + /* Open failed, no mount present */ + ops->open(ap->logopt, &fd, devid, path); + if (fd == -1) + return REMOUNT_OPEN_FAIL; + + /* Re-reading the map, set timeout and return */ + if (ap->state == ST_READMAP) { + ops->timeout(ap->logopt, fd, &timeout); + ops->close(ap->logopt, fd); + return REMOUNT_READ_MAP; + } + + debug(ap->logopt, "trying to re-connect to mount %s", path); + + /* Mounted so set pipefd and timeout etc. */ + if (ops->catatonic(ap->logopt, fd) == -1) { + char *estr = strerror_r(errno, buf, MAX_ERR_BUF); + error(ap->logopt, "set catatonic failed: %s", estr); + debug(ap->logopt, "couldn't re-connect to mount %s", path); + ops->close(ap->logopt, fd); + return REMOUNT_OPEN_FAIL; + } + if (ops->setpipefd(ap->logopt, fd, ap->kpipefd) == -1) { + char *estr = strerror_r(errno, buf, MAX_ERR_BUF); + error(ap->logopt, "set pipefd failed: %s", estr); + debug(ap->logopt, "couldn't re-connect to mount %s", path); + ops->close(ap->logopt, fd); + return REMOUNT_OPEN_FAIL; + } + ops->timeout(ap->logopt, fd, &timeout); + if (fstat(fd, &st) == -1) { + error(ap->logopt, + "failed to stat %s mount %s", str_type, path); + debug(ap->logopt, "couldn't re-connect to mount %s", path); + ops->close(ap->logopt, fd); + return REMOUNT_STAT_FAIL; + } + if (mc) + cache_set_ino_index(mc, path, st.st_dev, st.st_ino); + else + ap->dev = st.st_dev; + notify_mount_result(ap, path, str_type); + + *ioctlfd = fd; + + /* Any mounts on or below? */ + if (ops->ismountpoint(ap->logopt, fd, path, &mounted) == -1) { + char *estr = strerror_r(errno, buf, MAX_ERR_BUF); + error(ap->logopt, "ismountpoint %s failed: %s", path, estr); + debug(ap->logopt, "couldn't re-connect to mount %s", path); + ops->close(ap->logopt, fd); + return REMOUNT_FAIL; + } + if (!mounted) { + /* + * If we're an indirect mount we pass back the fd. + * But if were a direct or offset mount with no active + * mount we don't retain an open file descriptor. + */ + if (type != t_indirect) { + ops->close(ap->logopt, fd); + *ioctlfd = -1; + } + } else { + int ret; + /* + * What can I do if we can't remount the existing + * mount(s) (possibly a partial failure), everything + * following will be broken? + */ + if (type == t_indirect) + ret = do_remount_indirect(ap, fd, path); + else + ret = do_remount_direct(ap, fd, path); + } + + debug(ap->logopt, "re-connected to mount %s", path); + + return REMOUNT_SUCCESS; +} + +int try_remount(struct autofs_point *ap, struct mapent *me, unsigned int type) +{ + struct ioctl_ops *ops = get_ioctl_ops(); + struct mapent_cache *mc; + const char *path; + int ret, fd; + dev_t devid; + + if (type == t_indirect) { + mc = NULL; + path = ap->path; + } else { + mc = me->mc; + path = me->key; + } + + ret = ops->mount_device(ap->logopt, path, type, &devid); + if (ret == -1 || ret == 0) + return -1; + + ret = remount_active_mount(ap, mc, path, devid, type, &fd); + + /* + * The directory must exist since we found a device + * number for the mount but we can't know if we created + * it or not. However, if we're mounted on an autofs fs + * then we need to cleanup the path anyway. + */ + if (type == t_indirect) { + ap->flags &= ~MOUNT_FLAG_DIR_CREATED; + if (ret == DEV_IOCTL_IS_AUTOFS) + ap->flags |= MOUNT_FLAG_DIR_CREATED; + } else { + me->flags &= ~MOUNT_FLAG_DIR_CREATED; + if (ret == DEV_IOCTL_IS_AUTOFS) + me->flags |= MOUNT_FLAG_DIR_CREATED; + } + + /* + * Either we opened the mount or we're re-reading the map. + * If we opened the mount and ioctlfd is not -1 we have + * a descriptor for the indirect mount so we need to + * record that in the mount point struct. Otherwise we're + * re-reading the map. + */ + if (ret == REMOUNT_SUCCESS || ret == REMOUNT_READ_MAP) { + if (fd != -1) { + if (type == t_indirect) + ap->ioctlfd = fd; + else + me->ioctlfd = fd; + return 1; + } + + /* Indirect mount requires a valid fd */ + if (type != t_indirect) + return 1; + } + + /* + * Since we got the device number above a mount exists so + * any other failure warrants a failure return here. + */ + return 0; +} + int umount_ent(struct autofs_point *ap, const char *path) { int rv; diff --git a/modules/lookup_hosts.c b/modules/lookup_hosts.c index 1ef420e..bf24d7f 100644 --- a/modules/lookup_hosts.c +++ b/modules/lookup_hosts.c @@ -147,7 +147,7 @@ int lookup_mount(struct autofs_point *ap, const char *name, int name_len, void * * We haven't read the list of hosts into the * cache so go straight to the lookup. */ - if (!ap->ghost) { + if (!(ap->flags & MOUNT_FLAG_GHOST)) { /* * If name contains a '/' we're searching for an * offset that doesn't exist in the export list diff --git a/modules/mount_afs.c b/modules/mount_afs.c index 3b4261a..96a1367 100644 --- a/modules/mount_afs.c +++ b/modules/mount_afs.c @@ -35,6 +35,9 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, int size_t r_len = strlen(root); size_t d_len = r_len + name_len + 2; + if (ap->flags & MOUNT_FLAG_REMOUNT) + return 0; + if (d_len > PATH_MAX) return 1; diff --git a/modules/mount_autofs.c b/modules/mount_autofs.c index 8a7dd78..eb63d8e 100644 --- a/modules/mount_autofs.c +++ b/modules/mount_autofs.c @@ -50,7 +50,7 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, pthread_t thid; char *realpath, *mountpoint; const char **argv; - int argc, status, ghost = ap->ghost; + int argc, status, ghost = ap->flags & MOUNT_FLAG_GHOST; time_t timeout = ap->exp_timeout; unsigned logopt = ap->logopt; char *type, *format, *tmp, *tmp2; @@ -76,8 +76,13 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, } else if (*name == '/') { realpath = alloca(name_len + 1); mountpoint = alloca(len + 1); - strcpy(mountpoint, root); - strcpy(realpath, name); + if (ap->flags & MOUNT_FLAG_REMOUNT) { + strcpy(mountpoint, name); + strcpy(realpath, name); + } else { + strcpy(mountpoint, root); + strcpy(realpath, name); + } } else { realpath = alloca(len + name_len + 2); mountpoint = alloca(len + name_len + 2); diff --git a/modules/mount_bind.c b/modules/mount_bind.c index 396a3ca..022d183 100644 --- a/modules/mount_bind.c +++ b/modules/mount_bind.c @@ -76,6 +76,9 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, int int err; int i, len; + if (ap->flags & MOUNT_FLAG_REMOUNT) + return 0; + /* Root offset of multi-mount */ len = strlen(root); if (root[len - 1] == '/') { @@ -127,7 +130,8 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, int if (ap->type != LKP_INDIRECT) return 1; - if ((!ap->ghost && name_len) && !existed) + if (!existed && + (!(ap->flags & MOUNT_FLAG_GHOST) && name_len)) rmdir_path(ap, fullpath, ap->dev); return err; @@ -170,7 +174,7 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, int MODPREFIX "failed to create local mount %s -> %s", fullpath, what); - if (ap->ghost && !status) + if (ap->flags & MOUNT_FLAG_GHOST && !status) mkdir_path(fullpath, 0555); else { if (ap->type == LKP_INDIRECT) diff --git a/modules/mount_changer.c b/modules/mount_changer.c index e838fcf..43b8355 100644 --- a/modules/mount_changer.c +++ b/modules/mount_changer.c @@ -51,6 +51,9 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, int int err; int len, status, existed = 1; + if (ap->flags & MOUNT_FLAG_REMOUNT) + return 0; + fstype = "iso9660"; /* Root offset of multi-mount */ @@ -122,7 +125,7 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, int if (ap->type != LKP_INDIRECT) return 1; - if ((!ap->ghost && name_len) || !existed) + if ((!(ap->flags & MOUNT_FLAG_GHOST) && name_len) || !existed) rmdir_path(ap, fullpath, ap->dev); return 1; diff --git a/modules/mount_ext2.c b/modules/mount_ext2.c index 7589991..4c5b271 100644 --- a/modules/mount_ext2.c +++ b/modules/mount_ext2.c @@ -45,6 +45,9 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, int const char *fsck_prog; int len, status, existed = 1; + if (ap->flags & MOUNT_FLAG_REMOUNT) + return 0; + /* Root offset of multi-mount */ len = strlen(root); if (root[len - 1] == '/') { @@ -132,7 +135,7 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, int if (ap->type != LKP_INDIRECT) return 1; - if ((!ap->ghost && name_len) || !existed) + if ((!(ap->flags & MOUNT_FLAG_GHOST) && name_len) || !existed) rmdir_path(ap, fullpath, ap->dev); return 1; diff --git a/modules/mount_generic.c b/modules/mount_generic.c index 4b1213e..f094d07 100644 --- a/modules/mount_generic.c +++ b/modules/mount_generic.c @@ -44,6 +44,9 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, int int err; int len, status, existed = 1; + if (ap->flags & MOUNT_FLAG_REMOUNT) + return 0; + /* Root offset of multi-mount */ len = strlen(root); if (root[len - 1] == '/') { @@ -92,7 +95,7 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, int if (ap->type != LKP_INDIRECT) return 1; - if ((!ap->ghost && name_len) || !existed) + if ((!(ap->flags & MOUNT_FLAG_GHOST) && name_len) || !existed) rmdir_path(ap, fullpath, ap->dev); return 1; diff --git a/modules/mount_nfs.c b/modules/mount_nfs.c index 0b253d8..c747078 100644 --- a/modules/mount_nfs.c +++ b/modules/mount_nfs.c @@ -64,10 +64,14 @@ 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; + unsigned int random_selection = ap->flags & MOUNT_FLAG_RANDOM_SELECT; int len, status, err, existed = 1; int nosymlink = 0; int ro = 0; /* Set if mount bind should be read-only */ + if (ap->flags & MOUNT_FLAG_REMOUNT) + return 0; + debug(ap->logopt, MODPREFIX "root=%s name=%s what=%s, fstype=%s, options=%s", root, name, what, fstype, options); @@ -136,7 +140,7 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, int info(ap->logopt, MODPREFIX "no hosts available"); return 1; } - prune_host_list(ap->logopt, &hosts, vers, nfsoptions, ap->random_selection); + prune_host_list(ap->logopt, &hosts, vers, nfsoptions, random_selection); if (!hosts) { info(ap->logopt, MODPREFIX "no hosts available"); @@ -260,7 +264,7 @@ forced_fail: if (ap->type != LKP_INDIRECT) return 1; - if ((!ap->ghost && name_len) || !existed) + if ((!(ap->flags & MOUNT_FLAG_GHOST) && name_len) || !existed) rmdir_path(ap, fullpath, ap->dev); return 1; diff --git a/modules/parse_sun.c b/modules/parse_sun.c index 329b255..2c4f8b2 100644 --- a/modules/parse_sun.c +++ b/modules/parse_sun.c @@ -1120,9 +1120,9 @@ static int mount_subtree(struct autofs_point *ap, struct mapent *me, 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; + char *mnt_tmp_root, *mm_root, *mm_base, *mm_key; + const char *mnt_root, *target; + unsigned int mm_root_len, mnt_root_len; int start, ret = 0, rv; unsigned int move; @@ -1155,11 +1155,19 @@ static int mount_subtree(struct autofs_point *ap, struct mapent *me, strcat(mm_root, "/"); strcat(mm_root, mm_key); } + mm_root_len = strlen(mm_root); - mm_tmp_root = mkdtemp(t_dir); - if (!mm_tmp_root) - return 1; - mm_tmp_root_len = strlen(mm_tmp_root); + mnt_tmp_root = NULL; + if (ap->flags & MOUNT_FLAG_REMOUNT) { + mnt_root = mm_root; + mnt_root_len = mm_root_len; + } else { + mnt_root = mkdtemp(t_dir); + if (!mnt_root) + return 1; + mnt_root_len = strlen(mnt_root); + mnt_tmp_root = (char *) mnt_root; + } if (me == me->multi) { /* name = NULL */ @@ -1181,20 +1189,20 @@ static int mount_subtree(struct autofs_point *ap, struct mapent *me, warn(ap->logopt, MODPREFIX "failed to parse root offset"); cache_delete_offset_list(me->mc, name); - rmdir(mm_tmp_root); - return 1; + goto error_out; } ro_len = strlen(ro_loc); - move = MOUNT_MOVE_OTHER; - if (check_fstype_autofs_option(myoptions)) - move = MOUNT_MOVE_AUTOFS; + if (!(ap->flags & MOUNT_FLAG_REMOUNT)) { + 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'; + tmp = alloca(mnt_root_len + 1); + strcpy(tmp, mnt_root); + tmp[mnt_root_len] = '/'; + tmp[mnt_root_len + 1] = '\0'; root = tmp; rv = sun_mount(ap, root, name, namelen, ro_loc, ro_len, myoptions, ctxt); @@ -1204,14 +1212,13 @@ static int mount_subtree(struct autofs_point *ap, struct mapent *me, } if (ro && rv == 0) { - ret = mount_multi_triggers(ap, me, mm_tmp_root, start, mm_base); + ret = mount_multi_triggers(ap, me, mnt_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; + cleanup_multi_triggers(ap, me, mnt_root, start, mm_base); + cleanup_multi_root(ap, mnt_root, mm_root, move); + goto error_out; } } else if (rv <= 0) { move = MOUNT_MOVE_NONE; @@ -1219,34 +1226,34 @@ static int mount_subtree(struct autofs_point *ap, struct mapent *me, 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; + cleanup_multi_triggers(ap, me, mm_root, start, mm_base); + goto error_out; } } } else { int loclen = strlen(loc); int namelen = strlen(name); - move = MOUNT_MOVE_OTHER; - if (check_fstype_autofs_option(options)) - move = MOUNT_MOVE_AUTOFS; + if (!(ap->flags & MOUNT_FLAG_REMOUNT)) { + 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); + rv = sun_mount(ap, mnt_root, name, namelen, loc, loclen, options, ctxt); if (rv == 0) { - ret = mount_multi_triggers(ap, me->multi, mm_tmp_root, start, mm_base); + ret = mount_multi_triggers(ap, me->multi, mnt_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; + cleanup_multi_triggers(ap, me, mnt_root, start, mm_base); + cleanup_multi_root(ap, mnt_root, mm_root, move); + goto error_out; } } else if (rv < 0) { char *mm_root_base = alloca(strlen(mm_root) + strlen(mm_base) + 1); @@ -1260,21 +1267,20 @@ static int mount_subtree(struct autofs_point *ap, struct mapent *me, 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; + cleanup_multi_triggers(ap, me, mm_root, start, mm_base); + goto error_out; } } } - 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; + if (!move_mount(ap, mnt_root, target, move)) { + cleanup_multi_triggers(ap, me, mnt_root, start, mm_base); + cleanup_multi_root(ap, mnt_root, mm_root, move); + goto error_out; } - rmdir(mm_tmp_root); + if (mnt_tmp_root) + rmdir(mnt_tmp_root); /* Mount for base of tree failed */ if (rv > 0) @@ -1289,6 +1295,12 @@ done: rv = 0; return rv; + +error_out: + if (mnt_tmp_root) + rmdir(mnt_tmp_root); + + return 1; } /* @@ -1484,7 +1496,7 @@ int parse_mount(struct autofs_point *ap, const char *name, * us to fail on the check for duplicate offsets in * we don't know when submounts go away. */ - cache_multi_lock(me); + cache_multi_writelock(me); cache_delete_offset_list(mc, name); cache_multi_unlock(me); } @@ -1507,7 +1519,7 @@ int parse_mount(struct autofs_point *ap, const char *name, } pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state); - cache_multi_lock(me); + cache_multi_writelock(me); /* It's a multi-mount; deal with it */ do { char *path, *myoptions, *loc; @@ -1702,7 +1714,7 @@ mount_it: options, loclen, loc); cache_readlock(mc); - cache_multi_lock(me); + cache_multi_writelock(me); rv = mount_subtree(ap, me, name, loc, options, ctxt);