1 /* vi: set sw=4 ts=4: */
3 * Mini mount implementation for busybox
5 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
6 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7 * Copyright (C) 2005 by Rob Landley <rob@landley.net>
9 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
20 #include <sys/mount.h>
21 #include <fcntl.h> // for CONFIG_FEATURE_MOUNT_LOOP
22 #include <sys/ioctl.h> // for CONFIG_FEATURE_MOUNT_LOOP
25 // These two aren't always defined in old headers
33 /* Consume standard mount options (from -o options or --options).
34 * Set appropriate flags and collect unrecognized ones as a comma separated
35 * string to pass to kernel */
40 } static const mount_options[] = {
46 {"nosuid", MS_NOSUID},
51 {"noexec", MS_NOEXEC},
52 {"sync", MS_SYNCHRONOUS},
53 {"async", ~MS_SYNCHRONOUS},
54 {"remount", MS_REMOUNT},
55 {"atime", ~MS_NOATIME},
56 {"noatime", MS_NOATIME},
57 {"diratime", ~MS_NODIRATIME},
58 {"nodiratime", MS_NODIRATIME},
63 /* Uses the mount_options list above */
64 static void parse_mount_options(char *options, int *flags, char **strflags)
66 // Loop through options
69 char *comma = strchr(options, ',');
73 // 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;
82 // Unrecognized mount option?
83 if(i == (sizeof(mount_options) / sizeof(*mount_options))) {
84 // 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
88 if(i) (*strflags)[i++] = ',';
89 strcpy((*strflags)+i, options);
91 // Advance to next option, or finish
99 /* This does the work */
101 extern int mount_main(int argc, char **argv)
103 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,
107 rc = 1, useMtab = ENABLE_FEATURE_MTAB_SUPPORT;
108 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];
114 /* parse long options, like --bind and --move. Note that -o option
115 * and --option are synonymous. Yes, this means --remount,rw works. */
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];
124 // Parse remaining options
126 while((opt = getopt(argc, argv, "o:t:rwafnv")) > 0) {
129 parse_mount_options(optarg, &flags, &string_flags);
156 // If we have no arguments, show currently mounted filesystems
158 if(!all && (optind == argc)) {
159 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
161 if(!mountTable) bb_perror_msg_and_die(bb_path_mtab_file);
163 while (getmntent_r(mountTable,&m,path,sizeof(path))) {
164 blockDevice = m.mnt_fsname;
166 // Clean up display a little bit regarding root device
167 if(!strcmp(blockDevice, "rootfs")) continue;
168 if(!strcmp(blockDevice, "/dev/root"))
169 blockDevice = find_block_device("/");
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)
177 endmntent(mountTable);
181 /* 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. */
187 blockDevice = !stat(argv[optind], &statbuf) ?
188 bb_simplify_path(argv[optind]) :
189 (ENABLE_FEATURE_CLEAN_UP ? strdup(argv[optind]) : argv[optind]);
190 if(optind+1 < argc) directory = bb_simplify_path(argv[optind+1]);
192 // If we don't have to loop through fstab, skip ahead a bit.
194 if(!all && optind+1!=argc) goto singlemount;
196 // Loop through /etc/fstab entries to look up this entry.
198 if(!(file=setmntent("/etc/fstab","r")))
199 bb_perror_msg_and_die("\nCannot read /etc/fstab");
202 // Get next fstab entry
204 if(!getmntent_r(file,&m,path,sizeof(path))) {
206 bb_perror_msg("Can't find %s in /etc/fstab\n", blockDevice);
210 // If we're mounting all and all doesn't mount this one, skip it.
213 if(strstr(m.mnt_opts,"noauto") || strstr(m.mnt_type,"swap"))
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 */
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;
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
233 if(ENABLE_FEATURE_CLEAN_UP) free(string_flags);
235 parse_mount_options(m.mnt_opts, &flags, &string_flags);
238 /* Fill out remaining fields with info from mtab */
240 if(ENABLE_FEATURE_CLEAN_UP) {
242 blockDevice=strdup(m.mnt_fsname);
244 directory=strdup(m.mnt_dir);
246 blockDevice=m.mnt_fsname;
251 /* Ok, we're ready to actually mount a specific source on a specific
256 // If they said -w, override fstab
258 if(allowWrite) flags&=~MS_RDONLY;
260 // Might this be an NFS filesystem?
262 if(ENABLE_FEATURE_MOUNT_NFS && (!fsType || !strcmp(fsType,"nfs")) &&
263 strchr(blockDevice, ':') != NULL)
265 if(nfsmount(blockDevice, directory, &flags, &string_flags, 1))
266 bb_perror_msg("nfsmount failed");
270 // Strangely enough, nfsmount() doesn't actually mount()
275 // Do we need to allocate a loopback device?
277 if(ENABLE_FEATURE_MOUNT_LOOP && !fakeIt && S_ISREG(statbuf.st_mode))
279 loopFile = blockDevice;
281 switch(set_loop(&blockDevice, loopFile, 0)) {
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");
294 /* If we know the fstype (or don't need to), jump straight
295 * to the actual mount. */
297 if(fsType || (flags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
301 // Loop through filesystem types until mount succeeds or we run out
303 for(i = 0; files[i] && rc; i++) {
304 f = fopen(files[i], "r");
306 // Get next block device backed filesystem
307 for(buf = 0; (buf = fsType = bb_get_chomped_line_from_file(f));
310 // Skip funky entries in /proc
311 if(!strncmp(buf,"nodev",5) && isspace(buf[5])) continue;
313 while(isspace(*fsType)) fsType++;
314 if(*buf=='#' || *buf=='*') continue;
315 if(!*fsType) continue;
317 // Okay, try to mount
321 rc = mount(blockDevice, directory, fsType, flags, string_flags);
322 if(!rc || (flags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS))
324 bb_error_msg("%s is write-protected, mounting read-only", blockDevice);
332 // goto mount_it_now with -a can jump past the initialization
337 /* If the mount was successful, and we're maintaining an old-style
338 * mtab file by hand, add new entry to it now. */
339 if((!rc || fakeIt) && useMtab) {
340 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
342 if(!mountTable) bb_perror_msg(bb_path_mtab_file);
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;
349 // Fill out structure (should be ok to re-use existing one).
350 m.mnt_fsname=blockDevice;
352 m.mnt_type=fsType ? : "--bind";
353 m.mnt_opts=string_flags ? :
354 ((flags & MS_RDONLY) ? "ro" : "rw");
359 addmntent(mountTable, &m);
360 endmntent(mountTable);
363 // Mount failed. Clean up
365 del_loop(blockDevice);
366 if(ENABLE_FEATURE_CLEAN_UP) free(loopFile);
368 // Don't whine about already mounted fs when mounting all.
369 if(rc<0 && errno == EBUSY && all) rc = 0;
370 else if (errno == EPERM)
371 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
373 // We couldn't free this earlier becase fsType could be in buf.
374 if(ENABLE_FEATURE_CLEAN_UP) free(buf);
378 if(file) endmntent(file);
379 if(rc) bb_perror_msg("Mounting %s on %s failed", blockDevice, directory);
380 if(ENABLE_FEATURE_CLEAN_UP) {