c34bf5fc3f2c778a1cae0a95dd36e38e39de8ebf
[oweals/busybox.git] / umount.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini umount implementation for busybox
4  *
5  *
6  * Copyright (C) 1999 by Lineo, inc.
7  * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22  *
23  */
24
25 #include "internal.h"
26 #include <stdio.h>
27 #include <sys/mount.h>
28 #include <mntent.h>
29 #include <errno.h>
30
31
32 static const char umount_usage[] =
33         "umount [flags] filesystem|directory\n\n"
34         "Flags:\n" "\t-a:\tUnmount all file systems"
35 #ifdef BB_MTAB
36         " in /etc/mtab\n\t-n:\tDon't erase /etc/mtab entries\n"
37 #else
38         "\n"
39 #endif
40 #ifdef BB_FEATURE_REMOUNT
41         "\t-r:\tTry to remount devices as read-only if mount is busy\n"
42 #endif
43 #if defined BB_FEATURE_MOUNT_LOOP
44         "\t-f:\tDo not free loop device (if a loop device has been used)\n"
45 #endif
46 ;
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 BB_FEATURE_MOUNT_LOOP
59 static int freeLoop = TRUE;
60 #endif
61 static int useMtab = TRUE;
62 static int umountAll = FALSE;
63 #if defined BB_FEATURE_REMOUNT
64 static int doRemount = FALSE;
65 #endif
66 extern const char mtab_file[];  /* Defined in utility.c */
67
68
69
70 /* These functions are here because the getmntent functions do not appear
71  * to be re-entrant, which leads to all sorts of problems when we try to
72  * use them recursively - randolph
73  *
74  * TODO: Perhaps switch to using Glibc's getmntent_r
75  *        -Erik
76  */
77 void mtab_read(void)
78 {
79         struct _mtab_entry_t *entry = NULL;
80         struct mntent *e;
81         FILE *fp;
82
83         if (mtab_cache != NULL)
84                 return;
85
86         if ((fp = setmntent(mtab_file, "r")) == NULL) {
87                 fprintf(stderr, "Cannot open %s\n", mtab_file);
88                 return;
89         }
90         while ((e = getmntent(fp))) {
91                 entry = xmalloc(sizeof(struct _mtab_entry_t));
92                 entry->device = strdup(e->mnt_fsname);
93                 entry->mountpt = strdup(e->mnt_dir);
94                 entry->next = mtab_cache;
95                 mtab_cache = entry;
96         }
97         endmntent(fp);
98 }
99
100 char *mtab_getinfo(const char *match, const char which)
101 {
102         struct _mtab_entry_t *cur = mtab_cache;
103
104         while (cur) {
105                 if (strcmp(cur->mountpt, match) == 0 ||
106                         strcmp(cur->device, match) == 0) {
107                         if (which == MTAB_GETMOUNTPT) {
108                                 return cur->mountpt;
109                         } else {
110 #if !defined BB_MTAB
111                                 if (strcmp(cur->device, "/dev/root") == 0) {
112                                         /* Adjusts device to be the real root device,
113                                          * or leaves device alone if it can't find it */
114                                         find_real_root_device_name( cur->device);
115                                         return ( cur->device);
116                                 }
117 #endif
118                                 return cur->device;
119                         }
120                 }
121                 cur = cur->next;
122         }
123         return NULL;
124 }
125
126 char *mtab_first(void **iter)
127 {
128         struct _mtab_entry_t *mtab_iter;
129
130         if (!iter)
131                 return NULL;
132         mtab_iter = mtab_cache;
133         *iter = (void *) mtab_iter;
134         return mtab_next(iter);
135 }
136
137 char *mtab_next(void **iter)
138 {
139         char *mp;
140
141         if (iter == NULL || *iter == NULL)
142                 return NULL;
143         mp = ((struct _mtab_entry_t *) (*iter))->mountpt;
144         *iter = (void *) ((struct _mtab_entry_t *) (*iter))->next;
145         return mp;
146 }
147
148 void mtab_free(void)
149 {
150         struct _mtab_entry_t *this, *next;
151
152         this = mtab_cache;
153         while (this) {
154                 next = this->next;
155                 if (this->device)
156                         free(this->device);
157                 if (this->mountpt)
158                         free(this->mountpt);
159                 free(this);
160                 this = next;
161         }
162 }
163
164 static int do_umount(const char *name, int useMtab)
165 {
166         int status;
167         char *blockDevice = mtab_getinfo(name, MTAB_GETDEVICE);
168
169         if (blockDevice && strcmp(blockDevice, name) == 0)
170                 name = mtab_getinfo(blockDevice, MTAB_GETMOUNTPT);
171
172         status = umount(name);
173
174 #if defined BB_FEATURE_MOUNT_LOOP
175         if (freeLoop == TRUE && blockDevice != NULL && !strncmp("/dev/loop", blockDevice, 9))
176                 /* this was a loop device, delete it */
177                 del_loop(blockDevice);
178 #endif
179 #if defined BB_FEATURE_REMOUNT
180         if (status != 0 && doRemount == TRUE && errno == EBUSY) {
181                 status = mount(blockDevice, name, NULL,
182                                            MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL);
183                 if (status == 0) {
184                         fprintf(stderr, "umount: %s busy - remounted read-only\n",
185                                         blockDevice);
186                         /* TODO: update mtab if BB_MTAB is defined */
187                 } else {
188                         fprintf(stderr, "umount: Cannot remount %s read-only\n",
189                                         blockDevice);
190                 }
191         }
192 #endif
193         if (status == 0) {
194 #if defined BB_MTAB
195                 if (useMtab == TRUE)
196                         erase_mtab(name);
197 #endif
198                 return (TRUE);
199         }
200         return (FALSE);
201 }
202
203 static int umount_all(int useMtab)
204 {
205         int status = TRUE;
206         char *mountpt;
207         void *iter;
208
209         for (mountpt = mtab_first(&iter); mountpt; mountpt = mtab_next(&iter)) {
210                 /* Never umount /proc on a umount -a */
211                 if (strstr(mountpt, "proc")!= NULL)
212                         continue;
213                 status = do_umount(mountpt, useMtab);
214                 if (status != 0) {
215                         /* Don't bother retrying the umount on busy devices */
216                         if (errno == EBUSY) {
217                                 perror(mountpt);
218                                 continue;
219                         }
220                         status = do_umount(mountpt, useMtab);
221                         if (status != 0) {
222                                 printf("Couldn't umount %s on %s: %s\n",
223                                            mountpt, mtab_getinfo(mountpt, MTAB_GETDEVICE),
224                                            strerror(errno));
225                         }
226                 }
227         }
228         return (status);
229 }
230
231 extern int umount_main(int argc, char **argv)
232 {
233         if (argc < 2) {
234                 usage(umount_usage);
235         }
236
237         /* Parse any options */
238         while (--argc > 0 && **(++argv) == '-') {
239                 while (*++(*argv))
240                         switch (**argv) {
241                         case 'a':
242                                 umountAll = TRUE;
243                                 break;
244 #if defined BB_FEATURE_MOUNT_LOOP
245                         case 'f':
246                                 freeLoop = FALSE;
247                                 break;
248 #endif
249 #ifdef BB_MTAB
250                         case 'n':
251                                 useMtab = FALSE;
252                                 break;
253 #endif
254 #ifdef BB_FEATURE_REMOUNT
255                         case 'r':
256                                 doRemount = TRUE;
257                                 break;
258 #endif
259                         default:
260                                 usage(umount_usage);
261                         }
262         }
263
264         mtab_read();
265         if (umountAll == TRUE) {
266                 exit(umount_all(useMtab));
267         }
268         if (do_umount(*argv, useMtab) == 0)
269                 exit(TRUE);
270         else {
271                 perror("umount");
272                 exit(FALSE);
273         }
274 }
275