/* * umount_one_bw: unmount FILE that has last occurrence MC0 * * Why this loop? * 1. People who boot a system with a bad fstab root entry * will get an incorrect "/dev/foo on /" in mtab. * If later /dev/foo is actually mounted elsewhere, * it will occur twice in mtab. * 2. With overmounting one can get the situation that * the same filename is used as mount point twice. * In both cases, it is best to try the last occurrence first. */ static int umount_one_bw (const char *file, struct mntentchn *mc0) { struct mntentchn *mc; int res = 1; mc = mc0; while (res && mc) { res = umount_one(mc->m.mnt_fsname, mc->m.mnt_dir, mc->m.mnt_type, mc->m.mnt_opts, mc); mc = getmntdirbackward(file, mc); } mc = mc0; while (res && mc) { res = umount_one(mc->m.mnt_fsname, mc->m.mnt_dir, mc->m.mnt_type, mc->m.mnt_opts, mc); mc = getmntdevbackward(file, mc); } return res; }
/* * Why this loop? * 1. People who boot a system with a bad fstab root entry * will get an incorrect "/dev/foo on /" in mtab. * If later /dev/foo is actually mounted elsewhere, * it will occur twice in mtab. * 2. With overmounting one can get the situation that * the same filename is used as mount point twice. * In both cases, it is best to try the last occurrence first. */ static int umount_one_bw (const char *file, struct mntentchn *mc) { int res = 1; while (res && mc) { res = umount_one(mc->mnt_fsname, mc->mnt_dir, mc->mnt_type, mc->mnt_opts, mc); mc = getmntfilesbackward (file, mc); } return res; }
/* Unmount all filesystems of type VFSTYPES found in mtab. Since we are concurrently updating mtab after every succesful umount, we have to slurp in the entire file before we start. This isn't too bad, because in any case it's important to umount mtab entries in reverse order to mount, e.g. /usr/spool before /usr. */ int umount_all (string_list types) { struct mntentchn *mc, *hd; int errors = 0; hd = mtab_head(); if (!hd->prev) die (2, "umount: cannot find list of filesystems to unmount"); for (mc = hd->prev; mc != hd; mc = mc->prev) { if (matching_type (mc->mnt_type, types)) { errors |= umount_one (mc->mnt_fsname, mc->mnt_dir, mc->mnt_type, mc->mnt_opts); } } sync (); return errors; }
static int umount_partitions(const char *disk, int checkonly) { struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY; dev_t devno; DIR *dir = NULL; struct dirent *d; int count = 0; devno = sysfs_devname_to_devno(disk, NULL); if (sysfs_init(&cxt, devno, NULL) != 0) return 0; /* open /sys/block/<wholedisk> */ if (!(dir = sysfs_opendir(&cxt, NULL))) goto done; /* scan for partition subdirs */ while ((d = readdir(dir))) { if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) continue; if (sysfs_is_partition_dirent(dir, d, disk)) { char *mnt = NULL; char *dev = find_device(d->d_name); if (dev && device_get_mountpoint(&dev, &mnt) == 0) { verbose(_("%s: mounted on %s"), dev, mnt); if (!checkonly) umount_one(mnt); count++; } free(dev); free(mnt); } } done: if (dir) closedir(dir); sysfs_deinit(&cxt); return count; }
/* Unmount all filesystems of type VFSTYPES found in mtab. Since we are concurrently updating mtab after every successful umount, we have to slurp in the entire file before we start. This isn't too bad, because in any case it's important to umount mtab entries in reverse order to mount, e.g. /usr/spool before /usr. */ static int umount_all (char *types, char *test_opts) { struct mntentchn *mc, *hd; int errors = 0; hd = mtab_head(); if (!hd->prev) die (2, _("umount: cannot find list of filesystems to unmount")); for (mc = hd->prev; mc != hd; mc = mc->prev) { if (matching_type (mc->m.mnt_type, types) && matching_opts (mc->m.mnt_opts, test_opts)) { errors |= umount_one (mc->m.mnt_fsname, mc->m.mnt_dir, mc->m.mnt_type, mc->m.mnt_opts, mc); } } sync (); return errors; }
/* main program */ int main(int argc, char **argv) { char *device = NULL; char *disk = NULL; char *mountpoint = NULL; int worked = 0; /* set to 1 when successfully ejected */ int fd; /* file descriptor for device */ setlocale(LC_ALL,""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); atexit(close_stdout); /* parse the command line arguments */ parse_args(argc, argv, &device); /* handle -d option */ if (d_option) { info(_("default device: `%s'"), EJECT_DEFAULT_DEVICE); return EXIT_SUCCESS; } if (!device) { device = mnt_resolve_path(EJECT_DEFAULT_DEVICE, NULL); verbose(_("using default device `%s'"), device); } else { char *p; if (device[strlen(device)-1] == '/') device[strlen(device)-1] = '\0'; /* figure out full device or mount point name */ p = find_device(device); if (p) free(device); else p = device; device = mnt_resolve_spec(p, NULL); free(p); } if (!device) errx(EXIT_FAILURE, _("%s: unable to find device"), device); verbose(_("device name is `%s'"), device); device_get_mountpoint(&device, &mountpoint); if (mountpoint) verbose(_("%s: mounted on %s"), device, mountpoint); else verbose(_("%s: not mounted"), device); disk = get_disk_devname(device); if (disk) { verbose(_("%s: disc device: %s (disk device will be used for eject)"), device, disk); free(device); device = disk; disk = NULL; } else { struct stat st; if (stat(device, &st) != 0 || !S_ISBLK(st.st_mode)) errx(EXIT_FAILURE, _("%s: not found mountpoint or device " "with the given name"), device); verbose(_("%s: is whole-disk device"), device); } if (F_option == 0 && is_hotpluggable(device) == 0) errx(EXIT_FAILURE, _("%s: is not hot-pluggable device"), device); /* handle -n option */ if (n_option) { info(_("device is `%s'"), device); verbose(_("exiting due to -n/--noop option")); return EXIT_SUCCESS; } /* handle -i option */ if (i_option) { fd = open_device(device); manual_eject(fd, i_arg); return EXIT_SUCCESS; } /* handle -a option */ if (a_option) { if (a_arg) verbose(_("%s: enabling auto-eject mode"), device); else verbose(_("%s: disabling auto-eject mode"), device); fd = open_device(device); auto_eject(fd, a_arg); return EXIT_SUCCESS; } /* handle -t option */ if (t_option) { verbose(_("%s: closing tray"), device); fd = open_device(device); close_tray(fd); set_device_speed(device); return EXIT_SUCCESS; } /* handle -T option */ if (T_option) { verbose(_("%s: toggling tray"), device); fd = open_device(device); toggle_tray(fd); set_device_speed(device); return EXIT_SUCCESS; } /* handle -X option */ if (X_option) { verbose(_("%s: listing CD-ROM speed"), device); fd = open_device(device); list_speeds(device, fd); return EXIT_SUCCESS; } /* handle -x option only */ if (!c_option) set_device_speed(device); /* * Unmount all partitions if -m is not specified; or umount given * mountpoint if -M is specified, otherwise print error of another * partition is mounted. */ if (!m_option) { int ct = umount_partitions(device, M_option); if (ct == 0 && mountpoint) umount_one(mountpoint); /* probably whole-device */ if (M_option) { if (ct == 1 && mountpoint) umount_one(mountpoint); else if (ct) errx(EXIT_FAILURE, _("error: %s: device in use"), device); } } /* handle -c option */ if (c_option) { verbose(_("%s: selecting CD-ROM disc #%ld"), device, c_arg); fd = open_device(device); changer_select(fd, c_arg); set_device_speed(device); return EXIT_SUCCESS; } /* if user did not specify type of eject, try all four methods */ if (r_option + s_option + f_option + q_option == 0) r_option = s_option = f_option = q_option = 1; /* open device */ fd = open_device(device); /* try various methods of ejecting until it works */ if (r_option) { verbose(_("%s: trying to eject using CD-ROM eject command"), device); worked = eject_cdrom(fd); verbose(worked ? _("CD-ROM eject command succeeded") : _("CD-ROM eject command failed")); } if (s_option && !worked) { verbose(_("%s: trying to eject using SCSI commands"), device); worked = eject_scsi(fd); verbose(worked ? _("SCSI eject succeeded") : _("SCSI eject failed")); } if (f_option && !worked) { verbose(_("%s: trying to eject using floppy eject command"), device); worked = eject_floppy(fd); verbose(worked ? _("floppy eject command succeeded") : _("floppy eject command failed")); } if (q_option && !worked) { verbose(_("%s: trying to eject using tape offline command"), device); worked = eject_tape(fd); verbose(worked ? _("tape offline command succeeded") : _("tape offline command failed")); } if (!worked) errx(EXIT_FAILURE, _("unable to eject")); /* cleanup */ close(fd); free(device); free(mountpoint); mnt_unref_table(mtab); return EXIT_SUCCESS; }
int this_was_main_int_mount_c (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; while ((c = getopt_long (argc, argv, "afhnrvVt:", longopts, NULL)) != EOF) switch (c) { case 'a': /* umount everything */ ++all; break; case 'f': /* force umount (needs kernel support) */ #if 0 ++force; #else die (2, "umount: forced umount not supported yet"); #endif break; case 'h': /* help */ usage (stdout, 0); break; case 'n': ++umount_nomtab; break; case 'r': /* remount read-only if umount fails */ ++remount; break; case 'v': /* make noise */ ++umount_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 ()) { umount_suid = 1; if (all || types || umount_nomtab) die (2, "umount: only root can do that"); } argc -= optind; argv += optind; if (all) { if (types == NULL) types = parse_list(xstrdup("noproc")); result = umount_all (types); } else if (argc < 1) { usage (stderr, 2); } else while (argc--) { file = canonicalize (*argv); /* mtab paths are canonicalized */ if (umount_verbose > 1) printf("Trying to umount %s\n", file); mc = getmntfile (file); if (!mc && umount_verbose) printf("Could not find %s in mtab\n", file); if (umount_suid) { if (!mc) die (2, "umount: %s is not mounted (according to mtab)", file); if (!(fs = getfsspec (file)) && !(fs = getfsfile (file))) die (2, "umount: %s is not in the fstab (and you are not root)", file); if ((!streq (mc->mnt_fsname, fs->mnt_fsname) && !streq (mc->mnt_fsname, canonicalize (fs->mnt_fsname))) || (!streq (mc->mnt_dir, fs->mnt_dir) && !streq (mc->mnt_dir, canonicalize (fs->mnt_dir)))) { die (2, "umount: %s mount disagrees with the fstab", file); } options = parse_list (fs->mnt_opts); while (options) { if (streq (car (options), "user")) break; options = cdr (options); } if (!options) die (2, "umount: only root can unmount %s from %s", fs->mnt_fsname, fs->mnt_dir); } if (mc) result = umount_one (xstrdup(mc->mnt_fsname), xstrdup(mc->mnt_dir), xstrdup(mc->mnt_type), xstrdup(mc->mnt_opts)); else result = umount_one (*argv, *argv, *argv, *argv); argv++; } exit (result); }
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); }