autofs-5.0.3 - add autofs mount contol library From: Ian Kent This patch adds a library for autofs mount point control functions. It will use either the standard ioctl interface or, if detected, the newer miscellaneous device file ioctl interface. It isolates the use of autofs control functions to one place and provides a common call interface for the daemon to use. In addition it will check to see if the kernel supports the newer miscellaneous device node ioctl interface and uses it if present. --- CHANGELOG | 1 daemon/automount.c | 38 -- daemon/direct.c | 273 ++++++-------- daemon/indirect.c | 139 +++---- daemon/lookup.c | 3 daemon/state.c | 8 include/automount.h | 3 include/dev-ioctl-lib.h | 63 +++ include/linux/auto_dev-ioctl.h | 224 +++++++++++ include/linux/auto_fs4.h | 63 +++ include/mounts.h | 14 + lib/Makefile | 5 lib/dev-ioctl-lib.c | 790 ++++++++++++++++++++++++++++++++++++++++ lib/master_parse.y | 3 lib/mounts.c | 29 + modules/parse_sun.c | 3 redhat/autofs.init.in | 20 + redhat/autofs.sysconfig.in | 5 samples/autofs.conf.default.in | 5 samples/rc.autofs.in | 19 + 20 files changed, 1409 insertions(+), 299 deletions(-) create mode 100644 include/dev-ioctl-lib.h create mode 100644 include/linux/auto_dev-ioctl.h create mode 100644 lib/dev-ioctl-lib.c diff --git a/CHANGELOG b/CHANGELOG index 07feb29..0d0a7c4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -49,6 +49,7 @@ - fix init script stop function. - fix master map lexer eval order. - fix bad alloca usage. +- add miscellaneous device node interface library. 14/01/2008 autofs-5.0.3 ----------------------- diff --git a/daemon/automount.c b/daemon/automount.c index d8e5f68..03e4851 100644 --- a/daemon/automount.c +++ b/daemon/automount.c @@ -595,40 +595,6 @@ int umount_autofs(struct autofs_point *ap, const char *root, int force) return ret; } -int send_ready(unsigned logopt, int ioctlfd, unsigned int wait_queue_token) -{ - char buf[MAX_ERR_BUF]; - - if (wait_queue_token == 0) - return 0; - - debug(logopt, "token = %d", wait_queue_token); - - if (ioctl(ioctlfd, AUTOFS_IOC_READY, wait_queue_token) < 0) { - char *estr = strerror_r(errno, buf, MAX_ERR_BUF); - logerr("AUTOFS_IOC_READY: error %s", estr); - return 1; - } - return 0; -} - -int send_fail(unsigned logopt, int ioctlfd, unsigned int wait_queue_token) -{ - char buf[MAX_ERR_BUF]; - - if (wait_queue_token == 0) - return 0; - - debug(logopt, "token = %d", wait_queue_token); - - if (ioctl(ioctlfd, AUTOFS_IOC_FAIL, wait_queue_token) < 0) { - char *estr = strerror_r(errno, buf, MAX_ERR_BUF); - logerr("AUTOFS_IOC_FAIL: error %s", estr); - return 1; - } - return 0; -} - static size_t get_kpkt_len(void) { size_t pkt_len = sizeof(struct autofs_v5_packet); @@ -2069,6 +2035,8 @@ int main(int argc, char *argv[]) exit(1); } + init_ioctl_ctl(); + if (!alarm_start_handler()) { logerr("%s: failed to create alarm handler thread!", program); master_kill(master_list); @@ -2117,6 +2085,8 @@ int main(int argc, char *argv[]) if (dh) dlclose(dh); #endif + close_ioctl_ctl(); + info(logging, "autofs stopped"); exit(0); diff --git a/daemon/direct.c b/daemon/direct.c index 0fcab7f..99d6d1f 100644 --- a/daemon/direct.c +++ b/daemon/direct.c @@ -85,6 +85,7 @@ static void mnts_cleanup(void *arg) int do_umount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, struct mapent *me) { + struct ioctl_ops *ops = get_ioctl_ops(); char buf[MAX_ERR_BUF]; int ioctlfd, rv, left, retries; @@ -103,23 +104,13 @@ int do_umount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, stru return 1; } ioctlfd = me->ioctlfd; - } else { - int cl_flags; - - ioctlfd = open(me->key, O_RDONLY); - if (ioctlfd != -1) { - if ((cl_flags = fcntl(ioctlfd, F_GETFD, 0)) != -1) { - cl_flags |= FD_CLOEXEC; - fcntl(ioctlfd, F_SETFD, cl_flags); - } - } - } - + } else + ops->open(ap->logopt, &ioctlfd, me->dev, me->key); if (ioctlfd >= 0) { - int status = 1; + unsigned int status = 1; - rv = ioctl(ioctlfd, AUTOFS_IOC_ASKUMOUNT, &status); + rv = ops->askumount(ap->logopt, ioctlfd, &status); if (rv) { char *estr = strerror_r(errno, buf, MAX_ERR_BUF); error(ap->logopt, "ioctl failed: %s", estr); @@ -132,19 +123,17 @@ int do_umount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, stru return 1; } else { me->ioctlfd = -1; - ioctl(ioctlfd, AUTOFS_IOC_CATATONIC, 0); - close(ioctlfd); + ops->catatonic(ap->logopt, ioctlfd); + ops->close(ap->logopt, ioctlfd); goto force_umount; } } me->ioctlfd = -1; - ioctl(ioctlfd, AUTOFS_IOC_CATATONIC, 0); - close(ioctlfd); + ops->catatonic(ap->logopt, ioctlfd); + ops->close(ap->logopt, ioctlfd); } else { - char *estr = strerror_r(errno, buf, MAX_ERR_BUF); error(ap->logopt, "couldn't get ioctl fd for direct mount %s", me->key); - debug(ap->logopt, "open: %s", estr); return 1; } @@ -291,10 +280,11 @@ static int unlink_mount_tree(struct autofs_point *ap, struct list_head *list) int do_mount_autofs_direct(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, cl_flags; + int status, ret, ioctlfd; struct list_head list; const char *map_name; @@ -307,16 +297,9 @@ int do_mount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, struc save_ioctlfd = ioctlfd = me->ioctlfd; - if (ioctlfd == -1) { - ioctlfd = open(me->key, O_RDONLY); - if (ioctlfd != -1) { - cl_flags = fcntl(ioctlfd, F_GETFD, 0); - if (cl_flags != -1) { - cl_flags |= FD_CLOEXEC; - fcntl(ioctlfd, F_SETFD, cl_flags); - } - } - } + if (ioctlfd == -1) + ops->open(ap->logopt, + &ioctlfd, me->dev, me->key); if (ioctlfd < 0) { error(ap->logopt, @@ -325,13 +308,14 @@ int do_mount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, struc return 0; } - ioctl(ioctlfd, AUTOFS_IOC_SETTIMEOUT, &tout); + ops->timeout(ap->logopt, ioctlfd, &tout); if (save_ioctlfd == -1) - close(ioctlfd); + ops->close(ap->logopt, ioctlfd); return 0; } + if (!unlink_mount_tree(ap, &list)) { debug(ap->logopt, "already mounted as other than autofs " @@ -396,22 +380,23 @@ int do_mount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, struc goto out_err; } - /* Root directory for ioctl()'s */ - ioctlfd = open(me->key, O_RDONLY); - if (ioctlfd < 0) { - crit(ap->logopt, "failed to create ioctl fd for %s", me->key); + ret = stat(me->key, &st); + if (ret == -1) { + error(ap->logopt, + "failed to stat direct mount trigger %s", me->key); goto out_umount; } - if ((cl_flags = fcntl(ioctlfd, F_GETFD, 0)) != -1) { - cl_flags |= FD_CLOEXEC; - fcntl(ioctlfd, F_SETFD, cl_flags); + ops->open(ap->logopt, &ioctlfd, st.st_dev, me->key); + if (ioctlfd < 0) { + crit(ap->logopt, "failed to create ioctl fd for %s", me->key); + goto out_umount; } /* Calculate the timeouts */ ap->exp_runfreq = (timeout + CHECK_RATIO - 1) / CHECK_RATIO; - ioctl(ioctlfd, AUTOFS_IOC_SETTIMEOUT, &timeout); + ops->timeout(ap->logopt, ioctlfd, &timeout); if (ap->exp_timeout) info(ap->logopt, @@ -424,22 +409,14 @@ int do_mount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, struc "mounted direct mount on %s with timeouts disabled", me->key); - ret = fstat(ioctlfd, &st); - if (ret == -1) { - error(ap->logopt, - "failed to stat direct mount trigger %s", me->key); - goto out_close; - } cache_set_ino_index(me->mc, me->key, st.st_dev, st.st_ino); - close(ioctlfd); + ops->close(ap->logopt, ioctlfd); debug(ap->logopt, "mounted trigger %s", me->key); return 0; -out_close: - close(ioctlfd); out_umount: /* TODO: maybe force umount (-l) */ umount(me->key); @@ -531,8 +508,9 @@ int mount_autofs_direct(struct autofs_point *ap) int umount_autofs_offset(struct autofs_point *ap, struct mapent *me) { + struct ioctl_ops *ops = get_ioctl_ops(); char buf[MAX_ERR_BUF]; - int ioctlfd, cl_flags, rv = 1, retries; + int ioctlfd, rv = 1, retries; if (me->ioctlfd != -1) { if (is_mounted(_PATH_MOUNTED, me->key, MNTS_REAL)) { @@ -549,20 +527,13 @@ int umount_autofs_offset(struct autofs_point *ap, struct mapent *me) me->key); return 0; } - - ioctlfd = open(me->key, O_RDONLY); - if (ioctlfd != -1) { - if ((cl_flags = fcntl(ioctlfd, F_GETFD, 0)) != -1) { - cl_flags |= FD_CLOEXEC; - fcntl(ioctlfd, F_SETFD, cl_flags); - } - } + ops->open(ap->logopt, &ioctlfd, me->dev, me->key); } if (ioctlfd >= 0) { - int status = 1; + unsigned int status = 1; - rv = ioctl(ioctlfd, AUTOFS_IOC_ASKUMOUNT, &status); + rv = ops->askumount(ap->logopt, ioctlfd, &status); if (rv) { char *estr = strerror_r(errno, buf, MAX_ERR_BUF); logerr("ioctl failed: %s", estr); @@ -576,14 +547,14 @@ int umount_autofs_offset(struct autofs_point *ap, struct mapent *me) return 1; } else { me->ioctlfd = -1; - ioctl(ioctlfd, AUTOFS_IOC_CATATONIC, 0); - close(ioctlfd); + ops->catatonic(ap->logopt, ioctlfd); + ops->close(ap->logopt, ioctlfd); goto force_umount; } } me->ioctlfd = -1; - ioctl(ioctlfd, AUTOFS_IOC_CATATONIC, 0); - close(ioctlfd); + ops->catatonic(ap->logopt, ioctlfd); + ops->close(ap->logopt, ioctlfd); } else { struct stat st; char *estr; @@ -648,11 +619,12 @@ force_umount: int mount_autofs_offset(struct autofs_point *ap, struct mapent *me, const char *root, const char *offset) { + struct ioctl_ops *ops = get_ioctl_ops(); char buf[MAX_ERR_BUF]; struct mnt_params *mp; time_t timeout = ap->exp_timeout; struct stat st; - int ioctlfd, cl_flags, status, ret; + int ioctlfd, status, ret; const char *type, *map_name = NULL; char mountpoint[PATH_MAX]; @@ -765,37 +737,27 @@ int mount_autofs_offset(struct autofs_point *ap, struct mapent *me, const char * goto out_err; } - /* Root directory for ioctl()'s */ - ioctlfd = open(mountpoint, O_RDONLY); - if (ioctlfd < 0) { - crit(ap->logopt, "failed to create ioctl fd for %s", mountpoint); - goto out_umount; - } - - if ((cl_flags = fcntl(ioctlfd, F_GETFD, 0)) != -1) { - cl_flags |= FD_CLOEXEC; - fcntl(ioctlfd, F_SETFD, cl_flags); - } - - ioctl(ioctlfd, AUTOFS_IOC_SETTIMEOUT, &timeout); - - ret = fstat(ioctlfd, &st); + ret = stat(mountpoint, &st); if (ret == -1) { error(ap->logopt, "failed to stat direct mount trigger %s", mountpoint); - goto out_close; + goto out_umount; } - cache_set_ino_index(me->mc, me->key, st.st_dev, st.st_ino); + ops->open(ap->logopt, &ioctlfd, st.st_dev, mountpoint); + if (ioctlfd < 0) { + crit(ap->logopt, "failed to create ioctl fd for %s", mountpoint); + goto out_umount; + } - close(ioctlfd); + ops->timeout(ap->logopt, ioctlfd, &timeout); + 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 at %s", me->key, mountpoint); return MOUNT_OFFSET_OK; -out_close: - close(ioctlfd); out_umount: umount(mountpoint); out_err: @@ -805,43 +767,9 @@ out_err: return MOUNT_OFFSET_FAIL; } -static int expire_direct(int ioctlfd, const char *path, unsigned int when, unsigned int logopt) -{ - int ret, retries = EXPIRE_RETRIES; - - while (retries--) { - struct timespec tm = {0, 100000000}; - - /* Ggenerate expire message for the mount. */ - ret = ioctl(ioctlfd, AUTOFS_IOC_EXPIRE_DIRECT, &when); - if (ret == -1) { - /* Mount has gone away */ - if (errno == EBADF || errno == EINVAL) - return 1; - - /* - * Other than EAGAIN is an expire error so continue. - * Kernel try the same mount again, limited by - * retries (ie. number of mounts directly under - * mount point, should always be one for direct - * mounts). - */ - if (errno == EAGAIN) - break; - } - nanosleep(&tm, NULL); - } - - if (!ioctl(ioctlfd, AUTOFS_IOC_ASKUMOUNT, &ret)) { - if (!ret) - return 0; - } - - return 1; -} - void *expire_proc_direct(void *arg) { + struct ioctl_ops *ops = get_ioctl_ops(); struct mnt_list *mnts = NULL, *next; struct list_head list, *p; struct expire_args *ea; @@ -940,28 +868,33 @@ void *expire_proc_direct(void *arg) */ /* Offsets always have a real mount at their base */ + cache_writelock(me->mc); if (strstr(next->opts, "offset")) { - close(me->ioctlfd); + ops->close(ap->logopt, me->ioctlfd); me->ioctlfd = -1; + cache_unlock(me->mc); pthread_setcancelstate(cur_state, NULL); continue; } + cache_unlock(me->mc); ioctlfd = me->ioctlfd; - ret = expire_direct(ioctlfd, next->path, now, ap->logopt); - if (!ret) { + ret = ops->expire(ap->logopt, ioctlfd, next->path, now); + if (ret) { left++; pthread_setcancelstate(cur_state, NULL); continue; } + cache_writelock(me->mc); if (me->ioctlfd != -1 && fstat(ioctlfd, &st) != -1 && !count_mounts(ap->logopt, next->path, st.st_dev)) { - close(ioctlfd); + ops->close(ap->logopt, ioctlfd); me->ioctlfd = -1; } + cache_unlock(me->mc); pthread_setcancelstate(cur_state, NULL); continue; @@ -982,8 +915,8 @@ void *expire_proc_direct(void *arg) debug(ap->logopt, "send expire to trigger %s", next->path); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state); - ret = expire_direct(ioctlfd, next->path, now, ap->logopt); - if (!ret) + ret = ops->expire(ap->logopt, ioctlfd, next->path, now); + if (ret) left++; pthread_setcancelstate(cur_state, NULL); } @@ -1014,8 +947,11 @@ static void pending_cond_destroy(void *arg) static void expire_send_fail(void *arg) { + struct ioctl_ops *ops = get_ioctl_ops(); struct pending_args *mt = arg; - send_fail(mt->ap->logopt, mt->ioctlfd, mt->wait_queue_token); + struct autofs_point *ap = mt->ap; + ops->send_fail(ap->logopt, + mt->ioctlfd, mt->wait_queue_token, -ENOENT); } static void free_pending_args(void *arg) @@ -1033,6 +969,7 @@ static void expire_mutex_unlock(void *arg) static void *do_expire_direct(void *arg) { + struct ioctl_ops *ops = get_ioctl_ops(); struct pending_args *args, mt; struct autofs_point *ap; size_t len; @@ -1067,15 +1004,16 @@ static void *do_expire_direct(void *arg) status = do_expire(ap, mt.name, len); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); if (status) - send_fail(ap->logopt, mt.ioctlfd, mt.wait_queue_token); + ops->send_fail(ap->logopt, + mt.ioctlfd, mt.wait_queue_token, -ENOENT); else { struct mapent *me; - cache_readlock(mt.mc); + cache_writelock(mt.mc); me = cache_lookup_distinct(mt.mc, mt.name); me->ioctlfd = -1; cache_unlock(mt.mc); - send_ready(ap->logopt, mt.ioctlfd, mt.wait_queue_token); - close(mt.ioctlfd); + ops->send_ready(ap->logopt, mt.ioctlfd, mt.wait_queue_token); + ops->close(ap->logopt, mt.ioctlfd); } pthread_setcancelstate(state, NULL); @@ -1086,6 +1024,7 @@ static void *do_expire_direct(void *arg) int handle_packet_expire_direct(struct autofs_point *ap, autofs_packet_expire_direct_t *pkt) { + struct ioctl_ops *ops = get_ioctl_ops(); struct map_source *map; struct mapent_cache *mc = NULL; struct mapent *me = NULL; @@ -1134,15 +1073,16 @@ int handle_packet_expire_direct(struct autofs_point *ap, autofs_packet_expire_di /* Can't expire it if it isn't mounted */ if (me->ioctlfd == -1) { - int ioctlfd = open(me->key, O_RDONLY); + int ioctlfd; + ops->open(ap->logopt, &ioctlfd, me->dev, me->key); if (ioctlfd == -1) { crit(ap->logopt, "can't open ioctlfd for %s", me->key); pthread_setcancelstate(state, NULL); return 1; } - send_ready(ap->logopt, ioctlfd, pkt->wait_queue_token); - close(ioctlfd); + ops->send_ready(ap->logopt, ioctlfd, pkt->wait_queue_token); + ops->close(ap->logopt, ioctlfd); cache_unlock(mc); master_source_unlock(ap->entry); pthread_setcancelstate(state, NULL); @@ -1153,7 +1093,8 @@ int handle_packet_expire_direct(struct autofs_point *ap, autofs_packet_expire_di if (!mt) { char *estr = strerror_r(errno, buf, MAX_ERR_BUF); error(ap->logopt, "malloc: %s", estr); - send_fail(ap->logopt, me->ioctlfd, pkt->wait_queue_token); + ops->send_fail(ap->logopt, + me->ioctlfd, pkt->wait_queue_token, -ENOMEM); cache_unlock(mc); master_source_unlock(ap->entry); pthread_setcancelstate(state, NULL); @@ -1183,7 +1124,8 @@ int handle_packet_expire_direct(struct autofs_point *ap, autofs_packet_expire_di status = pthread_create(&thid, &thread_attr, do_expire_direct, mt); if (status) { error(ap->logopt, "expire thread create failed"); - send_fail(ap->logopt, mt->ioctlfd, pkt->wait_queue_token); + ops->send_fail(ap->logopt, + mt->ioctlfd, pkt->wait_queue_token, -status); cache_unlock(mc); master_source_unlock(ap->entry); expire_mutex_unlock(NULL); @@ -1220,9 +1162,11 @@ int handle_packet_expire_direct(struct autofs_point *ap, autofs_packet_expire_di static void mount_send_fail(void *arg) { + struct ioctl_ops *ops = get_ioctl_ops(); struct pending_args *mt = arg; - send_fail(mt->ap->logopt, mt->ioctlfd, mt->wait_queue_token); - close(mt->ioctlfd); + struct autofs_point *ap = mt->ap; + ops->send_fail(ap->logopt, mt->ioctlfd, mt->wait_queue_token, -ENOENT); + ops->close(ap->logopt, mt->ioctlfd); } static void pending_mutex_destroy(void *arg) @@ -1243,6 +1187,7 @@ static void mount_mutex_unlock(void *arg) static void *do_mount_direct(void *arg) { + struct ioctl_ops *ops = get_ioctl_ops(); struct pending_args *args, mt; struct autofs_point *ap; struct stat st; @@ -1273,9 +1218,9 @@ static void *do_mount_direct(void *arg) if (status == -1) { error(ap->logopt, "can't stat direct mount trigger %s", mt.name); - send_fail(ap->logopt, - mt.ioctlfd, mt.wait_queue_token); - close(mt.ioctlfd); + ops->send_fail(ap->logopt, + mt.ioctlfd, mt.wait_queue_token, -ENOENT); + ops->close(ap->logopt, mt.ioctlfd); pthread_setcancelstate(state, NULL); pthread_exit(NULL); } @@ -1285,8 +1230,8 @@ static void *do_mount_direct(void *arg) error(ap->logopt, "direct trigger not valid or already mounted %s", mt.name); - send_ready(ap->logopt, mt.ioctlfd, mt.wait_queue_token); - close(mt.ioctlfd); + ops->send_ready(ap->logopt, mt.ioctlfd, mt.wait_queue_token); + ops->close(ap->logopt, mt.ioctlfd); pthread_setcancelstate(state, NULL); pthread_exit(NULL); } @@ -1324,14 +1269,16 @@ static void *do_mount_direct(void *arg) if (!close_fd) me->ioctlfd = mt.ioctlfd; } - send_ready(ap->logopt, mt.ioctlfd, mt.wait_queue_token); + ops->send_ready(ap->logopt, mt.ioctlfd, mt.wait_queue_token); cache_unlock(mt.mc); if (close_fd) - close(mt.ioctlfd); + ops->close(ap->logopt, mt.ioctlfd); info(ap->logopt, "mounted %s", mt.name); } else { - send_fail(ap->logopt, mt.ioctlfd, mt.wait_queue_token); - close(mt.ioctlfd); + /* TODO: get mount return status from lookup_nss_mount */ + ops->send_fail(ap->logopt, + mt.ioctlfd, mt.wait_queue_token, -ENOENT); + ops->close(ap->logopt, mt.ioctlfd); info(ap->logopt, "failed to mount %s", mt.name); } pthread_setcancelstate(state, NULL); @@ -1343,6 +1290,7 @@ static void *do_mount_direct(void *arg) int handle_packet_missing_direct(struct autofs_point *ap, autofs_packet_missing_direct_t *pkt) { + struct ioctl_ops *ops = get_ioctl_ops(); struct map_source *map; struct mapent_cache *mc = NULL; struct mapent *me = NULL; @@ -1352,7 +1300,7 @@ int handle_packet_missing_direct(struct autofs_point *ap, autofs_packet_missing_ int status = 0; struct timespec wait; struct timeval now; - int ioctlfd, len, cl_flags, state; + int ioctlfd, len, state; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); @@ -1402,7 +1350,7 @@ int handle_packet_missing_direct(struct autofs_point *ap, autofs_packet_missing_ ioctlfd = me->ioctlfd; me->ioctlfd = -1; } else - ioctlfd = open(me->key, O_RDONLY); + ops->open(ap->logopt, &ioctlfd, me->dev, me->key); if (ioctlfd == -1) { cache_unlock(mc); @@ -1413,18 +1361,14 @@ int handle_packet_missing_direct(struct autofs_point *ap, autofs_packet_missing_ return 1; } - if ((cl_flags = fcntl(ioctlfd, F_GETFD, 0)) != -1) { - cl_flags |= FD_CLOEXEC; - fcntl(ioctlfd, F_SETFD, cl_flags); - } - debug(ap->logopt, "token %ld, name %s, request pid %u", (unsigned long) pkt->wait_queue_token, me->key, pkt->pid); /* Ignore packet if we're trying to shut down */ if (ap->shutdown || ap->state == ST_SHUTDOWN_FORCE) { - send_fail(ap->logopt, ioctlfd, pkt->wait_queue_token); - close(ioctlfd); + ops->send_fail(ap->logopt, + ioctlfd, pkt->wait_queue_token, -ENOENT); + ops->close(ap->logopt, ioctlfd); cache_unlock(mc); master_source_unlock(ap->entry); pthread_setcancelstate(state, NULL); @@ -1434,8 +1378,9 @@ int handle_packet_missing_direct(struct autofs_point *ap, autofs_packet_missing_ len = strlen(me->key); if (len >= PATH_MAX) { error(ap->logopt, "direct mount path too long %s", me->key); - send_fail(ap->logopt, ioctlfd, pkt->wait_queue_token); - close(ioctlfd); + ops->send_fail(ap->logopt, + ioctlfd, pkt->wait_queue_token, -ENAMETOOLONG); + ops->close(ap->logopt, ioctlfd); cache_unlock(mc); master_source_unlock(ap->entry); pthread_setcancelstate(state, NULL); @@ -1446,8 +1391,9 @@ int handle_packet_missing_direct(struct autofs_point *ap, autofs_packet_missing_ if (!mt) { char *estr = strerror_r(errno, buf, MAX_ERR_BUF); error(ap->logopt, "malloc: %s", estr); - send_fail(ap->logopt, ioctlfd, pkt->wait_queue_token); - close(ioctlfd); + ops->send_fail(ap->logopt, + ioctlfd, pkt->wait_queue_token, -ENOMEM); + ops->close(ap->logopt, ioctlfd); cache_unlock(mc); master_source_unlock(ap->entry); pthread_setcancelstate(state, NULL); @@ -1481,8 +1427,9 @@ int handle_packet_missing_direct(struct autofs_point *ap, autofs_packet_missing_ status = pthread_create(&thid, &thread_attr, do_mount_direct, mt); if (status) { error(ap->logopt, "missing mount thread create failed"); - send_fail(ap->logopt, ioctlfd, pkt->wait_queue_token); - close(ioctlfd); + ops->send_fail(ap->logopt, + ioctlfd, pkt->wait_queue_token, -status); + ops->close(ap->logopt, ioctlfd); cache_unlock(mc); master_source_unlock(ap->entry); mount_mutex_unlock(mt); diff --git a/daemon/indirect.c b/daemon/indirect.c index 90f8f63..4f75a4f 100644 --- a/daemon/indirect.c +++ b/daemon/indirect.c @@ -85,19 +85,20 @@ 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) { + struct ioctl_ops *ops = get_ioctl_ops(); time_t timeout = ap->exp_timeout; char *options = NULL; const char *type, *map_name = NULL; struct stat st; struct mnt_list *mnts; - int cl_flags, ret; + 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) { - debug(ap->logopt, + error(ap->logopt, "already mounted as other than autofs " "or failed to unlink entry in tree"); goto out_err; @@ -105,8 +106,10 @@ static int do_mount_autofs_indirect(struct autofs_point *ap, const char *root) } options = make_options_string(ap->path, ap->kpipefd, NULL); - if (!options) + if (!options) { + error(ap->logopt, "options string error"); goto out_err; + } /* In case the directory doesn't exist, try to mkdir it */ if (mkdir_path(root, 0555) < 0) { @@ -143,24 +146,25 @@ static int do_mount_autofs_indirect(struct autofs_point *ap, const char *root) free(options); + ret = stat(root, &st); + if (ret == -1) { + crit(ap->logopt, + "failed to stat mount for autofs path %s", ap->path); + goto out_umount; + } + options = NULL; - /* Root directory for ioctl()'s */ - ap->ioctlfd = open(root, O_RDONLY); - if (ap->ioctlfd < 0) { + if (ops->open(ap->logopt, &ap->ioctlfd, st.st_dev, root)) { crit(ap->logopt, "failed to create ioctl fd for autofs path %s", ap->path); goto out_umount; } - if ((cl_flags = fcntl(ap->ioctlfd, F_GETFD, 0)) != -1) { - cl_flags |= FD_CLOEXEC; - fcntl(ap->ioctlfd, F_SETFD, cl_flags); - } - + ap->dev = st.st_dev; /* Device number for mount point checks */ ap->exp_runfreq = (timeout + CHECK_RATIO - 1) / CHECK_RATIO; - ioctl(ap->ioctlfd, AUTOFS_IOC_SETTIMEOUT, &timeout); + ops->timeout(ap->logopt, ap->ioctlfd, &timeout); if (ap->exp_timeout) info(ap->logopt, @@ -173,9 +177,6 @@ static int do_mount_autofs_indirect(struct autofs_point *ap, const char *root) "mounted indirect mount for %s with timeouts disabled", ap->path); - fstat(ap->ioctlfd, &st); - ap->dev = st.st_dev; /* Device number for mount point checks */ - return 0; out_umount: @@ -258,9 +259,11 @@ void close_mount_fds(struct autofs_point *ap) int umount_autofs_indirect(struct autofs_point *ap, const char *root) { + struct ioctl_ops *ops = get_ioctl_ops(); char buf[MAX_ERR_BUF]; char mountpoint[PATH_MAX + 1]; - int ret, rv, retries; + int rv, retries; + unsigned int unused; if (root) strcpy(mountpoint, root); @@ -268,12 +271,12 @@ int umount_autofs_indirect(struct autofs_point *ap, const char *root) strcpy(mountpoint, ap->path); /* If we are trying to shutdown make sure we can umount */ - rv = ioctl(ap->ioctlfd, AUTOFS_IOC_ASKUMOUNT, &ret); + rv = ops->askumount(ap->logopt, ap->ioctlfd, &unused); if (rv == -1) { char *estr = strerror_r(errno, buf, MAX_ERR_BUF); logerr("ioctl failed: %s", estr); return 1; - } else if (!ret) { + } else if (!unused) { #if defined(ENABLE_IGNORE_BUSY_MOUNTS) || defined(ENABLE_FORCED_SHUTDOWN) if (!ap->shutdown) return 1; @@ -284,9 +287,9 @@ int umount_autofs_indirect(struct autofs_point *ap, const char *root) } if (ap->shutdown) - ioctl(ap->ioctlfd, AUTOFS_IOC_CATATONIC, 0); + ops->catatonic(ap->logopt, ap->ioctlfd); - close(ap->ioctlfd); + ops->close(ap->logopt, ap->ioctlfd); ap->ioctlfd = -1; sched_yield(); @@ -314,7 +317,6 @@ int umount_autofs_indirect(struct autofs_point *ap, const char *root) close_mount_fds(ap); goto force_umount; } else { - int cl_flags; /* * If the umount returns EBUSY there may be * a mount request in progress so we need to @@ -328,7 +330,8 @@ int umount_autofs_indirect(struct autofs_point *ap, const char *root) return 0; } #endif - ap->ioctlfd = open(mountpoint, O_RDONLY); + ops->open(ap->logopt, + &ap->ioctlfd, ap->dev, mountpoint); if (ap->ioctlfd < 0) { warn(ap->logopt, "could not recover autofs path %s", @@ -336,11 +339,6 @@ int umount_autofs_indirect(struct autofs_point *ap, const char *root) close_mount_fds(ap); return 0; } - - if ((cl_flags = fcntl(ap->ioctlfd, F_GETFD, 0)) != -1) { - cl_flags |= FD_CLOEXEC; - fcntl(ap->ioctlfd, F_SETFD, cl_flags); - } } break; case ENOTDIR: @@ -373,38 +371,6 @@ force_umount: return rv; } -static int expire_indirect(struct autofs_point *ap, int ioctlfd, const char *path, unsigned int when) -{ - int ret, retries = EXPIRE_RETRIES; - - while (retries--) { - struct timespec tm = {0, 100000000}; - - /* Ggenerate expire message for the mount. */ - ret = ioctl(ioctlfd, AUTOFS_IOC_EXPIRE_DIRECT, &when); - if (ret == -1) { - /* Mount has gone away */ - if (errno == EBADF || errno == EINVAL) - return 1; - - /* - * Other than EAGAIN is an expire error so continue. - * Kernel will try the next mount. - */ - if (errno == EAGAIN) - break; - } - nanosleep(&tm, NULL); - } - - if (!ioctl(ioctlfd, AUTOFS_IOC_ASKUMOUNT, &ret)) { - if (!ret) - return 0; - } - - return 1; -} - static void mnts_cleanup(void *arg) { struct mnt_list *mnts = (struct mnt_list *) arg; @@ -414,6 +380,7 @@ static void mnts_cleanup(void *arg) void *expire_proc_indirect(void *arg) { + struct ioctl_ops *ops = get_ioctl_ops(); struct autofs_point *ap; struct mapent *me = NULL; struct mnt_list *mnts = NULL, *next; @@ -504,8 +471,8 @@ void *expire_proc_indirect(void *arg) debug(ap->logopt, "expire %s", next->path); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state); - ret = expire_indirect(ap, ioctlfd, next->path, now); - if (!ret) + ret = ops->expire(ap->logopt, ioctlfd, next->path, now); + if (ret) left++; pthread_setcancelstate(cur_state, NULL); } @@ -520,8 +487,8 @@ void *expire_proc_indirect(void *arg) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state); retries = (count_mounts(ap->logopt, ap->path, ap->dev) + 1); while (retries--) { - ret = expire_indirect(ap, ap->ioctlfd, ap->path, now); - if (!ret) + ret = ops->expire(ap->logopt, ap->ioctlfd, ap->path, now); + if (ret) left++; } pthread_setcancelstate(cur_state, NULL); @@ -545,8 +512,8 @@ void *expire_proc_indirect(void *arg) pthread_cleanup_pop(1); if (submnts) - debug(ap->logopt, - "%d submounts remaining in %s", submnts, ap->path); + info(ap->logopt, + "%d submounts remaining in %s", submnts, ap->path); /* * EXPIRE_MULTI is synchronous, so we can be sure (famous last @@ -577,8 +544,11 @@ static void pending_cond_destroy(void *arg) static void expire_send_fail(void *arg) { + struct ioctl_ops *ops = get_ioctl_ops(); struct pending_args *mt = arg; - send_fail(mt->ap->logopt, mt->ap->ioctlfd, mt->wait_queue_token); + struct autofs_point *ap = mt->ap; + ops->send_fail(ap->logopt, + ap->ioctlfd, mt->wait_queue_token, -ENOENT); } static void free_pending_args(void *arg) @@ -596,6 +566,7 @@ static void expire_mutex_unlock(void *arg) static void *do_expire_indirect(void *arg) { + struct ioctl_ops *ops = get_ioctl_ops(); struct pending_args *args, mt; struct autofs_point *ap; int status, state; @@ -622,9 +593,11 @@ static void *do_expire_indirect(void *arg) status = do_expire(mt.ap, mt.name, mt.len); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); if (status) - send_fail(ap->logopt, ap->ioctlfd, mt.wait_queue_token); + ops->send_fail(ap->logopt, + ap->ioctlfd, mt.wait_queue_token, -status); else - send_ready(ap->logopt, ap->ioctlfd, mt.wait_queue_token); + ops->send_ready(ap->logopt, + ap->ioctlfd, mt.wait_queue_token); pthread_setcancelstate(state, NULL); pthread_cleanup_pop(0); @@ -634,6 +607,7 @@ static void *do_expire_indirect(void *arg) int handle_packet_expire_indirect(struct autofs_point *ap, autofs_packet_expire_indirect_t *pkt) { + struct ioctl_ops *ops = get_ioctl_ops(); struct pending_args *mt; char buf[MAX_ERR_BUF]; pthread_t thid; @@ -650,7 +624,8 @@ int handle_packet_expire_indirect(struct autofs_point *ap, autofs_packet_expire_ if (!mt) { char *estr = strerror_r(errno, buf, MAX_ERR_BUF); logerr("malloc: %s", estr); - send_fail(ap->logopt, ap->ioctlfd, pkt->wait_queue_token); + ops->send_fail(ap->logopt, + ap->ioctlfd, pkt->wait_queue_token, -ENOMEM); pthread_setcancelstate(state, NULL); return 1; } @@ -672,7 +647,8 @@ int handle_packet_expire_indirect(struct autofs_point *ap, autofs_packet_expire_ status = pthread_create(&thid, &thread_attr, do_expire_indirect, mt); if (status) { error(ap->logopt, "expire thread create failed"); - send_fail(ap->logopt, ap->ioctlfd, pkt->wait_queue_token); + ops->send_fail(ap->logopt, + ap->ioctlfd, pkt->wait_queue_token, -status); expire_mutex_unlock(NULL); pending_cond_destroy(mt); free_pending_args(mt); @@ -704,8 +680,11 @@ int handle_packet_expire_indirect(struct autofs_point *ap, autofs_packet_expire_ static void mount_send_fail(void *arg) { + struct ioctl_ops *ops = get_ioctl_ops(); struct pending_args *mt = arg; - send_fail(mt->ap->logopt, mt->ap->ioctlfd, mt->wait_queue_token); + struct autofs_point *ap = mt->ap; + ops->send_fail(ap->logopt, + ap->ioctlfd, mt->wait_queue_token, -ENOENT); } static void pending_mutex_destroy(void *arg) @@ -726,6 +705,7 @@ static void mount_mutex_unlock(void *arg) static void *do_mount_indirect(void *arg) { + struct ioctl_ops *ops = get_ioctl_ops(); struct pending_args *args, mt; struct autofs_point *ap; char buf[PATH_MAX + 1]; @@ -777,10 +757,13 @@ static void *do_mount_indirect(void *arg) status = lookup_nss_mount(ap, NULL, mt.name, mt.len); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); if (status) { - send_ready(ap->logopt, ap->ioctlfd, mt.wait_queue_token); + ops->send_ready(ap->logopt, + ap->ioctlfd, mt.wait_queue_token); info(ap->logopt, "mounted %s", buf); } else { - send_fail(ap->logopt, ap->ioctlfd, mt.wait_queue_token); + /* TODO: get mount return status from lookup_nss_mount */ + ops->send_fail(ap->logopt, + ap->ioctlfd, mt.wait_queue_token, -ENOENT); info(ap->logopt, "failed to mount %s", buf); } pthread_setcancelstate(state, NULL); @@ -792,6 +775,7 @@ static void *do_mount_indirect(void *arg) int handle_packet_missing_indirect(struct autofs_point *ap, autofs_packet_missing_indirect_t *pkt) { + struct ioctl_ops *ops = get_ioctl_ops(); pthread_t thid; char buf[MAX_ERR_BUF]; struct pending_args *mt; @@ -806,7 +790,8 @@ int handle_packet_missing_indirect(struct autofs_point *ap, autofs_packet_missin /* Ignore packet if we're trying to shut down */ if (ap->shutdown || ap->state == ST_SHUTDOWN_FORCE) { - send_fail(ap->logopt, ap->ioctlfd, pkt->wait_queue_token); + ops->send_fail(ap->logopt, + ap->ioctlfd, pkt->wait_queue_token, -ENOENT); pthread_setcancelstate(state, NULL); return 0; } @@ -815,7 +800,8 @@ int handle_packet_missing_indirect(struct autofs_point *ap, autofs_packet_missin if (!mt) { char *estr = strerror_r(errno, buf, MAX_ERR_BUF); logerr("malloc: %s", estr); - send_fail(ap->logopt, ap->ioctlfd, pkt->wait_queue_token); + ops->send_fail(ap->logopt, + ap->ioctlfd, pkt->wait_queue_token, -ENOMEM); pthread_setcancelstate(state, NULL); return 1; } @@ -845,7 +831,8 @@ int handle_packet_missing_indirect(struct autofs_point *ap, autofs_packet_missin status = pthread_create(&thid, &thread_attr, do_mount_indirect, mt); if (status) { error(ap->logopt, "expire thread create failed"); - send_fail(ap->logopt, ap->ioctlfd, pkt->wait_queue_token); + ops->send_fail(ap->logopt, + ap->ioctlfd, pkt->wait_queue_token, -status); mount_mutex_unlock(mt); pending_cond_destroy(mt); pending_mutex_destroy(mt); diff --git a/daemon/lookup.c b/daemon/lookup.c index 49030e1..797640f 100644 --- a/daemon/lookup.c +++ b/daemon/lookup.c @@ -1149,7 +1149,8 @@ int lookup_source_close_ioctlfd(struct autofs_point *ap, const char *key) me = cache_lookup_distinct(mc, key); if (me) { if (me->ioctlfd != -1) { - close(me->ioctlfd); + struct ioctl_ops *ops = get_ioctl_ops(); + ops->close(ap->logopt, me->ioctlfd); me->ioctlfd = -1; } cache_unlock(mc); diff --git a/daemon/state.c b/daemon/state.c index 1915a9f..3766649 100644 --- a/daemon/state.c +++ b/daemon/state.c @@ -93,6 +93,7 @@ void nextstate(int statefd, enum states next) */ void expire_cleanup(void *arg) { + struct ioctl_ops *ops = get_ioctl_ops(); pthread_t thid = pthread_self(); struct expire_args *ec; struct autofs_point *ap; @@ -111,7 +112,8 @@ void expire_cleanup(void *arg) /* Check to see if expire process finished */ if (thid == ap->exp_thread) { - int rv, idle; + unsigned int idle; + int rv; ap->exp_thread = 0; @@ -133,7 +135,7 @@ void expire_cleanup(void *arg) * allowing it to shutdown. */ if (ap->submount && !success) { - rv = ioctl(ap->ioctlfd, AUTOFS_IOC_ASKUMOUNT, &idle); + rv = ops->askumount(ap->logopt, ap->ioctlfd, &idle); if (!rv && idle && ap->submount > 1) { next = ST_SHUTDOWN_PENDING; break; @@ -158,7 +160,7 @@ void expire_cleanup(void *arg) * shutdown return to ready state unless we have * been signaled to shutdown. */ - rv = ioctl(ap->ioctlfd, AUTOFS_IOC_ASKUMOUNT, &idle); + rv = ops->askumount(ap->logopt, ap->ioctlfd, &idle); if (!idle && !ap->shutdown) { next = ST_READY; if (!ap->submount) diff --git a/include/automount.h b/include/automount.h index 61cdac6..c9f6bc1 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 "dev-ioctl-lib.h" #ifdef WITH_DMALLOC #include @@ -422,8 +423,6 @@ struct autofs_point { void *handle_mounts(void *arg); int umount_multi(struct autofs_point *ap, const char *path, int incl); -int send_ready(unsigned logopt, int ioctlfd, unsigned int wait_queue_token); -int send_fail(unsigned logopt, int ioctlfd, unsigned int wait_queue_token); int do_expire(struct autofs_point *ap, const char *name, int namelen); void *expire_proc_indirect(void *); void *expire_proc_direct(void *); diff --git a/include/dev-ioctl-lib.h b/include/dev-ioctl-lib.h new file mode 100644 index 0000000..b7b8211 --- /dev/null +++ b/include/dev-ioctl-lib.h @@ -0,0 +1,63 @@ +/* ----------------------------------------------------------------------- * + * + * dev-ioctl-lib.h - autofs device control. + * + * Copyright 2008 Red Hat, Inc. All rights reserved. + * Copyright 2008 Ian Kent + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ----------------------------------------------------------------------- */ + +#ifndef AUTOFS_DEV_IOCTL_LIB_H +#define AUTOFS_DEV_IOCTL_LIB_H + +#include +#include "linux/auto_dev-ioctl.h" + +#define CONTROL_DEVICE "/dev/autofs" + +#define DEV_IOCTL_IS_MOUNTED 0x0001 +#define DEV_IOCTL_IS_AUTOFS 0x0002 +#define DEV_IOCTL_IS_OTHER 0x0004 + +struct ioctl_ctl { + int devfd; + struct ioctl_ops *ops; +}; + +struct ioctl_ops { + int (*version)(unsigned int, int, struct autofs_dev_ioctl *); + int (*protover)(unsigned int, int, unsigned int *); + int (*protosubver)(unsigned int, int, unsigned int *); + int (*mount_device)(unsigned int, const char *, unsigned int, dev_t *); + int (*open)(unsigned int, int *, dev_t, const char *); + int (*close)(unsigned int, int); + int (*send_ready)(unsigned int, int, unsigned int); + int (*send_fail)(unsigned int, int, unsigned int, int); + int (*setpipefd)(unsigned int, int, int); + int (*catatonic)(unsigned int, int); + int (*timeout)(unsigned int, int, time_t *); + int (*requestor)(unsigned int, int, const char *, uid_t *, gid_t *); + int (*expire)(unsigned int, int, const char *, unsigned int); + int (*askumount)(unsigned int, int, unsigned int *); + int (*ismountpoint)(unsigned int, int, const char *, unsigned int *); +}; + +void init_ioctl_ctl(void); +void close_ioctl_ctl(void); +struct ioctl_ops *get_ioctl_ops(void); +struct autofs_dev_ioctl *alloc_ioctl_ctl_open(const char *, unsigned int); +void free_ioctl_ctl_open(struct autofs_dev_ioctl *); + +#endif + diff --git a/include/linux/auto_dev-ioctl.h b/include/linux/auto_dev-ioctl.h new file mode 100644 index 0000000..91a7739 --- /dev/null +++ b/include/linux/auto_dev-ioctl.h @@ -0,0 +1,224 @@ +/* + * Copyright 2008 Red Hat, Inc. All rights reserved. + * Copyright 2008 Ian Kent + * + * This file is part of the Linux kernel and is made available under + * the terms of the GNU General Public License, version 2, or at your + * option, any later version, incorporated herein by reference. + */ + +#ifndef _LINUX_AUTO_DEV_IOCTL_H +#define _LINUX_AUTO_DEV_IOCTL_H + +#include +#include + +#define AUTOFS_DEVICE_NAME "autofs" + +#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 +#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 + +#define AUTOFS_DEVID_LEN 16 + +#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) + +/* + * An ioctl interface for autofs mount point control. + */ + +struct args_protover { + __u32 version; +}; + +struct args_protosubver { + __u32 sub_version; +}; + +struct args_openmount { + __u32 devid; +}; + +struct args_ready { + __u32 token; +}; + +struct args_fail { + __u32 token; + __s32 status; +}; + +struct args_setpipefd { + __s32 pipefd; +}; + +struct args_timeout { + __u64 timeout; +}; + +struct args_requester { + __u32 uid; + __u32 gid; +}; + +struct args_expire { + __u32 how; +}; + +struct args_askumount { + __u32 may_umount; +}; + +struct args_ismountpoint { + union { + struct args_in { + __u32 type; + } in; + struct args_out { + __u32 devid; + __u32 magic; + } out; + }; +}; + +/* + * All the ioctls use this structure. + * When sending a path size must account for the total length + * of the chunk of memory otherwise is is the size of the + * structure. + */ + +struct autofs_dev_ioctl { + __u32 ver_major; + __u32 ver_minor; + __u32 size; /* total size of data passed in + * including this struct */ + __s32 ioctlfd; /* automount command fd */ + + /* Command parameters */ + + union { + struct args_protover protover; + struct args_protosubver protosubver; + struct args_openmount openmount; + struct args_ready ready; + struct args_fail fail; + struct args_setpipefd setpipefd; + struct args_timeout timeout; + struct args_requester requester; + struct args_expire expire; + struct args_askumount askumount; + struct args_ismountpoint ismountpoint; + }; + + char path[0]; +}; + +static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) +{ + memset(in, 0, sizeof(struct autofs_dev_ioctl)); + in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; + in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; + in->size = sizeof(struct autofs_dev_ioctl); + in->ioctlfd = -1; + return; +} + +/* + * If you change this make sure you make the corresponding change + * to autofs-dev-ioctl.c:lookup_ioctl() + */ +enum { + /* Get various version info */ + AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, + AUTOFS_DEV_IOCTL_PROTOVER_CMD, + AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, + + /* Open mount ioctl fd */ + AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, + + /* Close mount ioctl fd */ + AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, + + /* Mount/expire status returns */ + AUTOFS_DEV_IOCTL_READY_CMD, + AUTOFS_DEV_IOCTL_FAIL_CMD, + + /* Activate/deactivate autofs mount */ + AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, + AUTOFS_DEV_IOCTL_CATATONIC_CMD, + + /* Expiry timeout */ + AUTOFS_DEV_IOCTL_TIMEOUT_CMD, + + /* Get mount last requesting uid and gid */ + AUTOFS_DEV_IOCTL_REQUESTER_CMD, + + /* Check for eligible expire candidates */ + AUTOFS_DEV_IOCTL_EXPIRE_CMD, + + /* Request busy status */ + AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, + + /* Check if path is a mountpoint */ + AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, +}; + +#define AUTOFS_IOCTL 0x93 + +#define AUTOFS_DEV_IOCTL_VERSION \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_PROTOVER \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_OPENMOUNT \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_READY \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_FAIL \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_SETPIPEFD \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_CATATONIC \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_TIMEOUT \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_REQUESTER \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_EXPIRE \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) + +#endif /* _LINUX_AUTO_DEV_IOCTL_H */ diff --git a/include/linux/auto_fs4.h b/include/linux/auto_fs4.h index 31a2954..55fa478 100644 --- a/include/linux/auto_fs4.h +++ b/include/linux/auto_fs4.h @@ -23,12 +23,71 @@ #define AUTOFS_MIN_PROTO_VERSION 3 #define AUTOFS_MAX_PROTO_VERSION 5 -#define AUTOFS_PROTO_SUBVERSION 0 +#define AUTOFS_PROTO_SUBVERSION 1 /* Mask for expire behaviour */ #define AUTOFS_EXP_IMMEDIATE 1 #define AUTOFS_EXP_LEAVES 2 +#define AUTOFS_TYPE_ANY 0U +#define AUTOFS_TYPE_INDIRECT 1U +#define AUTOFS_TYPE_DIRECT 2U +#define AUTOFS_TYPE_OFFSET 4U + +static inline void set_autofs_type_indirect(unsigned int *type) +{ + *type = AUTOFS_TYPE_INDIRECT; + return; +} + +static inline unsigned int autofs_type_indirect(unsigned int type) +{ + return (type == AUTOFS_TYPE_INDIRECT); +} + +static inline void set_autofs_type_direct(unsigned int *type) +{ + *type = AUTOFS_TYPE_DIRECT; + return; +} + +static inline unsigned int autofs_type_direct(unsigned int type) +{ + return (type == AUTOFS_TYPE_DIRECT); +} + +static inline void set_autofs_type_offset(unsigned int *type) +{ + *type = AUTOFS_TYPE_OFFSET; + return; +} + +static inline unsigned int autofs_type_offset(unsigned int type) +{ + return (type == AUTOFS_TYPE_OFFSET); +} + +static inline unsigned int autofs_type_trigger(unsigned int type) +{ + return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); +} + +/* + * This isn't really a type as we use it to say "no type set" to + * indicate we want to search for "any" mount in the + * autofs_dev_ioctl_ismountpoint() device ioctl function. + */ +static inline void set_autofs_type_any(unsigned int *type) +{ + *type = AUTOFS_TYPE_ANY; + return; +} + +static inline unsigned int autofs_type_any(unsigned int type) +{ + return (type == AUTOFS_TYPE_ANY); +} + /* Daemon notification packet types */ enum autofs_notify { NFY_NONE, @@ -98,8 +157,6 @@ union autofs_v5_packet_union { #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) -#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) -#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) diff --git a/include/mounts.h b/include/mounts.h index ca4f9f3..27e6089 100644 --- a/include/mounts.h +++ b/include/mounts.h @@ -16,10 +16,18 @@ #ifndef MOUNTS_H #define MOUNTS_H +#ifndef AUTOFS_TYPE_ANY #define AUTOFS_TYPE_ANY 0x0000 +#endif +#ifndef AUTOFS_TYPE_INDIRECT #define AUTOFS_TYPE_INDIRECT 0x0001 +#endif +#ifndef AUTOFS_TYPE_DIRECT #define AUTOFS_TYPE_DIRECT 0x0002 +#endif +#ifndef AUTOFS_TYPE_OFFSET #define AUTOFS_TYPE_OFFSET 0x0004 +#endif #define MNTS_ALL 0x0001 #define MNTS_REAL 0x0002 @@ -30,9 +38,9 @@ #define REMOUNT_STAT_FAIL 0x0002 #define REMOUNT_READ_MAP 0x0004 -extern const unsigned int indirect; -extern const unsigned int direct; -extern const unsigned int offset; +extern const unsigned int t_indirect; +extern const unsigned int t_direct; +extern const unsigned int t_offset; struct mapent; diff --git a/lib/Makefile b/lib/Makefile index c9f0733..5418009 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -7,12 +7,13 @@ include ../Makefile.rules SRCS = cache.c cat_path.c rpc_subs.c mounts.c log.c nsswitch.c \ master_tok.l master_parse.y nss_tok.c nss_parse.tab.c \ - args.c alarm.c macros.c master.c defaults.c parse_subs.c + args.c alarm.c macros.c master.c defaults.c parse_subs.c \ + dev-ioctl-lib.c RPCS = mount.h mount_clnt.c mount_xdr.c OBJS = cache.o mount_clnt.o mount_xdr.o cat_path.o rpc_subs.o \ mounts.o log.o nsswitch.o master_tok.o master_parse.tab.o \ nss_tok.o nss_parse.tab.o args.o alarm.o macros.o master.o \ - defaults.o parse_subs.o + defaults.o parse_subs.o dev-ioctl-lib.o YACCSRC = nss_tok.c nss_parse.tab.c nss_parse.tab.h \ master_tok.c master_parse.tab.c master_parse.tab.h diff --git a/lib/dev-ioctl-lib.c b/lib/dev-ioctl-lib.c new file mode 100644 index 0000000..57af785 --- /dev/null +++ b/lib/dev-ioctl-lib.c @@ -0,0 +1,790 @@ +/* ----------------------------------------------------------------------- * + * + * ctl-dev-lib.c - module for Linux automount mount table lookup functions + * + * Copyright 2008 Red Hat, Inc. All rights reserved. + * Copyright 2008 Ian Kent - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "automount.h" + +/* ioctld control function interface */ +static struct ioctl_ctl ctl = { -1, NULL }; + +#ifndef AUTOFS_SUPER_MAGIC +#define AUTOFS_SUPER_MAGIC 0x0187 +#endif + +/* + * Define functions for autofs ioctl control. + * + * We provide two interfaces. One which routes ioctls via a + * miscelaneous device node and can be used to obtain an ioctl + * file descriptor for autofs mounts that are covered by an + * active mount (eg. active direct or multi-mount offsets). + * The other provides the traditional autofs ioctl implementation. + * + * The miscielaneous device control functions are prefixed with + * dev_ctl_ and the traditional ones are prefixed with ioctl_. + */ +static int dev_ioctl_version(unsigned int, int, struct autofs_dev_ioctl *); +static int dev_ioctl_protover(unsigned int, int, unsigned int *); +static int dev_ioctl_protosubver(unsigned int, int, unsigned int *); +static int dev_ioctl_mount_device(unsigned int, const char *, unsigned int, dev_t *); +static int dev_ioctl_open(unsigned int, int *, dev_t, const char *); +static int dev_ioctl_close(unsigned int, int); +static int dev_ioctl_send_ready(unsigned int, int, unsigned int); +static int dev_ioctl_send_fail(unsigned int, int, unsigned int, int); +static int dev_ioctl_setpipefd(unsigned int, int, int); +static int dev_ioctl_catatonic(unsigned int, int); +static int dev_ioctl_timeout(unsigned int, int, time_t *); +static int dev_ioctl_requestor(unsigned int, int, const char *, uid_t *, gid_t *); +static int dev_ioctl_expire(unsigned int, int, const char *, unsigned int); +static int dev_ioctl_askumount(unsigned int, int, unsigned int *); +static int dev_ioctl_ismountpoint(unsigned int, int, const char *, unsigned int *); + +static int ioctl_protover(unsigned int, int, unsigned int *); +static int ioctl_protosubver(unsigned int, int, unsigned int *); +static int ioctl_mount_device(unsigned int, const char *, unsigned int, dev_t *); +static int ioctl_open(unsigned int, int *, dev_t, const char *); +static int ioctl_close(unsigned int, int); +static int ioctl_send_ready(unsigned int, int, unsigned int); +static int ioctl_send_fail(unsigned int, int, unsigned int, int); +static int ioctl_catatonic(unsigned int, int); +static int ioctl_timeout(unsigned int, int, time_t *); +static int ioctl_expire(unsigned int, int, const char *, unsigned int); +static int ioctl_askumount(unsigned int, int, unsigned int *); + +static struct ioctl_ops dev_ioctl_ops = { + .version = dev_ioctl_version, + .protover = dev_ioctl_protover, + .protosubver = dev_ioctl_protosubver, + .mount_device = dev_ioctl_mount_device, + .open = dev_ioctl_open, + .close = dev_ioctl_close, + .send_ready = dev_ioctl_send_ready, + .send_fail = dev_ioctl_send_fail, + .setpipefd = dev_ioctl_setpipefd, + .catatonic = dev_ioctl_catatonic, + .timeout = dev_ioctl_timeout, + .requestor = dev_ioctl_requestor, + .expire = dev_ioctl_expire, + .askumount = dev_ioctl_askumount, + .ismountpoint = dev_ioctl_ismountpoint +}; + +static struct ioctl_ops ioctl_ops = { + .version = NULL, + .protover = ioctl_protover, + .protosubver = ioctl_protosubver, + .mount_device = ioctl_mount_device, + .open = ioctl_open, + .close = ioctl_close, + .send_ready = ioctl_send_ready, + .send_fail = ioctl_send_fail, + .setpipefd = NULL, + .catatonic = ioctl_catatonic, + .timeout = ioctl_timeout, + .requestor = NULL, + .expire = ioctl_expire, + .askumount = ioctl_askumount, + .ismountpoint = NULL +}; + +/* + * Allocate the control struct that holds the misc device file + * descriptor and operation despatcher table. + */ +void init_ioctl_ctl(void) +{ + int devfd; + + if (ctl.ops) + return; + + devfd = open(CONTROL_DEVICE, O_RDONLY); + if (devfd == -1) + ctl.ops = &ioctl_ops; + else { + struct autofs_dev_ioctl param; + + int cl_flags = fcntl(devfd, F_GETFD, 0); + if (cl_flags != -1) { + cl_flags |= FD_CLOEXEC; + fcntl(devfd, F_SETFD, cl_flags); + } + /* + * Check compile version against kernel. + * Selinux may allow us to open the device but not + * actually allow us to do anything. + */ + init_autofs_dev_ioctl(¶m); + if (ioctl(devfd, AUTOFS_DEV_IOCTL_VERSION, ¶m) == -1) { + close(devfd); + ctl.ops = &ioctl_ops; + } else { + ctl.devfd = devfd; + ctl.ops = &dev_ioctl_ops; + } + } + return; +} + +void close_ioctl_ctl(void) +{ + if (ctl.devfd != -1) { + close(ctl.devfd); + ctl.devfd = -1; + } + ctl.ops = NULL; + return; +} + +/* Return a pointer to the operations control struct */ +struct ioctl_ops *get_ioctl_ops(void) +{ + if (!ctl.ops) + init_ioctl_ctl(); + return ctl.ops; +} + +/* Get kenrel version of misc device code */ +static int dev_ioctl_version(unsigned int logopt, + int ioctlfd, struct autofs_dev_ioctl *param) +{ + param->ioctlfd = ioctlfd; + + if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_VERSION, param) == -1) + return -1; + + return 0; +} + +/* Get major version of autofs kernel module mount protocol */ +static int dev_ioctl_protover(unsigned int logopt, + int ioctlfd, unsigned int *major) +{ + struct autofs_dev_ioctl param; + + init_autofs_dev_ioctl(¶m); + param.ioctlfd = ioctlfd; + + if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_PROTOVER, ¶m) == -1) + return -1; + + *major = param.protover.version; + + return 0; +} + +static int ioctl_protover(unsigned int logopt, + int ioctlfd, unsigned int *major) +{ + return ioctl(ioctlfd, AUTOFS_IOC_PROTOVER, major); +} + +/* Get minor version of autofs kernel module mount protocol */ +static int dev_ioctl_protosubver(unsigned int logopt, + int ioctlfd, unsigned int *minor) +{ + struct autofs_dev_ioctl param; + + init_autofs_dev_ioctl(¶m); + param.ioctlfd = ioctlfd; + + if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_PROTOSUBVER, ¶m) == -1) + return -1; + + *minor = param.protosubver.sub_version; + + return 0; +} + +static int ioctl_protosubver(unsigned int logopt, + int ioctlfd, unsigned int *minor) +{ + return ioctl(ioctlfd, AUTOFS_IOC_PROTOSUBVER, minor); +} + +/* + * Allocate a parameter struct for misc device ioctl used when + * opening an autofs mount point. Attach the path to the end + * of the struct. and lookup the device number if not given. + * Locating the device number relies on the mount option + * "dev=" being present in the autofs fs mount + * options. + */ +static struct autofs_dev_ioctl *alloc_dev_ioctl_open(const char *path, dev_t devid) +{ + struct autofs_dev_ioctl *ioctl; + size_t size, p_len; + dev_t devno = devid; + + if (!path) + return NULL; + + p_len = strlen(path); + size = sizeof(struct autofs_dev_ioctl) + p_len + 1; + ioctl = malloc(size); + if (!ioctl) { + errno = ENOMEM; + return NULL; + } + + init_autofs_dev_ioctl(ioctl); + ioctl->size = size; + memcpy(ioctl->path, path, p_len); + ioctl->path[p_len] = '\0'; + ioctl->openmount.devid = devno; + + return ioctl; +} + +static void free_dev_ioctl_open(struct autofs_dev_ioctl *ioctl) +{ + free(ioctl); + return; +} + +/* + * Allocate a parameter struct for misc device ioctl which includes + * a path. This is used when getting the last mount requestor uid + * and gid and when checking if a path within the autofs filesystem + * is a mount point. We add the path to the end of the struct. + */ +static struct autofs_dev_ioctl *alloc_dev_ioctl_path(int ioctlfd, const char *path) +{ + struct autofs_dev_ioctl *ioctl; + size_t size, p_len; + + if (!path) + return NULL; + + p_len = strlen(path); + size = sizeof(struct autofs_dev_ioctl) + p_len + 1; + ioctl = malloc(size); + if (!ioctl) { + errno = ENOMEM; + return NULL; + } + + init_autofs_dev_ioctl(ioctl); + ioctl->ioctlfd = ioctlfd; + ioctl->size = size; + memcpy(ioctl->path, path, p_len); + ioctl->path[p_len] = '\0'; + + return ioctl; +} + +static void free_dev_ioctl_path(struct autofs_dev_ioctl *ioctl) +{ + free(ioctl); + return; +} + +/* + * Find the device number of an autofs mount with given path and + * type (eg..AUTOFS_TYPE_DIRECT). The device number is used by + * the kernel to identify the autofs super block when searching + * for the mount. + */ +static int dev_ioctl_mount_device(unsigned int logopt, const char *path, unsigned int type, dev_t *devid) +{ + struct autofs_dev_ioctl *param; + int err; + + if (!path) { + errno = EINVAL; + return -1; + } + + *devid = -1; + + param = alloc_dev_ioctl_path(-1, path); + if (!param) + return -1; + param->ismountpoint.in.type = type; + + err = ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_ISMOUNTPOINT, param); + if (err == -1) { + int save_errno = errno; + free_dev_ioctl_path(param); + errno = save_errno; + return -1; + } + + if (err) + *devid = param->ismountpoint.out.devid; + + free_dev_ioctl_path(param); + + return err; +} + +static int ioctl_mount_device(unsigned int logopt, + const char *path, unsigned int type, + dev_t *devid) +{ + return -1; +} + +/* Get a file descriptor for control operations */ +static int dev_ioctl_open(unsigned int logopt, + int *ioctlfd, dev_t devid, const char *path) +{ + struct autofs_dev_ioctl *param; + + *ioctlfd = -1; + + param = alloc_dev_ioctl_open(path, devid); + if (!param) + return -1; + + if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_OPENMOUNT, param) == -1) { + int save_errno = errno; + free_dev_ioctl_open(param); + errno = save_errno; + return -1; + } + + *ioctlfd = param->ioctlfd; + + free_dev_ioctl_open(param); + + return 0; +} + +static int ioctl_open(unsigned int logopt, + int *ioctlfd, dev_t devid, const char *path) +{ + struct statfs sfs; + int save_errno, fd, cl_flags; + + *ioctlfd = -1; + + fd = open(path, O_RDONLY); + if (fd == -1) + return -1; + + cl_flags = fcntl(fd, F_GETFD, 0); + if (cl_flags != -1) { + cl_flags |= FD_CLOEXEC; + fcntl(fd, F_SETFD, cl_flags); + } + + if (fstatfs(fd, &sfs) == -1) { + save_errno = errno; + goto err; + } + + if (sfs.f_type != AUTOFS_SUPER_MAGIC) { + save_errno = ENOENT; + goto err; + } + + *ioctlfd = fd; + + return 0; +err: + close(fd); + errno = save_errno; + return -1; +} + +/* Close */ +static int dev_ioctl_close(unsigned int logopt, int ioctlfd) +{ + struct autofs_dev_ioctl param; + + init_autofs_dev_ioctl(¶m); + param.ioctlfd = ioctlfd; + + if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_CLOSEMOUNT, ¶m) == -1) + return -1; + + return 0; +} + +static int ioctl_close(unsigned int logopt, int ioctlfd) +{ + return close(ioctlfd); +} + +/* Send ready status for given token */ +static int dev_ioctl_send_ready(unsigned int logopt, + int ioctlfd, unsigned int token) +{ + struct autofs_dev_ioctl param; + + if (token == 0) { + errno = EINVAL; + return -1; + } + + debug(logopt, "token = %d", token); + + init_autofs_dev_ioctl(¶m); + param.ioctlfd = ioctlfd; + param.ready.token = token; + + if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_READY, ¶m) == -1) { + char *estr, buf[MAX_ERR_BUF]; + int save_errno = errno; + estr = strerror_r(errno, buf, MAX_ERR_BUF); + logerr("AUTOFS_DEV_IOCTL_READY: error %s", estr); + errno = save_errno; + return -1; + } + return 0; +} + +static int ioctl_send_ready(unsigned int logopt, + int ioctlfd, unsigned int token) +{ + if (token == 0) { + errno = EINVAL; + return -1; + } + + debug(logopt, "token = %d", token); + + if (ioctl(ioctlfd, AUTOFS_IOC_READY, token) == -1) { + char *estr, buf[MAX_ERR_BUF]; + int save_errno = errno; + estr = strerror_r(errno, buf, MAX_ERR_BUF); + logerr("AUTOFS_IOC_READY: error %s", estr); + errno = save_errno; + return -1; + } + return 0; +} + +/* + * Send ready status for given token. + * + * The device node ioctl implementation allows for sending a status + * of other than ENOENT, unlike the tradional interface. + */ +static int dev_ioctl_send_fail(unsigned int logopt, + int ioctlfd, unsigned int token, int status) +{ + struct autofs_dev_ioctl param; + + if (token == 0) { + errno = EINVAL; + return -1; + } + + debug(logopt, "token = %d", token); + + init_autofs_dev_ioctl(¶m); + param.ioctlfd = ioctlfd; + param.fail.token = token; + param.fail.status = status; + + if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_FAIL, ¶m) == -1) { + char *estr, buf[MAX_ERR_BUF]; + int save_errno = errno; + estr = strerror_r(errno, buf, MAX_ERR_BUF); + logerr("AUTOFS_DEV_IOCTL_FAIL: error %s", estr); + errno = save_errno; + return -1; + } + return 0; +} + +static int ioctl_send_fail(unsigned int logopt, + int ioctlfd, unsigned int token, int status) +{ + if (token == 0) { + errno = EINVAL; + return -1; + } + + debug(logopt, "token = %d", token); + + if (ioctl(ioctlfd, AUTOFS_IOC_FAIL, token) == -1) { + char *estr, buf[MAX_ERR_BUF]; + int save_errno = errno; + estr = strerror_r(errno, buf, MAX_ERR_BUF); + logerr("AUTOFS_IOC_FAIL: error %s", estr); + errno = save_errno; + return -1; + } + return 0; +} + +/* + * Set the pipe fd for kernel communication. + * + * Normally this is set at mount using an option but if we + * are reconnecting to a busy mount then we need to use this + * to tell the autofs kernel module about the new pipe fd. In + * order to protect mounts against incorrectly setting the + * pipefd we also require that the autofs mount be catatonic. + * + * If successful this also sets the process group id used to + * identify the controlling process to the process group of + * the caller. + */ +static int dev_ioctl_setpipefd(unsigned int logopt, int ioctlfd, int pipefd) +{ + struct autofs_dev_ioctl param; + + if (pipefd == -1) { + errno = EBADF; + return -1; + } + + init_autofs_dev_ioctl(¶m); + param.ioctlfd = ioctlfd; + param.setpipefd.pipefd = pipefd; + + if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_SETPIPEFD, ¶m) == -1) + return -1; + + return 0; +} + +/* + * Make the autofs mount point catatonic, no longer responsive to + * mount requests. Also closes the kernel pipe file descriptor. + */ +static int dev_ioctl_catatonic(unsigned int logopt, int ioctlfd) +{ + struct autofs_dev_ioctl param; + + init_autofs_dev_ioctl(¶m); + param.ioctlfd = ioctlfd; + + if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_CATATONIC, ¶m) == -1) + return -1; + + return 0; +} + +static int ioctl_catatonic(unsigned int logopt, int ioctlfd) +{ + return ioctl(ioctlfd, AUTOFS_IOC_CATATONIC, 0); +} + +/* Set the autofs mount timeout */ +static int dev_ioctl_timeout(unsigned int logopt, int ioctlfd, time_t *timeout) +{ + struct autofs_dev_ioctl param; + + init_autofs_dev_ioctl(¶m); + param.ioctlfd = ioctlfd; + param.timeout.timeout = *timeout; + + if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_TIMEOUT, ¶m) == -1) + return -1; + + *timeout = param.timeout.timeout; + + return 0; +} + +static int ioctl_timeout(unsigned int logopt, int ioctlfd, time_t *timeout) +{ + return ioctl(ioctlfd, AUTOFS_IOC_SETTIMEOUT, timeout); +} + +/* + * Get the uid and gid of the last request for the mountpoint, path. + * + * When reconstructing an autofs mount tree with active mounts + * we need to re-connect to mounts that may have used the original + * process uid and gid (or string variations of them) for mount + * lookups within the map entry. + */ +static int dev_ioctl_requestor(unsigned int logopt, + int ioctlfd, const char *path, + uid_t *uid, gid_t *gid) +{ + struct autofs_dev_ioctl *param; + int err; + + if (!path) + errno = EINVAL; + + *uid = -1; + *gid = -1; + + + param = alloc_dev_ioctl_path(ioctlfd, path); + if (!param) + return -1; + + err = ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_REQUESTER, param); + if (err == -1) { + int save_errno = errno; + free_dev_ioctl_open(param); + errno = save_errno; + return -1; + } + + *uid = param->requester.uid; + *gid = param->requester.gid; + + free_dev_ioctl_path(param); + + return 0; +} + +/* + * Call repeatedly until it returns EAGAIN, meaning there's nothing + * more that can be done. + */ +static int expire(unsigned int logopt, + int cmd, int fd, int ioctlfd, const char *path, void *arg) +{ + int ret, retries = EXPIRE_RETRIES; + unsigned int may_umount; + + while (retries--) { + struct timespec tm = {0, 100000000}; + + /* Ggenerate expire message for the mount. */ + ret = ioctl(fd, cmd, arg); + if (ret == -1) { + /* Mount has gone away */ + if (errno == EBADF || errno == EINVAL) + return 0; + + /* + * Other than EAGAIN is an expire error so continue. + * Kernel will try the next mount for indirect maps + * and the same mount again for direct maps, limited + * by retries. + */ + if (errno == EAGAIN) + break; + } + nanosleep(&tm, NULL); + } + + may_umount = 0; + if (ctl.ops->askumount(logopt, ioctlfd, &may_umount)) + return -1; + + if (!may_umount) + return 1; + + return 0; +} + +static int dev_ioctl_expire(unsigned int logopt, + int ioctlfd, const char *path, unsigned int when) +{ + struct autofs_dev_ioctl param; + + init_autofs_dev_ioctl(¶m); + param.ioctlfd = ioctlfd; + param.expire.how = when; + + return expire(logopt, AUTOFS_DEV_IOCTL_EXPIRE, + ctl.devfd, ioctlfd, path, (void *) ¶m); +} + +static int ioctl_expire(unsigned int logopt, + int ioctlfd, const char *path, unsigned int when) +{ + return expire(logopt, AUTOFS_IOC_EXPIRE_MULTI, + ioctlfd, ioctlfd, path, (void *) &when); +} + +/* Check if autofs mount point is in use */ +static int dev_ioctl_askumount(unsigned int logopt, + int ioctlfd, unsigned int *busy) +{ + struct autofs_dev_ioctl param; + + init_autofs_dev_ioctl(¶m); + param.ioctlfd = ioctlfd; + + if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_ASKUMOUNT, ¶m) == -1) + return -1; + + *busy = param.askumount.may_umount; + + return 0; +} + +static int ioctl_askumount(unsigned int logopt, + int ioctlfd, unsigned int *busy) +{ + return ioctl(ioctlfd, AUTOFS_IOC_ASKUMOUNT, busy); +} + +/* + * Check if the given path is a mountpoint. + * + * The path is considered a mountpoint if it is itself a mountpoint + * or contains a mount, such as a multi-mount without a root mount. + * In addition, if the path is itself a mountpoint we return whether + * the mounted file system is an autofs filesystem or other file + * system. + */ +static int dev_ioctl_ismountpoint(unsigned int logopt, + int ioctlfd, const char *path, + unsigned int *mountpoint) +{ + struct autofs_dev_ioctl *param; + int err; + + *mountpoint = 0; + + if (!path) { + errno = EINVAL; + return -1; + } + + param = alloc_dev_ioctl_path(ioctlfd, path); + if (!param) + return -1; + set_autofs_type_any(¶m->ismountpoint.in.type); + + err = ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_ISMOUNTPOINT, param); + if (err == -1) { + int save_errno = errno; + free_dev_ioctl_path(param); + errno = save_errno; + return -1; + } + + if (err) { + *mountpoint = DEV_IOCTL_IS_MOUNTED; + + if (param->ismountpoint.out.magic) { + if (param->ismountpoint.out.magic == AUTOFS_SUPER_MAGIC) + *mountpoint |= DEV_IOCTL_IS_AUTOFS; + else + *mountpoint |= DEV_IOCTL_IS_OTHER; + } + } + + free_dev_ioctl_path(param); + + return 0; +} diff --git a/lib/master_parse.y b/lib/master_parse.y index 2184b4f..b1186c0 100644 --- a/lib/master_parse.y +++ b/lib/master_parse.y @@ -796,6 +796,7 @@ int master_parse_entry(const char *buffer, unsigned int default_timeout, unsigne return 0; } } else { + struct ioctl_ops *ops = get_ioctl_ops(); struct autofs_point *ap = entry->ap; time_t tout = timeout; @@ -807,7 +808,7 @@ int master_parse_entry(const char *buffer, unsigned int default_timeout, unsigne ap->exp_timeout = timeout; ap->exp_runfreq = (ap->exp_timeout + CHECK_RATIO - 1) / CHECK_RATIO; if (ap->ioctlfd != -1 && ap->type == LKP_INDIRECT) - ioctl(ap->ioctlfd, AUTOFS_IOC_SETTIMEOUT, &tout); + ops->timeout(ap->logopt, ap->ioctlfd, &tout); } } entry->ap->random_selection = random_selection; diff --git a/lib/mounts.c b/lib/mounts.c index f6fc389..a04a922 100644 --- a/lib/mounts.c +++ b/lib/mounts.c @@ -33,9 +33,9 @@ #define MAX_OPTIONS_LEN 80 #define MAX_MNT_NAME_LEN 30 -const unsigned int indirect = AUTOFS_TYPE_INDIRECT; -const unsigned int direct = AUTOFS_TYPE_DIRECT; -const unsigned int offset = AUTOFS_TYPE_OFFSET; +const unsigned int t_indirect = AUTOFS_TYPE_INDIRECT; +const unsigned int t_direct = AUTOFS_TYPE_DIRECT; +const unsigned int t_offset = AUTOFS_TYPE_OFFSET; const unsigned int type_count = 3; static const char options_template[] = "fd=%d,pgrp=%u,minproto=5,maxproto=%d"; @@ -47,10 +47,12 @@ static const char kver_options_template[] = "fd=%d,pgrp=%u,minproto=3,maxproto= unsigned int query_kproto_ver(void) { + struct ioctl_ops *ops = get_ioctl_ops(); char dir[] = "/tmp/autoXXXXXX", *t_dir; char options[MAX_OPTIONS_LEN + 1]; pid_t pgrp = getpgrp(); int pipefd[2], ioctlfd, len; + struct stat st; t_dir = mkdtemp(dir); if (!t_dir) @@ -79,7 +81,14 @@ unsigned int query_kproto_ver(void) close(pipefd[1]); - ioctlfd = open(t_dir, O_RDONLY); + if (stat(t_dir, &st) == -1) { + umount(t_dir); + close(pipefd[0]); + rmdir(t_dir); + return 0; + } + + ops->open(LOGOPT_NONE, &ioctlfd, st.st_dev, t_dir); if (ioctlfd == -1) { umount(t_dir); close(pipefd[0]); @@ -87,11 +96,11 @@ unsigned int query_kproto_ver(void) return 0; } - ioctl(ioctlfd, AUTOFS_IOC_CATATONIC, 0); + ops->catatonic(LOGOPT_NONE, ioctlfd); /* If this ioctl() doesn't work, it is kernel version 2 */ - if (ioctl(ioctlfd, AUTOFS_IOC_PROTOVER, &kver.major) == -1) { - close(ioctlfd); + if (ops->protover(LOGOPT_NONE, ioctlfd, &kver.major)) { + ops->close(LOGOPT_NONE, ioctlfd); umount(t_dir); close(pipefd[0]); rmdir(t_dir); @@ -99,15 +108,15 @@ unsigned int query_kproto_ver(void) } /* If this ioctl() doesn't work, version is 4 or less */ - if (ioctl(ioctlfd, AUTOFS_IOC_PROTOSUBVER, &kver.minor) == -1) { - close(ioctlfd); + if (ops->protosubver(LOGOPT_NONE, ioctlfd, &kver.minor)) { + ops->close(LOGOPT_NONE, ioctlfd); umount(t_dir); close(pipefd[0]); rmdir(t_dir); return 0; } - close(ioctlfd); + ops->close(LOGOPT_NONE, ioctlfd); umount(t_dir); close(pipefd[0]); rmdir(t_dir); diff --git a/modules/parse_sun.c b/modules/parse_sun.c index 5a39113..329b255 100644 --- a/modules/parse_sun.c +++ b/modules/parse_sun.c @@ -1015,6 +1015,7 @@ static void cleanup_multi_root(struct autofs_point *ap, const char *root, if (move == MOUNT_MOVE_OTHER) spawn_umount(ap->logopt, root, NULL); else { + struct ioctl_ops *ops = get_ioctl_ops(); struct autofs_point *submount; mounts_mutex_lock(ap); @@ -1031,7 +1032,7 @@ static void cleanup_multi_root(struct autofs_point *ap, const char *root, submount->parent->submnt_count--; list_del_init(&submount->mounts); - ioctl(submount->ioctlfd, AUTOFS_IOC_CATATONIC, 0); + ops->catatonic(submount->logopt, submount->ioctlfd); mounts_mutex_unlock(ap); diff --git a/redhat/autofs.init.in b/redhat/autofs.init.in index 15a21be..65c786e 100644 --- a/redhat/autofs.init.in +++ b/redhat/autofs.init.in @@ -13,6 +13,7 @@ DAEMON=@@sbindir@@/automount prog=`basename $DAEMON` MODULE="autofs4" +DEVICE="autofs" initdir=@@initdir@@ confdir=@@autofsconfdir@@ @@ -56,6 +57,25 @@ function start() { echo return $RETVAL fi + + # Check misc device + if [ -n "$USE_MISC_DEVICE" -a "x$USE_MISC_DEVICE" = "xyes" ]; then + sleep 1 + if [ -e "/proc/misc" ]; then + MINOR=`awk "/$DEVICE/ {print \\$1}" /proc/misc` + if [ -n "$MINOR" -a ! -c "/dev/$DEVICE" ]; then + mknod -m 0600 /dev/$DEVICE c 10 $MINOR + fi + fi + if [ -x /sbin/restorecon ]; then + /sbin/restorecon /dev/$DEVICE + fi + else + if [ -c /dev/$DEVICE ]; then + rm /dev/$DEVICE + fi + fi + echo -n $"Starting $prog: " $prog $OPTIONS RETVAL=$? diff --git a/redhat/autofs.sysconfig.in b/redhat/autofs.sysconfig.in index ce64feb..8256888 100644 --- a/redhat/autofs.sysconfig.in +++ b/redhat/autofs.sysconfig.in @@ -91,5 +91,10 @@ BROWSE_MODE="no" # # General global options # +# If the kernel supports using the autofs miscellanous device +# and you wish to use it you must set this configuration option +# to "yes" otherwise it will not be used. +#USE_MISC_DEVICE="no" +# #OPTIONS="" # diff --git a/samples/autofs.conf.default.in b/samples/autofs.conf.default.in index 0231e1d..844a6f3 100644 --- a/samples/autofs.conf.default.in +++ b/samples/autofs.conf.default.in @@ -91,5 +91,10 @@ BROWSE_MODE="no" # # General global options # +# If the kernel supports using the autofs miscellanous device +# and you wish to use it you must set this configuration option +# to "yes" otherwise it will not be used. +#USE_MISC_DEVICE="no" +# #OPTIONS="" # diff --git a/samples/rc.autofs.in b/samples/rc.autofs.in index e333e95..51f5b02 100644 --- a/samples/rc.autofs.in +++ b/samples/rc.autofs.in @@ -12,6 +12,7 @@ DAEMON=@@sbindir@@/automount prog=`basename $DAEMON` MODULE="autofs4" +DEVICE="autofs" confdir=@@autofsconfdir@@ test -e $DAEMON || exit 0 @@ -47,6 +48,24 @@ function start() { return 1 fi + # Check misc device + if [ -n "$USE_MISC_DEVICE" -a "x$USE_MISC_DEVICE" = "xyes" ]; then + sleep 1 + if [ -e "/proc/misc" ]; then + MINOR=`awk "/$DEVICE/ {print \\$1}" /proc/misc` + if [ -n "$MINOR" -a ! -c "/dev/$DEVICE" ]; then + mknod -m 0600 /dev/$DEVICE c 10 $MINOR + fi + fi + if [ -x /sbin/restorecon ]; then + /sbin/restorecon /dev/$DEVICE + fi + else + if [ -c /dev/$DEVICE ]; then + rm /dev/$DEVICE + fi + fi + $prog $OPTIONS RETVAL=$? if [ $RETVAL -eq 0 ] ; then