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