diff --git a/CHANGELOG b/CHANGELOG index f68e16e..b0a255a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,6 +13,7 @@ - cthon more parser corrections and attempt to fix multi-mounts with various combinations of submounts (still broken). - cthon fix expire of various forms of nested mounts. +- cthon fix some shutdown races. 13/7/2006 autofs-5.0.1 rc1 -------------------------- diff --git a/Makefile.rules b/Makefile.rules index 7796269..7a19d5f 100644 --- a/Makefile.rules +++ b/Makefile.rules @@ -27,7 +27,7 @@ LDFLAGS = -g STRIP = : else ifdef DONTSTRIP -CFLAGS = -O2 -g +CFLAGS ?= -O2 -g LDFLAGS = -g STRIP = : else diff --git a/daemon/automount.c b/daemon/automount.c index 91f4d15..35d443c 100644 --- a/daemon/automount.c +++ b/daemon/automount.c @@ -36,6 +36,7 @@ #include #include #include #include +#include #include "automount.h" @@ -175,177 +176,6 @@ int rmdir_path(struct autofs_point *ap, return 0; } -static int umount_offsets(struct autofs_point *ap, struct mnt_list *mnts, const char *base) -{ - char path[PATH_MAX + 1]; - char *offset = path; - struct list_head list, head, *pos, *p; - char key[PATH_MAX + 1]; - struct map_source *map; - struct mapent_cache *mc = NULL; - struct mapent *me = NULL; - char *ind_key; - int ret = 0, status; - - INIT_LIST_HEAD(&list); - INIT_LIST_HEAD(&head); - - if (!tree_get_mnt_list(mnts, &list, base, 0)) - return 0; - - list_for_each(p, &list) { - struct mnt_list *this; - - this = list_entry(p, struct mnt_list, list); - - if (strcmp(this->fs_type, "autofs")) - continue; - - INIT_LIST_HEAD(&this->ordered); - add_ordered_list(this, &head); - } - - /* - * If it's a direct mount it's base is the key otherwise - * the last path component is the indirect entry key. - */ - ind_key = strrchr(base, '/'); - if (ind_key) - ind_key++; - - master_source_readlock(ap->entry); - map = ap->entry->first; - while (map) { - mc = map->mc; - cache_readlock(mc); - me = cache_lookup_distinct(mc, base); - if (!me) - me = cache_lookup_distinct(mc, ind_key); - if (me) - break; - cache_unlock(mc); - map = map->next; - } - master_source_unlock(ap->entry); - - if (!me) - return 0; - - pos = NULL; - while ((offset = get_offset(base, offset, &head, &pos))) { - struct mapent *oe; - - if (strlen(base) + strlen(offset) >= PATH_MAX) { - warn(ap->logopt, "can't umount - mount path too long"); - ret++; - continue; - } - - sched_yield(); - - strcpy(key, base); - strcat(key, offset); - oe = cache_lookup_distinct(mc, key); - - if (!oe) { - debug(ap->logopt, "offset key %s not found", key); - continue; - } - - warn(ap->logopt, "umount offset %s", key); - - /* - * We're in trouble if umounting the triggers fails. - * It should always succeed due to the expire design. - */ - pthread_cleanup_push(cache_lock_cleanup, mc); - if (umount_autofs_offset(ap, oe)) { - crit(ap->logopt, "failed to umount offset %s", key); - ret++; - } - pthread_cleanup_pop(0); - } - - if (!ret && me->multi == me) { - cache_multi_lock(mc); - status = cache_delete_offset_list(mc, me->key); - cache_multi_unlock(mc); - if (status != CHE_OK) - warn(ap->logopt, "couldn't delete offset list"); - } - cache_unlock(mc); - - return ret; -} - -static int umount_ent(struct autofs_point *ap, const char *path, const char *type) -{ - struct stat st; - int sav_errno; - int is_smbfs = (strcmp(type, "smbfs") == 0); - int status; - int ret, rv = 1; - - status = lstat(path, &st); - sav_errno = errno; - - if (status < 0) - warn(ap->logopt, "lstat of %s failed with %d", path, status); - - /* - * lstat failed and we're an smbfs fs returning an error that is not - * EIO or EBADSLT or the lstat failed so it's a bad path. Return - * a fail. - * - * EIO appears to correspond to an smb mount that has gone away - * and EBADSLT relates to CD changer not responding. - */ - if (!status && (S_ISDIR(st.st_mode) && st.st_dev != ap->dev)) { - rv = spawnll(log_debug, PATH_UMOUNT, PATH_UMOUNT, path, NULL); - } else if (is_smbfs && (sav_errno == EIO || sav_errno == EBADSLT)) { - rv = spawnll(log_debug, PATH_UMOUNT, PATH_UMOUNT, path, NULL); - } - - /* We are doing a forced shutcwdown down so unlink busy mounts */ - if (rv && (ap->state == ST_SHUTDOWN_FORCE || ap->state == ST_SHUTDOWN)) { - ret = stat(path, &st); - if (ret == -1 && errno == ENOENT) { - warn(ap->logopt, "mount point does not exist"); - return 0; - } - - if (ret == 0 && !S_ISDIR(st.st_mode)) { - warn(ap->logopt, "mount point is not a directory"); - return 0; - } - - if (ap->state == ST_SHUTDOWN_FORCE) { - msg("forcing umount of %s", path); - rv = spawnll(log_debug, PATH_UMOUNT, PATH_UMOUNT, "-l", path, NULL); - } - - /* - * Verify that we actually unmounted the thing. This is a - * belt and suspenders approach to not eating user data. - * We have seen cases where umount succeeds, but there is - * still a file system mounted on the mount point. How - * this happens has not yet been determined, but we want to - * make sure to return failure here, if that is the case, - * so that we do not try to call rmdir_path on the - * directory. - */ - if (!rv && is_mounted(_PATH_MOUNTED, path, MNTS_REAL)) { - crit(ap->logopt, - "the umount binary reported that %s was " - "unmounted, but there is still something " - "mounted on this path.", path); - rv = -1; - } - } - - return rv; -} - /* Like ftw, except fn gets called twice: before a directory is entered, and after. If the before call returns 0, the directory isn't entered. */ @@ -517,7 +347,7 @@ static void update_map_cache(struct auto mc = map->mc; cache_writelock(mc); me = cache_lookup_distinct(mc, key); - if (me) + if (me && me->ioctlfd == -1) cache_delete(mc, key); cache_unlock(mc); @@ -530,73 +360,79 @@ static void update_map_cache(struct auto /* umount all filesystems mounted under path. If incl is true, then it also tries to umount path itself */ -int umount_multi(struct autofs_point *ap, struct mnt_list *mnts, const char *path, int incl) +int umount_multi(struct autofs_point *ap, const char *path, int incl) { - int left, n; - struct dirent **de; - struct mnt_list *mptr; - struct list_head *p; - struct list_head list; - - INIT_LIST_HEAD(&list); + struct mapent *me; + struct statfs fs; + int is_autofs_fs; + int ret, left; debug(ap->logopt, "path %s incl %d", path, incl); - if (!tree_get_mnt_list(mnts, &list, path, incl)) { - debug(ap->logopt, "no mounts found under %s", path); - check_rm_dirs(ap, path, incl); - return 0; + ret = statfs(path, &fs); + if (ret == -1) { + error(ap->logopt, "could not stat fs of %s", path); + return 1; } - left = 0; - list_for_each(p, &list) { - struct autofs_point *oap = ap; - char *has_opt_direct; - int is_autofs; + is_autofs_fs = fs.f_type == AUTOFS_SUPER_MAGIC ? 1 : 0; - mptr = list_entry(p, struct mnt_list, list); + me = lookup_source_mapent(ap, path); + if (!me) { + char *ind_key; - is_autofs = !strcmp(mptr->fs_type, "autofs"); - has_opt_direct = strstr(mptr->opts, "direct"); + ind_key = strrchr(path, '/'); + if (ind_key) + ind_key++; - /* We only want real mounts and top level direct mounts */ - if (is_autofs && !has_opt_direct) - continue; + me = lookup_source_mapent(ap, ind_key); + if (!me) { + warn(ap->logopt, "no mounts found under %s", path); + return 0; + } + } + + left = 0; + + if (me && me->multi) { + struct autofs_point *oap = ap; + char *root, *base; if (ap->submount) oap = ap->parent; - sched_yield(); + if (me == me->multi && !strchr(me->key, '/')) { + /* Indirect multi-mount root */ + root = alloca(strlen(ap->path) + strlen(me->key) + 2); + strcpy(root, ap->path); + strcat(root, "/"); + strcat(root, me->key); + base = NULL; + } else { + root = me->multi->key; + base = me->key + strlen(root); + } - if (umount_offsets(oap, mnts, mptr->path)) + if (umount_multi_triggers(oap, root, me, base)) { warn(ap->logopt, - "could not umount some offsets under %s", - mptr->path); - - if (is_autofs) - continue; - - sched_yield(); - - warn(ap->logopt, "unmounting dir = %s", mptr->path); - - if (umount_ent(ap, mptr->path, mptr->fs_type)) { - warn(ap->logopt, "could not umount dir %s", - mptr->path); + "could not umount some offsets under %s", path); left++; } } + cache_unlock(me->source->mc); - /* Catch any offsets of indirect mounts with no root mount */ - if (!left && !tree_is_mounted(mnts, path, MNTS_ALL)) { - struct autofs_point *rap = ap; + if (left || is_autofs_fs || ap->submount) + return left; - if (ap->submount) - rap = ap->parent; + /* + * If this is the root of a multi-mount we've had to umount + * it already to ensure it's ok to remove any offset triggers + */ + if (me != me->multi) { + warn(ap->logopt, "unmounting dir = %s", path); - if (umount_offsets(rap, mnts, path)) { - warn(ap->logopt, - "could not umount some offsets under %s", path); + if (umount_ent(ap, path)) { + warn(ap->logopt, "could not umount dir %s", path); left++; } } @@ -612,18 +448,13 @@ int umount_multi(struct autofs_point *ap static int umount_all(struct autofs_point *ap, int force) { - struct mnt_list *mnts; int left; - mnts = tree_make_mnt_tree(_PROC_MOUNTS, ap->path); - - left = umount_multi(ap, mnts, ap->path, 0); + left = umount_multi(ap, ap->path, 0); if (force && left) warn(ap->logopt, "could not unmount %d dirs under %s", left, ap->path); - tree_free_mnt_tree(mnts); - return left; } @@ -729,7 +560,6 @@ static int get_pkt(struct autofs_point * enum states next_state, post_state; size_t read_size = sizeof(next_state); int state_pipe; - int status; next_state = post_state = ST_INVAL; @@ -767,7 +597,6 @@ static int get_pkt(struct autofs_point * int do_expire(struct autofs_point *ap, const char *name, int namelen) { - struct mnt_list *mnts; char buf[PATH_MAX + 1]; int len, ret; @@ -786,15 +615,11 @@ int do_expire(struct autofs_point *ap, c msg("expiring path %s", buf); - mnts = tree_make_mnt_tree(_PROC_MOUNTS, buf); - - ret = umount_multi(ap, mnts, buf, 1); + ret = umount_multi(ap, buf, 1); if (ret == 0) msg("expired %s", buf); else - warn(ap->logopt, "couldn't complet expire of %s", buf); - - tree_free_mnt_tree(mnts); + warn(ap->logopt, "couldn't complete expire of %s", buf); return ret; } diff --git a/daemon/direct.c b/daemon/direct.c index f4f60f3..a2aea9c 100644 --- a/daemon/direct.c +++ b/daemon/direct.c @@ -112,40 +112,50 @@ static int autofs_init_direct(struct aut int do_umount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, struct mapent *me) { char buf[MAX_ERR_BUF]; - int rv, left; + int ioctlfd, rv, left; - left = umount_multi(ap, mnts, me->key, 1); + left = umount_multi(ap, me->key, 1); if (left) { warn(ap->logopt, "could not unmount %d dirs under %s", left, me->key); return -1; } - if (me->ioctlfd < 0) - me->ioctlfd = open(me->key, O_RDONLY); + if (me->ioctlfd != -1) { + if (tree_is_mounted(mnts, me->key, MNTS_REAL)) { + error(ap->logopt, + "attempt to umount busy direct mount %s", + me->key); + return 1; + } + ioctlfd = me->ioctlfd; + } else + ioctlfd = open(me->key, O_RDONLY); - if (me->ioctlfd >= 0) { + if (ioctlfd >= 0) { int status = 1; - rv = ioctl(me->ioctlfd, AUTOFS_IOC_ASKUMOUNT, &status); + rv = ioctl(ioctlfd, AUTOFS_IOC_ASKUMOUNT, &status); if (rv) { char *estr = strerror_r(errno, buf, MAX_ERR_BUF); error(ap->logopt, "ioctl failed: %s", estr); return 1; } else if (!status) { if (ap->state != ST_SHUTDOWN_FORCE) { - debug(ap->logopt, "ask umount returned busy"); + error(ap->logopt, + "ask umount returned busy for %s", + me->key); return 1; } else { - ioctl(me->ioctlfd, AUTOFS_IOC_CATATONIC, 0); - close(me->ioctlfd); me->ioctlfd = -1; + ioctl(ioctlfd, AUTOFS_IOC_CATATONIC, 0); + close(ioctlfd); goto force_umount; } } - ioctl(me->ioctlfd, AUTOFS_IOC_CATATONIC, 0); - close(me->ioctlfd); me->ioctlfd = -1; + ioctl(ioctlfd, AUTOFS_IOC_CATATONIC, 0); + close(ioctlfd); } else { char *estr = strerror_r(errno, buf, MAX_ERR_BUF); error(ap->logopt, @@ -283,7 +293,7 @@ int do_mount_autofs_direct(struct autofs struct mnt_params *mp; time_t timeout = ap->exp_timeout; struct stat st; - int status, ret; + int status, ret, ioctlfd; struct list_head list; INIT_LIST_HEAD(&list); @@ -299,6 +309,11 @@ int do_mount_autofs_direct(struct autofs } } + 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); if (status) fatal(status); @@ -349,8 +364,8 @@ int do_mount_autofs_direct(struct autofs } /* Root directory for ioctl()'s */ - me->ioctlfd = open(me->key, O_RDONLY); - if (me->ioctlfd < 0) { + ioctlfd = open(me->key, O_RDONLY); + if (ioctlfd < 0) { crit(ap->logopt, "failed to create ioctl fd for %s", me->key); goto out_umount; } @@ -363,7 +378,7 @@ int do_mount_autofs_direct(struct autofs ap->kver.minor = 0; /* If this ioctl() doesn't work, it is kernel version 2 */ - if (!ioctl(me->ioctlfd, AUTOFS_IOC_PROTOVER, &ap->kver.major)) { + if (!ioctl(ioctlfd, AUTOFS_IOC_PROTOVER, &ap->kver.major)) { /* If this ioctl() fails the kernel doesn't support direct mounts */ if (ioctl(me->ioctlfd, AUTOFS_IOC_PROTOSUBVER, &ap->kver.minor)) { ap->kver.minor = 0; @@ -389,9 +404,9 @@ int do_mount_autofs_direct(struct autofs } got_version: - ioctl(me->ioctlfd, AUTOFS_IOC_SETTIMEOUT, &timeout); + ioctl(ioctlfd, AUTOFS_IOC_SETTIMEOUT, &timeout); - ret = fstat(me->ioctlfd, &st); + ret = fstat(ioctlfd, &st); if (ret == -1) { error(ap->logopt, "failed to stat direct mount trigger %s", me->key); @@ -399,16 +414,14 @@ got_version: } cache_set_ino_index(me->source->mc, me->key, st.st_dev, st.st_ino); - close(me->ioctlfd); - me->ioctlfd = -1; + close(ioctlfd); debug(ap->logopt, "mounted trigger %s", me->key); return 0; out_close: - close(me->ioctlfd); - me->ioctlfd = -1; + close(ioctlfd); out_umount: /* TODO: maybe force umount (-l) */ umount(me->key); @@ -478,38 +491,55 @@ int mount_autofs_direct(struct autofs_po int umount_autofs_offset(struct autofs_point *ap, struct mapent *me) { char buf[MAX_ERR_BUF]; - int rv = 1; + int ioctlfd, rv = 1; - if (me->ioctlfd < 0) - me->ioctlfd = open(me->key, O_RDONLY); + if (me->ioctlfd != -1) { + if (is_mounted(_PATH_MOUNTED, me->key, MNTS_REAL)) { + error(ap->logopt, + "attempt to umount busy offset %s", me->key); + return 1; + } + ioctlfd = me->ioctlfd; + } else + ioctlfd = open(me->key, O_RDONLY); - if (me->ioctlfd >= 0) { + if (ioctlfd >= 0) { int status = 1; - rv = ioctl(me->ioctlfd, AUTOFS_IOC_ASKUMOUNT, &status); + rv = ioctl(ioctlfd, AUTOFS_IOC_ASKUMOUNT, &status); if (rv) { char *estr = strerror_r(errno, buf, MAX_ERR_BUF); error(ap->logopt, "ioctl failed: %s", estr); return 1; } else if (!status) { if (ap->state != ST_SHUTDOWN_FORCE) { - debug(ap->logopt, "ask umount returned busy"); + error(ap->logopt, + "ask umount returned busy for %s", + me->key); return 1; } else { - ioctl(me->ioctlfd, AUTOFS_IOC_CATATONIC, 0); - close(me->ioctlfd); me->ioctlfd = -1; + ioctl(ioctlfd, AUTOFS_IOC_CATATONIC, 0); + close(ioctlfd); goto force_umount; } } - ioctl(me->ioctlfd, AUTOFS_IOC_CATATONIC, 0); - close(me->ioctlfd); me->ioctlfd = -1; + ioctl(ioctlfd, AUTOFS_IOC_CATATONIC, 0); + close(ioctlfd); } else { - char *estr = strerror_r(errno, buf, MAX_ERR_BUF); + struct stat st; + char *estr; + int save_errno = errno; + + /* Non existent directory on remote fs - no mount */ + if (stat(me->key, &st) == -1 && errno == ENOENT) + return 0; + + estr = strerror_r(save_errno, buf, MAX_ERR_BUF); error(ap->logopt, - "couldn't get ioctl fd for offset %s", me->key); - debug(ap->logopt, "open: %s", estr); + "couldn't get ioctl fd for offset %s: %s", + me->key, estr); goto force_umount; } @@ -521,7 +551,7 @@ int umount_autofs_offset(struct autofs_p return 0; break; case EBUSY: - warn(ap->logopt, "mount point %s is in use", me->key); + error(ap->logopt, "mount point %s is in use", me->key); if (ap->state != ST_SHUTDOWN_FORCE) return 1; break; @@ -548,7 +578,7 @@ int mount_autofs_offset(struct autofs_po struct mnt_params *mp; time_t timeout = ap->exp_timeout; struct stat st; - int status, ret; + int ioctlfd, status, ret; if (is_mounted(_PROC_MOUNTS, me->key, MNTS_AUTOFS)) { if (ap->state != ST_READMAP) @@ -557,6 +587,11 @@ int mount_autofs_offset(struct autofs_po return 0; } + if (me->ioctlfd != -1) { + error(ap->logopt, "active offset mount %s", me->key); + return -1; + } + status = pthread_once(&key_mnt_params_once, key_mnt_params_init); if (status) fatal(status); @@ -634,15 +669,15 @@ int mount_autofs_offset(struct autofs_po } /* Root directory for ioctl()'s */ - me->ioctlfd = open(me->key, O_RDONLY); - if (me->ioctlfd < 0) { + ioctlfd = open(me->key, O_RDONLY); + if (ioctlfd < 0) { crit(ap->logopt, "failed to create ioctl fd for %s", me->key); goto out_umount; } - ioctl(me->ioctlfd, AUTOFS_IOC_SETTIMEOUT, &timeout); + ioctl(ioctlfd, AUTOFS_IOC_SETTIMEOUT, &timeout); - ret = fstat(me->ioctlfd, &st); + ret = fstat(ioctlfd, &st); if (ret == -1) { error(ap->logopt, "failed to stat direct mount trigger %s", me->key); @@ -651,16 +686,14 @@ int mount_autofs_offset(struct autofs_po cache_set_ino_index(me->source->mc, me->key, st.st_dev, st.st_ino); - close(me->ioctlfd); - me->ioctlfd = -1; + close(ioctlfd); debug(ap->logopt, "mounted trigger %s", me->key); return 0; out_close: - close(me->ioctlfd); - me->ioctlfd = -1; + close(ioctlfd); out_umount: umount(me->key); out_err: @@ -677,7 +710,7 @@ void *expire_proc_direct(void *arg) struct expire_args *ea; struct autofs_point *ap; struct mapent_cache *mc = NULL; - struct mapent *me = NULL; + struct mapent *ro, *me = NULL; unsigned int now; int ioctlfd = -1; int status, ret; @@ -713,12 +746,12 @@ void *expire_proc_direct(void *arg) /* Get a list of real mounts and expire them if possible */ mnts = get_mnt_list(_PROC_MOUNTS, "/", 0); for (next = mnts; next; next = next->next) { - /* Consider only autofs mounts */ - if (!strstr(next->fs_type, "autofs")) + /* Skip submounts */ + if (strstr(next->fs_type, "indirect")) continue; - /* Skip submounts */ - if (strstr(next->opts, "indirect")) + /* Skip offsets */ + if (strstr(next->opts, "offsets")) continue; sched_yield(); @@ -760,7 +793,6 @@ void *expire_proc_direct(void *arg) debug(ap->logopt, "failed to expire mount %s", next->path); ea->status = 1; - break; } } free_mnt_list(mnts); @@ -802,43 +834,6 @@ void *expire_proc_direct(void *arg) return NULL; } -static void kernel_callback_cleanup(void *arg) -{ - struct autofs_point *ap; - struct pending_args *mt; - struct mapent_cache *mc; - struct mapent *me; - - mt = (struct pending_args *) arg; - ap = mt->ap; - mc = mt->mc; - - if (mt->status) { - cache_writelock(mc); - send_ready(mt->ioctlfd, mt->wait_queue_token); - if (mt->type == NFY_EXPIRE) { - close(mt->ioctlfd); - me = cache_lookup_distinct(mc, mt->name); - if (me) - me->ioctlfd = -1; - } - cache_unlock(mc); - } else { - cache_writelock(mc); - send_fail(mt->ioctlfd, mt->wait_queue_token); - if (mt->type == NFY_MOUNT) { - close(mt->ioctlfd); - me = cache_lookup_distinct(mc, mt->name); - if (me) - me->ioctlfd = -1; - } - cache_unlock(mc); - } - - free(mt); - return; -} - void pending_cleanup(void *arg) { struct pending_args *mt; @@ -859,21 +854,28 @@ void pending_cleanup(void *arg) fatal(status); } +static void expire_send_fail(void *arg) +{ + struct pending_args *mt = arg; + send_fail(mt->ioctlfd, mt->wait_queue_token); +} + static void *do_expire_direct(void *arg) { struct pending_args *mt; struct autofs_point *ap; size_t len; - int status; + int status, state; mt = (struct pending_args *) arg; + pthread_cleanup_push(expire_send_fail, mt); + status = pthread_mutex_lock(&mt->mutex); if (status) fatal(status); ap = mt->ap; - mt->status = 1; mt->signaled = 1; status = pthread_cond_signal(&mt->cond); @@ -884,21 +886,28 @@ static void *do_expire_direct(void *arg) if (status) fatal(status); - pthread_cleanup_push(kernel_callback_cleanup, mt); - len = _strlen(mt->name, KEY_MAX_LEN); if (!len) { warn(ap->logopt, "direct key path too long %s", mt->name); - mt->status = 0; /* TODO: force umount ?? */ pthread_exit(NULL); } status = do_expire(ap, mt->name, len); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); if (status) - mt->status = 0; + send_fail(mt->ioctlfd, mt->wait_queue_token); + else { + struct mapent *me; + me = lookup_source_mapent(ap, mt->name); + me->ioctlfd = -1; + cache_unlock(me->source->mc); + send_ready(mt->ioctlfd, mt->wait_queue_token); + close(mt->ioctlfd); + } + pthread_setcancelstate(state, NULL); - pthread_cleanup_pop(1); + pthread_cleanup_pop(0); return NULL; } @@ -910,7 +919,9 @@ int handle_packet_expire_direct(struct a struct pending_args *mt; char buf[MAX_ERR_BUF]; pthread_t thid; - int status = 0; + int status, state; + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); /* * This is a bit of a big deal. @@ -942,18 +953,19 @@ int handle_packet_expire_direct(struct a */ crit(ap->logopt, "can't find map entry for (%lu,%lu)", (unsigned long) pkt->dev, (unsigned long) pkt->ino); + pthread_setcancelstate(state, NULL); return 1; } - pthread_cleanup_push(cache_lock_cleanup, mc); mt = malloc(sizeof(struct pending_args)); if (!mt) { char *estr = strerror_r(errno, buf, MAX_ERR_BUF); error(ap->logopt, "malloc: %s", estr); send_fail(me->ioctlfd, pkt->wait_queue_token); - status = 1; - goto done; + cache_unlock(mc); + pthread_setcancelstate(state, NULL); + return 1; } status = pthread_mutex_init(&mt->mutex, NULL); @@ -985,11 +997,15 @@ int handle_packet_expire_direct(struct a error(ap->logopt, "expire thread create failed"); free(mt); send_fail(mt->ioctlfd, pkt->wait_queue_token); - status = 1; - goto done; + cache_unlock(mc); + pending_cleanup(mt); + pthread_setcancelstate(state, NULL); + return 1; } + cache_unlock(mc); pthread_cleanup_push(pending_cleanup, mt); + pthread_setcancelstate(state, NULL); mt->signaled = 0; while (!mt->signaled) { @@ -999,12 +1015,14 @@ int handle_packet_expire_direct(struct a } pthread_cleanup_pop(1); - pthread_cleanup_pop(1); - return status; + return 0; +} -done: - cache_lock_cleanup(mc); - return status; +static void mount_send_fail(void *arg) +{ + struct pending_args *mt = arg; + send_fail(mt->ioctlfd, mt->wait_queue_token); + close(mt->ioctlfd); } static void *do_mount_direct(void *arg) @@ -1021,7 +1039,7 @@ static void *do_mount_direct(void *arg) struct thread_stdenv_vars *tsv; int tmplen; struct stat st; - int status; + int status, state; mt = (struct pending_args *) arg; @@ -1030,7 +1048,6 @@ static void *do_mount_direct(void *arg) fatal(status); ap = mt->ap; - mt->status = 0; mt->signaled = 1; status = pthread_cond_signal(&mt->cond); @@ -1041,12 +1058,14 @@ static void *do_mount_direct(void *arg) if (status) fatal(status); - pthread_cleanup_push(kernel_callback_cleanup, mt); + pthread_cleanup_push(mount_send_fail, mt); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); status = fstat(mt->ioctlfd, &st); if (status == -1) { error(ap->logopt, "can't stat direct mount trigger %s", mt->name); + pthread_setcancelstate(state, NULL); pthread_exit(NULL); } @@ -1055,9 +1074,12 @@ static void *do_mount_direct(void *arg) error(ap->logopt, "direct trigger not valid or already mounted %s", mt->name); + pthread_setcancelstate(state, NULL); pthread_exit(NULL); } + pthread_setcancelstate(state, NULL); + msg("attempting to mount entry %s", mt->name); /* @@ -1173,13 +1195,22 @@ cont: * Direct mounts are always a single mount. If it fails there's * nothing to undo so just complain */ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); if (status) { + struct mapent *me; + me = lookup_source_mapent(ap, mt->name); + me->ioctlfd = mt->ioctlfd; + cache_unlock(me->source->mc); + send_ready(mt->ioctlfd, mt->wait_queue_token); msg("mounted %s", mt->name); - mt->status = 1; - } else + } else { + send_fail(mt->ioctlfd, mt->wait_queue_token); + close(mt->ioctlfd); msg("failed to mount %s", mt->name); + } + pthread_setcancelstate(state, NULL); - pthread_cleanup_pop(1); + pthread_cleanup_pop(0); return NULL; } @@ -1192,7 +1223,9 @@ int handle_packet_missing_direct(struct struct pending_args *mt; char buf[MAX_ERR_BUF]; int status = 0; - int ioctlfd; + int ioctlfd, state; + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); master_source_readlock(ap->entry); map = ap->entry->first; @@ -1223,19 +1256,18 @@ int handle_packet_missing_direct(struct */ crit(ap->logopt, "can't find map entry for (%lu,%lu)", (unsigned long) pkt->dev, (unsigned long) pkt->ino); + pthread_setcancelstate(state, NULL); return 1; } - pthread_cleanup_push(cache_lock_cleanup, mc); - ioctlfd = open(me->key, O_RDONLY); if (ioctlfd < 0) { + cache_unlock(mc); + 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 ?? */ - status = 1; - goto done; + return 1; } - me->ioctlfd = ioctlfd; debug(ap->logopt, "token %ld, name %s, request pid %u", (unsigned long) pkt->wait_queue_token, me->key, pkt->pid); @@ -1244,21 +1276,22 @@ int handle_packet_missing_direct(struct if (ap->state == ST_SHUTDOWN_PENDING || ap->state == ST_SHUTDOWN_FORCE || ap->state == ST_SHUTDOWN) { - send_fail(me->ioctlfd, pkt->wait_queue_token); - close(me->ioctlfd); - me->ioctlfd = -1; - status = 1; - goto done; + send_fail(ioctlfd, pkt->wait_queue_token); + close(ioctlfd); + cache_unlock(mc); + pthread_setcancelstate(state, NULL); + return 1; } mt = malloc(sizeof(struct pending_args)); if (!mt) { char *estr = strerror_r(errno, buf, MAX_ERR_BUF); error(ap->logopt, "malloc: %s", estr); - send_fail(me->ioctlfd, pkt->wait_queue_token); - close(me->ioctlfd); - me->ioctlfd = -1; - goto done; + send_fail(ioctlfd, pkt->wait_queue_token); + close(ioctlfd); + cache_unlock(mc); + pthread_setcancelstate(state, NULL); + return 1; } status = pthread_mutex_init(&mt->mutex, NULL); @@ -1274,7 +1307,7 @@ int handle_packet_missing_direct(struct fatal(status); mt->ap = ap; - mt->ioctlfd = me->ioctlfd; + mt->ioctlfd = ioctlfd; mt->mc = mc; /* TODO: check length here */ strcpy(mt->name, me->key); @@ -1288,13 +1321,15 @@ int handle_packet_missing_direct(struct if (status) { error(ap->logopt, "missing mount thread create failed"); free(mt); - send_fail(me->ioctlfd, pkt->wait_queue_token); - close(me->ioctlfd); - me->ioctlfd = -1; - status = 1; - goto done; + send_fail(ioctlfd, pkt->wait_queue_token); + close(ioctlfd); + cache_unlock(mc); + pending_cleanup(mt); + pthread_setcancelstate(state, NULL); + return 1; } + cache_unlock(mc); pthread_cleanup_push(pending_cleanup, mt); mt->signaled = 0; @@ -1305,11 +1340,7 @@ int handle_packet_missing_direct(struct } pthread_cleanup_pop(1); - pthread_cleanup_pop(1); - return status; -done: - cache_lock_cleanup(mc); - return status; + return 0; } diff --git a/daemon/indirect.c b/daemon/indirect.c index 079b278..406bf72 100644 --- a/daemon/indirect.c +++ b/daemon/indirect.c @@ -281,6 +281,7 @@ int mount_autofs_indirect(struct autofs_ int umount_autofs_indirect(struct autofs_point *ap) { char buf[MAX_ERR_BUF]; + struct stat st; int ret, rv; /* @@ -300,17 +301,16 @@ int umount_autofs_indirect(struct autofs return 1; } else if (!ret) { error(ap->logopt, "ask umount returned busy %s", ap->path); + return 1; } - if (ap->ioctlfd >= 0) { - ioctl(ap->ioctlfd, AUTOFS_IOC_CATATONIC, 0); - close(ap->ioctlfd); - ap->ioctlfd = -1; - close(ap->state_pipe[0]); - close(ap->state_pipe[1]); - ap->state_pipe[0] = -1; - ap->state_pipe[1] = -1; - } + ioctl(ap->ioctlfd, AUTOFS_IOC_CATATONIC, 0); + close(ap->ioctlfd); + ap->ioctlfd = -1; + close(ap->state_pipe[0]); + close(ap->state_pipe[1]); + ap->state_pipe[0] = -1; + ap->state_pipe[1] = -1; if (ap->pipefd >= 0) close(ap->pipefd); @@ -366,7 +366,7 @@ void *expire_proc_indirect(void *arg) struct expire_args *ea; unsigned int now; int offsets, submnts, count; - int ioctlfd; + int ioctlfd, limit; int status, ret; char buf[MAX_ERR_BUF]; @@ -442,7 +442,7 @@ void *expire_proc_indirect(void *arg) if (!me) continue; - if (me && *me->key == '/') { + if (*me->key == '/') { ioctlfd = me->ioctlfd; cache_unlock(mc); } else { @@ -458,45 +458,29 @@ void *expire_proc_indirect(void *arg) warn(ap->logopt, "failed to expire mount %s:", next->path, estr); ea->status = 1; - break; } - } - free_mnt_list(mnts); - count = offsets = submnts = 0; - mnts = get_mnt_list(_PROC_MOUNTS, ap->path, 1); - /* Are there any real mounts left */ - for (next = mnts; next; next = next->next) { - if (strcmp(next->fs_type, "autofs")) - count++; - else { - if (strstr(next->opts, "indirect")) - submnts++; - else - offsets++; - } } + free_mnt_list(mnts); /* * If there are no more real mounts left we could still * have some offset mounts with no '/' offset so we need to * umount them here. */ - if (mnts) { - int ret, tries = (count + submnts + offsets + 2) * 2; - - while (tries--) { - ret = ioctl(ap->ioctlfd, AUTOFS_IOC_EXPIRE_MULTI, &now); - if (ret < 0 && errno != EAGAIN) { - warn(ap->logopt, - "failed to expire ofsets under %s", - ap->path); - ea->status = 1; + limit = count_mounts(ap, ap->path); + while (limit--) { + ret = ioctl(ap->ioctlfd, AUTOFS_IOC_EXPIRE_MULTI, &now); + if (ret < 0) { + if (errno == EAGAIN) break; - } + warn(ap->logopt, + "failed to expire offsets under %s", + ap->path); + ea->status = 1; + break; } } - free_mnt_list(mnts); count = offsets = submnts = 0; mnts = get_mnt_list(_PROC_MOUNTS, ap->path, 0); @@ -543,30 +527,20 @@ void *expire_proc_indirect(void *arg) return NULL; } -static void kernel_callback_cleanup(void *arg) -{ - struct autofs_point *ap; - struct pending_args *mt; - - mt = (struct pending_args *) arg; - ap = mt->ap; - - if (mt->status) - send_ready(ap->ioctlfd, mt->wait_queue_token); - else - send_fail(ap->ioctlfd, mt->wait_queue_token); - - free(mt); - return; -} - /* See direct.c */ extern void pending_cleanup(void *); +static void expire_send_fail(void *arg) +{ + struct pending_args *mt = arg; + send_fail(mt->ap->ioctlfd, mt->wait_queue_token); +} + static void *do_expire_indirect(void *arg) { struct pending_args *mt; - int status; + struct autofs_point *ap; + int status, state; mt = (struct pending_args *) arg; @@ -574,7 +548,7 @@ static void *do_expire_indirect(void *ar if (status) fatal(status); - mt->status = 1; + ap = mt->ap; mt->signaled = 1; status = pthread_cond_signal(&mt->cond); @@ -585,13 +559,17 @@ static void *do_expire_indirect(void *ar if (status) fatal(status); - pthread_cleanup_push(kernel_callback_cleanup, mt); + pthread_cleanup_push(expire_send_fail, mt); status = do_expire(mt->ap, mt->name, mt->len); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); if (status) - mt->status = 0; + send_fail(ap->ioctlfd, mt->wait_queue_token); + else + send_ready(ap->ioctlfd, mt->wait_queue_token); + pthread_setcancelstate(state, NULL); - pthread_cleanup_pop(1); + pthread_cleanup_pop(0); return NULL; } @@ -601,7 +579,9 @@ int handle_packet_expire_indirect(struct struct pending_args *mt; char buf[MAX_ERR_BUF]; pthread_t thid; - int status; + int status, state; + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); debug(ap->logopt, "token %ld, name %s", (unsigned long) pkt->wait_queue_token, pkt->name); @@ -611,6 +591,7 @@ int handle_packet_expire_indirect(struct char *estr = strerror_r(errno, buf, MAX_ERR_BUF); error(ap->logopt, "malloc: %s", estr); send_fail(ap->ioctlfd, pkt->wait_queue_token); + pthread_setcancelstate(state, NULL); return 1; } @@ -637,9 +618,13 @@ int handle_packet_expire_indirect(struct error(ap->logopt, "expire thread create failed"); send_fail(ap->ioctlfd, pkt->wait_queue_token); free(mt); + pending_cleanup(mt); + pthread_setcancelstate(state, NULL); + return 1; } pthread_cleanup_push(pending_cleanup, mt); + pthread_setcancelstate(state, NULL); mt->signaled = 0; while (!mt->signaled) { @@ -653,6 +638,12 @@ int handle_packet_expire_indirect(struct return 0; } +static void mount_send_fail(void *arg) +{ + struct pending_args *mt = arg; + send_fail(mt->ap->ioctlfd, mt->wait_queue_token); +} + static void *do_mount_indirect(void *arg) { struct pending_args *mt; @@ -667,7 +658,7 @@ static void *do_mount_indirect(void *arg struct group **ppgr = &pgr; char *pw_tmp, *gr_tmp; struct thread_stdenv_vars *tsv; - int len, tmplen, status; + int len, tmplen, status, state; mt = (struct pending_args *) arg; @@ -687,11 +678,13 @@ static void *do_mount_indirect(void *arg if (status) fatal(status); - pthread_cleanup_push(kernel_callback_cleanup, mt); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); len = ncat_path(buf, sizeof(buf), ap->path, mt->name, mt->len); if (!len) { crit(ap->logopt, "path to be mounted is to long"); + send_fail(ap->ioctlfd, mt->wait_queue_token); + pthread_setcancelstate(state, NULL); pthread_exit(NULL); } @@ -699,9 +692,14 @@ static void *do_mount_indirect(void *arg if (status != -1 && !(S_ISDIR(st.st_mode) && st.st_dev == mt->dev)) { error(ap->logopt, "indirect trigger not valid or already mounted %s", buf); + send_fail(ap->ioctlfd, mt->wait_queue_token); + pthread_setcancelstate(state, NULL); pthread_exit(NULL); } + pthread_cleanup_push(mount_send_fail, mt); + pthread_setcancelstate(state, NULL); + msg("attempting to mount entry %s", buf); /* @@ -812,13 +810,17 @@ static void *do_mount_indirect(void *arg } cont: status = lookup_nss_mount(ap, mt->name, mt->len); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); if (status) { - mt->status = 1; + send_ready(ap->ioctlfd, mt->wait_queue_token); msg("mounted %s", buf); - } else + } else { + send_fail(ap->ioctlfd, mt->wait_queue_token); msg("failed to mount %s", buf); + } + pthread_setcancelstate(state, NULL); - pthread_cleanup_pop(1); + pthread_cleanup_pop(0); return NULL; } @@ -827,7 +829,9 @@ int handle_packet_missing_indirect(struc pthread_t thid; char buf[MAX_ERR_BUF]; struct pending_args *mt; - int status; + int status, state; + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); debug(ap->logopt, "token %ld, name %s, request pid %u", (unsigned long) pkt->wait_queue_token, pkt->name, pkt->pid); @@ -837,6 +841,7 @@ int handle_packet_missing_indirect(struc ap->state == ST_SHUTDOWN_FORCE || ap->state == ST_SHUTDOWN) { send_fail(ap->ioctlfd, pkt->wait_queue_token); + pthread_setcancelstate(state, NULL); return 0; } @@ -845,6 +850,7 @@ int handle_packet_missing_indirect(struc char *estr = strerror_r(errno, buf, MAX_ERR_BUF); error(ap->logopt, "malloc: %s", estr); send_fail(ap->ioctlfd, pkt->wait_queue_token); + pthread_setcancelstate(state, NULL); return 1; } @@ -874,10 +880,13 @@ int handle_packet_missing_indirect(struc error(ap->logopt, "expire thread create failed"); send_fail(ap->ioctlfd, pkt->wait_queue_token); free(mt); + pending_cleanup(mt); + pthread_setcancelstate(state, NULL); return 1; } pthread_cleanup_push(pending_cleanup, mt); + pthread_setcancelstate(state, NULL); mt->signaled = 0; while (!mt->signaled) { diff --git a/daemon/lookup.c b/daemon/lookup.c index a8ef518..6942121 100644 --- a/daemon/lookup.c +++ b/daemon/lookup.c @@ -931,7 +931,9 @@ int lookup_prune_cache(struct autofs_poi free(path); goto next; } - status = cache_delete(mc, key); + status = CHE_FAIL; + if (this->ioctlfd == -1) + status = cache_delete(mc, key); cache_unlock(mc); if (status != CHE_FAIL) { @@ -968,7 +970,7 @@ struct mapent *lookup_source_mapent(stru struct master_mapent *entry = ap->entry; struct map_source *map; struct mapent_cache *mc; - struct mapent *me; + struct mapent *me = NULL; pthread_cleanup_push(master_source_lock_cleanup, entry); master_source_readlock(entry); diff --git a/daemon/state.c b/daemon/state.c index caeb3e3..67d8031 100644 --- a/daemon/state.c +++ b/daemon/state.c @@ -702,7 +702,8 @@ static void *do_run_task(void *arg) struct state_queue *task; struct autofs_point *ap; enum states next_state, state; - int status, ret; + unsigned long ret = 1; + int status; status = pthread_mutex_lock(&task_mutex); if (status) @@ -755,7 +756,7 @@ static void *do_run_task(void *arg) state_mutex_unlock(ap); - return; + return (void *) ret; } static int run_state_task(struct state_queue *task) diff --git a/include/automount.h b/include/automount.h index b10cfad..4c4d507 100644 --- a/include/automount.h +++ b/include/automount.h @@ -66,7 +66,9 @@ #define SLOPPYOPT #define SLOPPY #endif -#define AUTOFS_SUPER_MAGIC 0x0187L +#define AUTOFS_SUPER_MAGIC 0x00000187L +#define SMB_SUPER_MAGIC 0x0000517BL +#define CIFS_MAGIC_NUMBER 0xFF534D42L /* This sould be enough for at least 20 host aliases */ #define HOST_ENT_BUF_SIZE 2048 @@ -336,6 +338,7 @@ struct mnt_list { struct mnt_list *right; struct list_head self; struct list_head list; + struct list_head sublist; /* * Offset mount handling ie. add_ordered_list * and get_offset. @@ -357,6 +360,7 @@ void add_ordered_list(struct mnt_list *e void tree_free_mnt_tree(struct mnt_list *tree); struct mnt_list *tree_make_mnt_tree(const char *table, const char *path); int tree_get_mnt_list(struct mnt_list *mnts, struct list_head *list, const char *path, int include); +int tree_get_mnt_sublist(struct mnt_list *mnts, struct list_head *list, const char *path, int include); 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); @@ -448,7 +452,7 @@ struct autofs_point { /* Standard functions used by daemon or modules */ void *handle_mounts(void *arg); -int umount_multi(struct autofs_point *ap, struct mnt_list *mnts, const char *path, int incl); +int umount_multi(struct autofs_point *ap, const char *path, int incl); int send_ready(int ioctlfd, unsigned int wait_queue_token); int send_fail(int ioctlfd, unsigned int wait_queue_token); int do_expire(struct autofs_point *ap, const char *name, int namelen); diff --git a/include/parse_subs.h b/include/parse_subs.h index e87cea5..10c6083 100644 --- a/include/parse_subs.h +++ b/include/parse_subs.h @@ -18,6 +18,8 @@ #ifndef PARSE_SUBS_H #define PARSE_SUBS_H +struct mapent; + const char *skipspace(const char *); int check_colon(const char *); int chunklen(const char *, int); @@ -25,5 +27,8 @@ int strmcmp(const char *, const char *, char *dequote(const char *, int, unsigned int); int span_space(const char *, unsigned int); char *sanitize_path(const char *, int, unsigned int, unsigned int); +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 *); #endif diff --git a/lib/cache.c b/lib/cache.c index 4d0b196..a047320 100644 --- a/lib/cache.c +++ b/lib/cache.c @@ -473,7 +473,6 @@ static void cache_add_ordered_offset(str { struct list_head *p; struct mapent *this; - int status = CHE_OK; list_for_each(p, head) { size_t tlen; @@ -639,6 +638,18 @@ int cache_delete_offset_list(struct mape while (next != head) { this = list_entry(next, struct mapent, multi_list); next = next->next; + if (this->ioctlfd != -1) { + error(LOGOPT_ANY, + "active offset mount key %s", this->key); + return CHE_FAIL; + } + } + + head = &me->multi_list; + next = head->next; + while (next != head) { + this = list_entry(next, struct mapent, multi_list); + next = next->next; list_del_init(&this->multi_list); this->multi = NULL; debug(LOGOPT_NONE, "deleting offset key %s", this->key); diff --git a/lib/master.c b/lib/master.c index d5e58d2..bcda72a 100644 --- a/lib/master.c +++ b/lib/master.c @@ -296,7 +296,6 @@ struct map_source *master_find_map_sourc int argc, const char **argv) { struct map_source *source = NULL; - int status; master_mutex_lock(); @@ -559,7 +558,6 @@ void master_source_current_signal(struct struct master_mapent *master_find_mapent(struct master *master, const char *path) { struct list_head *head, *p; - int status; master_mutex_lock(); @@ -625,27 +623,18 @@ struct master_mapent *master_new_mapent( void master_add_mapent(struct master *master, struct master_mapent *entry) { - int status; - master_mutex_lock(); list_add_tail(&entry->list, &master->mounts); master_mutex_unlock(); - return; } void master_remove_mapent(struct master_mapent *entry) { - struct autofs_point *ap; - int status; - master_mutex_lock(); - if (!list_empty(&entry->list)) list_del_init(&entry->list); - master_mutex_unlock(); - return; } @@ -729,8 +718,6 @@ struct master *master_new(const char *na int master_read_master(struct master *master, time_t age, int readall) { - int status; - if (!lookup_nss_read_master(master, age)) { error(LOGOPT_ANY, "can't read master map %s", master->name); @@ -842,7 +829,6 @@ void master_notify_state_change(struct m struct autofs_point *ap; struct list_head *p; int state_pipe; - int status; master_mutex_lock(); @@ -862,8 +848,7 @@ void master_notify_state_change(struct m switch (sig) { case SIGTERM: - if (ap->state != ST_SHUTDOWN && - ap->state != ST_SHUTDOWN_PENDING && + if (ap->state != ST_SHUTDOWN_PENDING && ap->state != ST_SHUTDOWN_FORCE) { next = ST_SHUTDOWN_PENDING; nextstate(state_pipe, next); @@ -871,8 +856,7 @@ void master_notify_state_change(struct m break; #ifdef ENABLE_FORCED_SHUTDOWN case SIGUSR2: - if (ap->state != ST_SHUTDOWN && - ap->state != ST_SHUTDOWN_FORCE && + if (ap->state != ST_SHUTDOWN_FORCE && ap->state != ST_SHUTDOWN_PENDING) { next = ST_SHUTDOWN_FORCE; nextstate(state_pipe, next); @@ -950,7 +934,7 @@ static int master_do_mount(struct master static void shutdown_entry(struct master_mapent *entry) { - int status, state_pipe; + int state_pipe; struct autofs_point *ap; struct stat st; int ret; @@ -977,7 +961,7 @@ next: static void check_update_map_sources(struct master_mapent *entry, int readall) { struct map_source *source, *last; - int status, state_pipe, map_stale = 0; + int state_pipe, map_stale = 0; struct autofs_point *ap; struct stat st; int ret; @@ -1053,7 +1037,6 @@ static void check_update_map_sources(str int master_mount_mounts(struct master *master, time_t age, int readall) { struct list_head *p, *head; - int status; master_mutex_lock(); @@ -1104,14 +1087,11 @@ int master_mount_mounts(struct master *m int master_list_empty(struct master *master) { - int status; int res = 0; master_mutex_lock(); - if (list_empty(&master->mounts)) res = 1; - master_mutex_unlock(); return res; diff --git a/lib/mounts.c b/lib/mounts.c index b11c4c7..f446a6a 100644 --- a/lib/mounts.c +++ b/lib/mounts.c @@ -112,7 +112,6 @@ struct mnt_list *get_mnt_list(const char struct mntent *mnt; struct mnt_list *ent, *mptr, *last; struct mnt_list *list = NULL; - unsigned long count = 0; char *pgrp; size_t len; @@ -126,7 +125,7 @@ struct mnt_list *get_mnt_list(const char return NULL; } - while ((mnt = getmntent_r(tab, &mnt_wrk, buf, PATH_MAX)) != NULL) { + while ((mnt = getmntent_r(tab, &mnt_wrk, buf, PATH_MAX * 3))) { len = strlen(mnt->mnt_dir); if ((!include && len <= pathlen) || @@ -198,9 +197,6 @@ struct mnt_list *get_mnt_list(const char *end = '\0'; sscanf(pgrp, "pgrp=%d", &ent->owner); } - - if (count++ % 100) - sched_yield(); } endmntent(tab); @@ -258,7 +254,7 @@ int is_mounted(const char *table, const { struct mntent *mnt; struct mntent mnt_wrk; - char buf[PATH_MAX]; + char buf[PATH_MAX * 3]; size_t pathlen = strlen(path); FILE *tab; int ret = 0; @@ -273,7 +269,7 @@ int is_mounted(const char *table, const return 0; } - while ((mnt = getmntent_r(tab, &mnt_wrk, buf, PATH_MAX))) { + while ((mnt = getmntent_r(tab, &mnt_wrk, buf, PATH_MAX * 3))) { size_t len = strlen(mnt->mnt_dir); if (type) { @@ -304,7 +300,7 @@ int has_fstab_option(const char *opt) { struct mntent *mnt; struct mntent mnt_wrk; - char buf[PATH_MAX]; + char buf[PATH_MAX * 3]; FILE *tab; int ret = 0; @@ -318,7 +314,7 @@ int has_fstab_option(const char *opt) return 0; } - while ((mnt = getmntent_r(tab, &mnt_wrk, buf, PATH_MAX))) { + while ((mnt = getmntent_r(tab, &mnt_wrk, buf, PATH_MAX * 3))) { if (hasmntopt(mnt, opt)) { ret = 1; break; @@ -333,7 +329,7 @@ char *find_mnt_ino(const char *table, de { struct mntent mnt_wrk; struct mntent *mnt; - char buf[PATH_MAX]; + char buf[PATH_MAX * 3]; char *path = NULL; unsigned long l_dev = (unsigned long) dev; unsigned long l_ino = (unsigned long) ino; @@ -346,7 +342,7 @@ char *find_mnt_ino(const char *table, de return 0; } - while ((mnt = getmntent_r(tab, &mnt_wrk, buf, PATH_MAX))) { + while ((mnt = getmntent_r(tab, &mnt_wrk, buf, PATH_MAX * 3))) { char *p_dev, *p_ino; unsigned long m_dev, m_ino; @@ -542,7 +538,7 @@ struct mnt_list *tree_make_mnt_tree(cons plen = strlen(path); - while ((mnt = getmntent_r(tab, &mnt_wrk, buf, PATH_MAX))) { + while ((mnt = getmntent_r(tab, &mnt_wrk, buf, PATH_MAX * 3))) { size_t len = strlen(mnt->mnt_dir); /* Not matching path */ @@ -563,6 +559,7 @@ struct mnt_list *tree_make_mnt_tree(cons INIT_LIST_HEAD(&ent->self); INIT_LIST_HEAD(&ent->list); + INIT_LIST_HEAD(&ent->sublist); INIT_LIST_HEAD(&ent->ordered); ent->path = malloc(len + 1); @@ -700,6 +697,53 @@ skip: return 1; } +/* + * Get list of mounts under "path" in longest->shortest order + */ +int tree_get_mnt_sublist(struct mnt_list *mnts, struct list_head *list, const char *path, int include) +{ + size_t mlen, plen; + + if (!mnts) + return 0; + + plen = strlen(path); + mlen = strlen(mnts->path); + if (mlen < plen) + return tree_get_mnt_sublist(mnts->right, list, path, include); + else { + struct list_head *self, *p; + + tree_get_mnt_sublist(mnts->left, list, path, include); + + if ((!include && mlen <= plen) || + strncmp(mnts->path, path, plen)) + goto skip; + + if (plen > 1 && mlen > plen && mnts->path[plen] != '/') + goto skip; + + INIT_LIST_HEAD(&mnts->sublist); + list_add(&mnts->sublist, list); + + self = &mnts->self; + list_for_each(p, self) { + struct mnt_list *this; + + this = list_entry(p, struct mnt_list, self); + INIT_LIST_HEAD(&this->sublist); + list_add(&this->sublist, list); + } +skip: + tree_get_mnt_sublist(mnts->right, list, path, include); + } + + if (list_empty(list)) + return 0; + + return 1; +} + int tree_find_mnt_ents(struct mnt_list *mnts, struct list_head *list, const char *path) { int mlen, plen; diff --git a/lib/parse_subs.c b/lib/parse_subs.c index 5847eb0..6fb9895 100644 --- a/lib/parse_subs.c +++ b/lib/parse_subs.c @@ -16,6 +16,12 @@ * ----------------------------------------------------------------------- */ #include +#include +#include +#include +#include +#include +#include #include "automount.h" /* @@ -265,3 +271,214 @@ char *sanitize_path(const char *path, in return s_path; } +int umount_ent(struct autofs_point *ap, const char *path) +{ + struct stat st; + struct statfs fs; + int sav_errno; + int status, is_smbfs = 0; + int ret, rv = 1; + + ret = statfs(path, &fs); + if (ret == -1) { + warn(ap->logopt, "could not stat fs of %s", path); + is_smbfs = 0; + } else { + int cifsfs = fs.f_type == CIFS_MAGIC_NUMBER; + int smbfs = fs.f_type == SMB_SUPER_MAGIC; + is_smbfs = (cifsfs | smbfs) ? 1 : 0; + } + + status = lstat(path, &st); + sav_errno = errno; + + if (status < 0) + warn(ap->logopt, "lstat of %s failed with %d", path, status); + + /* + * lstat failed and we're an smbfs fs returning an error that is not + * EIO or EBADSLT or the lstat failed so it's a bad path. Return + * a fail. + * + * EIO appears to correspond to an smb mount that has gone away + * and EBADSLT relates to CD changer not responding. + */ + if (!status && (S_ISDIR(st.st_mode) && st.st_dev != ap->dev)) { + rv = spawnll(log_debug, PATH_UMOUNT, PATH_UMOUNT, path, NULL); + } else if (is_smbfs && (sav_errno == EIO || sav_errno == EBADSLT)) { + rv = spawnll(log_debug, PATH_UMOUNT, PATH_UMOUNT, path, NULL); + } + + /* We are doing a forced shutcwdown down so unlink busy mounts */ + if (rv && (ap->state == ST_SHUTDOWN_FORCE || ap->state == ST_SHUTDOWN)) { + ret = stat(path, &st); + if (ret == -1 && errno == ENOENT) { + warn(ap->logopt, "mount point does not exist"); + return 0; + } + + if (ret == 0 && !S_ISDIR(st.st_mode)) { + warn(ap->logopt, "mount point is not a directory"); + return 0; + } + + if (ap->state == ST_SHUTDOWN_FORCE) { + msg("forcing umount of %s", path); + rv = spawnll(log_debug, PATH_UMOUNT, PATH_UMOUNT, "-l", path, NULL); + } + + /* + * Verify that we actually unmounted the thing. This is a + * belt and suspenders approach to not eating user data. + * We have seen cases where umount succeeds, but there is + * still a file system mounted on the mount point. How + * this happens has not yet been determined, but we want to + * make sure to return failure here, if that is the case, + * so that we do not try to call rmdir_path on the + * directory. + */ + if (!rv && is_mounted(_PATH_MOUNTED, path, MNTS_REAL)) { + crit(ap->logopt, + "the umount binary reported that %s was " + "unmounted, but there is still something " + "mounted on this path.", path); + rv = -1; + } + } + + return rv; +} + +int mount_multi_triggers(struct autofs_point *ap, char *root, struct mapent *me, const char *base) +{ + char path[PATH_MAX + 1]; + char *offset = path; + struct mapent *oe; + struct list_head *pos = NULL; + unsigned int fs_path_len; + struct statfs fs; + struct stat st; + unsigned int is_autofs_fs; + int ret, start; + + fs_path_len = strlen(root) + strlen(base); + if (fs_path_len > PATH_MAX) + return 0; + + strcpy(path, root); + strcat(path, base); + ret = statfs(path, &fs); + if (ret == -1) { + /* There's no mount yet - it must be autofs */ + if (errno == ENOENT) + is_autofs_fs = 1; + else + return 0; + } else + is_autofs_fs = fs.f_type == AUTOFS_SUPER_MAGIC ? 1 : 0; + + start = strlen(root); + offset = cache_get_offset(base, offset, start, &me->multi_list, &pos); + while (offset) { + int plen = fs_path_len + strlen(offset); + + if (plen > PATH_MAX) { + warn(ap->logopt, "path loo long"); + goto cont; + } + + oe = cache_lookup_offset(base, offset, start, &me->multi_list); + if (!oe) + goto cont; + + /* + * If the host filesystem is not an autofs fs + * we require the mount point directory exist + * and that permissions are OK. + */ + if (!is_autofs_fs) { + ret = stat(oe->key, &st); + if (ret == -1) + goto cont; + } + + debug(ap->logopt, "mount offset %s", oe->key); + + if (mount_autofs_offset(ap, oe, is_autofs_fs) < 0) + warn(ap->logopt, "failed to mount offset"); +cont: + offset = cache_get_offset(base, + offset, start, &me->multi_list, &pos); + } + + return 1; +} + +int umount_multi_triggers(struct autofs_point *ap, char *root, struct mapent *me, const char *base) +{ + char path[PATH_MAX + 1]; + char *offset = path; + struct mapent *oe; + struct list_head *mm_root, *pos = NULL; + const char o_root[] = "/"; + const char *mm_base; + int left, start; + + left = 0; + start = strlen(root); + + mm_root = &me->multi->multi_list; + + if (!base) + mm_base = o_root; + else + mm_base = base; + + offset = cache_get_offset(mm_base, offset, start, mm_root, &pos); + while (offset) { + oe = cache_lookup_offset(mm_base, offset, start, &me->multi_list); + /* root offset is a special case */ + if (!oe || (strlen(oe->key) - start) == 1) + goto cont; + + debug(ap->logopt, "umount offset %s", oe->key); + + if (umount_autofs_offset(ap, oe)) { + warn(ap->logopt, "failed to umount offset"); + left++; + } +cont: + offset = cache_get_offset(mm_base, + offset, start, &me->multi_list, &pos); + } + + if (!left && me->multi == me) { + struct mapent_cache *mc = me->source->mc; + int status; + + /* + * Special case. + * If we can't umount the root container then we can't + * delete the offsets from the cache and we need to put + * the offset triggers back. + */ + if (is_mounted(_PATH_MOUNTED, path, MNTS_REAL)) { + if (umount_ent(ap, root)) { + if (!mount_multi_triggers(ap, root, me, "/")) + warn(ap->logopt, + "failed to remount offset triggers"); + return left++; + } + } + + /* We're done - clean out the offsets */ + cache_multi_lock(mc); + status = cache_delete_offset_list(mc, me->key); + cache_multi_unlock(mc); + if (status != CHE_OK) + warn(ap->logopt, "couldn't delete offset list"); + } + + return left; +} + diff --git a/modules/lookup_file.c b/modules/lookup_file.c index 1a3ee13..376da2e 100644 --- a/modules/lookup_file.c +++ b/modules/lookup_file.c @@ -113,7 +113,7 @@ int lookup_init(const char *mapfmt, int return 0; } -static int read_one(FILE *f, char *key, unsigned *k_len, char *mapent, unsigned *m_len) +static int read_one(FILE *f, char *key, unsigned int *k_len, char *mapent, unsigned int *m_len) { char *kptr, *p; int mapent_len, key_len; @@ -346,7 +346,8 @@ int lookup_read_master(struct master *ma char *ent; struct stat st; FILE *f; - int entry, path_len, ent_len; + unsigned int path_len, ent_len; + int entry; if (master->recurse) return NSS_STATUS_UNAVAIL; @@ -593,7 +594,8 @@ int lookup_read_map(struct autofs_point char *mapent; struct stat st; FILE *f; - int entry, k_len, m_len; + unsigned int k_len, m_len; + int entry; source = ap->entry->current; ap->entry->current = NULL; @@ -720,7 +722,8 @@ static int lookup_one(struct autofs_poin char mapent[MAPENT_MAX_LEN + 1]; time_t age = time(NULL); FILE *f; - int entry, ret, k_len, m_len; + unsigned int k_len, m_len; + int entry, ret; source = ap->entry->current; ap->entry->current = NULL; @@ -817,7 +820,8 @@ static int lookup_wild(struct autofs_poi char mapent[MAPENT_MAX_LEN + 1]; time_t age = time(NULL); FILE *f; - int entry, ret, k_len, m_len; + unsigned int k_len, m_len; + int entry, ret; source = ap->entry->current; ap->entry->current = NULL; diff --git a/modules/lookup_ldap.c b/modules/lookup_ldap.c index 8c627b4..4959c12 100644 --- a/modules/lookup_ldap.c +++ b/modules/lookup_ldap.c @@ -1146,9 +1146,9 @@ static int read_one_map(struct autofs_po while (e) { char *mapent = NULL; + size_t mapent_len = 0; char *k_val; ber_len_t k_len; - size_t mapent_len; char *s_key; bvKey = ldap_get_values_len(ldap, e, entry); @@ -1388,10 +1388,10 @@ static int lookup_one(struct autofs_poin while (e) { char *mapent = NULL; + size_t mapent_len = 0; char *k_val; ber_len_t k_len; char *s_key; - size_t mapent_len; bvKey = ldap_get_values_len(ldap, e, entry); if (!bvKey || !*bvKey) { diff --git a/modules/lookup_yp.c b/modules/lookup_yp.c index dec4a21..483e586 100644 --- a/modules/lookup_yp.c +++ b/modules/lookup_yp.c @@ -248,7 +248,7 @@ int yp_all_callback(int status, char *yp struct map_source *source = cbdata->source; struct mapent_cache *mc = source->mc; time_t age = cbdata->age; - char *tmp, *key, *mapent; + char *key, *mapent; int ret; if (status != YP_TRUE) diff --git a/modules/mount_autofs.c b/modules/mount_autofs.c index 9bccdd1..c9baf57 100644 --- a/modules/mount_autofs.c +++ b/modules/mount_autofs.c @@ -59,7 +59,6 @@ int mount_mount(struct autofs_point *ap, char buf[MAX_ERR_BUF]; char *options, *p; int ret; - struct list_head *l; fullpath = alloca(strlen(root) + name_len + 2); if (!fullpath) { diff --git a/modules/parse_sun.c b/modules/parse_sun.c index 6007828..0f2c3ab 100644 --- a/modules/parse_sun.c +++ b/modules/parse_sun.c @@ -661,71 +661,6 @@ add_offset_entry(struct autofs_point *ap return ret; } -static int mount_multi_triggers(struct autofs_point *ap, char *root, struct mapent *me, const char *base) -{ - char path[PATH_MAX + 1]; - char *offset = path; - struct mapent *oe; - struct list_head *pos = NULL; - unsigned int fs_path_len; - struct statfs fs; - struct stat st; - unsigned int is_autofs_fs; - int ret, start; - - fs_path_len = strlen(root) + strlen(base); - if (fs_path_len > PATH_MAX) - return 0; - - strcpy(path, root); - strcat(path, base); - ret = statfs(path, &fs); - if (ret == -1) { - /* There's no mount yet - it must be autofs */ - if (errno == ENOENT) - is_autofs_fs = 1; - else - return 0; - } else - is_autofs_fs = fs.f_type == AUTOFS_SUPER_MAGIC ? 1 : 0; - - start = strlen(root); - offset = cache_get_offset(base, offset, start, &me->multi_list, &pos); - while (offset) { - int plen = fs_path_len + strlen(offset); - - if (plen > PATH_MAX) { - warn(ap->logopt, MODPREFIX "path loo long"); - goto cont; - } - - oe = cache_lookup_offset(base, offset, start, &me->multi_list); - if (!oe) - goto cont; - - /* - * If the host filesystem is not an autofs fs - * we require the mount point directory exist - * and that permissions are OK. - */ - if (!is_autofs_fs) { - ret = stat(oe->key, &st); - if (ret == -1) - goto cont; - } - - debug(ap->logopt, MODPREFIX "mount offset %s", oe->key); - - if (mount_autofs_offset(ap, oe, is_autofs_fs) < 0) - warn(ap->logopt, MODPREFIX "failed to mount offset"); -cont: - offset = cache_get_offset(base, - offset, start, &me->multi_list, &pos); - } - - return 1; -} - static int validate_location(char *loc) { char *ptr = loc; @@ -762,7 +697,7 @@ static int parse_mapent(const char *ent, { char buf[MAX_ERR_BUF]; const char *p; - char *tmp, *myoptions, *loc; + char *myoptions, *loc; int l; p = ent; @@ -1040,7 +975,6 @@ int parse_mount(struct autofs_point *ap, /* It's a multi-mount; deal with it */ do { char *path, *myoptions, *loc; - unsigned int s_len = 0; int status; if (*p != '/') { @@ -1272,6 +1206,8 @@ int parse_mount(struct autofs_point *ap, free(loc); free(options); + if (rv) + return rv; /* * If it's a multi-mount insert the triggers * These are always direct mount triggers so root = ""