blob: 68542f9bcee23ad6a25d4d58838398c6a7559da5 [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 *
Rob Landley7b363fd2005-12-20 17:18:01 +00009 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Erik Andersenb7cc49d2000-01-13 06:38:14 +000010 */
Eric Andersencc8ed391999-10-05 16:24:54 +000011
Matt Kraai34251112001-05-02 21:17:38 +000012#include <limits.h>
Eric Andersencc8ed391999-10-05 16:24:54 +000013#include <stdlib.h>
14#include <unistd.h>
15#include <errno.h>
16#include <string.h>
17#include <stdio.h>
18#include <mntent.h>
Eric Andersencc8ed391999-10-05 16:24:54 +000019#include <ctype.h>
Rob Landley6a6798b2005-08-10 20:35:54 +000020#include <sys/mount.h>
21#include <fcntl.h> // for CONFIG_FEATURE_MOUNT_LOOP
22#include <sys/ioctl.h> // for CONFIG_FEATURE_MOUNT_LOOP
Eric Andersencbe31da2001-02-20 06:14:08 +000023#include "busybox.h"
Eric Andersenbd22ed82000-07-08 18:55:24 +000024
Rob Landley6a6798b2005-08-10 20:35:54 +000025// These two aren't always defined in old headers
26#ifndef MS_BIND
27#define MS_BIND 4096
28#endif
29#ifndef MS_MOVE
30#define MS_MOVE 8192
Eric Andersend9fe9582003-07-22 08:25:37 +000031#endif
32
Rob Landley6a6798b2005-08-10 20:35:54 +000033/* Consume standard mount options (from -o options or --options).
34 * Set appropriate flags and collect unrecognized ones as a comma separated
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000035 * string to pass to kernel */
Eric Andersencc8ed391999-10-05 16:24:54 +000036
Rob Landley6a6798b2005-08-10 20:35:54 +000037struct {
Erik Andersene49d5ec2000-02-08 19:58:47 +000038 const char *name;
Rob Landley6a6798b2005-08-10 20:35:54 +000039 long flags;
40} static const mount_options[] = {
41 {"loop", 0},
42 {"defaults", 0},
43 {"noauto", 0},
44 {"ro", MS_RDONLY},
45 {"rw", ~MS_RDONLY},
46 {"nosuid", MS_NOSUID},
47 {"suid", ~MS_NOSUID},
48 {"dev", ~MS_NODEV},
49 {"nodev", MS_NODEV},
50 {"exec", ~MS_NOEXEC},
51 {"noexec", MS_NOEXEC},
52 {"sync", MS_SYNCHRONOUS},
53 {"async", ~MS_SYNCHRONOUS},
54 {"remount", MS_REMOUNT},
Rob Landley8b0efdb2006-01-10 02:37:20 +000055 {"atime", ~MS_NOATIME},
Rob Landley6a6798b2005-08-10 20:35:54 +000056 {"noatime", MS_NOATIME},
Rob Landley8b0efdb2006-01-10 02:37:20 +000057 {"diratime", ~MS_NODIRATIME},
Rob Landley6a6798b2005-08-10 20:35:54 +000058 {"nodiratime", MS_NODIRATIME},
59 {"bind", MS_BIND},
60 {"move", MS_MOVE}
Eric Andersencc8ed391999-10-05 16:24:54 +000061};
62
Rob Landley6a6798b2005-08-10 20:35:54 +000063/* Uses the mount_options list above */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000064static void parse_mount_options(char *options, int *flags, char **strflags)
Eric Andersencc8ed391999-10-05 16:24:54 +000065{
Rob Landley6a6798b2005-08-10 20:35:54 +000066 // Loop through options
67 for(;;) {
68 int i;
Erik Andersene49d5ec2000-02-08 19:58:47 +000069 char *comma = strchr(options, ',');
Eric Andersencc8ed391999-10-05 16:24:54 +000070
Rob Landley6a6798b2005-08-10 20:35:54 +000071 if(comma) *comma = 0;
Eric Andersen3ae0c781999-11-04 01:13:21 +000072
Rob Landley6a6798b2005-08-10 20:35:54 +000073 // Find this option in mount_options
74 for(i = 0; i < (sizeof(mount_options) / sizeof(*mount_options)); i++) {
75 if(!strcasecmp(mount_options[i].name, options)) {
76 long fl = mount_options[i].flags;
77 if(fl < 0) *flags &= fl;
78 else *flags |= fl;
Erik Andersene49d5ec2000-02-08 19:58:47 +000079 break;
80 }
Erik Andersene49d5ec2000-02-08 19:58:47 +000081 }
Rob Landley6a6798b2005-08-10 20:35:54 +000082 // Unrecognized mount option?
Rob Landley9a643142005-08-30 17:07:49 +000083 if(i == (sizeof(mount_options) / sizeof(*mount_options))) {
Rob Landley6a6798b2005-08-10 20:35:54 +000084 // Add it to strflags, to pass on to kernel
85 i = *strflags ? strlen(*strflags) : 0;
86 *strflags = xrealloc(*strflags, i+strlen(options)+2);
87 // Comma separated if it's not the first one
Rob Landley9a643142005-08-30 17:07:49 +000088 if(i) (*strflags)[i++] = ',';
Rob Landley6a6798b2005-08-10 20:35:54 +000089 strcpy((*strflags)+i, options);
Erik Andersene49d5ec2000-02-08 19:58:47 +000090 }
Rob Landley6a6798b2005-08-10 20:35:54 +000091 // Advance to next option, or finish
92 if(comma) {
Erik Andersene49d5ec2000-02-08 19:58:47 +000093 *comma = ',';
94 options = ++comma;
Rob Landley6a6798b2005-08-10 20:35:54 +000095 } else break;
Eric Andersencc8ed391999-10-05 16:24:54 +000096 }
Eric Andersencc8ed391999-10-05 16:24:54 +000097}
98
Rob Landley6a6798b2005-08-10 20:35:54 +000099/* This does the work */
Matt Kraai12400822001-04-17 04:32:50 +0000100
Erik Andersene49d5ec2000-02-08 19:58:47 +0000101extern int mount_main(int argc, char **argv)
Eric Andersencc8ed391999-10-05 16:24:54 +0000102{
Rob Landley6a6798b2005-08-10 20:35:54 +0000103 char *string_flags = 0, *fsType = 0, *blockDevice = 0, *directory = 0,
104 *loopFile = 0, *buf = 0,
105 *files[] = {"/etc/filesystems", "/proc/filesystems", 0};
106 int i, opt, all = FALSE, fakeIt = FALSE, allowWrite = FALSE,
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000107 rc = 1, useMtab = ENABLE_FEATURE_MTAB_SUPPORT;
Rob Landley6a6798b2005-08-10 20:35:54 +0000108 int flags=0xc0ed0000; // Needed for linux 2.2, ignored by 2.4 and 2.6.
109 FILE *file = 0,*f = 0;
110 char path[PATH_MAX*2];
111 struct mntent m;
Glenn L McGrath3aae1002001-05-07 01:38:03 +0000112 struct stat statbuf;
Eric Andersencc8ed391999-10-05 16:24:54 +0000113
Rob Landley6a6798b2005-08-10 20:35:54 +0000114 /* parse long options, like --bind and --move. Note that -o option
115 * and --option are synonymous. Yes, this means --remount,rw works. */
116
117 for(i = opt = 0; i < argc; i++) {
118 if(argv[i][0] == '-' && argv[i][1] == '-')
119 parse_mount_options(argv[i]+2, &flags, &string_flags);
120 else argv[opt++] = argv[i];
121 }
122 argc = opt;
123
124 // Parse remaining options
125
126 while((opt = getopt(argc, argv, "o:t:rwafnv")) > 0) {
Matt Kraaia3045df2001-04-17 04:48:51 +0000127 switch (opt) {
128 case 'o':
Eric Andersen8b1aa4d2002-06-22 17:20:50 +0000129 parse_mount_options(optarg, &flags, &string_flags);
Matt Kraaia3045df2001-04-17 04:48:51 +0000130 break;
Rob Landley6a6798b2005-08-10 20:35:54 +0000131 case 't':
132 fsType = optarg;
133 break;
Matt Kraaia3045df2001-04-17 04:48:51 +0000134 case 'r':
135 flags |= MS_RDONLY;
136 break;
Matt Kraaia3045df2001-04-17 04:48:51 +0000137 case 'w':
Rob Landley6a6798b2005-08-10 20:35:54 +0000138 allowWrite=TRUE;
Matt Kraaia3045df2001-04-17 04:48:51 +0000139 break;
140 case 'a':
141 all = TRUE;
142 break;
143 case 'f':
144 fakeIt = TRUE;
145 break;
Matt Kraaia3045df2001-04-17 04:48:51 +0000146 case 'n':
147 useMtab = FALSE;
Eric Andersenee4b7d42004-04-12 15:02:53 +0000148 break;
Matt Kraaia3045df2001-04-17 04:48:51 +0000149 case 'v':
Rob Landley6a6798b2005-08-10 20:35:54 +0000150 break; // ignore -v
151 default:
152 bb_show_usage();
Erik Andersene49d5ec2000-02-08 19:58:47 +0000153 }
Matt Kraaia3045df2001-04-17 04:48:51 +0000154 }
155
Rob Landley6a6798b2005-08-10 20:35:54 +0000156 // If we have no arguments, show currently mounted filesystems
157
158 if(!all && (optind == argc)) {
159 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
160
161 if(!mountTable) bb_perror_msg_and_die(bb_path_mtab_file);
162
163 while (getmntent_r(mountTable,&m,path,sizeof(path))) {
164 blockDevice = m.mnt_fsname;
165
Rob Landley7b363fd2005-12-20 17:18:01 +0000166 // Clean up display a little bit regarding root device
Rob Landley6a6798b2005-08-10 20:35:54 +0000167 if(!strcmp(blockDevice, "rootfs")) continue;
168 if(!strcmp(blockDevice, "/dev/root"))
169 blockDevice = find_block_device("/");
170
171 if(!fsType || !strcmp(m.mnt_type, fsType))
172 printf("%s on %s type %s (%s)\n", blockDevice, m.mnt_dir,
173 m.mnt_type, m.mnt_opts);
174 if(ENABLE_FEATURE_CLEAN_UP && blockDevice != m.mnt_fsname)
175 free(blockDevice);
176 }
177 endmntent(mountTable);
178 return EXIT_SUCCESS;
Glenn L McGrath8042f652002-08-23 06:17:46 +0000179 }
Matt Kraai12400822001-04-17 04:32:50 +0000180
Rob Landley6a6798b2005-08-10 20:35:54 +0000181 /* The next argument is what to mount. if there's an argument after that
182 * it's where to mount it. If we're not mounting all, and we have both
183 * of these arguments, jump straight to the actual mount. */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000184
Rob Landley6a6798b2005-08-10 20:35:54 +0000185 statbuf.st_mode=0;
186 if(optind < argc)
187 blockDevice = !stat(argv[optind], &statbuf) ?
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000188 bb_simplify_path(argv[optind]) :
Rob Landley6a6798b2005-08-10 20:35:54 +0000189 (ENABLE_FEATURE_CLEAN_UP ? strdup(argv[optind]) : argv[optind]);
190 if(optind+1 < argc) directory = bb_simplify_path(argv[optind+1]);
191
192 // If we don't have to loop through fstab, skip ahead a bit.
193
194 if(!all && optind+1!=argc) goto singlemount;
195
196 // Loop through /etc/fstab entries to look up this entry.
197
198 if(!(file=setmntent("/etc/fstab","r")))
199 bb_perror_msg_and_die("\nCannot read /etc/fstab");
200 for(;;) {
201
202 // Get next fstab entry
203
204 if(!getmntent_r(file,&m,path,sizeof(path))) {
205 if(!all)
206 bb_perror_msg("Can't find %s in /etc/fstab\n", blockDevice);
207 break;
208 }
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000209
Rob Landley6a6798b2005-08-10 20:35:54 +0000210 // If we're mounting all and all doesn't mount this one, skip it.
211
212 if(all) {
213 if(strstr(m.mnt_opts,"noauto") || strstr(m.mnt_type,"swap"))
214 continue;
215 flags=0;
216
217 /* If we're mounting something specific and this isn't it, skip it.
218 * Note we must match both the exact text in fstab (ala "proc") or
219 * a full path from root */
220
221 } else if(strcmp(blockDevice,m.mnt_fsname) &&
222 strcmp(argv[optind],m.mnt_fsname) &&
223 strcmp(blockDevice,m.mnt_dir) &&
224 strcmp(argv[optind],m.mnt_dir)) continue;
225
226 /* Parse flags from /etc/fstab (unless this is a single mount
227 * overriding fstab -- note the "all" test above zeroed the flags,
228 * to prevent flags from previous entries affecting this one, so
229 * the only way we could get here with nonzero flags is a single
230 * mount). */
231
232 if(!flags) {
233 if(ENABLE_FEATURE_CLEAN_UP) free(string_flags);
234 string_flags=NULL;
235 parse_mount_options(m.mnt_opts, &flags, &string_flags);
236 }
237
238 /* Fill out remaining fields with info from mtab */
239
240 if(ENABLE_FEATURE_CLEAN_UP) {
241 free(blockDevice);
242 blockDevice=strdup(m.mnt_fsname);
243 free(directory);
Rob Landley7b363fd2005-12-20 17:18:01 +0000244 directory=strdup(m.mnt_dir);
Glenn L McGrath3aae1002001-05-07 01:38:03 +0000245 } else {
Rob Landley6a6798b2005-08-10 20:35:54 +0000246 blockDevice=m.mnt_fsname;
247 directory=m.mnt_dir;
Glenn L McGrath3aae1002001-05-07 01:38:03 +0000248 }
Rob Landley6a6798b2005-08-10 20:35:54 +0000249 fsType=m.mnt_type;
Matt Kraai34251112001-05-02 21:17:38 +0000250
Rob Landley6a6798b2005-08-10 20:35:54 +0000251 /* Ok, we're ready to actually mount a specific source on a specific
252 * directory now. */
Matt Kraaia7cecbc2001-08-10 15:05:27 +0000253
Rob Landley6a6798b2005-08-10 20:35:54 +0000254singlemount:
Erik Andersene49d5ec2000-02-08 19:58:47 +0000255
Rob Landley6a6798b2005-08-10 20:35:54 +0000256 // If they said -w, override fstab
Erik Andersen246cc6d2000-03-07 07:41:42 +0000257
Rob Landley6a6798b2005-08-10 20:35:54 +0000258 if(allowWrite) flags&=~MS_RDONLY;
259
260 // Might this be an NFS filesystem?
261
Rob Landley4a7252b2005-10-08 18:49:45 +0000262 if(ENABLE_FEATURE_MOUNT_NFS && (!fsType || !strcmp(fsType,"nfs")) &&
Rob Landley6a6798b2005-08-10 20:35:54 +0000263 strchr(blockDevice, ':') != NULL)
264 {
265 if(nfsmount(blockDevice, directory, &flags, &string_flags, 1))
266 bb_perror_msg("nfsmount failed");
267 else {
Rob Landley6e985212005-08-14 18:46:34 +0000268 rc = 0;
Rob Landley6a6798b2005-08-10 20:35:54 +0000269 fsType="nfs";
Rob Landley9a643142005-08-30 17:07:49 +0000270 // Strangely enough, nfsmount() doesn't actually mount()
271 goto mount_it_now;
Eric Andersenfdd51032000-08-02 18:48:26 +0000272 }
Rob Landley6a6798b2005-08-10 20:35:54 +0000273 } else {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000274
Rob Landley6a6798b2005-08-10 20:35:54 +0000275 // Do we need to allocate a loopback device?
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000276
Rob Landley6a6798b2005-08-10 20:35:54 +0000277 if(ENABLE_FEATURE_MOUNT_LOOP && !fakeIt && S_ISREG(statbuf.st_mode))
Eric Andersen5ef44822003-02-28 06:29:27 +0000278 {
Rob Landley6a6798b2005-08-10 20:35:54 +0000279 loopFile = blockDevice;
280 blockDevice = 0;
281 switch(set_loop(&blockDevice, loopFile, 0)) {
282 case 0:
283 case 1:
284 break;
285 default:
286 bb_error_msg_and_die(
287 errno == EPERM || errno == EACCES ?
288 bb_msg_perm_denied_are_you_root :
289 "Couldn't setup loop device");
290 break;
Eric Andersenfdd51032000-08-02 18:48:26 +0000291 }
Eric Andersen252bacc2000-09-19 01:21:13 +0000292 }
Rob Landley6a6798b2005-08-10 20:35:54 +0000293
294 /* If we know the fstype (or don't need to), jump straight
295 * to the actual mount. */
296
297 if(fsType || (flags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
298 goto mount_it_now;
299 }
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000300
Rob Landley6a6798b2005-08-10 20:35:54 +0000301 // Loop through filesystem types until mount succeeds or we run out
302
303 for(i = 0; files[i] && rc; i++) {
304 f = fopen(files[i], "r");
305 if(!f) continue;
306 // Get next block device backed filesystem
307 for(buf = 0; (buf = fsType = bb_get_chomped_line_from_file(f));
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000308 free(buf))
Rob Landley6a6798b2005-08-10 20:35:54 +0000309 {
310 // Skip funky entries in /proc
311 if(!strncmp(buf,"nodev",5) && isspace(buf[5])) continue;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000312
Rob Landley6a6798b2005-08-10 20:35:54 +0000313 while(isspace(*fsType)) fsType++;
314 if(*buf=='#' || *buf=='*') continue;
315 if(!*fsType) continue;
316mount_it_now:
317 // Okay, try to mount
318
319 if (!fakeIt) {
320 for(;;) {
321 rc = mount(blockDevice, directory, fsType, flags, string_flags);
322 if(!rc || (flags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS))
323 break;
324 bb_error_msg("%s is write-protected, mounting read-only", blockDevice);
325 flags|=MS_RDONLY;
326 }
327 }
Rob Landleyde5fd232005-08-14 19:26:14 +0000328 if(!rc || !f) break;
Glenn L McGrath8042f652002-08-23 06:17:46 +0000329 }
Rob Landley71d6ccd2006-01-10 05:30:28 +0000330 if(!f) break;
331 fclose(f);
332 // goto mount_it_now with -a can jump past the initialization
333 f=0;
334 if(!rc) break;
Rob Landley6a6798b2005-08-10 20:35:54 +0000335 }
336
Bernhard Reutner-Fischer126da9e2005-12-12 11:20:39 +0000337 /* If the mount was successful, and we're maintaining an old-style
Rob Landley6a6798b2005-08-10 20:35:54 +0000338 * mtab file by hand, add new entry to it now. */
339 if((!rc || fakeIt) && useMtab) {
340 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
341
342 if(!mountTable) bb_perror_msg(bb_path_mtab_file);
343 else {
344 // Remove trailing / (if any) from directory we mounted on
345 int length=strlen(directory);
346 if(length>1 && directory[length-1] == '/')
347 directory[length-1]=0;
348
349 // Fill out structure (should be ok to re-use existing one).
350 m.mnt_fsname=blockDevice;
351 m.mnt_dir=directory;
352 m.mnt_type=fsType ? : "--bind";
353 m.mnt_opts=string_flags ? :
354 ((flags & MS_RDONLY) ? "ro" : "rw");
355 m.mnt_freq = 0;
356 m.mnt_passno = 0;
357
358 // Write and close
359 addmntent(mountTable, &m);
360 endmntent(mountTable);
Glenn L McGrath8042f652002-08-23 06:17:46 +0000361 }
Rob Landley6a6798b2005-08-10 20:35:54 +0000362 } else {
363 // Mount failed. Clean up
364 if(loopFile) {
365 del_loop(blockDevice);
366 if(ENABLE_FEATURE_CLEAN_UP) free(loopFile);
367 }
368 // Don't whine about already mounted fs when mounting all.
Rob Landley6e985212005-08-14 18:46:34 +0000369 if(rc<0 && errno == EBUSY && all) rc = 0;
Rob Landley6a6798b2005-08-10 20:35:54 +0000370 else if (errno == EPERM)
371 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
Eric Andersenfdd51032000-08-02 18:48:26 +0000372 }
Rob Landley6a6798b2005-08-10 20:35:54 +0000373 // We couldn't free this earlier becase fsType could be in buf.
Rob Landley7b363fd2005-12-20 17:18:01 +0000374 if(ENABLE_FEATURE_CLEAN_UP) free(buf);
Rob Landley6a6798b2005-08-10 20:35:54 +0000375 if(!all) break;
Eric Andersenfdd51032000-08-02 18:48:26 +0000376 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000377
Rob Landley6a6798b2005-08-10 20:35:54 +0000378 if(file) endmntent(file);
379 if(rc) bb_perror_msg("Mounting %s on %s failed", blockDevice, directory);
Rob Landley7b363fd2005-12-20 17:18:01 +0000380 if(ENABLE_FEATURE_CLEAN_UP) {
381 free(blockDevice);
382 free(directory);
383 }
Rob Landley6a6798b2005-08-10 20:35:54 +0000384
Rob Landley6e985212005-08-14 18:46:34 +0000385 return rc;
Eric Andersencc8ed391999-10-05 16:24:54 +0000386}