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