mirror of
https://git.busybox.net/busybox
synced 2026-01-31 16:43:21 +00:00
things down a bit, fixed a number of funky corner cases, added support for several new features (things like mount --move, mount --bind, lazy unounts, automatic detection of loop mounts, and so on). Probably broke several other things, but it's fixable. (Bang on it, tell me what doesn't work for you...) Note: you no longer need to say "-o loop". It does that for you when necessary. Still need to add "user mount" support, which involves making mount suid. Not too hard to do under the new infrastructure, just haven't done it yet... The previous code had the following notes, that belong in the version control comments: - * 3/21/1999 Charles P. Wright <cpwright@cpwright.com> - * searches through fstab when -a is passed - * will try mounting stuff with all fses when passed -t auto - * - * 1999-04-17 Dave Cinege...Rewrote -t auto. Fixed ro mtab. - * - * 1999-10-07 Erik Andersen <andersen@codepoet.org>. - * Rewrite of a lot of code. Removed mtab usage (I plan on - * putting it back as a compile-time option some time), - * major adjustments to option parsing, and some serious - * dieting all around. - * - * 1999-11-06 mtab support is back - andersee - * - * 2000-01-12 Ben Collins <bcollins@debian.org>, Borrowed utils-linux's - * mount to add loop support. - * - * 2000-04-30 Dave Cinege <dcinege@psychosis.com> - * Rewrote fstab while loop and lower mount section. Can now do - * single mounts from fstab. Can override fstab options for single - * mount. Common mount_one call for single mounts and 'all'. Fixed - * mtab updating and stale entries. Removed 'remount' default. - *
400 lines
11 KiB
C
400 lines
11 KiB
C
/* vi: set sw=4 ts=4: */
|
|
/*
|
|
* Mini mount implementation for busybox
|
|
*
|
|
* Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
|
|
* Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
|
|
* Copyright (C) 2005 by Rob Landley <rob@landley.net>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <mntent.h>
|
|
#include <ctype.h>
|
|
#include <sys/mount.h>
|
|
#include <fcntl.h> // for CONFIG_FEATURE_MOUNT_LOOP
|
|
#include <sys/ioctl.h> // for CONFIG_FEATURE_MOUNT_LOOP
|
|
#include "busybox.h"
|
|
|
|
/* This is just a warning of a common mistake. Possibly this should be a
|
|
* uclibc faq entry rather than in busybox... */
|
|
#if ENABLE_NFSMOUNT && defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
|
|
#error "You need to build uClibc with UCLIBC_HAS_RPC for busybox mount with NFS support to compile."
|
|
#endif
|
|
|
|
// These two aren't always defined in old headers
|
|
#ifndef MS_BIND
|
|
#define MS_BIND 4096
|
|
#endif
|
|
#ifndef MS_MOVE
|
|
#define MS_MOVE 8192
|
|
#endif
|
|
|
|
/* Consume standard mount options (from -o options or --options).
|
|
* Set appropriate flags and collect unrecognized ones as a comma separated
|
|
* string to pass to kernel */
|
|
|
|
struct {
|
|
const char *name;
|
|
long flags;
|
|
} static const mount_options[] = {
|
|
{"loop", 0},
|
|
{"defaults", 0},
|
|
{"noauto", 0},
|
|
{"ro", MS_RDONLY},
|
|
{"rw", ~MS_RDONLY},
|
|
{"nosuid", MS_NOSUID},
|
|
{"suid", ~MS_NOSUID},
|
|
{"dev", ~MS_NODEV},
|
|
{"nodev", MS_NODEV},
|
|
{"exec", ~MS_NOEXEC},
|
|
{"noexec", MS_NOEXEC},
|
|
{"sync", MS_SYNCHRONOUS},
|
|
{"async", ~MS_SYNCHRONOUS},
|
|
{"remount", MS_REMOUNT},
|
|
{"atime", MS_NOATIME},
|
|
{"noatime", MS_NOATIME},
|
|
{"diratime", MS_NODIRATIME},
|
|
{"nodiratime", MS_NODIRATIME},
|
|
{"bind", MS_BIND},
|
|
{"move", MS_MOVE}
|
|
};
|
|
|
|
/* Uses the mount_options list above */
|
|
static void parse_mount_options(char *options, int *flags, char **strflags)
|
|
{
|
|
// Loop through options
|
|
for(;;) {
|
|
int i;
|
|
char *comma = strchr(options, ',');
|
|
|
|
if(comma) *comma = 0;
|
|
|
|
// Find this option in mount_options
|
|
for(i = 0; i < (sizeof(mount_options) / sizeof(*mount_options)); i++) {
|
|
if(!strcasecmp(mount_options[i].name, options)) {
|
|
long fl = mount_options[i].flags;
|
|
if(fl < 0) *flags &= fl;
|
|
else *flags |= fl;
|
|
break;
|
|
}
|
|
}
|
|
// Unrecognized mount option?
|
|
if(i == sizeof(mount_options)) {
|
|
// Add it to strflags, to pass on to kernel
|
|
i = *strflags ? strlen(*strflags) : 0;
|
|
*strflags = xrealloc(*strflags, i+strlen(options)+2);
|
|
// Comma separated if it's not the first one
|
|
if(i) (*strflags)[i] = ',';
|
|
strcpy((*strflags)+i, options);
|
|
}
|
|
// Advance to next option, or finish
|
|
if(comma) {
|
|
*comma = ',';
|
|
options = ++comma;
|
|
} else break;
|
|
}
|
|
}
|
|
|
|
/* This does the work */
|
|
|
|
extern int mount_main(int argc, char **argv)
|
|
{
|
|
char *string_flags = 0, *fsType = 0, *blockDevice = 0, *directory = 0,
|
|
*loopFile = 0, *buf = 0,
|
|
*files[] = {"/etc/filesystems", "/proc/filesystems", 0};
|
|
int i, opt, all = FALSE, fakeIt = FALSE, allowWrite = FALSE,
|
|
rc = EXIT_FAILURE, useMtab = ENABLE_FEATURE_MTAB_SUPPORT;
|
|
int flags=0xc0ed0000; // Needed for linux 2.2, ignored by 2.4 and 2.6.
|
|
FILE *file = 0,*f = 0;
|
|
char path[PATH_MAX*2];
|
|
struct mntent m;
|
|
struct stat statbuf;
|
|
|
|
/* parse long options, like --bind and --move. Note that -o option
|
|
* and --option are synonymous. Yes, this means --remount,rw works. */
|
|
|
|
for(i = opt = 0; i < argc; i++) {
|
|
if(argv[i][0] == '-' && argv[i][1] == '-')
|
|
parse_mount_options(argv[i]+2, &flags, &string_flags);
|
|
else argv[opt++] = argv[i];
|
|
}
|
|
argc = opt;
|
|
|
|
// Parse remaining options
|
|
|
|
while((opt = getopt(argc, argv, "o:t:rwafnv")) > 0) {
|
|
switch (opt) {
|
|
case 'o':
|
|
parse_mount_options(optarg, &flags, &string_flags);
|
|
break;
|
|
case 't':
|
|
fsType = optarg;
|
|
break;
|
|
case 'r':
|
|
flags |= MS_RDONLY;
|
|
break;
|
|
case 'w':
|
|
allowWrite=TRUE;
|
|
break;
|
|
case 'a':
|
|
all = TRUE;
|
|
break;
|
|
case 'f':
|
|
fakeIt = TRUE;
|
|
break;
|
|
case 'n':
|
|
useMtab = FALSE;
|
|
break;
|
|
case 'v':
|
|
break; // ignore -v
|
|
default:
|
|
bb_show_usage();
|
|
}
|
|
}
|
|
|
|
// If we have no arguments, show currently mounted filesystems
|
|
|
|
if(!all && (optind == argc)) {
|
|
FILE *mountTable = setmntent(bb_path_mtab_file, "r");
|
|
|
|
if(!mountTable) bb_perror_msg_and_die(bb_path_mtab_file);
|
|
|
|
while (getmntent_r(mountTable,&m,path,sizeof(path))) {
|
|
blockDevice = m.mnt_fsname;
|
|
|
|
// Clean up display a little bit regarding root devie
|
|
if(!strcmp(blockDevice, "rootfs")) continue;
|
|
if(!strcmp(blockDevice, "/dev/root"))
|
|
blockDevice = find_block_device("/");
|
|
|
|
if(!fsType || !strcmp(m.mnt_type, fsType))
|
|
printf("%s on %s type %s (%s)\n", blockDevice, m.mnt_dir,
|
|
m.mnt_type, m.mnt_opts);
|
|
if(ENABLE_FEATURE_CLEAN_UP && blockDevice != m.mnt_fsname)
|
|
free(blockDevice);
|
|
}
|
|
endmntent(mountTable);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
/* The next argument is what to mount. if there's an argument after that
|
|
* it's where to mount it. If we're not mounting all, and we have both
|
|
* of these arguments, jump straight to the actual mount. */
|
|
|
|
statbuf.st_mode=0;
|
|
if(optind < argc)
|
|
blockDevice = !stat(argv[optind], &statbuf) ?
|
|
bb_simplify_path(argv[optind]) :
|
|
(ENABLE_FEATURE_CLEAN_UP ? strdup(argv[optind]) : argv[optind]);
|
|
if(optind+1 < argc) directory = bb_simplify_path(argv[optind+1]);
|
|
|
|
// If we don't have to loop through fstab, skip ahead a bit.
|
|
|
|
if(!all && optind+1!=argc) goto singlemount;
|
|
|
|
// Loop through /etc/fstab entries to look up this entry.
|
|
|
|
if(!(file=setmntent("/etc/fstab","r")))
|
|
bb_perror_msg_and_die("\nCannot read /etc/fstab");
|
|
for(;;) {
|
|
|
|
// Get next fstab entry
|
|
|
|
if(!getmntent_r(file,&m,path,sizeof(path))) {
|
|
if(!all)
|
|
bb_perror_msg("Can't find %s in /etc/fstab\n", blockDevice);
|
|
break;
|
|
}
|
|
|
|
// If we're mounting all and all doesn't mount this one, skip it.
|
|
|
|
if(all) {
|
|
if(strstr(m.mnt_opts,"noauto") || strstr(m.mnt_type,"swap"))
|
|
continue;
|
|
flags=0;
|
|
|
|
/* If we're mounting something specific and this isn't it, skip it.
|
|
* Note we must match both the exact text in fstab (ala "proc") or
|
|
* a full path from root */
|
|
|
|
} else if(strcmp(blockDevice,m.mnt_fsname) &&
|
|
strcmp(argv[optind],m.mnt_fsname) &&
|
|
strcmp(blockDevice,m.mnt_dir) &&
|
|
strcmp(argv[optind],m.mnt_dir)) continue;
|
|
|
|
/* Parse flags from /etc/fstab (unless this is a single mount
|
|
* overriding fstab -- note the "all" test above zeroed the flags,
|
|
* to prevent flags from previous entries affecting this one, so
|
|
* the only way we could get here with nonzero flags is a single
|
|
* mount). */
|
|
|
|
if(!flags) {
|
|
if(ENABLE_FEATURE_CLEAN_UP) free(string_flags);
|
|
string_flags=NULL;
|
|
parse_mount_options(m.mnt_opts, &flags, &string_flags);
|
|
}
|
|
|
|
/* Fill out remaining fields with info from mtab */
|
|
|
|
if(ENABLE_FEATURE_CLEAN_UP) {
|
|
free(blockDevice);
|
|
blockDevice=strdup(m.mnt_fsname);
|
|
free(directory);
|
|
directory=strdup(m.mnt_type);
|
|
} else {
|
|
blockDevice=m.mnt_fsname;
|
|
directory=m.mnt_dir;
|
|
}
|
|
fsType=m.mnt_type;
|
|
|
|
/* Ok, we're ready to actually mount a specific source on a specific
|
|
* directory now. */
|
|
|
|
singlemount:
|
|
|
|
// If they said -w, override fstab
|
|
|
|
if(allowWrite) flags&=~MS_RDONLY;
|
|
|
|
// Might this be an NFS filesystem?
|
|
|
|
if(ENABLE_NFSMOUNT && (!fsType || !strcmp(fsType,"nfs")) &&
|
|
strchr(blockDevice, ':') != NULL)
|
|
{
|
|
if(nfsmount(blockDevice, directory, &flags, &string_flags, 1))
|
|
bb_perror_msg("nfsmount failed");
|
|
else {
|
|
rc=EXIT_SUCCESS;
|
|
fsType="nfs";
|
|
}
|
|
} else {
|
|
|
|
// Do we need to allocate a loopback device?
|
|
|
|
if(ENABLE_FEATURE_MOUNT_LOOP && !fakeIt && S_ISREG(statbuf.st_mode))
|
|
{
|
|
loopFile = blockDevice;
|
|
blockDevice = 0;
|
|
switch(set_loop(&blockDevice, loopFile, 0)) {
|
|
case 0:
|
|
case 1:
|
|
break;
|
|
default:
|
|
bb_error_msg_and_die(
|
|
errno == EPERM || errno == EACCES ?
|
|
bb_msg_perm_denied_are_you_root :
|
|
"Couldn't setup loop device");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If we know the fstype (or don't need to), jump straight
|
|
* to the actual mount. */
|
|
|
|
if(fsType || (flags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
|
|
goto mount_it_now;
|
|
}
|
|
|
|
// Loop through filesystem types until mount succeeds or we run out
|
|
|
|
for(i = 0; files[i] && rc; i++) {
|
|
f = fopen(files[i], "r");
|
|
if(!f) continue;
|
|
// Get next block device backed filesystem
|
|
for(buf = 0; (buf = fsType = bb_get_chomped_line_from_file(f));
|
|
free(buf))
|
|
{
|
|
// Skip funky entries in /proc
|
|
if(!strncmp(buf,"nodev",5) && isspace(buf[5])) continue;
|
|
|
|
while(isspace(*fsType)) fsType++;
|
|
if(*buf=='#' || *buf=='*') continue;
|
|
if(!*fsType) continue;
|
|
mount_it_now:
|
|
// Okay, try to mount
|
|
|
|
if (!fakeIt) {
|
|
for(;;) {
|
|
rc = mount(blockDevice, directory, fsType, flags, string_flags);
|
|
if(!rc || (flags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS))
|
|
break;
|
|
bb_error_msg("%s is write-protected, mounting read-only", blockDevice);
|
|
flags|=MS_RDONLY;
|
|
}
|
|
}
|
|
if(!rc) break;
|
|
}
|
|
if(f) fclose(f);
|
|
if(!f || !rc) break;
|
|
}
|
|
|
|
/* If the mount was sucessful, and we're maintaining an old-style
|
|
* mtab file by hand, add new entry to it now. */
|
|
if((!rc || fakeIt) && useMtab) {
|
|
FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
|
|
|
|
if(!mountTable) bb_perror_msg(bb_path_mtab_file);
|
|
else {
|
|
// Remove trailing / (if any) from directory we mounted on
|
|
int length=strlen(directory);
|
|
if(length>1 && directory[length-1] == '/')
|
|
directory[length-1]=0;
|
|
|
|
// Fill out structure (should be ok to re-use existing one).
|
|
m.mnt_fsname=blockDevice;
|
|
m.mnt_dir=directory;
|
|
m.mnt_type=fsType ? : "--bind";
|
|
m.mnt_opts=string_flags ? :
|
|
((flags & MS_RDONLY) ? "ro" : "rw");
|
|
m.mnt_freq = 0;
|
|
m.mnt_passno = 0;
|
|
|
|
// Write and close
|
|
addmntent(mountTable, &m);
|
|
endmntent(mountTable);
|
|
}
|
|
} else {
|
|
// Mount failed. Clean up
|
|
if(loopFile) {
|
|
del_loop(blockDevice);
|
|
if(ENABLE_FEATURE_CLEAN_UP) free(loopFile);
|
|
}
|
|
// Don't whine about already mounted fs when mounting all.
|
|
if(rc<0 && errno == EBUSY && all) rc=0;
|
|
else if (errno == EPERM)
|
|
bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
|
|
}
|
|
// We couldn't free this earlier becase fsType could be in buf.
|
|
if(ENABLE_FEATURE_CLEAN_UP) {
|
|
free(buf);
|
|
free(blockDevice);
|
|
free(directory);
|
|
}
|
|
if(!all) break;
|
|
}
|
|
|
|
if(file) endmntent(file);
|
|
if(rc) bb_perror_msg("Mounting %s on %s failed", blockDevice, directory);
|
|
|
|
return rc ? : EXIT_FAILURE;
|
|
}
|