hush/util-linux/umount.c
Rob Landley 4470b74e29 The kernel can't handle umount /dev/hdc, we have to do it through mtab,
except that we still have to work when there is no mtab.

Oh, and while we're at it, take advantage of the fact that modern processors
avoid branches via conditional assignment where possible.  ("x = a ? b : c;"
turns into "x = c; if (a) x = b;" because that way there's no branch to
potentially mispredict and thus never a bubble in the pipeline.  The if(a)
turns into an assembly test followed by a conditional assignment (rather
than a conditional jump).)  So since the compiler is going to do that _anyway_,
we might as well take advantage of it to produce a slightly smaller binary.

So there.
2006-08-17 19:07:20 +00:00

152 lines
4.2 KiB
C

/* vi: set sw=4 ts=4: */
/*
* Mini umount implementation for busybox
*
* Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
* Copyright (C) 2005 by Rob Landley <rob@landley.net>
*
* This program is licensed under the GNU General Public license (GPL)
* version 2 or later, see http://www.fsf.org/licensing/licenses/gpl.html
* or the file "LICENSE" in the busybox source tarball for the full text.
*
*/
#include "busybox.h"
#include <mntent.h>
#include <getopt.h>
#define OPTION_STRING "flDnravd"
#define OPT_FORCE 1
#define OPT_LAZY 2
#define OPT_DONTFREELOOP 4
#define OPT_NO_MTAB 8
#define OPT_REMOUNT 16
#define OPT_ALL (ENABLE_FEATURE_UMOUNT_ALL ? 32 : 0)
int umount_main(int argc, char **argv)
{
int doForce;
char path[2*PATH_MAX];
struct mntent me;
FILE *fp;
int status = EXIT_SUCCESS;
unsigned long opt;
struct mtab_list {
char *dir;
char *device;
struct mtab_list *next;
} *mtl, *m;
/* Parse any options */
opt = bb_getopt_ulflags(argc, argv, OPTION_STRING);
argc -= optind;
argv += optind;
doForce = MAX((opt & OPT_FORCE), (opt & OPT_LAZY));
/* Get a list of mount points from mtab. We read them all in now mostly
* for umount -a (so we don't have to worry about the list changing while
* we iterate over it, or about getting stuck in a loop on the same failing
* entry. Notice that this also naturally reverses the list so that -a
* umounts the most recent entries first. */
m = mtl = 0;
/* If we're umounting all, then m points to the start of the list and
* the argument list should be empty (which will match all). */
if (!(fp = setmntent(bb_path_mtab_file, "r"))) {
if (opt & OPT_ALL)
bb_error_msg_and_die("Cannot open %s", bb_path_mtab_file);
} else while (getmntent_r(fp,&me,path,sizeof(path))) {
m = xmalloc(sizeof(struct mtab_list));
m->next = mtl;
m->device = xstrdup(me.mnt_fsname);
m->dir = xstrdup(me.mnt_dir);
mtl = m;
}
endmntent(fp);
/* If we're not mounting all, we need at least one argument. */
if (!(opt & OPT_ALL)) {
m = 0;
if (!argc) bb_show_usage();
}
// Loop through everything we're supposed to umount, and do so.
for (;;) {
int curstat;
char *zapit = *argv;
// Do we already know what to umount this time through the loop?
if (m) safe_strncpy(path, m->dir, PATH_MAX);
// For umount -a, end of mtab means time to exit.
else if (opt & OPT_ALL) break;
// Get next command line argument (and look it up in mtab list)
else if (!argc--) break;
else {
argv++;
realpath(zapit, path);
for (m = mtl; m; m = m->next)
if (!strcmp(path, m->dir) || !strcmp(path, m->device))
break;
}
// If we couldn't find this sucker in /etc/mtab, punt by passing our
// command line argument straight to the umount syscall. Otherwise,
// umount the directory even if we were given the block device.
if (m) zapit = m->dir;
// Let's ask the thing nicely to unmount.
curstat = umount(zapit);
// Force the unmount, if necessary.
if (curstat && doForce) {
curstat = umount2(zapit, doForce);
if (curstat)
bb_error_msg_and_die("forced umount of %s failed!", zapit);
}
// If still can't umount, maybe remount read-only?
if (curstat && (opt & OPT_REMOUNT) && errno == EBUSY && m) {
curstat = mount(m->device, zapit, NULL, MS_REMOUNT|MS_RDONLY, NULL);
bb_error_msg(curstat ? "Cannot remount %s read-only" :
"%s busy - remounted read-only", m->device);
}
if (curstat) {
status = EXIT_FAILURE;
bb_perror_msg("Couldn't umount %s", zapit);
} else {
/* De-allocate the loop device. This ioctl should be ignored on
* any non-loop block devices. */
if (ENABLE_FEATURE_MOUNT_LOOP && !(opt & OPT_DONTFREELOOP) && m)
del_loop(m->device);
if (ENABLE_FEATURE_MTAB_SUPPORT && !(opt & OPT_NO_MTAB) && m)
erase_mtab(m->dir);
}
// Find next matching mtab entry for -a or umount /dev
// Note this means that "umount /dev/blah" will unmount all instances
// of /dev/blah, not just the most recent.
while (m && (m = m->next))
if ((opt & OPT_ALL) || !strcmp(path,m->device))
break;
}
// Free mtab list if necessary
if (ENABLE_FEATURE_CLEAN_UP) {
while (mtl) {
m = mtl->next;
free(mtl->device);
free(mtl->dir);
free(mtl);
mtl=m;
}
}
return status;
}