blob: 9f51db3101025e0eb305c4056e2ce0aad63c43fa [file] [log] [blame]
/*
* A mount(8) for Linux 0.99.
* mount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
*
* Wed Sep 14 22:43:00 1994: Mitchum DSouza
* (mitch@mrc-applied-psychology.cambridge.ac.uk) added support for mounting
* the "loop" device.
*
* Wed Sep 14 22:55:10 1994: Sander van Malssen (svm@kozmix.hacktic.nl)
* added support for remounting readonly file systems readonly.
*
* Wed Feb 8 12:27:00 1995: Andries.Brouwer@cwi.nl fixed up error messages.
* Sat Jun 3 20:44:38 1995: Patches from Andries.Brouwer@cwi.nl applied.
* Tue Sep 26 22:38:20 1995: aeb@cwi.nl, many changes
* Fri Feb 23 13:47:00 1996: aeb@cwi.nl, loop device related changes
*
* Since then, many changes - aeb.
*
* Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
* Implemented the "bg", "fg" and "retry" mount options for NFS.
*
* Tue Aug 4 15:54:31 1998: aeb@cwi.nl:
* Open fd 0,1,2 so that printf's do not clobber /etc/mtab or so.
* Mangle filenames with embedded spaces. Add ufsmagic. Add locking.
* Avoid unnecessary error messages about /proc.
* Improve support for noncanonical names in /etc/fstab.
* Add support for volume labels and UUIDs.
*
* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
* - added Native Language Support
* 1999-03-21 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
* - fixed strerr(errno) in gettext calls
* 1999-07-05 Hirokazu Takahashi <h-takaha@sss.abk.nec.co.jp>
* - fixed use of nouser option
*/
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include "mount_constants.h"
#include "sundries.h"
#include "mntent.h"
#include "fstab.h"
#include "lomount.h"
#include "loop.h"
#include "linux_fs.h" /* for BLKGETSIZE */
#include "mount_guess_fstype.h"
#include "mount_by_label.h"
#include "getusername.h"
#include "nls.h"
#define DO_PS_FIDDLING
#ifdef DO_PS_FIDDLING
#include "setproctitle.h"
#endif
/* True for fake mount (-f). */
int fake = 0;
/* Don't write a entry in /etc/mtab (-n). */
int nomtab = 0;
/* True for explicit readonly (-r). */
int readonly = 0;
/* Nonzero for chatty (-v). */
int verbose = 0;
/* Nonzero for sloppy (-s). */
int sloppy = 0;
/* True for explicit read/write (-w). */
int readwrite = 0;
/* True for all mount (-a). */
int all = 0;
/* True for fork() during all mount (-F). */
int optfork = 0;
/* True if ruid != euid. */
int suid = 0;
/* Map from -o and fstab option strings to the flag argument to mount(2). */
struct opt_map
{
const char *opt; /* option name */
int skip; /* skip in mtab option string */
int inv; /* true if flag value should be inverted */
int mask; /* flag mask value */
};
/* Custom mount options for our own purposes. */
/* We can use the high-order 16 bits, since the mount call
has MS_MGC_VAL there. */
#define MS_NOAUTO 0x80000000
#define MS_USERS 0x40000000
#define MS_USER 0x20000000
#define MS_LOOP 0x00010000
/* Options that we keep the mount system call from seeing. */
#define MS_NOSYS (MS_NOAUTO|MS_USERS|MS_USER|MS_LOOP)
/* Options that we keep from appearing in the options field in the mtab. */
#define MS_NOMTAB (MS_REMOUNT|MS_NOAUTO|MS_USERS|MS_USER)
/* OPTIONS that we make ordinary users have by default. */
#define MS_SECURE (MS_NOEXEC|MS_NOSUID|MS_NODEV)
const struct opt_map opt_map[] = {
{ "defaults", 0, 0, 0 }, /* default options */
{ "ro", 1, 0, MS_RDONLY }, /* read-only */
{ "rw", 1, 1, MS_RDONLY }, /* read-write */
{ "exec", 0, 1, MS_NOEXEC }, /* permit execution of binaries */
{ "noexec", 0, 0, MS_NOEXEC }, /* don't execute binaries */
{ "suid", 0, 1, MS_NOSUID }, /* honor suid executables */
{ "nosuid", 0, 0, MS_NOSUID }, /* don't honor suid executables */
{ "dev", 0, 1, MS_NODEV }, /* interpret device files */
{ "nodev", 0, 0, MS_NODEV }, /* don't interpret devices */
{ "sync", 0, 0, MS_SYNCHRONOUS}, /* synchronous I/O */
{ "async", 0, 1, MS_SYNCHRONOUS}, /* asynchronous I/O */
{ "remount", 0, 0, MS_REMOUNT}, /* Alter flags of mounted FS */
{ "auto", 0, 1, MS_NOAUTO }, /* Can be mounted using -a */
{ "noauto", 0, 0, MS_NOAUTO }, /* Can only be mounted explicitly */
{ "users", 0, 0, MS_USERS }, /* Allow ordinary user to mount */
{ "nousers", 0, 1, MS_USERS }, /* Forbid ordinary user to mount */
{ "user", 0, 0, MS_USER }, /* Allow ordinary user to mount */
{ "nouser", 0, 1, MS_USER }, /* Forbid ordinary user to mount */
/* add new options here */
#ifdef MS_NOSUB
{ "sub", 0, 1, MS_NOSUB }, /* allow submounts */
{ "nosub", 0, 0, MS_NOSUB }, /* don't allow submounts */
#endif
#ifdef MS_SILENT
{ "quiet", 0, 0, MS_SILENT }, /* be quiet */
{ "loud", 0, 1, MS_SILENT }, /* print out messages. */
#endif
#ifdef MS_MANDLOCK
{ "mand", 0, 0, MS_MANDLOCK }, /* Allow mandatory locks on this FS */
{ "nomand", 0, 1, MS_MANDLOCK }, /* Forbid mandatory locks on this FS */
#endif
{ "loop", 1, 0, MS_LOOP }, /* use a loop device */
#ifdef MS_NOATIME
{ "atime", 0, 1, MS_NOATIME }, /* Update access time */
{ "noatime", 0, 0, MS_NOATIME }, /* Do not update access time */
#endif
#ifdef MS_NODIRATIME
{ "diratime", 0, 1, MS_NODIRATIME }, /* Update dir access times */
{ "nodiratime", 0, 0, MS_NODIRATIME },/* Do not update dir access times */
#endif
{ NULL, 0, 0, 0 }
};
static char *opt_loopdev, *opt_vfstype, *opt_offset, *opt_encryption;
struct string_opt_map {
char *tag;
int skip;
char **valptr;
} string_opt_map[] = {
{ "loop=", 0, &opt_loopdev },
{ "vfs=", 1, &opt_vfstype },
{ "offset=", 0, &opt_offset },
{ "encryption=", 0, &opt_encryption },
{ NULL, 0, NULL }
};
static void
clear_string_opts(void) {
struct string_opt_map *m;
for (m = &string_opt_map[0]; m->tag; m++)
*(m->valptr) = NULL;
}
static int
parse_string_opt(char *s) {
struct string_opt_map *m;
int lth;
for (m = &string_opt_map[0]; m->tag; m++) {
lth = strlen(m->tag);
if (!strncmp(s, m->tag, lth)) {
*(m->valptr) = xstrdup(s + lth);
return 1;
}
}
return 0;
}
int mount_quiet=0;
/* Report on a single mount. */
static void
print_one (const struct mntentchn *mc) {
if (mount_quiet)
return;
printf ("%s on %s", mc->mnt_fsname, mc->mnt_dir);
if (mc->mnt_type != NULL && *(mc->mnt_type) != '\0')
printf (" type %s", mc->mnt_type);
if (mc->mnt_opts != NULL)
printf (" (%s)", mc->mnt_opts);
printf ("\n");
}
/* Report on everything in mtab (of the specified types if any). */
static int
print_all (string_list types)
{
struct mntentchn *mc;
for (mc = mtab_head()->nxt; mc; mc = mc->nxt) {
if (matching_type (mc->mnt_type, types))
print_one (mc);
}
exit (0);
}
/* Look for OPT in opt_map table and return mask value. If OPT isn't found,
tack it onto extra_opts (which is non-NULL). */
static inline void
parse_opt (const char *opt, int *mask, char *extra_opts)
{
const struct opt_map *om;
for (om = opt_map; om->opt != NULL; om++)
if (streq (opt, om->opt)) {
if (om->inv)
*mask &= ~om->mask;
else
*mask |= om->mask;
if ((om->mask == MS_USER || om->mask == MS_USERS)
&& !om->inv)
*mask |= MS_SECURE;
#ifdef MS_SILENT
if (om->mask == MS_SILENT && om->inv) {
mount_quiet = 1;
verbose = 0;
}
#endif
return;
}
if (*extra_opts)
strcat(extra_opts, ",");
strcat(extra_opts, opt);
}
/* Take -o options list and compute 4th and 5th args to mount(2). flags
gets the standard options and extra_opts anything we don't recognize. */
static void
parse_opts (char *opts, int *flags, char **extra_opts)
{
char *opt;
*flags = 0;
*extra_opts = NULL;
clear_string_opts();
if (opts != NULL)
{
*extra_opts = xmalloc (strlen (opts) + 1);
**extra_opts = '\0';
for (opt = strtok (opts, ","); opt; opt = strtok (NULL, ","))
if (!parse_string_opt (opt))
parse_opt (opt, flags, *extra_opts);
}
if (readonly)
*flags |= MS_RDONLY;
if (readwrite)
*flags &= ~MS_RDONLY;
}
/* Try to build a canonical options string. */
static char *
fix_opts_string (int flags, const char *extra_opts, const char *user) {
const struct opt_map *om;
const struct string_opt_map *m;
char *new_opts;
new_opts = (flags & MS_RDONLY) ? "ro" : "rw";
for (om = opt_map; om->opt != NULL; om++) {
if (om->skip)
continue;
if (om->inv || !om->mask || (flags & om->mask) != om->mask)
continue;
new_opts = xstrconcat3(new_opts, ",", om->opt);
flags &= ~om->mask;
}
for (m = &string_opt_map[0]; m->tag; m++) {
if (!m->skip && *(m->valptr))
new_opts = xstrconcat4(new_opts, ",", m->tag, *(m->valptr));
}
if (extra_opts && *extra_opts) {
new_opts = xstrconcat3(new_opts, ",", extra_opts);
}
if (user) {
new_opts = xstrconcat3(new_opts, ",user=", user);
}
return new_opts;
}
static int
already (const char *spec, const char *node) {
struct mntentchn *mc;
int ret = 1;
if ((mc = getmntfile(node)) != NULL)
error (_("mount: according to mtab, %s is already mounted on %s"),
mc->mnt_fsname, node);
else if ((mc = getmntfile(spec)) != NULL)
error (_("mount: according to mtab, %s is mounted on %s"),
spec, mc->mnt_dir);
else
ret = 0;
return ret;
}
/* Create mtab with a root entry. */
static void
create_mtab (void) {
struct mntentchn *fstab;
struct mntent mnt;
int flags;
char *extra_opts;
mntFILE *mfp;
lock_mtab();
mfp = my_setmntent (MOUNTED, "a+");
if (mfp == NULL || mfp->mntent_fp == NULL) {
int errsv = errno;
die (EX_FILEIO, _("mount: can't open %s for writing: %s"),
MOUNTED, strerror (errsv));
}
/* Find the root entry by looking it up in fstab */
if ((fstab = getfsfile ("/")) || (fstab = getfsfile ("root"))) {
parse_opts (xstrdup (fstab->mnt_opts), &flags, &extra_opts);
mnt.mnt_dir = "/";
mnt.mnt_fsname = canonicalize (fstab->mnt_fsname);
mnt.mnt_type = fstab->mnt_type;
mnt.mnt_opts = fix_opts_string (flags, extra_opts, NULL);
mnt.mnt_freq = mnt.mnt_passno = 0;
if (my_addmntent (mfp, &mnt) == 1) {
int errsv = errno;
die (EX_FILEIO, _("mount: error writing %s: %s"),
MOUNTED, strerror (errsv));
}
}
if (fchmod (fileno (mfp->mntent_fp), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0)
if (errno != EROFS) {
int errsv = errno;
die (EX_FILEIO, _("mount: error changing mode of %s: %s"),
MOUNTED, strerror (errsv));
}
my_endmntent (mfp);
unlock_mtab();
}
/* count successful mount system calls */
static int mountcount = 0;
static int
mount5 (struct mountargs *args) {
int ret = mount (args->spec, args->node, args->type,
MS_MGC_VAL | (args->flags), args->data);
if (ret == 0)
mountcount++;
return ret;
}
/* Mount a single file system. Return status,
so don't exit on non-fatal errors. */
static int
try_mount5 (char *spec, char *node, char **type, int flags, char *mount_opts) {
struct mountargs args = { spec, node, NULL, flags & ~MS_NOSYS, mount_opts };
if (*type && strcasecmp (*type, "auto") == 0)
*type = NULL;
if (!*type && !(flags & MS_REMOUNT))
*type = guess_fstype_from_superblock(spec);
if (*type || (flags & MS_REMOUNT)) {
args.type = *type;
return mount5 (&args);
}
return procfsloop(mount5, &args, type);
}
/*
* try_mount_one()
* Try to mount one file system. When "bg" is 1, this is a retry
* in the background. One additional exit code EX_BG is used here.
* It is used to instruct the caller to retry the mount in the
* background.
*/
static int
try_mount_one (const char *spec0, const char *node0, char *type0,
const char *opts0, int freq, int pass, int bg, int ro) {
struct mntentchn mcn;
struct mntent mnt;
int mnt_err;
int flags;
char *extra_opts; /* written in mtab */
char *mount_opts; /* actually used on system call */
int loop, looptype, offset;
const char *opts;
char *spec, *node, *type, *loopdev, *loopfile;
char *user = 0;
struct stat statbuf;
spec = xstrdup(spec0);
node = xstrdup(node0);
type = xstrdup(type0);
opts = xstrdup(opts0);
parse_opts (xstrdup (opts), &flags, &extra_opts);
/* root may allow certain types of mounts by ordinary users */
if (suid) {
if (!(flags & (MS_USER | MS_USERS))) {
if (already (spec, node))
die (EX_USAGE, _("mount failed"));
else
die (EX_USAGE, _("mount: only root can mount %s on %s"), spec, node);
}
if (flags & MS_USER)
user = getusername();
}
/* quietly succeed for fstab entries that don't get mounted automatically */
if (all && (flags & MS_NOAUTO))
return 0;
mount_opts = extra_opts;
/*
* In the case of a loop mount, either type is of the form lo@/dev/loop5
* or the option "-o loop=/dev/loop5" or just "-o loop" is given, or
* mount just has to figure things out for itself from the fact that
* spec is not a block device. We do not test for a block device
* immediately: maybe later other types of mountable objects will occur.
*/
loopdev = opt_loopdev;
looptype = (type && strncmp("lo@", type, 3) == 0);
if (looptype) {
if (loopdev)
error(_("mount: loop device specified twice"));
loopdev = type+3;
type = opt_vfstype;
}
else if (opt_vfstype) {
if (type)
error(_("mount: type specified twice"));
else
type = opt_vfstype;
}
loop = ((flags & MS_LOOP) || loopdev || opt_offset || opt_encryption);
loopfile = spec;
if (loop) {
flags |= MS_LOOP;
if (fake) {
if (verbose)
printf(_("mount: skipping the setup of a loop device\n"));
} else {
int loopro = (flags & MS_RDONLY);
if (!loopdev || !*loopdev)
loopdev = find_unused_loop_device();
if (!loopdev)
return EX_SYSERR; /* no more loop devices */
if (verbose)
printf(_("mount: going to use the loop device %s\n"), loopdev);
offset = opt_offset ? strtoul(opt_offset, NULL, 0) : 0;
if (set_loop (loopdev, loopfile, offset, opt_encryption, &loopro)) {
if (verbose)
printf(_("mount: failed setting up loop device\n"));
return EX_FAIL;
}
if (verbose > 1)
printf(_("mount: setup loop device successfully\n"));
spec = loopdev;
if (loopro)
flags |= MS_RDONLY;
}
}
if (!fake && type && streq (type, "nfs")) {
#ifdef HAVE_NFS
mnt_err = nfsmount (spec, node, &flags, &extra_opts, &mount_opts, bg);
if (mnt_err)
return mnt_err;
#else
die (EX_SOFTWARE, _("mount: this version was compiled "
"without support for the type `nfs'"));
#endif
}
/*
* Call mount.TYPE for types that require a separate
* mount program. For the moment these types are ncp and smb.
*/
if (type)
#ifndef ALWAYS_STAT
if (streq (type, "smb") || streq (type, "ncp")
/* these are incorrect but perhaps used by smbmount or so */
|| streq (type, "smbfs") || streq (type, "ncpfs")
)
#else
if (strlen (type) < 100)
#endif
{
char mountprog[120];
sprintf(mountprog, "/sbin/mount.%s", type);
if (stat(mountprog, &statbuf) == 0) {
if (fork() == 0) {
char *oo, *mountargs[10];
int i = 0;
setuid(getuid());
setgid(getgid());
oo = fix_opts_string (flags, extra_opts, NULL);
mountargs[i++] = mountprog;
mountargs[i++] = spec;
mountargs[i++] = node;
if (nomtab)
mountargs[i++] = "-n";
if (verbose)
mountargs[i++] = "-v";
if (oo && *oo) {
mountargs[i++] = "-o";
mountargs[i++] = oo;
}
mountargs[i] = NULL;
execv(mountprog, mountargs);
exit(1); /* exec failed */
} else if (fork() != -1) {
int status;
wait(&status);
return status;
} else {
int errsv = errno;
error(_("mount: cannot fork: %s"), strerror(errsv));
}
}
}
block_signals (SIG_BLOCK);
if (fake
|| (try_mount5 (spec, node, &type, flags & ~MS_NOSYS, mount_opts)) == 0)
/* Mount succeeded, report this (if verbose) and write mtab entry. */
{
if (loop)
opt_loopdev = loopdev;
mcn.mnt_fsname = mnt.mnt_fsname = canonicalize (loop ? loopfile : spec);
mcn.mnt_dir = mnt.mnt_dir = canonicalize (node);
mcn.mnt_type = mnt.mnt_type = type ? type : "unknown";
mcn.mnt_opts = mnt.mnt_opts = fix_opts_string (flags & ~MS_NOMTAB,
extra_opts, user);
mcn.nxt = 0;
mnt.mnt_freq = freq;
mnt.mnt_passno = pass;
/* We get chatty now rather than after the update to mtab since the
mount succeeded, even if the write to /etc/mtab should fail. */
if (verbose)
print_one (&mcn);
if (!nomtab && mtab_is_writable()) {
if (flags & MS_REMOUNT)
update_mtab (mnt.mnt_dir, &mnt);
else {
mntFILE *mfp;
lock_mtab();
mfp = my_setmntent(MOUNTED, "a+");
if (mfp == NULL || mfp->mntent_fp == NULL) {
int errsv = errno;
error(_("mount: can't open %s: %s"), MOUNTED,
strerror (errsv));
} else {
if ((my_addmntent (mfp, &mnt)) == 1) {
int errsv = errno;
error(_("mount: error writing %s: %s"), MOUNTED,
strerror (errsv));
}
my_endmntent(mfp);
}
unlock_mtab();
}
}
block_signals (SIG_UNBLOCK);
return 0;
}
mnt_err = errno;
if (loop)
del_loop(spec);
block_signals (SIG_UNBLOCK);
/* Mount failed, complain, but don't die. */
if (type == 0)
error (_("mount: you must specify the filesystem type"));
else
switch (mnt_err)
{
case EPERM:
if (geteuid() == 0) {
if (stat (node, &statbuf) || !S_ISDIR(statbuf.st_mode))
error (_("mount: mount point %s is not a directory"), node);
else
error (_("mount: permission denied"));
} else
error (_("mount: must be superuser to use mount"));
break;
case EBUSY:
if (flags & MS_REMOUNT) {
error (_("mount: %s is busy"), node);
} else if (!strcmp(type, "proc") && !strcmp(node, "/proc")) {
/* heuristic: if /proc/version exists, then probably proc is mounted */
if (stat ("/proc/version", &statbuf)) /* proc mounted? */
error (_("mount: %s is busy"), node); /* no */
else if(!all || verbose) /* yes, don't mention it */
error (_("mount: proc already mounted"));
} else {
error (_("mount: %s already mounted or %s busy"), spec, node);
already (spec, node);
}
break;
case ENOENT:
if (lstat (node, &statbuf))
error (_("mount: mount point %s does not exist"), node);
else if (stat (node, &statbuf))
error (_("mount: mount point %s is a symbolic link to nowhere"),
node);
else if (stat (spec, &statbuf))
error (_("mount: special device %s does not exist"), spec);
else {
errno = mnt_err;
perror("mount");
}
break;
case ENOTDIR:
if (stat (node, &statbuf) || ! S_ISDIR(statbuf.st_mode))
error (_("mount: mount point %s is not a directory"), node);
else if (stat (spec, &statbuf) && errno == ENOTDIR)
error (_("mount: special device %s does not exist\n"
" (a path prefix is not a directory)\n"), spec);
else {
errno = mnt_err;
perror("mount");
}
break;
case EINVAL:
{ int fd;
long size;
if (flags & MS_REMOUNT) {
error (_("mount: %s not mounted already, or bad option"), node);
} else {
error (_("mount: wrong fs type, bad option, bad superblock on %s,\n"
" or too many mounted file systems"),
spec);
if (stat (spec, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)
&& (fd = open(spec, O_RDONLY | O_NONBLOCK)) >= 0) {
if(ioctl(fd, BLKGETSIZE, &size) == 0 && size <= 2)
error (" (aren't you trying to mount an extended partition,\n"
" instead of some logical partition inside?)");
close(fd);
}
}
break;
}
case EMFILE:
error (_("mount table full")); break;
case EIO:
error (_("mount: %s: can't read superblock"), spec); break;
case ENODEV:
if (is_in_procfs(type) || !strcmp(type, "guess"))
error(_("mount: %s has wrong major or minor number"), spec);
else if (have_procfs()) {
char *lowtype, *p;
int u;
error (_("mount: fs type %s not supported by kernel"), type);
/* maybe this loser asked for FAT or ISO9660 or isofs */
lowtype = xstrdup(type);
u = 0;
for(p=lowtype; *p; p++) {
if(tolower(*p) != *p) {
*p = tolower(*p);
u++;
}
}
if (u && is_in_procfs(lowtype))
error (_("mount: probably you meant %s"), lowtype);
else if (!strncmp(lowtype, "iso", 3) && is_in_procfs("iso9660"))
error (_("mount: maybe you meant iso9660 ?"));
free(lowtype);
} else
error (_("mount: %s has wrong device number or fs type %s not supported"),
spec, type);
break;
case ENOTBLK:
if (stat (spec, &statbuf)) /* strange ... */
error (_("mount: %s is not a block device, and stat fails?"), spec);
else if (S_ISBLK(statbuf.st_mode))
error (_("mount: the kernel does not recognize %s as a block device\n"
" (maybe `insmod driver'?)"), spec);
else if (S_ISREG(statbuf.st_mode))
error (_("mount: %s is not a block device (maybe try `-o loop'?)"),
spec);
else
error (_("mount: %s is not a block device"), spec);
break;
case ENXIO:
error (_("mount: %s is not a valid block device"), spec); break;
case EACCES: /* pre-linux 1.1.38, 1.1.41 and later */
case EROFS: /* linux 1.1.38 and later */
if (ro) {
error (_("mount: block device %s is not permitted on its filesystem"),
spec);
break;
} else {
if (loop) {
opts = opts0;
type = type0;
}
if (opts) {
char *opts1 = realloc(xstrdup(opts), strlen(opts)+4);
strcat(opts1, ",ro");
opts = opts1;
} else
opts = "ro";
if (type && !strcmp(type, "guess"))
type = 0;
error (_("mount: %s%s is write-protected, mounting read-only"),
loop ? "" : _("block device "), spec0);
return try_mount_one (spec0, node0, type, opts, freq, pass, bg, 1);
}
break;
default:
error ("mount: %s", strerror (mnt_err)); break;
}
return EX_FAIL;
}
/*
* set_proc_name()
* Update the argument vector, so that this process may be easily
* identified in a "ps" listing.
*/
static void
set_proc_name (const char *spec)
{
#ifdef DO_PS_FIDDLING
setproctitle ("mount", spec);
#endif
}
static char *
subst_string(const char *s, const char *sub, int sublen, const char *repl) {
char *n;
n = (char *) xmalloc(strlen(s)-sublen+strlen(repl)+1);
strncpy (n, s, sub-s);
strcpy (n + (sub-s), repl);
strcat (n, sub+sublen);
return n;
}
static const char *
usersubst(const char *opts) {
char *s, *w;
char id[40];
s = "uid=useruid";
if (opts && (w = strstr(opts, s)) != NULL) {
sprintf(id, "uid=%d", getuid());
opts = subst_string(opts, w, strlen(s), id);
}
s = "gid=usergid";
if (opts && (w = strstr(opts, s)) != NULL) {
sprintf(id, "gid=%d", getgid());
opts = subst_string(opts, w, strlen(s), id);
}
return opts;
}
/*
* Return 0 for success (either mounted sth or -a and NOAUTO was given)
*/
static int
mount_one (const char *spec, const char *node, char *type, const char *opts,
char *cmdlineopts, int freq, int pass) {
int status;
int status2;
int specset = 0;
char *nspec;
/* Substitute values in opts, if required */
opts = usersubst(opts);
/* Merge the fstab and command line options. */
if (opts == NULL)
opts = cmdlineopts;
else if (cmdlineopts != NULL)
opts = xstrconcat3(opts, ",", cmdlineopts);
if (!strncmp(spec, "UUID=", 5)) {
nspec = get_spec_by_uuid(spec+5);
specset = 1;
} else if (!strncmp(spec, "LABEL=", 6)) {
nspec = get_spec_by_volume_label(spec+6);
specset = 2;
} else
nspec = 0; /* just for gcc */
if (specset) {
if (nspec) {
spec = nspec;
if (verbose)
printf(_("mount: consider mounting %s by %s\n"), spec,
(specset==1) ? _("UUID") : _("label"));
} else if(!all)
die (EX_USAGE, _("mount: no such partition found"));
/* if -a then we may be rescued by a noauto option */
}
if (type == NULL) {
if (strchr (spec, ':') != NULL) {
type = "nfs";
if (verbose)
printf(_("mount: no type was given - "
"I'll assume nfs because of the colon\n"));
}
}
/*
* Try to mount the file system. When the exit status is EX_BG,
* we will retry in the background. Otherwise, we're done.
*/
status = try_mount_one (spec, node, type, opts, freq, pass, 0, 0);
if (status != EX_BG)
return status;
/*
* Retry in the background.
*/
printf (_("mount: backgrounding \"%s\"\n"), spec);
fflush( stdout ); /* prevent duplicate output */
if (fork() > 0)
return 0; /* parent returns "success" */
spec = xstrdup(spec); /* arguments will be destroyed */
node = xstrdup(node); /* by set_proc_name() */
type = xstrdup(type);
opts = xstrdup(opts);
set_proc_name (spec); /* make a nice "ps" listing */
status2 = try_mount_one (spec, node, type, opts, freq, pass, 1, 0);
if (verbose && status2)
printf (_("mount: giving up \"%s\"\n"), spec);
exit (0); /* child stops here */
}
/* Check if an fsname/dir pair was already in the old mtab. */
static int
mounted (char *spec, char *node) {
struct mntentchn *mc;
spec = canonicalize (spec);
node = canonicalize (node);
for (mc = mtab_head()->nxt; mc; mc = mc->nxt)
if (streq (spec, mc->mnt_fsname) && streq (node, mc->mnt_dir))
return 1;
return 0;
}
/* Mount all filesystems of the specified types except swap and root. */
/* With the --fork option: fork and let different incarnations of
mount handle different filesystems. However, try to avoid several
simultaneous mounts on the same physical disk, since that is very slow. */
#define DISKMAJOR(m) (((int) m) & ~0xf)
static int
mount_all (string_list types, char *options) {
struct mntentchn *mc, *mtmp;
int status = 0;
struct stat statbuf;
struct child {
pid_t pid;
char *group;
struct mntentchn *mec;
struct mntentchn *meclast;
struct child *nxt;
} childhead, *childtail, *cp;
char major[22];
char *g, *colon;
/* build a chain of what we have to do, or maybe
several chains, one for each major or NFS host */
childhead.nxt = 0;
childtail = &childhead;
for (mc = fstab_head()->nxt; mc; mc = mc->nxt) {
if (matching_type (mc->mnt_type, types)
&& !streq (mc->mnt_dir, "/")
&& !streq (mc->mnt_dir, "root")) {
if (mounted (mc->mnt_fsname, mc->mnt_dir)) {
if (verbose)
printf(_("mount: %s already mounted on %s\n"),
mc->mnt_fsname, mc->mnt_dir);
} else {
mtmp = (struct mntentchn *) xmalloc(sizeof(*mtmp));
*mtmp = *mc;
mtmp->nxt = 0;
g = NULL;
if (optfork) {
if (stat(mc->mnt_fsname, &statbuf) == 0 &&
S_ISBLK(statbuf.st_mode)) {
sprintf(major, "#%x", DISKMAJOR(statbuf.st_rdev));
g = major;
}
#ifdef HAVE_NFS
if (strcmp(mc->mnt_type, "nfs") == 0) {
g = xstrdup(mc->mnt_fsname);
colon = strchr(g, ':');
if (colon)
*colon = '\0';
}
#endif
}
if (g) {
for (cp = childhead.nxt; cp; cp = cp->nxt)
if (cp->group && strcmp(cp->group, g) == 0) {
cp->meclast->nxt = mtmp;
cp->meclast = mtmp;
goto fnd;
}
}
cp = (struct child *) xmalloc(sizeof *cp);
cp->nxt = 0;
cp->mec = cp->meclast = mtmp;
cp->group = xstrdup(g);
cp->pid = 0;
childtail->nxt = cp;
childtail = cp;
fnd:;
}
}
}
/* now do everything */
for (cp = childhead.nxt; cp; cp = cp->nxt) {
pid_t p = -1;
if (optfork) {
p = fork();
if (p == -1) {
int errsv = errno;
error(_("mount: cannot fork: %s"), strerror (errsv));
}
else if (p != 0)
cp->pid = p;
}
/* if child, or not forked, do the mounting */
if (p == 0 || p == -1) {
for (mc = cp->mec; mc; mc = mc->nxt)
status |= mount_one (mc->mnt_fsname, mc->mnt_dir,
mc->mnt_type, mc->mnt_opts,
options, 0, 0);
if (mountcount)
status |= EX_SOMEOK;
if (p == 0)
exit(status);
}
}
/* wait for children, if any */
while ((cp = childhead.nxt) != NULL) {
childhead.nxt = cp->nxt;
if (cp->pid) {
int ret;
keep_waiting:
if(waitpid(cp->pid, &ret, 0) == -1) {
if (errno == EINTR)
goto keep_waiting;
perror("waitpid");
} else if (WIFEXITED(ret))
status |= WEXITSTATUS(ret);
else
status |= EX_SYSERR;
}
}
if (mountcount)
status |= EX_SOMEOK;
return status;
}
extern char version[];
static struct option longopts[] =
{
{ "all", 0, 0, 'a' },
{ "fake", 0, 0, 'f' },
{ "fork", 0, 0, 'F' },
{ "help", 0, 0, 'h' },
{ "no-mtab", 0, 0, 'n' },
{ "read-only", 0, 0, 'r' },
{ "ro", 0, 0, 'r' },
{ "verbose", 0, 0, 'v' },
{ "version", 0, 0, 'V' },
{ "read-write", 0, 0, 'w' },
{ "rw", 0, 0, 'w' },
{ "options", 1, 0, 'o' },
{ "types", 1, 0, 't' },
{ NULL, 0, 0, 0 }
};
static void
usage (FILE *fp, int n)
{
fprintf (fp, _("Usage: mount [-hV]\n"
" mount -a [-nfFrsvw] [-t vfstypes]\n"
" mount [-nfrsvw] [-o options] special | node\n"
" mount [-nfrsvw] [-t vfstype] [-o options] special node\n"
" A special device can be indicated by -L label or -U uuid .\n"));
unlock_mtab();
exit (n);
}
int
main (int argc, char *argv[]) {
int c, result = 0, specseen;
char *options = NULL, *spec, *node;
char *volumelabel = NULL;
char *uuid = NULL;
string_list types = NULL;
struct mntentchn *mc;
int fd;
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
/* People report that a mount called from init without console
writes error messages to /etc/mtab
Let us try to avoid getting fd's 0,1,2 */
while((fd = open("/dev/null", O_RDWR)) == 0 || fd == 1 || fd == 2) ;
if (fd > 2)
close(fd);
#ifdef DO_PS_FIDDLING
initproctitle(argc, argv);
#endif
while ((c = getopt_long (argc, argv, "afFhL:no:rsU:vVwt:", longopts, NULL))
!= EOF)
switch (c) {
case 'a': /* mount everything in fstab */
++all;
break;
case 'f': /* fake (don't actually do mount(2) call) */
++fake;
break;
case 'F':
++optfork;
break;
case 'h': /* help */
usage (stdout, 0);
break;
case 'L':
volumelabel = optarg;
break;
case 'n': /* mount without writing in /etc/mtab */
++nomtab;
break;
case 'o': /* specify mount options */
if (options)
options = xstrconcat3(options, ",", optarg);
else
options = xstrdup(optarg);
break;
case 'r': /* mount readonly */
readonly = 1;
readwrite = 0;
break;
case 's': /* allow sloppy mount options */
sloppy = 1;
break;
case 't': /* specify file system types */
types = parse_list (optarg);
break;
case 'U':
uuid = optarg;
break;
case 'v': /* be chatty - very chatty if repeated */
++verbose;
break;
case 'V': /* version */
printf ("mount: %s\n", version);
exit (0);
case 'w': /* mount read/write */
readwrite = 1;
readonly = 0;
break;
case 0:
break;
case '?':
default:
usage (stderr, EX_USAGE);
}
argc -= optind;
argv += optind;
specseen = (uuid || volumelabel) ? 1 : 0; /* yes, .. i know */
if (argc+specseen == 0 && !all) {
if (options)
usage (stderr, EX_USAGE);
return print_all (types);
}
if (getuid () != geteuid ()) {
suid = 1;
if (types || options || readwrite || nomtab || all || fake ||
(argc + specseen) != 1)
die (EX_USAGE, _("mount: only root can do that"));
}
if (!nomtab && mtab_does_not_exist()) {
if (verbose > 1)
printf(_("mount: no %s found - creating it..\n"), MOUNTED);
create_mtab ();
}
if (specseen) {
if (uuid)
spec = get_spec_by_uuid(uuid);
else
spec = get_spec_by_volume_label(volumelabel);
if (!spec)
die (EX_USAGE, _("mount: no such partition found"));
if (verbose)
printf(_("mount: mounting %s\n"), spec);
} else
spec = NULL; /* just for gcc */
switch (argc+specseen) {
case 0:
/* mount -a */
result = mount_all (types, options);
if (result == 0 && verbose)
error(_("not mounted anything"));
break;
case 1:
/* mount [-nfrvw] [-o options] special | node */
if (types != NULL)
usage (stderr, EX_USAGE);
if (specseen) {
/* We know the device. Where shall we mount it? */
mc = (uuid ? getfsuuidspec (uuid) : getfsvolspec (volumelabel));
if (mc == NULL)
mc = getfsspec (spec);
if (mc == NULL)
die (EX_USAGE, _("mount: cannot find %s in %s"),
spec, _PATH_FSTAB);
mc->mnt_fsname = spec;
} else {
/* Try to find the other pathname in fstab. */
spec = canonicalize (*argv);
if ((mc = getfsspec (spec)) == NULL &&
(mc = getfsfile (spec)) == NULL &&
/* Try noncanonical name in fstab
perhaps /dev/cdrom or /dos is a symlink */
(mc = getfsspec (*argv)) == NULL &&
(mc = getfsfile (*argv)) == NULL &&
/* Try mtab - maybe this was a remount */
(mc = getmntfile (spec)) == NULL)
die (EX_USAGE, _("mount: can't find %s in %s or %s"),
spec, _PATH_FSTAB, MOUNTED);
/* Earlier mtab was tried first, but this would
sometimes try the wrong mount in case mtab had
the root device entry wrong. */
}
result = mount_one (xstrdup (mc->mnt_fsname), xstrdup (mc->mnt_dir),
xstrdup (mc->mnt_type), mc->mnt_opts, options, 0, 0);
break;
case 2:
/* mount [-nfrvw] [-t vfstype] [-o options] special node */
if (specseen) {
/* we have spec already */
node = argv[0];
} else {
spec = argv[0];
node = argv[1];
}
if (types == NULL)
result = mount_one (spec, node, NULL, NULL, options, 0, 0);
else if (cdr (types) == NULL)
result = mount_one (spec, node, car (types), NULL, options, 0, 0);
else
usage (stderr, EX_USAGE);
break;
default:
usage (stderr, EX_USAGE);
}
if (result == EX_SOMEOK)
result = 0;
exit (result);
}