Major rework of the directory structure and the entire build system.
[oweals/busybox.git] / util-linux / umount.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini umount implementation for busybox
4  *
5  * Copyright (C) 1999,2000 by Lineo, inc. and Erik Andersen
6  * Copyright (C) 1999,2000,2001 by Erik Andersen <andersee@debian.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  *
22  */
23
24 #include <limits.h>
25 #include <stdio.h>
26 #include <mntent.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include "busybox.h"
31
32 /* Teach libc5 about realpath -- it includes it but the 
33  * prototype is missing... */
34 #if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1)
35 extern char *realpath(const char *path, char *resolved_path);
36 #endif
37
38 static const int MNT_FORCE = 1;
39 static const int MS_MGC_VAL = 0xc0ed0000; /* Magic number indicatng "new" flags */
40 static const int MS_REMOUNT = 32;       /* Alter flags of a mounted FS.  */
41 static const int MS_RDONLY = 1; /* Mount read-only.  */
42
43 extern int mount (__const char *__special_file, __const char *__dir,
44                         __const char *__fstype, unsigned long int __rwflag,
45                         __const void *__data);
46 extern int umount (__const char *__special_file);
47 extern int umount2 (__const char *__special_file, int __flags);
48
49 struct _mtab_entry_t {
50         char *device;
51         char *mountpt;
52         struct _mtab_entry_t *next;
53 };
54
55 static struct _mtab_entry_t *mtab_cache = NULL;
56
57
58
59 #if defined CONFIG_FEATURE_MOUNT_FORCE
60 static int doForce = FALSE;
61 #endif
62 #if defined CONFIG_FEATURE_MOUNT_LOOP
63 static int freeLoop = TRUE;
64 #endif
65 #if defined CONFIG_FEATURE_MTAB_SUPPORT
66 static int useMtab = TRUE;
67 #endif
68 static int umountAll = FALSE;
69 static int doRemount = FALSE;
70 extern const char mtab_file[];  /* Defined in utility.c */
71
72
73
74 /* These functions are here because the getmntent functions do not appear
75  * to be re-entrant, which leads to all sorts of problems when we try to
76  * use them recursively - randolph
77  *
78  * TODO: Perhaps switch to using Glibc's getmntent_r
79  *        -Erik
80  */
81 static void mtab_read(void)
82 {
83         struct _mtab_entry_t *entry = NULL;
84         struct mntent *e;
85         FILE *fp;
86
87         if (mtab_cache != NULL)
88                 return;
89
90         if ((fp = setmntent(mtab_file, "r")) == NULL) {
91                 error_msg("Cannot open %s", mtab_file);
92                 return;
93         }
94         while ((e = getmntent(fp))) {
95                 entry = xmalloc(sizeof(struct _mtab_entry_t));
96                 entry->device = strdup(e->mnt_fsname);
97                 entry->mountpt = strdup(e->mnt_dir);
98                 entry->next = mtab_cache;
99                 mtab_cache = entry;
100         }
101         endmntent(fp);
102 }
103
104 static char *mtab_getinfo(const char *match, const char which)
105 {
106         struct _mtab_entry_t *cur = mtab_cache;
107
108         while (cur) {
109                 if (strcmp(cur->mountpt, match) == 0 ||
110                         strcmp(cur->device, match) == 0) {
111                         if (which == MTAB_GETMOUNTPT) {
112                                 return cur->mountpt;
113                         } else {
114 #if !defined CONFIG_FEATURE_MTAB_SUPPORT
115                                 if (strcmp(cur->device, "/dev/root") == 0) {
116                                         /* Adjusts device to be the real root device,
117                                          * or leaves device alone if it can't find it */
118                                         cur->device = find_real_root_device_name(cur->device);
119                                 }
120 #endif
121                                 return cur->device;
122                         }
123                 }
124                 cur = cur->next;
125         }
126         return NULL;
127 }
128
129 static char *mtab_next(void **iter)
130 {
131         char *mp;
132
133         if (iter == NULL || *iter == NULL)
134                 return NULL;
135         mp = ((struct _mtab_entry_t *) (*iter))->mountpt;
136         *iter = (void *) ((struct _mtab_entry_t *) (*iter))->next;
137         return mp;
138 }
139
140 static char *mtab_first(void **iter)
141 {
142         struct _mtab_entry_t *mtab_iter;
143
144         if (!iter)
145                 return NULL;
146         mtab_iter = mtab_cache;
147         *iter = (void *) mtab_iter;
148         return mtab_next(iter);
149 }
150
151 /* Don't bother to clean up, since exit() does that 
152  * automagically, so we can save a few bytes */
153 #ifdef CONFIG_FEATURE_CLEAN_UP
154 static void mtab_free(void)
155 {
156         struct _mtab_entry_t *this, *next;
157
158         this = mtab_cache;
159         while (this) {
160                 next = this->next;
161                 if (this->device)
162                         free(this->device);
163                 if (this->mountpt)
164                         free(this->mountpt);
165                 free(this);
166                 this = next;
167         }
168 }
169 #endif
170
171 static int do_umount(const char *name)
172 {
173         int status;
174         char *blockDevice = mtab_getinfo(name, MTAB_GETDEVICE);
175
176         if (blockDevice && strcmp(blockDevice, name) == 0)
177                 name = mtab_getinfo(blockDevice, MTAB_GETMOUNTPT);
178
179         status = umount(name);
180
181 #if defined CONFIG_FEATURE_MOUNT_LOOP
182         if (freeLoop == TRUE && blockDevice != NULL && !strncmp("/dev/loop", blockDevice, 9))
183                 /* this was a loop device, delete it */
184                 del_loop(blockDevice);
185 #endif
186 #if defined CONFIG_FEATURE_MOUNT_FORCE
187         if (status != 0 && doForce == TRUE) {
188                 status = umount2(blockDevice, MNT_FORCE);
189                 if (status != 0) {
190                         error_msg_and_die("forced umount of %s failed!", blockDevice);
191                 }
192         }
193 #endif
194         if (status != 0 && doRemount == TRUE && errno == EBUSY) {
195                 status = mount(blockDevice, name, NULL,
196                                            MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL);
197                 if (status == 0) {
198                         error_msg("%s busy - remounted read-only", blockDevice);
199                 } else {
200                         error_msg("Cannot remount %s read-only", blockDevice);
201                 }
202         }
203         if (status == 0) {
204 #if defined CONFIG_FEATURE_MTAB_SUPPORT
205                 if (useMtab == TRUE)
206                         erase_mtab(name);
207 #endif
208                 return (TRUE);
209         }
210         return (FALSE);
211 }
212
213 static int umount_all(void)
214 {
215         int status = TRUE;
216         char *mountpt;
217         void *iter;
218
219         for (mountpt = mtab_first(&iter); mountpt; mountpt = mtab_next(&iter)) {
220                 /* Never umount /proc on a umount -a */
221                 if (strstr(mountpt, "proc")!= NULL)
222                         continue;
223                 if (!do_umount(mountpt)) {
224                         /* Don't bother retrying the umount on busy devices */
225                         if (errno == EBUSY) {
226                                 perror_msg("%s", mountpt);
227                                 status = FALSE;
228                                 continue;
229                         }
230                         if (!do_umount(mountpt)) {
231                                 printf("Couldn't umount %s on %s: %s\n",
232                                            mountpt, mtab_getinfo(mountpt, MTAB_GETDEVICE),
233                                            strerror(errno));
234                                 status = FALSE;
235                         }
236                 }
237         }
238         return (status);
239 }
240
241 extern int umount_main(int argc, char **argv)
242 {
243         char path[PATH_MAX];
244
245         if (argc < 2) {
246                 show_usage();
247         }
248 #ifdef CONFIG_FEATURE_CLEAN_UP
249         atexit(mtab_free);
250 #endif
251
252         /* Parse any options */
253         while (--argc > 0 && **(++argv) == '-') {
254                 while (*++(*argv))
255                         switch (**argv) {
256                         case 'a':
257                                 umountAll = TRUE;
258                                 break;
259 #if defined CONFIG_FEATURE_MOUNT_LOOP
260                         case 'l':
261                                 freeLoop = FALSE;
262                                 break;
263 #endif
264 #ifdef CONFIG_FEATURE_MTAB_SUPPORT
265                         case 'n':
266                                 useMtab = FALSE;
267                                 break;
268 #endif
269 #ifdef CONFIG_FEATURE_MOUNT_FORCE
270                         case 'f':
271                                 doForce = TRUE;
272                                 break;
273 #endif
274                         case 'r':
275                                 doRemount = TRUE;
276                                 break;
277                         case 'v':
278                                 break; /* ignore -v */
279                         default:
280                                 show_usage();
281                         }
282         }
283
284         mtab_read();
285         if (umountAll == TRUE) {
286                 if (umount_all() == TRUE)
287                         return EXIT_SUCCESS;
288                 else
289                         return EXIT_FAILURE;
290         }
291         if (realpath(*argv, path) == NULL)
292                 perror_msg_and_die("%s", path);
293         if (do_umount(path) == TRUE)
294                 return EXIT_SUCCESS;
295         perror_msg_and_die("%s", *argv);
296 }
297