Upates to include copyright 2000 to everything
[oweals/busybox.git] / util-linux / umount.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini umount implementation for busybox
4  *
5  *
6  * Copyright (C) 1999,2000 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 /* Don't bother to clean up, since exit() does that 
149  * automagically, so we can save a few bytes */
150 #if 0
151 void mtab_free(void)
152 {
153         struct _mtab_entry_t *this, *next;
154
155         this = mtab_cache;
156         while (this) {
157                 next = this->next;
158                 if (this->device)
159                         free(this->device);
160                 if (this->mountpt)
161                         free(this->mountpt);
162                 free(this);
163                 this = next;
164         }
165 }
166 #endif
167
168 static int do_umount(const char *name, int useMtab)
169 {
170         int status;
171         char *blockDevice = mtab_getinfo(name, MTAB_GETDEVICE);
172
173         if (blockDevice && strcmp(blockDevice, name) == 0)
174                 name = mtab_getinfo(blockDevice, MTAB_GETMOUNTPT);
175
176         status = umount(name);
177
178 #if defined BB_FEATURE_MOUNT_LOOP
179         if (freeLoop == TRUE && blockDevice != NULL && !strncmp("/dev/loop", blockDevice, 9))
180                 /* this was a loop device, delete it */
181                 del_loop(blockDevice);
182 #endif
183 #if defined BB_FEATURE_REMOUNT
184         if (status != 0 && doRemount == TRUE && errno == EBUSY) {
185                 status = mount(blockDevice, name, NULL,
186                                            MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL);
187                 if (status == 0) {
188                         fprintf(stderr, "umount: %s busy - remounted read-only\n",
189                                         blockDevice);
190                         /* TODO: update mtab if BB_MTAB is defined */
191                 } else {
192                         fprintf(stderr, "umount: Cannot remount %s read-only\n",
193                                         blockDevice);
194                 }
195         }
196 #endif
197         if (status == 0) {
198 #if defined BB_MTAB
199                 if (useMtab == TRUE)
200                         erase_mtab(name);
201 #endif
202                 return (TRUE);
203         }
204         return (FALSE);
205 }
206
207 static int umount_all(int useMtab)
208 {
209         int status = TRUE;
210         char *mountpt;
211         void *iter;
212
213         for (mountpt = mtab_first(&iter); mountpt; mountpt = mtab_next(&iter)) {
214                 /* Never umount /proc on a umount -a */
215                 if (strstr(mountpt, "proc")!= NULL)
216                         continue;
217                 status = do_umount(mountpt, useMtab);
218                 if (status != 0) {
219                         /* Don't bother retrying the umount on busy devices */
220                         if (errno == EBUSY) {
221                                 perror(mountpt);
222                                 continue;
223                         }
224                         status = do_umount(mountpt, useMtab);
225                         if (status != 0) {
226                                 printf("Couldn't umount %s on %s: %s\n",
227                                            mountpt, mtab_getinfo(mountpt, MTAB_GETDEVICE),
228                                            strerror(errno));
229                         }
230                 }
231         }
232         return (status);
233 }
234
235 extern int umount_main(int argc, char **argv)
236 {
237         if (argc < 2) {
238                 usage(umount_usage);
239         }
240
241         /* Parse any options */
242         while (--argc > 0 && **(++argv) == '-') {
243                 while (*++(*argv))
244                         switch (**argv) {
245                         case 'a':
246                                 umountAll = TRUE;
247                                 break;
248 #if defined BB_FEATURE_MOUNT_LOOP
249                         case 'f':
250                                 freeLoop = FALSE;
251                                 break;
252 #endif
253 #ifdef BB_MTAB
254                         case 'n':
255                                 useMtab = FALSE;
256                                 break;
257 #endif
258 #ifdef BB_FEATURE_REMOUNT
259                         case 'r':
260                                 doRemount = TRUE;
261                                 break;
262 #endif
263                         case 'v':
264                                 break; /* ignore -v */
265                         default:
266                                 usage(umount_usage);
267                         }
268         }
269
270         mtab_read();
271         if (umountAll == TRUE) {
272                 exit(umount_all(useMtab));
273         }
274         if (do_umount(*argv, useMtab) == 0)
275                 exit(TRUE);
276         else {
277                 perror("umount");
278                 exit(FALSE);
279         }
280 }
281