diff options
Diffstat (limited to 'mount/umount.c')
-rw-r--r-- | mount/umount.c | 297 |
1 files changed, 162 insertions, 135 deletions
diff --git a/mount/umount.c b/mount/umount.c index 98a1d330a..24622bc98 100644 --- a/mount/umount.c +++ b/mount/umount.c @@ -16,18 +16,21 @@ * 960223: aeb - several minor changes * 960324: aeb - added some changes from Rob Leslie <rob@mars.org> * 960823: aeb - also try umount(spec) when umount(node) fails - * 970307: aeb - canonise names from fstab + * 970307: aeb - canonicalise names from fstab * 970726: aeb - remount read-only in cases where umount fails * 980810: aeb - umount2 support * 981222: aeb - If mount point or special file occurs several times * in mtab, try them all, with last one tried first * - Differentiate "user" and "users" key words in fstab + * 001202: aeb - remove at most one line from /etc/mtab */ +#include <stdio.h> #include <unistd.h> #include <getopt.h> #include <string.h> #include <errno.h> +#include <ctype.h> #include <sys/stat.h> #include <sys/mount.h> #include "mount_constants.h" @@ -88,7 +91,10 @@ int remount = 0; /* Don't write a entry in /etc/mtab (-n). */ int nomtab = 0; -/* Nonzero for chatty (-v). This is a nonstandard flag (not in BSD). */ +/* Call losetup -d for each unmounted loop device. */ +int delloop = 0; + +/* Nonzero for chatty (-v). */ int verbose = 0; /* True if ruid != euid. */ @@ -107,6 +113,7 @@ nfs_umount_rpc_call(const char *spec, const char *opts) struct sockaddr_in saddr; struct timeval pertry, try; enum clnt_stat clnt_stat; + int port = 0; int so = RPC_ANYSOCK; struct hostent *hostp; char *hostname; @@ -131,6 +138,9 @@ nfs_umount_rpc_call(const char *spec, const char *opts) hostname = xstrndup(p,q-p); } + if (opts && (p = strstr(opts, "mountport=")) && isdigit(*(p+10))) + port = atoi(p+10); + if (hostname[0] >= '0' && hostname[0] <= '9') saddr.sin_addr.s_addr = inet_addr(hostname); else { @@ -147,7 +157,7 @@ nfs_umount_rpc_call(const char *spec, const char *opts) } saddr.sin_family = AF_INET; - saddr.sin_port = 0; + saddr.sin_port = htons(port); pertry.tv_sec = 3; pertry.tv_usec = 0; if ((clp = clntudp_create(&saddr, MOUNTPROG, MOUNTVERS, @@ -202,134 +212,147 @@ static void complain(int err, const char *dev) { on a non-fatal error. We lock/unlock around each umount. */ static int umount_one (const char *spec, const char *node, const char *type, - const char *opts, struct mntentchn *mc) -{ - int umnt_err, umnt_err2; - int isroot; - int res; - - /* Special case for root. As of 0.99pl10 we can (almost) unmount root; - the kernel will remount it readonly so that we can carry on running - afterwards. The readonly remount is illegal if any files are opened - for writing at the time, so we can't update mtab for an unmount of - root. As it is only really a remount, this doesn't matter too - much. [sct May 29, 1993] */ - isroot = (streq (node, "/") || streq (node, "root") - || streq (node, "rootfs")); - if (isroot) - nomtab++; + const char *opts, struct mntentchn *mc) { + int umnt_err, umnt_err2; + int isroot; + int res; + const char *loopdev; + + /* Special case for root. As of 0.99pl10 we can (almost) unmount root; + the kernel will remount it readonly so that we can carry on running + afterwards. The readonly remount is illegal if any files are opened + for writing at the time, so we can't update mtab for an unmount of + root. As it is only really a remount, this doesn't matter too + much. [sct May 29, 1993] */ + isroot = (streq (node, "/") || streq (node, "root") + || streq (node, "rootfs")); + if (isroot) + nomtab++; #ifdef HAVE_NFS - /* Ignore any RPC errors, so that you can umount the filesystem - if the server is down. */ - if (strcasecmp(type, "nfs") == 0) - nfs_umount_rpc_call(spec, opts); + /* Ignore any RPC errors, so that you can umount the filesystem + if the server is down. */ + if (strcasecmp(type, "nfs") == 0) + nfs_umount_rpc_call(spec, opts); #endif - umnt_err = umnt_err2 = 0; - if (force) { - /* completely untested - 2.1.116 only has some support in nfs case */ - /* probably this won't work */ - int flags = MNT_FORCE; - - res = umount2 (node, flags); - if (res == -1) { - perror("umount2"); - if (errno == ENOSYS) { - if (verbose) - printf(_("no umount2, trying umount...\n")); - res = umount (node); - } - } - } else - res = umount (node); - if (res < 0) { - umnt_err = errno; - /* A device might have been mounted on a node that has since - been deleted or renamed, so if node fails, also try spec. */ - /* if (umnt_err == ENOENT || umnt_err == EINVAL) */ - if (umnt_err != EBUSY && strcmp(node, spec)) { - if (verbose) - printf (_("could not umount %s - trying %s instead\n"), - node, spec); - res = umount (spec); - if (res < 0) - umnt_err2 = errno; - /* Do not complain about remote NFS mount points */ - if (errno == ENOENT && index(spec, ':')) - umnt_err2 = 0; - } - } - - if (res < 0 && remount && (umnt_err == EBUSY || umnt_err2 == EBUSY)) { - /* Umount failed - let us try a remount */ - res=mount(spec, node, NULL, MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL); - if (res == 0) { - struct mntent remnt; - fprintf(stderr, _("umount: %s busy - remounted read-only\n"), spec); - remnt.mnt_type = remnt.mnt_fsname = NULL; - remnt.mnt_dir = xstrdup(node); - remnt.mnt_opts = "ro"; - update_mtab(node, &remnt); - return 0; - } else if (errno != EBUSY) { /* hmm ... */ - perror("remount"); - fprintf(stderr, _("umount: could not remount %s read-only\n"), - spec); - } - } - - if (res >= 0) { - /* Umount succeeded, update mtab. */ - if (verbose) - printf (_("%s umounted\n"), spec); - - if (!nomtab && mtab_is_writable()) { - /* Special stuff for loop devices */ - if (mc) { - char *optl; - - /* old style mtab line? */ - if (streq(mc->mnt_type, "loop")) - if (del_loop(spec)) - goto fail; - - /* new style mtab line? */ - optl = mc->mnt_opts ? xstrdup(mc->mnt_opts) : ""; - for (optl = strtok (optl, ","); optl; optl = strtok (NULL, ",")) { - if (!strncmp(optl, "loop=", 5)) { - if (del_loop(optl+5)) - goto fail; - break; - } - } - } else { - /* maybe spec is a loop device? */ - /* no del_loop() - just delete it from mtab */ - if ((mc = getmntoptfile (spec)) != NULL) - node = mc->mnt_dir; - } - - /* Non-loop stuff */ - update_mtab (node, NULL); - } - return 0; - } - -fail: - /* Umount or del_loop failed, complain, but don't die. */ - if (!nomtab) { - /* remove obsolete entry */ - if (umnt_err == EINVAL || umnt_err == ENOENT) - update_mtab (node, NULL); - } + umnt_err = umnt_err2 = 0; + if (force) { + /* completely untested; + 2.1.116 only has some support in nfs case */ + /* probably this won't work */ + int flags = MNT_FORCE; + + res = umount2 (node, flags); + if (res == -1) { + perror("umount2"); + if (errno == ENOSYS) { + if (verbose) + printf(_("no umount2, trying umount...\n")); + res = umount (node); + } + } + } else + res = umount (node); + + if (res < 0) { + umnt_err = errno; + /* A device might have been mounted on a node that has since + been deleted or renamed, so if node fails, also try spec. */ + /* Note that this is incorrect in case spec was mounted + several times. */ + /* if (umnt_err == ENOENT || umnt_err == EINVAL) */ + if (umnt_err != EBUSY && strcmp(node, spec)) { + if (verbose) + printf (_("could not umount %s - trying %s instead\n"), + node, spec); + res = umount (spec); + if (res < 0) + umnt_err2 = errno; + /* Do not complain about remote NFS mount points */ + if (errno == ENOENT && index(spec, ':')) + umnt_err2 = 0; + } + } + + if (res < 0 && remount && (umnt_err == EBUSY || umnt_err2 == EBUSY)) { + /* Umount failed - let us try a remount */ + res = mount(spec, node, NULL, + MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL); + if (res == 0) { + struct mntent remnt; + fprintf(stderr, + _("umount: %s busy - remounted read-only\n"), + spec); + remnt.mnt_type = remnt.mnt_fsname = NULL; + remnt.mnt_dir = xstrdup(node); + remnt.mnt_opts = "ro"; + update_mtab(node, &remnt); + return 0; + } else if (errno != EBUSY) { /* hmm ... */ + perror("remount"); + fprintf(stderr, + _("umount: could not remount %s read-only\n"), + spec); + } + } + + loopdev = 0; + if (res >= 0) { + /* Umount succeeded */ + if (verbose) + printf (_("%s umounted\n"), spec); + + /* Free any loop devices that we allocated ourselves */ + if (mc) { + char *optl; + + /* old style mtab line? */ + if (streq(mc->m.mnt_type, "loop")) { + loopdev = spec; + goto gotloop; + } + + /* new style mtab line? */ + optl = mc->m.mnt_opts ? xstrdup(mc->m.mnt_opts) : ""; + for (optl = strtok (optl, ","); optl; + optl = strtok (NULL, ",")) { + if (!strncmp(optl, "loop=", 5)) { + loopdev = optl+5; + goto gotloop; + } + } + } else { + /* + * If option "-o loop=spec" occurs in mtab, + * note the mount point, and delete mtab line. + */ + if ((mc = getmntoptfile (spec)) != NULL) + node = mc->m.mnt_dir; + } + + /* Also free loop devices when -d flag is given */ + if (delloop && is_loop_device(spec)) + loopdev = spec; + } + gotloop: + if (loopdev) + del_loop(loopdev); + + if (!nomtab && mtab_is_writable() && + (umnt_err == 0 || umnt_err == EINVAL || umnt_err == ENOENT)) { + update_mtab (node, NULL); + } + + if (res >= 0) + return 0; - if (umnt_err2) - complain(umnt_err2, spec); - if (umnt_err && umnt_err != umnt_err2) - complain(umnt_err, node); - return 1; + if (umnt_err2) + complain(umnt_err2, spec); + if (umnt_err && umnt_err != umnt_err2) + complain(umnt_err, node); + return 1; } /* @@ -347,8 +370,8 @@ 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); + res = umount_one(mc->m.mnt_fsname, mc->m.mnt_dir, + mc->m.mnt_type, mc->m.mnt_opts, mc); mc = getmntfilesbackward (file, mc); } return res; @@ -368,9 +391,9 @@ umount_all (string_list types) { 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, mc); + if (matching_type (mc->m.mnt_type, types)) { + errors |= umount_one (mc->m.mnt_fsname, mc->m.mnt_dir, + mc->m.mnt_type, mc->m.mnt_opts, mc); } } @@ -424,6 +447,10 @@ main (int argc, char *argv[]) case 'a': /* umount everything */ ++all; break; + /* fall through? */ + case 'd': /* do losetup -d for unmounted loop devices */ + ++delloop; + break; case 'f': /* force umount */ ++force; break; @@ -489,7 +516,7 @@ main (int argc, char *argv[]) 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); + fs = getfsspecfile(mc->m.mnt_fsname, mc->m.mnt_dir); if (!fs) { if (!getfsspec (file) && !getfsfile (file)) die (2, @@ -508,7 +535,7 @@ main (int argc, char *argv[]) /* 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); + options = parse_list (fs->m.mnt_opts); while (options) { if (streq (car (options), "user") || streq (car (options), "users") || @@ -518,18 +545,18 @@ main (int argc, char *argv[]) } if (!options) die (2, _("umount: only root can unmount %s from %s"), - fs->mnt_fsname, fs->mnt_dir); + fs->m.mnt_fsname, fs->m.mnt_dir); if (streq (car (options), "user") || streq (car (options), "owner")) { char *user = getusername(); - options = parse_list (mc->mnt_opts); + options = parse_list (mc->m.mnt_opts); while (options) { char *co = car (options); if (!strncmp(co, "user=", 5)) { if (!user || !streq(co+5,user)) die(2, _("umount: only %s can unmount %s from %s"), - co+5, fs->mnt_fsname, fs->mnt_dir); + co+5, fs->m.mnt_fsname, fs->m.mnt_dir); break; } options = cdr (options); |