+/* vi: set sw=4 ts=4: */
/*
* Mini umount implementation for busybox
*
- *
- * Copyright (C) 1999 by Lineo, inc.
- * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 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 "internal.h"
+#include <limits.h>
#include <stdio.h>
-#include <sys/mount.h>
#include <mntent.h>
-#include <fstab.h>
#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include "busybox.h"
-#if defined BB_FEATURE_MOUNT_LOOP
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <linux/loop.h>
-
-static int del_loop(const char *device);
-#endif
-
-static const char umount_usage[] =
-"Usage: umount [flags] filesystem|directory\n\n"
-"Flags:\n"
-"\t-a:\tUnmount all file systems"
-#ifdef BB_MTAB
-" in /etc/mtab\n\t-n:\tDon't erase /etc/mtab entries\n"
-#else
-"\n"
-#endif
-;
-
-
-static int useMtab = TRUE;
-static int umountAll = FALSE;
-extern const char mtab_file[]; /* Defined in utility.c */
-
-static int
-do_umount(const char* name, int useMtab)
+extern int umount_main(int argc, char **argv)
{
- int status;
-
-#if defined BB_FEATURE_MOUNT_LOOP
- /* check to see if this is a loop device */
- struct stat fst;
- char dev[20];
- const char *oldname = NULL;
- int i;
-
- if (stat(name, &fst)) {
- fprintf(stderr, "umount: %s: %s\n", name, strerror(errno));
- exit(1);
- }
- for (i = 0 ; i <= 7 ; i++) {
- struct stat lst;
- sprintf(dev, "/dev/loop%d", i);
- if (stat(dev, &lst))
- continue;
- if (lst.st_dev == fst.st_dev) {
- oldname = name;
- name = dev;
- break;
+ int doForce = 0;
+ int freeLoop = ENABLE_FEATURE_MOUNT_LOOP;
+ int useMtab = ENABLE_FEATURE_MTAB_SUPPORT;
+ int umountAll = FALSE;
+ int doRemount = FALSE;
+ char path[2*PATH_MAX];
+ struct mntent me;
+ FILE *fp;
+ int status=EXIT_SUCCESS;
+ struct mtab_list {
+ char *dir;
+ char *device;
+ struct mtab_list *next;
+ } *mtl, *m;
+
+ if(argc < 2) bb_show_usage();
+
+ /* Parse any options */
+ while (--argc > 0 && **(++argv) == '-') {
+ while (*++(*argv)) {
+ if(**argv=='a') umountAll = TRUE;
+ else if(ENABLE_FEATURE_MOUNT_LOOP && **argv=='D') freeLoop = FALSE;
+ else if(ENABLE_FEATURE_MTAB_SUPPORT && **argv=='n') useMtab = FALSE;
+ else if(**argv=='f') doForce = 1; // MNT_FORCE
+ else if(**argv=='l') doForce = 2; // MNT_DETACH
+ else if(**argv=='r') doRemount = TRUE;
+ else if(**argv=='v');
+ else bb_show_usage();
+ }
}
- }
-#endif
-
- status = umount(name);
-#if defined BB_FEATURE_MOUNT_LOOP
- if (!strncmp("/dev/loop", name, 9)) { /* this was a loop device, delete it */
- del_loop(name);
- if (oldname != NULL)
- name = oldname;
- }
-#endif
-#if defined BB_MTAB
- if ( status == 0 ) {
- if ( useMtab==TRUE )
- erase_mtab(name);
- return 0;
- }
- else
-#endif
- return(status);
-}
+ /* 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(!(fp = setmntent(bb_path_mtab_file, "r")))
+ bb_error_msg_and_die("Cannot open %s", bb_path_mtab_file);
+ while (getmntent_r(fp,&me,path,sizeof(path))) {
+ m=xmalloc(sizeof(struct mtab_list));
+ m->next=mtl;
+ m->device=bb_xstrdup(me.mnt_fsname);
+ m->dir=bb_xstrdup(me.mnt_dir);
+ mtl=m;
+ }
+ endmntent(fp);
+
+ /* 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(!umountAll) m=0;
+
+ // Loop through everything we're supposed to umount, and do so.
+ for(;;) {
+ int curstat;
+
+ // Do we alrady know what to umount this time through the loop?
+ if(m) safe_strncpy(path,m->dir,PATH_MAX);
+ // For umountAll, end of mtab means time to exit.
+ else if(umountAll) break;
+ // Get next command line argument (and look it up in mtab list)
+ else if(!argc--) break;
+ else {
+ // Get next command line argument (and look it up in mtab list)
+ realpath(*argv++, path);
+ for(m = mtl; m; m = m->next)
+ if(!strcmp(path, m->dir) || !strcmp(path, m->device))
+ break;
+ }
-static int
-umount_all(int useMtab)
-{
- int status;
- struct mntent *m;
- FILE *mountTable;
+ // Let's ask the thing nicely to unmount.
+ curstat = umount(path);
- if ((mountTable = setmntent (mtab_file, "r"))) {
- while ((m = getmntent (mountTable)) != 0) {
- char *blockDevice = m->mnt_fsname;
-#if ! defined BB_MTAB
- if (strcmp (blockDevice, "/dev/root") == 0) {
- struct fstab* fstabItem;
- fstabItem = getfsfile ("/");
- if (fstabItem != NULL) {
- blockDevice = fstabItem->fs_spec;
- }
+ // Force the unmount, if necessary.
+ if(curstat && doForce) {
+ curstat = umount2(path, doForce);
+ if(curstat)
+ bb_error_msg_and_die("forced umount of %s failed!", path);
}
-#endif
- /* Don't umount /proc when doing umount -a */
- if (strcmp (blockDevice, "proc") == 0)
- continue;
- status=do_umount (m->mnt_dir, useMtab);
- if (status!=0) {
- /* Don't bother retrying the umount on busy devices */
- if (errno==EBUSY) {
- perror(m->mnt_dir);
- continue;
- }
- status=do_umount (blockDevice, useMtab);
- if (status!=0) {
- printf ("Couldn't umount %s on %s (type %s): %s\n",
- blockDevice, m->mnt_dir, m->mnt_type, strerror(errno));
- }
+ // If still can't umount, maybe remount read-only?
+ if (curstat && doRemount && errno == EBUSY && m) {
+ curstat = mount(m->device, path, NULL, MS_REMOUNT|MS_RDONLY, NULL);
+ bb_error_msg(curstat ? "Cannot remount %s read-only" :
+ "%s busy - remounted read-only", m->device);
}
- }
- endmntent (mountTable);
- }
- return( TRUE);
-}
-extern int
-umount_main(int argc, char** argv)
-{
- if (argc < 2) {
- usage( umount_usage);
- }
+ /* De-allcate the loop device. This ioctl should be ignored on any
+ * non-loop block devices. */
+ if(ENABLE_FEATURE_MOUNT_LOOP && freeLoop && m)
+ del_loop(m->device);
- /* Parse any options */
- while (--argc > 0 && **(++argv) == '-') {
- while (*++(*argv)) switch (**argv) {
- case 'a':
- umountAll = TRUE;
- break;
-#ifdef BB_MTAB
- case 'n':
- useMtab = FALSE;
- break;
-#endif
- default:
- usage( umount_usage);
+ if(curstat) {
+ if(useMtab && m) erase_mtab(m->dir);
+ status = EXIT_FAILURE;
+ bb_perror_msg("Couldn't umount %s\n", path);
+ }
+ // Find next matching mtab entry for -a or umount /dev
+ while(m && (m = m->next))
+ if(umountAll || !strcmp(path,m->device))
+ break;
}
- }
-
- if(umountAll==TRUE) {
- exit(umount_all(useMtab));
- }
- if ( do_umount(*argv,useMtab) == 0 )
- exit (TRUE);
- else {
- perror("umount");
- exit(FALSE);
- }
-}
-
-#if defined BB_FEATURE_MOUNT_LOOP
-static int del_loop(const char *device)
-{
- int fd;
-
- if ((fd = open(device, O_RDONLY)) < 0) {
- perror(device);
- exit(1);
- }
- if (ioctl(fd, LOOP_CLR_FD, 0) < 0) {
- perror("ioctl: LOOP_CLR_FD");
- exit(1);
+ // 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;
+ }
}
- close(fd);
- return(0);
+
+ return status;
}
-#endif