/* Roughly Unix/Linux-compatible `umount' frontend for Hurd translators. Copyright (C) 2013 Free Software Foundation, Inc. Written by Justus Winter <4winter@informatik.uni-hamburg.de> This file is part of the GNU Hurd. The GNU Hurd 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; either version 2, or (at your option) any later version. The GNU Hurd 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. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include "match-options.h" #include "../sutils/fstab.h" /* XXX fix libc */ #undef _PATH_MOUNTED #define _PATH_MOUNTED "/etc/mtab" static char *targets; static size_t targets_len; static int readonly; static int verbose; static int active_flags = FS_TRANS_SET; static int goaway_flags; static int source_goaway; static int fake; static struct fstab_argp_params fstab_params; #define FAKE_KEY 0x80 /* !isascii (FAKE_KEY), so no short option. */ static const struct argp_option argp_opts[] = { {NULL, 'd', 0, 0, "Also ask the source translator to go away"}, {"fake", FAKE_KEY, 0, 0, "Do not actually umount, just pretend"}, {"force", 'f', 0, 0, "Force umount by killing the translator"}, {"no-mtab", 'n', 0, 0, "Do not update /etc/mtab"}, {"read-only", 'r', 0, 0, "If unmounting fails, try to remount read-only"}, {"nosync", 'S', 0, 0, "Don't sync a translator before killing it"}, {"test-opts", 'O', "OPTIONS", 0, "Only mount fstab entries matching the given set of options"}, {"verbose", 'v', 0, 0, "Give more detailed information"}, {}, }; static error_t parse_opt (int key, char *arg, struct argp_state *state) { struct fstab_argp_params *params = state->input; error_t err; switch (key) { case ARGP_KEY_INIT: state->child_inputs[0] = params; /* pass down to fstab_argp parser */ break; case 'd': source_goaway = 1; break; case FAKE_KEY: fake = 1; break; case 'f': goaway_flags |= FSYS_GOAWAY_FORCE; break; case 'n': /* do nothing */ break; case 'r': readonly = 1; break; case 'S': goaway_flags |= FSYS_GOAWAY_NOSYNC; break; case 'O': err = argz_create_sep (arg, ',', &test_opts, &test_opts_len); if (err) argp_failure (state, 100, ENOMEM, "%s", arg); break; case 'v': verbose += 1; break; case ARGP_KEY_ARG: err = argz_add (&targets, &targets_len, arg); if (err) argp_failure (state, 100, ENOMEM, "%s", arg); break; case ARGP_KEY_NO_ARGS: if (! params->do_all) { argp_error (state, "filesystem argument required if --all is not given"); return EINVAL; } break; case ARGP_KEY_END: if (params->do_all && targets) { argp_error (state, "filesystem argument not allowed with --all"); return EINVAL; } break; default: return ARGP_ERR_UNKNOWN; } return 0; } static const char doc[] = "Stop active filesystem translators"; static const char args_doc[] = "DEVICE|DIRECTORY [DEVICE|DIRECTORY ...]"; static struct argp fstab_argp_mtab; /* Slightly modified version. */ static const struct argp_child argp_kids[] = { {&fstab_argp_mtab, 0, "Filesystem selection (if no explicit filesystem arguments given):", 2}, {}, }; static struct argp argp = { options: argp_opts, parser: parse_opt, args_doc: args_doc, doc: doc, children: argp_kids, }; /* This is a trimmed and slightly modified version of fstab_argp.options which uses _PATH_MOUNTED instead of _PATH_MNTTAB in the doc strings. */ static const struct argp_option fstab_argp_mtab_opts[] = { {"all", 'a', 0, 0, "Do all filesystems in " _PATH_MOUNTED}, {0, 'A', 0, OPTION_ALIAS }, {"fstab", 'F', "FILE", 0, "File to use instead of " _PATH_MOUNTED}, {"fstype", 't', "TYPE", 0, "Do only filesystems of given type(s)"}, {"exclude-root",'R',0, 0, "Exclude root (/) filesystem from " _PATH_MOUNTED " list"}, {"exclude", 'X', "PATTERN", 0, "Exclude directories matching PATTERN"}, {} }; static error_t fstab_argp_mtab_parse_opt (int key, char *arg, struct argp_state *state) { return fstab_argp.parser (key, arg, state); } static struct argp fstab_argp_mtab = { options: fstab_argp_mtab_opts, parser: fstab_argp_mtab_parse_opt, }; /* Unmount one filesystem. */ static error_t do_umount (struct fs *fs) { error_t err = 0; file_t node = file_name_lookup (fs->mntent.mnt_dir, O_NOTRANS, 0666); if (node == MACH_PORT_NULL) { error (0, errno, "%s", fs->mntent.mnt_dir); return errno; } if (verbose) printf ("settrans -ag%s%s %s\n", goaway_flags & FSYS_GOAWAY_NOSYNC? "S": "", goaway_flags & FSYS_GOAWAY_FORCE? "f": "", fs->mntent.mnt_dir); if (! fake) { err = file_set_translator (node, 0, active_flags, goaway_flags, NULL, 0, MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND); if (! err) { if (strcmp (fs->mntent.mnt_fsname, "") != 0 && strcmp (fs->mntent.mnt_fsname, "none") != 0) { if (verbose) printf ("settrans -ag%s%s %s\n", goaway_flags & FSYS_GOAWAY_NOSYNC? "S": "", goaway_flags & FSYS_GOAWAY_FORCE? "f": "", fs->mntent.mnt_fsname); file_t source = file_name_lookup (fs->mntent.mnt_fsname, O_NOTRANS, 0666); if (source == MACH_PORT_NULL) { error (0, errno, "%s", fs->mntent.mnt_fsname); return errno; } err = file_set_translator (source, 0, active_flags, goaway_flags, NULL, 0, MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND); if (err) error (0, err, "%s", fs->mntent.mnt_fsname); mach_port_deallocate (mach_task_self (), source); } } else { error (0, err, "%s", fs->mntent.mnt_dir); /* Try remounting readonly instead if requested. */ if (readonly) { if (verbose) printf ("fsysopts %s --readonly\n", fs->mntent.mnt_dir); error_t e = fs_set_readonly (fs, TRUE); if (e) error (0, e, "%s", fs->mntent.mnt_dir); } } } /* Deallocate the reference so that unmounting nested translators works properly. */ mach_port_deallocate (mach_task_self (), node); return err; } int main (int argc, char **argv) { error_t err; err = argp_parse (&argp, argc, argv, 0, 0, &fstab_params); if (err) error (3, err, "parsing arguments"); /* Read the mtab file by default. */ if (! fstab_params.fstab_path) fstab_params.fstab_path = _PATH_MOUNTED; struct fstab *fstab = fstab_argp_create (&fstab_params, NULL, 0); if (! fstab) error (3, ENOMEM, "fstab creation"); if (targets) for (char *t = targets; t; t = argz_next (targets, targets_len, t)) { /* Figure out if t is the device or the mountpoint. */ struct fs *fs = fstab_find_mount (fstab, t); if (! fs) { fs = fstab_find_device (fstab, t); if (! fs) { error (0, 0, "could not find entry for: %s", t); /* As last resort, just assume it is the mountpoint. */ struct mntent m = { mnt_fsname: "", mnt_dir: t, mnt_type: "", mnt_opts: 0, mnt_freq: 0, mnt_passno: 0, }; err = fstab_add_mntent (fstab, &m, &fs); if (err) error (2, err, "%s", t); } } if (fs) err |= do_umount (fs); } else { /* Sort entries in reverse lexicographical order so that the longest mount points are unmounted first. This makes sure that nested mounts are handled properly. */ size_t count = 0; for (struct fs *fs = fstab->entries; fs; fs = fs->next) count += 1; char **entries = malloc (count * sizeof (char *)); if (! entries) error (3, ENOMEM, "allocating entries array"); char **p = entries; for (struct fs *fs = fstab->entries; fs; fs = fs->next) *p++ = fs->mntent.mnt_dir; /* Reverse lexicographical order. */ int compare_entries (const void *a, const void *b) { return -strcmp ((char *) a, (char *) b); } qsort (entries, count, sizeof (char *), compare_entries); for (int i = 0; i < count; i++) { struct fs *fs = fstab_find_mount (fstab, entries[i]); if (! fs) error (4, 0, "could not find entry for: %s", entries[i]); if (! match_options (&fs->mntent)) continue; err |= do_umount (fs); } } return err? EXIT_FAILURE: EXIT_SUCCESS; }