busybox/util-linux/umount.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini umount implementation for busybox
   4 *
   5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
   6 * Copyright (C) 2005 by Rob Landley <rob@landley.net>
   7 *
   8 * Licensed under GPL version 2, see file LICENSE in this tarball for details.
   9 */
  10
  11#include <mntent.h>
  12#include "libbb.h"
  13
  14#if defined(__dietlibc__)
  15/* 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
  16 * dietlibc-0.30 does not have implementation of getmntent_r() */
  17static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
  18                char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
  19{
  20        struct mntent* ment = getmntent(stream);
  21        return memcpy(result, ment, sizeof(*ment));
  22}
  23#endif
  24
  25/* ignored: -v -d -t -i */
  26#define OPTION_STRING           "fldnra" "vdt:i"
  27#define OPT_FORCE               (1 << 0)
  28#define OPT_LAZY                (1 << 1)
  29#define OPT_FREELOOP            (1 << 2)
  30#define OPT_NO_MTAB             (1 << 3)
  31#define OPT_REMOUNT             (1 << 4)
  32#define OPT_ALL                 (ENABLE_FEATURE_UMOUNT_ALL ? (1 << 5) : 0)
  33
  34// These constants from linux/fs.h must match OPT_FORCE and OPT_LAZY,
  35// otherwise "doForce" trick below won't work!
  36//#define MNT_FORCE  0x00000001 /* Attempt to forcibly umount */
  37//#define MNT_DETACH 0x00000002 /* Just detach from the tree */
  38
  39int umount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  40int umount_main(int argc UNUSED_PARAM, char **argv)
  41{
  42        int doForce;
  43        char *const path = xmalloc(PATH_MAX + 2); /* to save stack */
  44        struct mntent me;
  45        FILE *fp;
  46        char *fstype = NULL;
  47        int status = EXIT_SUCCESS;
  48        unsigned opt;
  49        struct mtab_list {
  50                char *dir;
  51                char *device;
  52                struct mtab_list *next;
  53        } *mtl, *m;
  54
  55        opt = getopt32(argv, OPTION_STRING, &fstype);
  56        //argc -= optind;
  57        argv += optind;
  58        doForce = MAX((opt & OPT_FORCE), (opt & OPT_LAZY));
  59
  60        /* Get a list of mount points from mtab.  We read them all in now mostly
  61         * for umount -a (so we don't have to worry about the list changing while
  62         * we iterate over it, or about getting stuck in a loop on the same failing
  63         * entry.  Notice that this also naturally reverses the list so that -a
  64         * umounts the most recent entries first. */
  65        m = mtl = NULL;
  66
  67        // If we're umounting all, then m points to the start of the list and
  68        // the argument list should be empty (which will match all).
  69        fp = setmntent(bb_path_mtab_file, "r");
  70        if (!fp) {
  71                if (opt & OPT_ALL)
  72                        bb_error_msg_and_die("can't open '%s'", bb_path_mtab_file);
  73        } else {
  74                while (getmntent_r(fp, &me, path, PATH_MAX)) {
  75                        /* Match fstype if passed */
  76                        if (!match_fstype(&me, fstype))
  77                                continue;
  78                        m = xzalloc(sizeof(*m));
  79                        m->next = mtl;
  80                        m->device = xstrdup(me.mnt_fsname);
  81                        m->dir = xstrdup(me.mnt_dir);
  82                        mtl = m;
  83                }
  84                endmntent(fp);
  85        }
  86
  87        // If we're not umounting all, we need at least one argument.
  88        if (!(opt & OPT_ALL) && !fstype) {
  89                if (!argv[0])
  90                        bb_show_usage();
  91                m = NULL;
  92        }
  93
  94        // Loop through everything we're supposed to umount, and do so.
  95        for (;;) {
  96                int curstat;
  97                char *zapit = *argv;
  98
  99                // Do we already know what to umount this time through the loop?
 100                if (m)
 101                        safe_strncpy(path, m->dir, PATH_MAX);
 102                // For umount -a, end of mtab means time to exit.
 103                else if (opt & OPT_ALL)
 104                        break;
 105                // Use command line argument (and look it up in mtab list)
 106                else {
 107                        if (!zapit)
 108                                break;
 109                        argv++;
 110                        realpath(zapit, path);
 111                        for (m = mtl; m; m = m->next)
 112                                if (!strcmp(path, m->dir) || !strcmp(path, m->device))
 113                                        break;
 114                }
 115                // If we couldn't find this sucker in /etc/mtab, punt by passing our
 116                // command line argument straight to the umount syscall.  Otherwise,
 117                // umount the directory even if we were given the block device.
 118                if (m) zapit = m->dir;
 119
 120                // Let's ask the thing nicely to unmount.
 121                curstat = umount(zapit);
 122
 123                // Force the unmount, if necessary.
 124                if (curstat && doForce)
 125                        curstat = umount2(zapit, doForce);
 126
 127                // If still can't umount, maybe remount read-only?
 128                if (curstat) {
 129                        if ((opt & OPT_REMOUNT) && errno == EBUSY && m) {
 130                                // Note! Even if we succeed here, later we should not
 131                                // free loop device or erase mtab entry!
 132                                const char *msg = "%s busy - remounted read-only";
 133                                curstat = mount(m->device, zapit, NULL, MS_REMOUNT|MS_RDONLY, NULL);
 134                                if (curstat) {
 135                                        msg = "can't remount %s read-only";
 136                                        status = EXIT_FAILURE;
 137                                }
 138                                bb_error_msg(msg, m->device);
 139                        } else {
 140                                status = EXIT_FAILURE;
 141                                bb_perror_msg("can't %sumount %s", (doForce ? "forcibly " : ""), zapit);
 142                        }
 143                } else {
 144                        // De-allocate the loop device.  This ioctl should be ignored on
 145                        // any non-loop block devices.
 146                        if (ENABLE_FEATURE_MOUNT_LOOP && (opt & OPT_FREELOOP) && m)
 147                                del_loop(m->device);
 148                        if (ENABLE_FEATURE_MTAB_SUPPORT && !(opt & OPT_NO_MTAB) && m)
 149                                erase_mtab(m->dir);
 150                }
 151
 152                // Find next matching mtab entry for -a or umount /dev
 153                // Note this means that "umount /dev/blah" will unmount all instances
 154                // of /dev/blah, not just the most recent.
 155                if (m) while ((m = m->next) != NULL)
 156                        if ((opt & OPT_ALL) || !strcmp(path, m->device))
 157                                break;
 158        }
 159
 160        // Free mtab list if necessary
 161        if (ENABLE_FEATURE_CLEAN_UP) {
 162                while (mtl) {
 163                        m = mtl->next;
 164                        free(mtl->device);
 165                        free(mtl->dir);
 166                        free(mtl);
 167                        mtl = m;
 168                }
 169                free(path);
 170        }
 171
 172        return status;
 173}
 174