diff options
Diffstat (limited to 'sutils/fstab.c')
-rw-r--r-- | sutils/fstab.c | 479 |
1 files changed, 432 insertions, 47 deletions
diff --git a/sutils/fstab.c b/sutils/fstab.c index 6c8e0f81..b66e5195 100644 --- a/sutils/fstab.c +++ b/sutils/fstab.c @@ -1,6 +1,6 @@ /* Fstab filesystem frobbing - Copyright (C) 1996 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. Written by Miles Bader <miles@gnu.ai.mit.edu> @@ -27,6 +27,8 @@ #include <fcntl.h> #include <error.h> #include <argz.h> +#include <argp.h> +#include <fnmatch.h> #include <hurd/fsys.h> @@ -34,7 +36,7 @@ extern error_t fsys_set_readonly (fsys_t fsys, int readonly); extern error_t fsys_get_readonly (fsys_t fsys, int *readonly); -extern error_t fsys_remount (fsys_t fsys); +extern error_t fsys_update (fsys_t fsys); extern file_t file_name_lookup_carefully (const char *file, int flags, mode_t mode); @@ -66,7 +68,7 @@ fstab_free (struct fstab *fstab) /* Return a new fstypes structure in TYPES. SEARCH_FMTS is copied. */ error_t -fstypes_create (char *search_fmts, size_t search_fmts_len, +fstypes_create (const char *search_fmts, size_t search_fmts_len, struct fstypes **types) { struct fstypes *new = malloc (sizeof (struct fstypes)); @@ -94,11 +96,12 @@ fstypes_create (char *search_fmts, size_t search_fmts_len, one is found, it is added to TYPES, otherwise an new entry is created with a NULL PROGRAM field. */ error_t -fstypes_get (struct fstypes *types, char *name, struct fstype **fstype) +fstypes_get (struct fstypes *types, const char *name, struct fstype **fstype) { char *fmts, *fmt; size_t fmts_len; struct fstype *type; + char *program = 0; for (type = types->entries; type; type = type->next) if (strcasecmp (type->name, name) == 0) @@ -108,14 +111,6 @@ fstypes_get (struct fstypes *types, char *name, struct fstype **fstype) } /* No existing entry, make a new one. */ - type = malloc (sizeof (struct fstype)); - if (! type) - return ENOMEM; - - type->name = strdup (name); - type->program = 0; - type->next = types->entries; - types->entries = type; fmts = types->program_search_fmts; fmts_len = types->program_search_fmts_len; @@ -123,7 +118,6 @@ fstypes_get (struct fstypes *types, char *name, struct fstype **fstype) for (fmt = fmts; fmt; fmt = argz_next (fmts, fmts_len, fmt)) { int fd; - char *program; asprintf (&program, fmt, name); fd = open (program, O_EXEC); @@ -144,23 +138,109 @@ fstypes_get (struct fstypes *types, char *name, struct fstype **fstype) close (fd); if (rv < 0) - return errno; - - if (stat.st_mode & S_IXUSR) - /* Yup execute bit is set. This must a program... */ { - type->program = program; - break; + free (program); + return errno; } + + if (stat.st_mode & S_IXUSR) + /* Yup execute bit is set. This must be a program... */ + break; + + free (program); } + + program = 0; + } + + type = malloc (sizeof (struct fstype)); + if (! type) + { + free (program); + return ENOMEM; } + type->name = strdup (name); + if (type->name == 0) + { + free (type); + return ENOMEM; + } + type->program = program; + type->next = types->entries; + types->entries = type; + + *fstype = type; + return 0; } + +#if 0 +/* XXX nice idea, but not that useful since scanf's %s always eats all + non-ws, and it seems a bit overkill to convert it to a .+ regexp match */ +error_t +fstypes_find_program (struct fstypes *types, const char *program, + struct fstype **fstype) +{ + char *fmts, *fmt; + size_t fmts_len; + struct fstype *type; + char *typename; + + /* First see if a known type matches this program. */ + for (type = types->entries; type; type = type->next) + if (type->program && !strcmp (type->program, program)) + { + *fstype = type; + return 0; + } + + /* No existing entry, see if we can make a new one. */ + + typename = alloca (strlen (program) + 1); + + fmts = types->program_search_fmts; + fmts_len = types->program_search_fmts_len; + for (fmt = fmts; fmt; fmt = argz_next (fmts, fmts_len, fmt)) + /* XXX this only works for trailing %s */ + if (sscanf (program, fmt, typename) == 1) + { + /* This format matches the program and yields the type name. + Create a new entry for this type. */ + + type = malloc (sizeof (struct fstype)); + if (! type) + return ENOMEM; + type->name = strdup (typename); + if (type->name == 0) + { + free (type); + return ENOMEM; + } + type->program = strdup (program); + if (type->program == 0) + { + free (type->name); + free (type); + return ENOMEM; + } + type->next = types->entries; + types->entries = type; + + *fstype = type; + return 0; + } + + /* We could find no program search format that could have yielded this + program name. */ + *fstype = 0; + return 0; +} +#endif /* Copy MNTENT into FS, copying component strings as well. */ error_t -fs_set_mntent (struct fs *fs, struct mntent *mntent) +fs_set_mntent (struct fs *fs, const struct mntent *mntent) { char *end; size_t needed = 0; @@ -180,7 +260,8 @@ fs_set_mntent (struct fs *fs, struct mntent *mntent) if (! fs->storage) return ENOMEM; - if (strcmp (fs->mntent.mnt_dir, mntent->mnt_dir) != 0) + if (!fs->mntent.mnt_dir || !mntent->mnt_dir + || strcmp (fs->mntent.mnt_dir, mntent->mnt_dir) != 0) { fs->mounted = fs->readonly = -1; if (fs->fsys != MACH_PORT_NULL) @@ -194,20 +275,28 @@ fs_set_mntent (struct fs *fs, struct mntent *mntent) /* Copy each mntent field from MNTENT into FS's version. */ end = fs->storage; #define STORE(field) \ - fs->mntent.field = end; end = stpcpy (end, mntent->field) + 1 + if (mntent->field) \ + { \ + fs->mntent.field = end; \ + end = stpcpy (end, mntent->field) + 1; \ + } \ + else \ + fs->mntent.field = 0; STORE (mnt_fsname); STORE (mnt_dir); STORE (mnt_type); STORE (mnt_opts); #undef STORE - if (fs->type && strcasecmp (fs->type->name, mntent->mnt_type) != 0) + if (fs->type + && (!mntent->mnt_type + || strcasecmp (fs->type->name, mntent->mnt_type) != 0)) fs->type = 0; /* Type is different. */ return 0; } -/* Returns an fstype for FS in TYPE, trying to fillin FS's type field if +/* Returns an fstype for FS in TYPE, trying to fill in FS's type field if necessary. */ error_t fs_type (struct fs *fs, struct fstype **type) @@ -257,7 +346,7 @@ _fs_check_mounted (struct fs *fs) the mntent, but oh well, nothing we can do about that.] */ { err = file_get_translator_cntl (mount_point, &fs->fsys); - if (err == EINVAL || err == EOPNOTSUPP) + if (err == EINVAL || err == EOPNOTSUPP || err == ENXIO) /* Either the mount point doesn't exist, or wasn't mounted. */ { fs->fsys = MACH_PORT_NULL; @@ -318,10 +407,12 @@ fs_readonly (struct fs *fs, int *readonly) err = fs_fsys (fs, &fsys); if (! err) - if (fsys == MACH_PORT_NULL) - fs->readonly = 1; - else - err = fsys_get_readonly (fsys, &fs->readonly); + { + if (fsys == MACH_PORT_NULL) + fs->readonly = 1; + else + err = fsys_get_readonly (fsys, &fs->readonly); + } } if (!err && readonly) @@ -354,7 +445,7 @@ fs_set_readonly (struct fs *fs, int readonly) return err; } -/* If FS is currently mounted tell lit to remount the device. XXX If FS is +/* If FS is currently mounted tell it to remount the device. XXX If FS is not mounted at all, then nothing is done. */ error_t fs_remount (struct fs *fs) @@ -362,14 +453,14 @@ fs_remount (struct fs *fs) fsys_t fsys; error_t err = fs_fsys (fs, &fsys); if (!err && fsys != MACH_PORT_NULL) /* XXX What to do if not mounted? */ - err = fsys_remount (fsys); + err = fsys_update (fsys); return err; } /* Returns the FS entry in FSTAB with the device field NAME (there can only be one such entry). */ inline struct fs * -fstab_find_device (struct fstab *fstab, char *name) +fstab_find_device (const struct fstab *fstab, const char *name) { struct fs *fs; for (fs = fstab->entries; fs; fs = fs->next) @@ -381,9 +472,20 @@ fstab_find_device (struct fstab *fstab, char *name) /* Returns the FS entry in FSTAB with the mount point NAME (there can only be one such entry). */ inline struct fs * -fstab_find_mount (struct fstab *fstab, char *name) +fstab_find_mount (const struct fstab *fstab, const char *name) { struct fs *fs; + + /* Don't count "none" or "-" as matching any other mount point. + It is canonical to use "none" for swap partitions, and multiple + such do not in fact conflict with each other. Likewise, the + special device name "ignore" is used for things that should not + be processed automatically. */ + if (!strcmp (name, "-") + || !strcmp (name, "none") + || !strcmp (name, "ignore")) + return 0; + for (fs = fstab->entries; fs; fs = fs->next) if (strcmp (fs->mntent.mnt_dir, name) == 0) return fs; @@ -393,9 +495,31 @@ fstab_find_mount (struct fstab *fstab, char *name) /* Returns the FS entry in FSTAB with the device or mount point NAME (there can only be one such entry). */ inline struct fs * -fstab_find (struct fstab *fstab, char *name) +fstab_find (const struct fstab *fstab, const char *name) { - return fstab_find_device (fstab, name) ?: fstab_find_mount (fstab, name); + struct fs *ret; + const char *real_name; + + ret = fstab_find_device (fstab, name); + if (ret) + return ret; + + ret = fstab_find_mount (fstab, name); + if (ret) + return ret; + + real_name = realpath (name, NULL); + + ret = fstab_find_device (fstab, real_name); + if (ret) { + free (real_name); + return ret; + } + + ret = fstab_find_mount (fstab, real_name); + free (real_name); + + return ret; } /* Cons FS onto the beginning of FSTAB's entry list. */ @@ -426,7 +550,7 @@ fs_free (struct fs *fs) conflict (in either the device or mount point). If RESULT is non-zero, the new entry is returne in it. */ error_t -fstab_add_mntent (struct fstab *fstab, struct mntent *mntent, +fstab_add_mntent (struct fstab *const fstab, const struct mntent *mntent, struct fs **result) { int new = 0; /* True if we didn't overwrite an old entry. */ @@ -441,7 +565,7 @@ fstab_add_mntent (struct fstab *fstab, struct mntent *mntent, fs = mounted_fs; mounted_fs = 0; } - + if (! fs) /* No old entry, make a new one. */ { @@ -462,15 +586,17 @@ fstab_add_mntent (struct fstab *fstab, struct mntent *mntent, err = fs_set_mntent (fs, mntent); if (new) - if (! err) - _fstab_add (fstab, fs); - else if (fs) - free (fs); + { + if (! err) + _fstab_add (fstab, fs); + else if (fs) + free (fs); + } - if (!err && mounted_fs) + if (!err && mounted_fs && mounted_fs != fs) /* Get rid of the conflicting entry MOUNTED_FS. */ fs_free (mounted_fs); - + if (!err && result) *result = fs; @@ -481,7 +607,7 @@ fstab_add_mntent (struct fstab *fstab, struct mntent *mntent, DST. If DST & SRC have different TYPES fields, EINVAL is returned. If COPY is non-zero, the copy is returned in it. */ error_t -fstab_add_fs (struct fstab *dst, struct fs *fs, struct fs **copy) +fstab_add_fs (struct fstab *dst, const struct fs *fs, struct fs **copy) { error_t err; struct fs *new; @@ -525,7 +651,7 @@ fstab_merge (struct fstab *dst, struct fstab *src) if (old_fs) fs_free (old_fs); } - + /* Now that we know there are no conflicts, steal all SRC's entries and cons them onto DST. */ for (fs = src->entries; fs; fs = fs->next) @@ -540,7 +666,7 @@ fstab_merge (struct fstab *dst, struct fstab *src) /* Reads fstab-format entries into FSTAB from the file NAME. Any entries duplicating one already in FS_LIST supersede the existing entry. */ error_t -fstab_read (struct fstab *fstab, char *name) +fstab_read (struct fstab *fstab, const char *name) { error_t err; /* Used to hold entries from the file, before merging with FSTAB at the @@ -583,7 +709,7 @@ fstab_read (struct fstab *fstab, char *name) /* Return the next pass number that applies to any filesystem in FSTAB that is greater than PASS, or -1 if there isn't any. */ -int fstab_next_pass (struct fstab *fstab, int pass) +int fstab_next_pass (const struct fstab *fstab, int pass) { int next_pass = -1; struct fs *fs; @@ -597,3 +723,262 @@ int fstab_next_pass (struct fstab *fstab, int pass) } return next_pass; } + + +static const struct argp_option options[] = +{ + {"all", 'a', 0, 0, "Do all filesystems in " _PATH_MNTTAB}, + {0, 'A', 0, OPTION_ALIAS }, + {"fstab", 'F', "FILE", 0, "File to use instead of " _PATH_MNTTAB}, + {"fstype", 't', "TYPE", 0, "Do only filesystems of given type(s)"}, + {"exclude-root",'R',0, 0, + "Exclude root (/) filesystem from " _PATH_MNTTAB " list"}, + {"exclude", 'X', "PATTERN", 0, "Exclude directories matching PATTERN"}, + + {"search-fmts",'S', "FMTS", 0, + "`:' separated list of formats to use for finding" + " filesystem-specific programs"}, + + {0, 0} +}; + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + error_t err; + struct fstab_argp_params *params = state->input; + + switch (key) + { + case ARGP_KEY_INIT: + /* Initialize our parsing state. */ + if (! params) + return EINVAL; /* Need at least a way to return a result. */ + bzero (params, sizeof *params); + break; + + case 'A': + case 'a': + params->do_all = 1; + break; + + case 'F': + params->fstab_path = arg; + break; + + case 'S': + argz_create_sep (arg, ':', + ¶ms->program_search_fmts, + ¶ms->program_search_fmts_len); + break; + + case 'R': + arg = "/"; + /* FALLTHROUGH */ + case 'X': + err = argz_add (¶ms->exclude, ¶ms->exclude_len, arg); + if (err) + argp_failure (state, 100, ENOMEM, "%s", arg); + break; + case 't': + err = argz_add_sep (¶ms->types, ¶ms->types_len, arg, ','); + if (err) + argp_failure (state, 100, ENOMEM, "%s", arg); + break; + + case ARGP_KEY_ARG: + err = argz_add (¶ms->names, ¶ms->names_len, arg); + if (err) + argp_failure (state, 100, ENOMEM, "%s", arg); + break; + + case ARGP_KEY_END: + /* Check for bogus combinations of arguments. */ + if (params->names) + { + if (params->do_all) + argp_error (state, "filesystem arguments not allowed with --all"); + if (params->exclude) + argp_error (state, + "--exclude not allowed with filesystem arguments"); + if (params->types) + argp_error (state, + "--fstype not allowed with filesystem arguments"); + } + break; + + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +const struct argp fstab_argp = {options, parse_opt, 0, 0}; + +struct fstab * +fstab_argp_create (struct fstab_argp_params *params, + const char *default_search_fmts, + size_t default_search_fmts_len) +{ + error_t err; + struct fstab *fstab, *check; + struct fstypes *types; + + if (params->fstab_path == 0) + params->fstab_path = _PATH_MNTTAB; + if (params->program_search_fmts == 0) + { + params->program_search_fmts = (char *) default_search_fmts; + params->program_search_fmts_len = default_search_fmts_len; + } + + err = fstypes_create (params->program_search_fmts, + params->program_search_fmts_len, + &types); + if (err) + error (102, err, "fstypes_create"); + + err = fstab_create (types, &fstab); + if (err) + error (101, err, "fstab_create"); + + err = fstab_read (fstab, params->fstab_path); + if (err) + error (103, err, "%s", params->fstab_path); + + if (params->names) + { + /* Process specified filesystems; also look at /var/run/mtab. */ + const char *name; + + err = fstab_read (fstab, _PATH_MOUNTED); + if (err && err != ENOENT) + error (104, err, "%s", _PATH_MOUNTED); + + err = fstab_create (types, &check); + if (err) + error (105, err, "fstab_create"); + + for (name = params->names; name; name = argz_next (params->names, + params->names_len, + name)) + { + struct fs *fs = fstab_find (fstab, name); + if (! fs) + error (106, 0, "%s: Unknown device or filesystem", name); + fstab_add_fs (check, fs, 0); + } + + /* fstab_free (fstab); XXX */ + } + else + { + /* Process everything in /etc/fstab. */ + + if (params->exclude == 0 && params->types == 0) + check = fstab; + else + { + struct fs *fs; + const char *tn; + unsigned int nonexclude_types; + + err = fstab_create (types, &check); + if (err) + error (105, err, "fstab_create"); + + /* For each excluded type (i.e. `-t notype'), clobber the + fstype entry's program with an empty string to mark it. */ + nonexclude_types = 0; + for (tn = params->types; tn; + tn = argz_next (params->types, params->types_len, tn)) + { + if (!strncasecmp (tn, "no", 2)) + { + struct fstype *type; + err = fstypes_get (types, &tn[2], &type); + if (err) + error (106, err, "fstypes_get"); + free (type->program); + type->program = strdup (""); + } + else + ++nonexclude_types; + } + + if (nonexclude_types != 0) + { + const char *tn; + struct fstypes *wanttypes; + + /* We will copy the types we want to include into a fresh + list in WANTTYPES. Since we specify no search formats, + `fstypes_get' applied to WANTTYPES can only create + elements with a null `program' field. */ + err = fstypes_create (0, 0, &wanttypes); + if (err) + error (102, err, "fstypes_create"); + + for (tn = params->types; tn; + tn = argz_next (params->types, params->types_len, tn)) + if (strncasecmp (tn, "no", 2)) + { + struct fstype *type; + err = fstypes_get (types, tn, &type); + if (err) + error (106, err, "fstypes_get"); + if (type->program == 0) + error (0, 0, + "requested filesystem type `%s' unknown", tn); + else + { + struct fstype *newtype = malloc (sizeof *newtype); + newtype->name = strdup (type->name); + newtype->program = strdup (type->program); + newtype->next = wanttypes->entries; + wanttypes->entries = newtype; + } + } + + /* fstypes_free (types); */ + types = wanttypes; + } + + for (fs = fstab->entries; fs; fs = fs->next) + { + const char *ptn; + struct fstype *type; + + err = fs_type (fs, &type); + if (err || nonexclude_types) + { + err = fstypes_get (types, fs->mntent.mnt_type, &type); + if (err) + error (106, err, "fstypes_get"); + if (params->types != 0) + continue; + } + if (nonexclude_types && type->program == 0) + continue; /* Freshly created, was not in WANTTYPES. */ + if (type->program != 0 && type->program[0] == '\0') + continue; /* This type is marked as excluded. */ + + for (ptn = params->exclude; ptn; + ptn = argz_next (params->exclude, params->exclude_len, ptn)) + if (fnmatch (ptn, fs->mntent.mnt_dir, 0) == 0) + break; + if (ptn) /* An exclude pattern matched. */ + continue; + + err = fstab_add_fs (check, fs, 0); + if (err) + error (107, err, "fstab_add_fs"); + } + + /* fstab_free (fstab); XXX */ + } + } + + return check; +} |