/* ----------------------------------------------------------------------- * * * mount_autofs.c - Module for recursive autofs mounts. * * Copyright 1997 Transmeta Corporation - All Rights Reserved * Copyright 2006 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; incorporated herein by reference. * * ----------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #define MODULE_MOUNT #include "automount.h" #define MODPREFIX "mount(autofs): " /* Attribute to create detached thread */ extern pthread_attr_t th_attr_detached; extern struct startup_cond suc; int mount_version = AUTOFS_MOUNT_VERSION; /* Required by protocol */ int mount_init(void **context) { return 0; } int mount_reinit(void **context) { return 0; } int mount_mount(struct autofs_point *ap, const char *root, const char *name, int name_len, const char *what, const char *fstype, const char *c_options, void *context) { struct startup_cond suc; pthread_t thid; char realpath[PATH_MAX]; char mountpoint[PATH_MAX]; const char **argv; int argc, status; int nobind = ap->flags & MOUNT_FLAG_NOBIND; int ghost = ap->flags & MOUNT_FLAG_GHOST; int symlnk = ap->flags & MOUNT_FLAG_SYMLINK; int strictexpire = ap->flags & MOUNT_FLAG_STRICTEXPIRE; time_t timeout = get_exp_timeout(ap, ap->entry->maps); unsigned logopt = ap->logopt; struct map_type_info *info; struct master *master; struct master_mapent *entry; struct map_source *source; struct autofs_point *nap; char buf[MAX_ERR_BUF]; char *options, *p; int len, ret; int hosts = 0; /* Root offset of multi-mount */ len = strlen(root); if (root[len - 1] == '/') { strcpy(realpath, ap->path); strcat(realpath, "/"); strcat(realpath, name); len--; strncpy(mountpoint, root, len); mountpoint[len] = '\0'; } else if (*name == '/') { if (ap->flags & MOUNT_FLAG_REMOUNT) { strcpy(mountpoint, name); strcpy(realpath, name); } else { strcpy(mountpoint, root); strcpy(realpath, name); } } else { strcpy(mountpoint, root); strcat(mountpoint, "/"); strcpy(realpath, mountpoint); strcat(mountpoint, name); strcat(realpath, name); } options = NULL; if (c_options) { char *noptions; const char *comma; char *np; int len = strlen(c_options) + 1; noptions = np = alloca(len); if (!np) { char *estr = strerror_r(errno, buf, MAX_ERR_BUF); error(ap->logopt, MODPREFIX "alloca: %s", estr); return 1; } memset(np, 0, len); /* Grab the autofs specific options */ for (comma = c_options; *comma != '\0';) { const char *cp; while (*comma == ',') comma++; cp = comma; while (*comma != '\0' && *comma != ',') comma++; if (_strncmp("nobrowse", cp, 8) == 0 || _strncmp("nobrowsable", cp, 11) == 0) ghost = 0; else if (_strncmp("nobind", cp, 6) == 0) nobind = 1; else if (_strncmp("browse", cp, 6) == 0 || _strncmp("browsable", cp, 9) == 0) ghost = 1; else if (_strncmp("symlink", cp, 7) == 0) symlnk = 1; else if (_strncmp("strictexpire", cp, 12) == 0) strictexpire = 1; else if (_strncmp("hosts", cp, 5) == 0) hosts = 1; else if (_strncmp("timeout=", cp, 8) == 0) { char *val = strchr(cp, '='); unsigned tout; if (val) { int ret = sscanf(cp, "timeout=%u", &tout); if (ret) timeout = tout; } } else { memcpy(np, cp, comma - cp + 1); np += comma - cp + 1; } } options = noptions; } debug(ap->logopt, MODPREFIX "mountpoint=%s what=%s options=%s", mountpoint, what, options); master = ap->entry->master; entry = master_new_mapent(master, realpath, ap->entry->age); if (!entry) { error(ap->logopt, MODPREFIX "failed to malloc master_mapent struct"); return 1; } ret = master_add_autofs_point(entry, logopt, nobind, ghost, 1); if (!ret) { error(ap->logopt, MODPREFIX "failed to add autofs_point to entry"); master_free_mapent(entry); return 1; } nap = entry->ap; nap->parent = ap; if (symlnk) nap->flags |= MOUNT_FLAG_SYMLINK; if (strictexpire) nap->flags |= MOUNT_FLAG_STRICTEXPIRE; if (hosts) argc = 0; else argc = 1; if (options) { char *t = options; do { argc++; if (*t == ',') t++; } while ((t = strchr(t, ',')) != NULL); } argv = (const char **) alloca((argc + 1) * sizeof(char *)); if (hosts) argc = 0; else argc = 1; /* * If a mount of a hosts map is being requested it will come * ro us via the options. Catch that below when processing the * option and create type info struct then. */ if (hosts) info = parse_map_type_info("hosts:"); else info = parse_map_type_info(what); if (!info) { error(ap->logopt, MODPREFIX "failed to parse map info"); master_free_mapent(entry); return 1; } if (info->map) argv[0] = info->map; if (options) { p = options; do { if (*p == ',') { *p = '\0'; p++; } argv[argc++] = p; } while ((p = strchr(p, ',')) != NULL); } argv[argc] = NULL; /* * For amd type "auto" the map is often re-used so check * if the the parent map can be used and use it if it * matches. * * Also if the parent map format is amd and the format * isn't specified in the map entry set it from the parent * map source. */ source = NULL; if (ap->entry->maps && ap->entry->maps->flags & MAP_FLAG_FORMAT_AMD) { struct map_source *s = ap->entry->maps; /* * For amd maps, if the format and source type aren't * specified try and set them from the parent. */ if (!info->format) { info->format = strdup("amd"); if (!info->format) warn(ap->logopt, MODPREFIX "failed to set amd map format"); if (!info->type && s->type) { info->type = strdup(s->type); if (!info->type) warn(ap->logopt, MODPREFIX "failed to set amd map type"); } } source = master_get_map_source(ap->entry, info->type, info->format, argc, argv); if (source) entry->maps = source; } if (!source) source = master_add_map_source(entry, info->type, info->format, monotonic_time(NULL), argc, argv); if (!source) { error(ap->logopt, MODPREFIX "failed to add map source to entry"); master_free_mapent(entry); free_map_type_info(info); return 1; } free_map_type_info(info); set_exp_timeout(nap, NULL, timeout); nap->exp_runfreq = (timeout + CHECK_RATIO - 1) / CHECK_RATIO; mounts_mutex_lock(ap); if (source->flags & MAP_FLAG_FORMAT_AMD) { struct amd_entry *am_entry = __master_find_amdmount(ap, entry->path); if (am_entry) { if (am_entry->pref) { nap->pref = am_entry->pref; am_entry->pref = NULL; } if (am_entry->cache_opts & AMD_CACHE_OPTION_ALL) nap->flags |= MOUNT_FLAG_AMD_CACHE_ALL; } } if (handle_mounts_startup_cond_init(&suc)) { crit(ap->logopt, MODPREFIX "failed to init startup cond for mount %s", entry->path); mounts_mutex_unlock(ap); master_free_map_source(source, 1); master_free_mapent(entry); return 1; } suc.ap = nap; suc.root = mountpoint; suc.done = 0; suc.status = 0; if (pthread_create(&thid, &th_attr_detached, handle_mounts, &suc)) { crit(ap->logopt, MODPREFIX "failed to create mount handler thread for %s", realpath); handle_mounts_startup_cond_destroy(&suc); mounts_mutex_unlock(ap); master_free_map_source(source, 1); master_free_mapent(entry); return 1; } while (!suc.done) { status = pthread_cond_wait(&suc.cond, &suc.mutex); if (status) { handle_mounts_startup_cond_destroy(&suc); mounts_mutex_unlock(ap); master_free_map_source(source, 1); master_free_mapent(entry); fatal(status); } } if (suc.status) { crit(ap->logopt, MODPREFIX "failed to create submount for %s", realpath); handle_mounts_startup_cond_destroy(&suc); mounts_mutex_unlock(ap); master_free_map_source(source, 1); master_free_mapent(entry); return 1; } nap->thid = thid; ap->submnt_count++; list_add(&nap->mounts, &ap->submounts); handle_mounts_startup_cond_destroy(&suc); mounts_mutex_unlock(ap); return 0; } int mount_done(void *context) { return 0; }