/watchdog-5.12/src/umount.c
C | 452 lines | 342 code | 51 blank | 59 comment | 100 complexity | 7754aa5648f097dd3d1da1376d4b872a MD5 | raw file
- /* $Header: /cvsroot/watchdog/watchdog/src/umount.c,v 1.2 2006/07/31 09:39:23 meskes Exp $ */
- /*
- * A umount(8) for Linux 0.99.
- * umount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
- *
- * Wed Sep 14 22:43:54 1994: Sebastian Lederer
- * (lederer@next-pc.informatik.uni-bonn.de) added support for sending an
- * unmount RPC call to the server when an NFS-filesystem is unmounted.
- *
- * Tue Sep 26 16:33:09 1995: Added patches from Greg Page (greg@caldera.com)
- * so that NetWare filesystems can be unmounted.
- *
- * 951213: Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>:
- * Ignore any RPC errors, so that you can umount an nfs mounted filesystem
- * if the server is down.
- *
- * 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
- * 970726: aeb - remount read-only in cases where umount fails
- */
- #include <unistd.h>
- #include <getopt.h>
- #include <stdio.h>
- #include <string.h>
- #include <errno.h>
- #include <sys/stat.h>
- #include <sys/mount.h>
- #include "mount_constants.h"
- #include "sundries.h"
- #include "lomount.h"
- #include "loop.h"
- #include "fstab.h"
- #if HAVE_NFS
- #include <sys/socket.h>
- #include <sys/time.h>
- #include <netdb.h>
- #include <rpc/rpc.h>
- #include <rpc/pmap_clnt.h>
- #include <rpc/pmap_prot.h>
- #include "nfsmount.h"
- #include <arpa/inet.h>
- #endif
- #ifdef notyet
- /* Nonzero for force umount (-f). This needs kernel support we don't have. */
- int force = 0;
- #endif
- /* When umount fails, attempt a read-only remount (-r). */
- int remount = 0;
- /* Don't write a entry in /etc/mtab (-n). */
- int umount_nomtab = 0;
- /* Nonzero for chatty (-v). This is a nonstandard flag (not in BSD). */
- int umount_verbose = 0;
- /* True if ruid != euid. */
- int umount_suid = 0;
- #if HAVE_NFS
- static int xdr_dir(XDR *xdrsp, char *dirp)
- {
- return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
- }
- static int
- nfs_umount_rpc_call(const char *spec, const char *opts)
- {
- register CLIENT *clp;
- struct sockaddr_in saddr;
- struct timeval pertry, try;
- enum clnt_stat clnt_stat;
- int so = RPC_ANYSOCK;
- struct hostent *hostp;
- char *hostname;
- char *dirname;
- char *p;
- if (spec == NULL || (p = strchr(spec,':')) == NULL)
- return 0;
- hostname = xstrndup(spec, p-spec);
- dirname = xstrdup(p+1);
- #ifdef DEBUG
- printf("host: %s, directory: %s\n", hostname, dirname);
- #endif
- if (opts && (p = strstr(opts, "addr="))) {
- char *q;
- free(hostname);
- p += 5;
- q = p;
- while (*q && *q != ',') q++;
- hostname = xstrndup(p,q-p);
- }
- if (hostname[0] >= '0' && hostname[0] <= '9')
- saddr.sin_addr.s_addr = inet_addr(hostname);
- else {
- if ((hostp = gethostbyname(hostname)) == NULL) {
- fprintf(stderr, "umount: can't get address for %s\n",
- hostname);
- return 1;
- }
- if (hostp->h_length > sizeof(struct in_addr)) {
- fprintf(stderr, "umount: got bad hostp->h_length\n");
- hostp->h_length = sizeof(struct in_addr);
- }
- memcpy(&saddr.sin_addr, hostp->h_addr, hostp->h_length);
- }
- saddr.sin_family = AF_INET;
- saddr.sin_port = 0;
- pertry.tv_sec = 3;
- pertry.tv_usec = 0;
- if ((clp = clntudp_create(&saddr, MOUNTPROG, MOUNTVERS,
- pertry, &so)) == NULL) {
- clnt_pcreateerror("Cannot MOUNTPROG RPC");
- return (1);
- }
- clp->cl_auth = authunix_create_default();
- try.tv_sec = 20;
- try.tv_usec = 0;
- clnt_stat = clnt_call(clp, MOUNTPROC_UMNT,
- (xdrproc_t) xdr_dir, dirname,
- (xdrproc_t) xdr_void, (caddr_t) 0,
- try);
- if (clnt_stat != RPC_SUCCESS) {
- clnt_perror(clp, "Bad UMNT RPC");
- return (1);
- }
- auth_destroy(clp->cl_auth);
- clnt_destroy(clp);
- return (0);
- }
- #endif /* HAVE_NFS */
- /* complain about a failed umount */
- static void complain(int err, const char *dev) {
- switch (err) {
- case ENXIO:
- error ("umount: %s: invalid block device", dev); break;
- case EINVAL:
- error ("umount: %s: not mounted", dev); break;
- case EIO:
- error ("umount: %s: can't write superblock", dev); break;
- case EBUSY:
- /* Let us hope fstab has a line "proc /proc ..."
- and not "none /proc ..."*/
- error ("umount: %s: device is busy", dev); break;
- case ENOENT:
- error ("umount: %s: not found", dev); break;
- case EPERM:
- error ("umount: %s: must be superuser to umount", dev); break;
- case EACCES:
- error ("umount: %s: block devices not permitted on fs", dev); break;
- default:
- error ("umount: %s: %s", dev, strerror (err)); break;
- }
- }
- /* Umount a single device. Return a status code, so don't exit
- 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)
- {
- 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)
- umount_nomtab++;
- #if 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);
- #endif
-
- umnt_err = umnt_err2 = 0;
- 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 (umount_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 && strchr(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 (umount_verbose)
- printf ("%s umounted\n", spec);
- if (!umount_nomtab && mtab_is_writable()) {
- struct mntentchn *mc;
- /* Special stuff for loop devices */
- if ((mc = getmntfile (spec)) || (mc = getmntfile (node))) {
- char *opts;
- /* old style mtab line? */
- if (streq(mc->mnt_type, "loop"))
- if (del_loop(spec))
- goto fail;
- /* new style mtab line? */
- opts = mc->mnt_opts ? xstrdup(mc->mnt_opts) : "";
- for (opts = strtok (opts, ","); opts; opts = strtok (NULL, ",")) {
- if (!strncmp(opts, "loop=", 5)) {
- if (del_loop(opts+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 (!umount_nomtab) {
- /* remove obsolete entry */
- if (umnt_err == EINVAL || umnt_err == ENOENT)
- update_mtab (node, NULL);
- }
- if (umnt_err2)
- complain(umnt_err2, spec);
- if (umnt_err && umnt_err != umnt_err2)
- complain(umnt_err, node);
- return 1;
- }
- /* 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;
- }
- extern char version[];
- static struct option longopts[] =
- {
- { "all", 0, 0, 'a' },
- { "force", 0, 0, 'f' },
- { "help", 0, 0, 'h' },
- { "no-mtab", 0, 0, 'n' },
- { "umount_verbose", 0, 0, 'v' },
- { "version", 0, 0, 'V' },
- { "read-only", 0, 0, 'r' },
- { "types", 1, 0, 't' },
- { NULL, 0, 0, 0 }
- };
- char *umount_usage_string = "\
- usage: umount [-hV]\n\
- umount -a [-r] [-n] [-v] [-t vfstypes]\n\
- umount [-r] [-n] [-v] special | node...\n\
- ";
- static void
- usage (FILE *fp, int n)
- {
- fprintf (fp, "%s", umount_usage_string);
- exit (n);
- }
- int umount_mount_quiet = 0;
- 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);
- }