blob: 15b2b192fda467458796e7e348a1b1c9a6d413b5 [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
Eric Andersencc8ed391999-10-05 16:24:54 +00002/*
Eric Andersen596e5461999-10-07 08:30:23 +00003 * Mini mount implementation for busybox
4 *
Eric Andersenc4996011999-10-20 22:08:37 +00005 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
Eric Andersenc7bda1c2004-03-15 08:29:22 +00006 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
Rob Landley6a6798b2005-08-10 20:35:54 +00007 * Copyright (C) 2005 by Rob Landley <rob@landley.net>
Eric Andersen596e5461999-10-07 08:30:23 +00008 *
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 *
Erik Andersenb7cc49d2000-01-13 06:38:14 +000023 */
Eric Andersencc8ed391999-10-05 16:24:54 +000024
Matt Kraai34251112001-05-02 21:17:38 +000025#include <limits.h>
Eric Andersencc8ed391999-10-05 16:24:54 +000026#include <stdlib.h>
27#include <unistd.h>
28#include <errno.h>
29#include <string.h>
30#include <stdio.h>
31#include <mntent.h>
Eric Andersencc8ed391999-10-05 16:24:54 +000032#include <ctype.h>
Rob Landley6a6798b2005-08-10 20:35:54 +000033#include <sys/mount.h>
34#include <fcntl.h> // for CONFIG_FEATURE_MOUNT_LOOP
35#include <sys/ioctl.h> // for CONFIG_FEATURE_MOUNT_LOOP
Eric Andersencbe31da2001-02-20 06:14:08 +000036#include "busybox.h"
Eric Andersenbd22ed82000-07-08 18:55:24 +000037
Rob Landley6a6798b2005-08-10 20:35:54 +000038/* This is just a warning of a common mistake. Possibly this should be a
39 * uclibc faq entry rather than in busybox... */
Rob Landley4a7252b2005-10-08 18:49:45 +000040#if ENABLE_FEATURE_MOUNT_NFS && defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
Eric Andersend9fe9582003-07-22 08:25:37 +000041#error "You need to build uClibc with UCLIBC_HAS_RPC for busybox mount with NFS support to compile."
42#endif
Rob Landley6a6798b2005-08-10 20:35:54 +000043
44// These two aren't always defined in old headers
45#ifndef MS_BIND
46#define MS_BIND 4096
47#endif
48#ifndef MS_MOVE
49#define MS_MOVE 8192
Eric Andersend9fe9582003-07-22 08:25:37 +000050#endif
51
Rob Landley6a6798b2005-08-10 20:35:54 +000052/* Consume standard mount options (from -o options or --options).
53 * Set appropriate flags and collect unrecognized ones as a comma separated
54 * string to pass to kernel */
Eric Andersencc8ed391999-10-05 16:24:54 +000055
Rob Landley6a6798b2005-08-10 20:35:54 +000056struct {
Erik Andersene49d5ec2000-02-08 19:58:47 +000057 const char *name;
Rob Landley6a6798b2005-08-10 20:35:54 +000058 long flags;
59} static const mount_options[] = {
60 {"loop", 0},
61 {"defaults", 0},
62 {"noauto", 0},
63 {"ro", MS_RDONLY},
64 {"rw", ~MS_RDONLY},
65 {"nosuid", MS_NOSUID},
66 {"suid", ~MS_NOSUID},
67 {"dev", ~MS_NODEV},
68 {"nodev", MS_NODEV},
69 {"exec", ~MS_NOEXEC},
70 {"noexec", MS_NOEXEC},
71 {"sync", MS_SYNCHRONOUS},
72 {"async", ~MS_SYNCHRONOUS},
73 {"remount", MS_REMOUNT},
74 {"atime", MS_NOATIME},
75 {"noatime", MS_NOATIME},
76 {"diratime", MS_NODIRATIME},
77 {"nodiratime", MS_NODIRATIME},
78 {"bind", MS_BIND},
79 {"move", MS_MOVE}
Eric Andersencc8ed391999-10-05 16:24:54 +000080};
81
Rob Landley6a6798b2005-08-10 20:35:54 +000082/* Uses the mount_options list above */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000083static void parse_mount_options(char *options, int *flags, char **strflags)
Eric Andersencc8ed391999-10-05 16:24:54 +000084{
Rob Landley6a6798b2005-08-10 20:35:54 +000085 // Loop through options
86 for(;;) {
87 int i;
Erik Andersene49d5ec2000-02-08 19:58:47 +000088 char *comma = strchr(options, ',');
Eric Andersencc8ed391999-10-05 16:24:54 +000089
Rob Landley6a6798b2005-08-10 20:35:54 +000090 if(comma) *comma = 0;
Eric Andersen3ae0c781999-11-04 01:13:21 +000091
Rob Landley6a6798b2005-08-10 20:35:54 +000092 // Find this option in mount_options
93 for(i = 0; i < (sizeof(mount_options) / sizeof(*mount_options)); i++) {
94 if(!strcasecmp(mount_options[i].name, options)) {
95 long fl = mount_options[i].flags;
96 if(fl < 0) *flags &= fl;
97 else *flags |= fl;
Erik Andersene49d5ec2000-02-08 19:58:47 +000098 break;
99 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000100 }
Rob Landley6a6798b2005-08-10 20:35:54 +0000101 // Unrecognized mount option?
Rob Landley9a643142005-08-30 17:07:49 +0000102 if(i == (sizeof(mount_options) / sizeof(*mount_options))) {
Rob Landley6a6798b2005-08-10 20:35:54 +0000103 // Add it to strflags, to pass on to kernel
104 i = *strflags ? strlen(*strflags) : 0;
105 *strflags = xrealloc(*strflags, i+strlen(options)+2);
106 // Comma separated if it's not the first one
Rob Landley9a643142005-08-30 17:07:49 +0000107 if(i) (*strflags)[i++] = ',';
Rob Landley6a6798b2005-08-10 20:35:54 +0000108 strcpy((*strflags)+i, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000109 }
Rob Landley6a6798b2005-08-10 20:35:54 +0000110 // Advance to next option, or finish
111 if(comma) {
Erik Andersene49d5ec2000-02-08 19:58:47 +0000112 *comma = ',';
113 options = ++comma;
Rob Landley6a6798b2005-08-10 20:35:54 +0000114 } else break;
Eric Andersencc8ed391999-10-05 16:24:54 +0000115 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000116}
117
Rob Landley6a6798b2005-08-10 20:35:54 +0000118/* This does the work */
Matt Kraai12400822001-04-17 04:32:50 +0000119
Erik Andersene49d5ec2000-02-08 19:58:47 +0000120extern int mount_main(int argc, char **argv)
Eric Andersencc8ed391999-10-05 16:24:54 +0000121{
Rob Landley6a6798b2005-08-10 20:35:54 +0000122 char *string_flags = 0, *fsType = 0, *blockDevice = 0, *directory = 0,
123 *loopFile = 0, *buf = 0,
124 *files[] = {"/etc/filesystems", "/proc/filesystems", 0};
125 int i, opt, all = FALSE, fakeIt = FALSE, allowWrite = FALSE,
Rob Landley6e985212005-08-14 18:46:34 +0000126 rc = 1, useMtab = ENABLE_FEATURE_MTAB_SUPPORT;
Rob Landley6a6798b2005-08-10 20:35:54 +0000127 int flags=0xc0ed0000; // Needed for linux 2.2, ignored by 2.4 and 2.6.
128 FILE *file = 0,*f = 0;
129 char path[PATH_MAX*2];
130 struct mntent m;
Glenn L McGrath3aae1002001-05-07 01:38:03 +0000131 struct stat statbuf;
Eric Andersencc8ed391999-10-05 16:24:54 +0000132
Rob Landley6a6798b2005-08-10 20:35:54 +0000133 /* parse long options, like --bind and --move. Note that -o option
134 * and --option are synonymous. Yes, this means --remount,rw works. */
135
136 for(i = opt = 0; i < argc; i++) {
137 if(argv[i][0] == '-' && argv[i][1] == '-')
138 parse_mount_options(argv[i]+2, &flags, &string_flags);
139 else argv[opt++] = argv[i];
140 }
141 argc = opt;
142
143 // Parse remaining options
144
145 while((opt = getopt(argc, argv, "o:t:rwafnv")) > 0) {
Matt Kraaia3045df2001-04-17 04:48:51 +0000146 switch (opt) {
147 case 'o':
Eric Andersen8b1aa4d2002-06-22 17:20:50 +0000148 parse_mount_options(optarg, &flags, &string_flags);
Matt Kraaia3045df2001-04-17 04:48:51 +0000149 break;
Rob Landley6a6798b2005-08-10 20:35:54 +0000150 case 't':
151 fsType = optarg;
152 break;
Matt Kraaia3045df2001-04-17 04:48:51 +0000153 case 'r':
154 flags |= MS_RDONLY;
155 break;
Matt Kraaia3045df2001-04-17 04:48:51 +0000156 case 'w':
Rob Landley6a6798b2005-08-10 20:35:54 +0000157 allowWrite=TRUE;
Matt Kraaia3045df2001-04-17 04:48:51 +0000158 break;
159 case 'a':
160 all = TRUE;
161 break;
162 case 'f':
163 fakeIt = TRUE;
164 break;
Matt Kraaia3045df2001-04-17 04:48:51 +0000165 case 'n':
166 useMtab = FALSE;
Eric Andersenee4b7d42004-04-12 15:02:53 +0000167 break;
Matt Kraaia3045df2001-04-17 04:48:51 +0000168 case 'v':
Rob Landley6a6798b2005-08-10 20:35:54 +0000169 break; // ignore -v
170 default:
171 bb_show_usage();
Erik Andersene49d5ec2000-02-08 19:58:47 +0000172 }
Matt Kraaia3045df2001-04-17 04:48:51 +0000173 }
174
Rob Landley6a6798b2005-08-10 20:35:54 +0000175 // If we have no arguments, show currently mounted filesystems
176
177 if(!all && (optind == argc)) {
178 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
179
180 if(!mountTable) bb_perror_msg_and_die(bb_path_mtab_file);
181
182 while (getmntent_r(mountTable,&m,path,sizeof(path))) {
183 blockDevice = m.mnt_fsname;
184
185 // Clean up display a little bit regarding root devie
186 if(!strcmp(blockDevice, "rootfs")) continue;
187 if(!strcmp(blockDevice, "/dev/root"))
188 blockDevice = find_block_device("/");
189
190 if(!fsType || !strcmp(m.mnt_type, fsType))
191 printf("%s on %s type %s (%s)\n", blockDevice, m.mnt_dir,
192 m.mnt_type, m.mnt_opts);
193 if(ENABLE_FEATURE_CLEAN_UP && blockDevice != m.mnt_fsname)
194 free(blockDevice);
195 }
196 endmntent(mountTable);
197 return EXIT_SUCCESS;
Glenn L McGrath8042f652002-08-23 06:17:46 +0000198 }
Matt Kraai12400822001-04-17 04:32:50 +0000199
Rob Landley6a6798b2005-08-10 20:35:54 +0000200 /* The next argument is what to mount. if there's an argument after that
201 * it's where to mount it. If we're not mounting all, and we have both
202 * of these arguments, jump straight to the actual mount. */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000203
Rob Landley6a6798b2005-08-10 20:35:54 +0000204 statbuf.st_mode=0;
205 if(optind < argc)
206 blockDevice = !stat(argv[optind], &statbuf) ?
207 bb_simplify_path(argv[optind]) :
208 (ENABLE_FEATURE_CLEAN_UP ? strdup(argv[optind]) : argv[optind]);
209 if(optind+1 < argc) directory = bb_simplify_path(argv[optind+1]);
210
211 // If we don't have to loop through fstab, skip ahead a bit.
212
213 if(!all && optind+1!=argc) goto singlemount;
214
215 // Loop through /etc/fstab entries to look up this entry.
216
217 if(!(file=setmntent("/etc/fstab","r")))
218 bb_perror_msg_and_die("\nCannot read /etc/fstab");
219 for(;;) {
220
221 // Get next fstab entry
222
223 if(!getmntent_r(file,&m,path,sizeof(path))) {
224 if(!all)
225 bb_perror_msg("Can't find %s in /etc/fstab\n", blockDevice);
226 break;
227 }
228
229 // If we're mounting all and all doesn't mount this one, skip it.
230
231 if(all) {
232 if(strstr(m.mnt_opts,"noauto") || strstr(m.mnt_type,"swap"))
233 continue;
234 flags=0;
235
236 /* If we're mounting something specific and this isn't it, skip it.
237 * Note we must match both the exact text in fstab (ala "proc") or
238 * a full path from root */
239
240 } else if(strcmp(blockDevice,m.mnt_fsname) &&
241 strcmp(argv[optind],m.mnt_fsname) &&
242 strcmp(blockDevice,m.mnt_dir) &&
243 strcmp(argv[optind],m.mnt_dir)) continue;
244
245 /* Parse flags from /etc/fstab (unless this is a single mount
246 * overriding fstab -- note the "all" test above zeroed the flags,
247 * to prevent flags from previous entries affecting this one, so
248 * the only way we could get here with nonzero flags is a single
249 * mount). */
250
251 if(!flags) {
252 if(ENABLE_FEATURE_CLEAN_UP) free(string_flags);
253 string_flags=NULL;
254 parse_mount_options(m.mnt_opts, &flags, &string_flags);
255 }
256
257 /* Fill out remaining fields with info from mtab */
258
259 if(ENABLE_FEATURE_CLEAN_UP) {
260 free(blockDevice);
261 blockDevice=strdup(m.mnt_fsname);
262 free(directory);
263 directory=strdup(m.mnt_type);
Glenn L McGrath3aae1002001-05-07 01:38:03 +0000264 } else {
Rob Landley6a6798b2005-08-10 20:35:54 +0000265 blockDevice=m.mnt_fsname;
266 directory=m.mnt_dir;
Glenn L McGrath3aae1002001-05-07 01:38:03 +0000267 }
Rob Landley6a6798b2005-08-10 20:35:54 +0000268 fsType=m.mnt_type;
Matt Kraai34251112001-05-02 21:17:38 +0000269
Rob Landley6a6798b2005-08-10 20:35:54 +0000270 /* Ok, we're ready to actually mount a specific source on a specific
271 * directory now. */
Matt Kraaia7cecbc2001-08-10 15:05:27 +0000272
Rob Landley6a6798b2005-08-10 20:35:54 +0000273singlemount:
Erik Andersene49d5ec2000-02-08 19:58:47 +0000274
Rob Landley6a6798b2005-08-10 20:35:54 +0000275 // If they said -w, override fstab
Erik Andersen246cc6d2000-03-07 07:41:42 +0000276
Rob Landley6a6798b2005-08-10 20:35:54 +0000277 if(allowWrite) flags&=~MS_RDONLY;
278
279 // Might this be an NFS filesystem?
280
Rob Landley4a7252b2005-10-08 18:49:45 +0000281 if(ENABLE_FEATURE_MOUNT_NFS && (!fsType || !strcmp(fsType,"nfs")) &&
Rob Landley6a6798b2005-08-10 20:35:54 +0000282 strchr(blockDevice, ':') != NULL)
283 {
284 if(nfsmount(blockDevice, directory, &flags, &string_flags, 1))
285 bb_perror_msg("nfsmount failed");
286 else {
Rob Landley6e985212005-08-14 18:46:34 +0000287 rc = 0;
Rob Landley6a6798b2005-08-10 20:35:54 +0000288 fsType="nfs";
Rob Landley9a643142005-08-30 17:07:49 +0000289 // Strangely enough, nfsmount() doesn't actually mount()
290 goto mount_it_now;
Eric Andersenfdd51032000-08-02 18:48:26 +0000291 }
Rob Landley6a6798b2005-08-10 20:35:54 +0000292 } else {
293
294 // Do we need to allocate a loopback device?
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000295
Rob Landley6a6798b2005-08-10 20:35:54 +0000296 if(ENABLE_FEATURE_MOUNT_LOOP && !fakeIt && S_ISREG(statbuf.st_mode))
Eric Andersen5ef44822003-02-28 06:29:27 +0000297 {
Rob Landley6a6798b2005-08-10 20:35:54 +0000298 loopFile = blockDevice;
299 blockDevice = 0;
300 switch(set_loop(&blockDevice, loopFile, 0)) {
301 case 0:
302 case 1:
303 break;
304 default:
305 bb_error_msg_and_die(
306 errno == EPERM || errno == EACCES ?
307 bb_msg_perm_denied_are_you_root :
308 "Couldn't setup loop device");
309 break;
Eric Andersenfdd51032000-08-02 18:48:26 +0000310 }
Eric Andersen252bacc2000-09-19 01:21:13 +0000311 }
Rob Landley6a6798b2005-08-10 20:35:54 +0000312
313 /* If we know the fstype (or don't need to), jump straight
314 * to the actual mount. */
315
316 if(fsType || (flags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
317 goto mount_it_now;
318 }
319
320 // Loop through filesystem types until mount succeeds or we run out
321
322 for(i = 0; files[i] && rc; i++) {
323 f = fopen(files[i], "r");
324 if(!f) continue;
325 // Get next block device backed filesystem
326 for(buf = 0; (buf = fsType = bb_get_chomped_line_from_file(f));
327 free(buf))
328 {
329 // Skip funky entries in /proc
330 if(!strncmp(buf,"nodev",5) && isspace(buf[5])) continue;
331
332 while(isspace(*fsType)) fsType++;
333 if(*buf=='#' || *buf=='*') continue;
334 if(!*fsType) continue;
335mount_it_now:
336 // Okay, try to mount
337
338 if (!fakeIt) {
339 for(;;) {
340 rc = mount(blockDevice, directory, fsType, flags, string_flags);
341 if(!rc || (flags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS))
342 break;
343 bb_error_msg("%s is write-protected, mounting read-only", blockDevice);
344 flags|=MS_RDONLY;
345 }
346 }
Rob Landleyde5fd232005-08-14 19:26:14 +0000347 if(!rc || !f) break;
Glenn L McGrath8042f652002-08-23 06:17:46 +0000348 }
Rob Landley6a6798b2005-08-10 20:35:54 +0000349 if(f) fclose(f);
350 if(!f || !rc) break;
351 }
352
353 /* If the mount was sucessful, and we're maintaining an old-style
354 * mtab file by hand, add new entry to it now. */
355 if((!rc || fakeIt) && useMtab) {
356 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
357
358 if(!mountTable) bb_perror_msg(bb_path_mtab_file);
359 else {
360 // Remove trailing / (if any) from directory we mounted on
361 int length=strlen(directory);
362 if(length>1 && directory[length-1] == '/')
363 directory[length-1]=0;
364
365 // Fill out structure (should be ok to re-use existing one).
366 m.mnt_fsname=blockDevice;
367 m.mnt_dir=directory;
368 m.mnt_type=fsType ? : "--bind";
369 m.mnt_opts=string_flags ? :
370 ((flags & MS_RDONLY) ? "ro" : "rw");
371 m.mnt_freq = 0;
372 m.mnt_passno = 0;
373
374 // Write and close
375 addmntent(mountTable, &m);
376 endmntent(mountTable);
Glenn L McGrath8042f652002-08-23 06:17:46 +0000377 }
Rob Landley6a6798b2005-08-10 20:35:54 +0000378 } else {
379 // Mount failed. Clean up
380 if(loopFile) {
381 del_loop(blockDevice);
382 if(ENABLE_FEATURE_CLEAN_UP) free(loopFile);
383 }
384 // Don't whine about already mounted fs when mounting all.
Rob Landley6e985212005-08-14 18:46:34 +0000385 if(rc<0 && errno == EBUSY && all) rc = 0;
Rob Landley6a6798b2005-08-10 20:35:54 +0000386 else if (errno == EPERM)
387 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
Eric Andersenfdd51032000-08-02 18:48:26 +0000388 }
Rob Landley6a6798b2005-08-10 20:35:54 +0000389 // We couldn't free this earlier becase fsType could be in buf.
390 if(ENABLE_FEATURE_CLEAN_UP) {
391 free(buf);
392 free(blockDevice);
393 free(directory);
Glenn L McGrath8042f652002-08-23 06:17:46 +0000394 }
Rob Landley6a6798b2005-08-10 20:35:54 +0000395 if(!all) break;
Eric Andersenfdd51032000-08-02 18:48:26 +0000396 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000397
Rob Landley6a6798b2005-08-10 20:35:54 +0000398 if(file) endmntent(file);
399 if(rc) bb_perror_msg("Mounting %s on %s failed", blockDevice, directory);
400
Rob Landley6e985212005-08-14 18:46:34 +0000401 return rc;
Eric Andersencc8ed391999-10-05 16:24:54 +0000402}