static int umount_file (char *arg) { struct mntentchn *mc, *fs; const char *file, *options; int fstab_has_user, fstab_has_users, fstab_has_owner, fstab_has_group; int ok; if (!*arg) { /* "" would be expanded to `pwd` */ die(2, _("Cannot umount \"\"\n")); return 0; } file = canonicalize(arg); /* mtab paths are canonicalized */ if (verbose > 1) printf(_("Trying to umount %s\n"), file); mc = getmntdirbackward(file, NULL); if (!mc) { mc = getmntdevbackward(file, NULL); if (mc) { struct mntentchn *mc1; mc1 = getmntdirbackward(mc->m.mnt_dir, NULL); if (!mc1) /* 'mc1' must exist, though not necessarily equals to `mc'. Otherwise we go mad. */ die(EX_SOFTWARE, _("umount: confused when analyzing mtab")); if (strcmp(file, mc1->m.mnt_fsname)) { /* Something was stacked over `file' on the same mount point. */ die(EX_FAIL, _("umount: cannot umount %s -- %s is " "mounted over it on the same point."), file, mc1->m.mnt_fsname); } } } if (!mc && verbose) printf(_("Could not find %s in mtab\n"), file); if (restricted) { char *mtab_user = NULL; if (!mc) die(2, _("umount: %s is not mounted (according to mtab)"), file); /* * uhelper - unprivileged umount helper * -- external umount (for example HAL mounts) */ if (external_allowed) { char *uhelper = NULL; if (mc->m.mnt_opts) uhelper = get_value(mc->m.mnt_opts, "uhelper="); if (uhelper) { int status = 0; if (check_special_umountprog(arg, arg, uhelper, &status)) return status; } } /* The 2.4 kernel will generally refuse to mount the same filesystem on the same mount point, but will accept NFS. So, unmounting must be possible. */ if (!is_mounted_once(file) && strcmp(mc->m.mnt_type,"nfs")) die(2, _("umount: it seems %s is mounted multiple times"), file); /* If fstab contains the two lines /dev/sda1 /mnt/zip auto user,noauto 0 0 /dev/sda4 /mnt/zip auto user,noauto 0 0 then "mount /dev/sda4" followed by "umount /mnt/zip" used to fail. So, we must not look for file, but for the pair (dev,file) in fstab. */ fs = getfs_by_devdir(mc->m.mnt_fsname, mc->m.mnt_dir); if (!fs) { fs = getfs_by_dir(file); if (!fs && !getfs_by_spec(file)) die (2, _("umount: %s is not in the fstab " "(and you are not root)"), file); /* spec could be a file which is loop mounted */ if (fs && !is_valid_loop(mc, fs)) die (2, _("umount: %s mount disagrees with " "the fstab"), file); } /* * User mounting and unmounting is allowed only * if fstab contains one of the options `user', * `users' or `owner' or `group'. * * The option `users' allows arbitrary users to mount * and unmount - this may be a security risk. * * The options `user', `owner' and `group' only allow * unmounting by the user that mounted (visible in mtab). */ options = fs->m.mnt_opts; if (!options) options = ""; fstab_has_user = contains(options, "user"); fstab_has_users = contains(options, "users"); fstab_has_owner = contains(options, "owner"); fstab_has_group = contains(options, "group"); ok = 0; if (fstab_has_users) ok = 1; if (!ok && (fstab_has_user || fstab_has_owner || fstab_has_group)) { char *user = getusername(); options = mc->m.mnt_opts; if (!options) options = ""; mtab_user = get_value(options, "user="******"umount: only %s can unmount %s from %s"), mtab_user ? mtab_user : "******", fs->m.mnt_fsname, fs->m.mnt_dir); } if (mc) return umount_one_bw (file, mc); else return umount_one (arg, arg, arg, arg, NULL); }
int main (int argc, char *argv[]) { int c; int all = 0; string_list types = NULL; string_list options; struct mntentchn *mc, *fs; char *file; int result = 0; setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); while ((c = getopt_long (argc, argv, "afhnrt:vV", longopts, NULL)) != EOF) switch (c) { case 'a': /* umount everything */ ++all; break; case 'f': /* force umount */ ++force; break; case 'h': /* help */ usage (stdout, 0); break; case 'n': /* do not write in /etc/mtab */ ++nomtab; break; case 'r': /* remount read-only if umount fails */ ++remount; break; case 'v': /* make noise */ ++verbose; break; case 'V': /* version */ printf ("umount: %s\n", version); exit (0); case 't': /* specify file system type */ types = parse_list (optarg); break; case 0: break; case '?': default: usage (stderr, 1); } if (getuid () != geteuid ()) { suid = 1; if (all || types || nomtab || force) die (2, _("umount: only root can do that")); } argc -= optind; argv += optind; if (all) { if (types == NULL) types = parse_list("noproc"); result = umount_all (types); } else if (argc < 1) { usage (stderr, 2); } else while (argc--) { file = canonicalize (*argv); /* mtab paths are canonicalized */ if (verbose > 1) printf(_("Trying to umount %s\n"), file); mc = getmntfilesbackward (file, NULL); if (!mc && verbose) printf(_("Could not find %s in mtab\n"), file); if (suid) { if (!mc) die (2, _("umount: %s is not mounted (according to mtab)"), file); if (getmntfilesbackward (file, mc)) die (2, _("umount: it seems %s is mounted multiple times"), file); /* If fstab contains the two lines /dev/sda1 /mnt/zip auto user,noauto 0 0 /dev/sda4 /mnt/zip auto user,noauto 0 0 then "mount /dev/sda4" followed by "umount /mnt/zip" used to fail. So, we must not look for file, but for the pair (spec,file) in fstab. */ fs = getfsspecfile(mc->mnt_fsname, mc->mnt_dir); if (!fs) { if (!getfsspec (file) && !getfsfile (file)) die (2, _("umount: %s is not in the fstab (and you are not root)"), file); else die (2, _("umount: %s mount disagrees with the fstab"), file); } /* User mounting and unmounting is allowed only if fstab contains the option `user' or `users' */ /* The option `users' allows arbitrary users to mount and unmount - this may be a security risk. */ /* The option `user' only allows unmounting by the user that mounted. */ /* The option `owner' only allows (un)mounting by the owner. */ /* A convenient side effect is that the user who mounted is visible in mtab. */ options = parse_list (fs->mnt_opts); while (options) { if (streq (car (options), "user") || streq (car (options), "users") || streq (car (options), "owner")) break; options = cdr (options); } if (!options) die (2, _("umount: only root can unmount %s from %s"), fs->mnt_fsname, fs->mnt_dir); if (streq (car (options), "user") || streq (car (options), "owner")) { char *user = getusername(); options = parse_list (mc->mnt_opts); while (options) { char *co = car (options); if (!strncmp(co, "user="******"umount: only %s can unmount %s from %s"), co+5, fs->mnt_fsname, fs->mnt_dir); break; } options = cdr (options); } } } if (mc) result = umount_one_bw (file, mc); else result = umount_one (*argv, *argv, *argv, *argv, NULL); argv++; } exit (result); }