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