mount.c revision a38ddfee9c8c6b6c5a2947ff52fd2338362a4444
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <stdio_ext.h>
#include <limits.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <sys/resource.h>
#include <stropts.h>
#include <locale.h>
#include "fslib.h"
#define ARGV_MAX 16
#define TIME_MAX 50
#define FSTYPE_MAX 8
#define REMOTE_MAX 64
#define OLD 0
#define NEW 1
#define READONLY 0
#define READWRITE 1
#define SUID 2
#define NOSUID 3
#define SETUID 4
#define NOSETUID 5
#define DEVICES 6
#define NODEVICES 7
/* a - abbreviated weekday name */
/* b - abbreviated month name */
/* e - day of month */
/* H - hour */
/* M - minute */
/* S - second */
/* Y - Year */
/* n - newline */
/*
* The fs-local method understands this exit code to mean that one or
* more failures occurred and that all the failures were of attempted
* lofs mounts.
*/
#define ALL_LOFS_FAILURES 111
extern int optind;
extern char *optarg;
extern void usage(void);
extern char *flags(char *, int);
extern char *default_fstype(char *);
char *myopts[] = {
};
static char *myname; /* point to argv[0] */
/*
* Set the limit to double the number of characters a user should be allowed to
* type in one line.
* This should cover the different shells, which don't use POSIX_MAX_INPUT,
* and should cover the case where a long option string can be in
*/
char *specific_opts; /* holds specific mount options */
char *generic_opts; /* holds generic mount options */
int maxrun;
int nrun;
int failcnt; /* total count of failures */
int lofscnt; /* presence of lofs prohibits parallel */
/* mounting */
int lofsfail; /* count of failures of lofs mounts */
int exitcode;
/*
* Currently, mounting cachefs instances simultaneously uncovers various
* problems. For the short term, we serialize cachefs activity while we fix
* these cachefs bugs.
*/
#define CACHEFS_BUG
#ifdef CACHEFS_BUG
int cachefs_running; /* parallel cachefs not supported yet */
#endif
/*
* Each vfsent_t describes a vfstab entry. It is used to manage and cleanup
* each child that performs the particular mount for the entry.
*/
typedef struct vfsent {
struct vfstab v; /* the vfstab entry */
char *rpath; /* resolved pathname so far */
int mlevel; /* how deep is this mount point */
int order; /* vfstab serial order of this vfs */
int flag;
int exitcode; /* process's exitcode */
#define RDPIPE 0
#define WRPIPE 1
} vfsent_t;
/* this mount point */
/* linked list of vfstab entries */
int vfsarraysize; /* length of the list */
/*
* This structure is used to build a linked list of
*/
typedef struct mountent {
int flag;
} mountent_t;
#define MSORTED 0x1
static vfsent_t **make_vfsarray(char **, int);
static void doexec(char *, char **);
static void nomem();
static void cleanup(int);
static int dowait();
static int setup_iopipe(vfsent_t *);
static void setup_output(vfsent_t *);
static void do_mounts();
static int parmount(char **, int, char *);
static int mlevelcmp(const void *, const void *);
static int mordercmp(const void *, const void *);
static int check_fields(char *, char *);
static int cleanupkid(pid_t, int);
static void print_mnttab(int, int);
static void vfserror(int, char *);
static void mnterror(int);
static int ignore(char *);
/*
* The -F flag and argument are NOT passed.
* If the usr file system is not mounted a duplicate copy
* can be found in /sbin and this version execs the
*
* If the -F fstype, special or directory are missing,
*
* -V will print the built command on the stdout.
* It isn't passed either.
*/
int
{
*mountp, /* argument of mount directory */
*fstype, /* wherein the fstype name is filled */
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
if (myname)
myname++;
else
/* Process the args. */
switch (cc) {
case 'a':
aflg++;
break;
case 'c':
cflg++;
break;
#ifdef DEBUG
case 'd':
break;
#endif
case 'f':
fflg++;
break;
case 'F':
Fflg++;
break;
case 'g':
gflg++;
break;
case 'm':
mflg++;
case 'o':
oflg++;
nomem();
break; /* fstype dependent options */
case 'O':
Oflg++;
break;
case 'p':
pflg++;
break;
case 'q':
qflg++;
break;
case 'r':
rflg++;
generic_opts = "ro";
break;
case 'v':
vflg++;
break;
case 'V':
Vflg++;
break;
case '?':
questflg++;
break;
}
/* copy '--' to specific */
dashflg++;
/* option checking */
/* more than two args not allowed if !aflg */
usage();
/* pv mututally exclusive */
("%s: -a, -p, and -v are mutually exclusive\n"),
myname);
usage();
}
/*
* Can't have overlaying mounts on the same mount point during
* a parallel mount.
*/
("%s: -a and -O are mutually exclusive\n"), myname);
usage();
}
/* dfF mutually exclusive */
("%s: More than one FSType specified\n"), myname);
usage();
}
/* no arguments, only allow p,v,V or [F]? */
usage();
usage();
if (questflg) {
if (Fflg) {
}
usage();
}
}
if (questflg)
usage();
/* one or two args, allow any but p,v */
usage();
}
/* if only reporting mnttab, generic prints mnttab and exits */
if (Vflg) {
if (pflg)
printf(" -p");
if (vflg)
printf(" -v");
printf("\n");
exit(0);
}
exit(0);
}
/*
* Get filesystem type here. If "-F FStype" is specified, use
* if the entry exists. Otherwise, determine the local or remote
*/
if (fflg) {
}
else
fstype = "ufs";
} else /* if (Fflg) */
/*
* Then don't bother with the parallel over head. Everything
*/
aflg = 0;
if (fscnt == 2)
else
else
/* lookup only if we need to */
"%s: Cannot open %s\n"),
exit(1);
} else {
/*
* No vfstab, but we know what we want
* to mount.
*/
goto out;
}
}
/* get a vfstab entry matching mountp or special */
/* if no entry and there was only one argument */
/* then the argument could be the special */
/* and not mount point as we thought earlier */
/* skip erroneous lines; they were reported above */
;
}
if (ret == 0) {
oflg++;
}
exit(2);
}
gettext("%s: mount point cannot be determined\n"),
myname);
exit(1);
} else
{
gettext("%s: special cannot be determined\n"),
myname);
exit(1);
}
}
out:
exit(1);
exit(1);
}
nomem();
/* create the new arg list, and end the list with a null pointer */
ii = 2;
if (cflg)
if (gflg)
if (mflg)
/*
* The q option needs to go before the -o option as some
* filesystems complain during first pass option parsing.
*/
if (qflg)
if (oflg) {
}
if (Oflg)
if (rflg)
if (dashflg)
return (0);
}
void
usage(void)
{
"%s [-F FSType] [-V] [current_options] [-o specific_options]"),
myname);
"%s [-F FSType] [-V] [current_options] [-o specific_options]"),
myname);
"%s -a [-F FSType ] [-V] [current_options] [-o specific_options]\n"),
myname);
exit(1);
}
/*
* Get rid of "dev=[hex string]" clause, if any. It's not legal
* when printing in vfstab format.
*/
void
{
/* last option */
*--dev = '\0';
} else {
*dev = '\0';
}
} else {
/* first or intermediate option */
}
}
}
}
void
{
int ret;
exit(1);
}
== 0) {
continue;
if (pflg) {
printf("%s - %s %s - no %s\n",
} else if (vflg) {
printf("%s on %s type %s %s%s on %s",
time_buf);
} else
printf("%s on %s %s%s on %s",
time_buf);
}
}
if (ret > 0)
}
char *
{
char *value;
int rdwr = 1;
int suid = 1;
int devices = 1;
int setuid = 1;
while (*mntopts != '\0') {
case READONLY:
rdwr = 0;
break;
case READWRITE:
rdwr = 1;
break;
case SUID:
suid = 1;
break;
case NOSUID:
suid = 0;
break;
case SETUID:
setuid = 1;
break;
case NOSETUID:
setuid = 0;
break;
case DEVICES:
devices = 1;
break;
case NODEVICES:
devices = 0;
break;
default:
/* cat '/' separator to mntflags */
break;
}
}
if (rdwr)
else
if (suid) {
if (setuid)
else
if (devices)
else
} else {
}
if (*opts != '\0') {
}
/*
* The assumed assertion
* assert (strlen(mntflags) < sizeof mntflags);
* is valid at this point in the code. Note that a call to "assert"
* is not appropriate in production code since it halts the program.
*/
return (mntflags);
}
char *
{
char *fs;
extern char *strtok();
return (""); /* not a remote */
return ("remote/"); /* is a remote fs */
}
return (""); /* not a remote */
}
void
{
special = "<null>";
switch (flag) {
case VFS_TOOLONG:
gettext("%s: Warning: Line in vfstab for \"%s\" exceeds %d characters\n"),
break;
case VFS_TOOFEW:
gettext("%s: Warning: Line for \"%s\" in vfstab has too few entries\n"),
break;
case VFS_TOOMANY:
gettext("%s: Warning: Line for \"%s\" in vfstab has too many entries\n"),
break;
default:
"%s: Warning: Error in line for \"%s\" in vfstab\n"),
}
}
void
{
switch (flag) {
case MNT_TOOLONG:
gettext("%s: Line in mnttab exceeds %d characters\n"),
break;
case MNT_TOOFEW:
gettext("%s: Line in mnttab has too few entries\n"),
myname);
break;
case MNT_TOOMANY:
gettext("%s: Line in mnttab has too many entries\n"),
myname);
break;
}
exit(1);
}
void
{
char alter_path[PATH_MAX];
int i;
int smbfs;
/*
* Special case smbfs file system.
* Execute command in profile if possible.
*/
/* build the full pathname of the fstype dependent command. */
if (Vflg) {
for (i = 2; newargv[i]; i++)
printf("\n");
exit(0);
}
/*
* Try to exec the fstype dependent portion of the mount.
* See if the directory is there before trying to exec dependent
* portion. This is only useful for eliminating the
* '..mount: not found' message when '/usr' is mounted
*/
if (smbfs) {
/*
* Run mount_smbfs(1m) with pfexec so that we can
* add sys_mount privilege, (via exec_attr, etc.)
* allowing normal users to mount on any directory
* they own.
*/
newargv[0] = "pfexec";
}
gettext("%s: Cannot execute %s - permission denied\n"),
}
newargv[0] = "sh";
}
}
if (smbfs) {
newargv[0] = "pfexec";
}
"%s: Cannot execute %s - permission denied\n"),
myname, alter_path);
exit(1);
}
newargv[0] = "sh";
}
gettext("%s: Operation not applicable to FSType %s\n"),
exit(1);
}
#define IGNORE 0
/*
* Return 1 if "ignore" appears in the options string
*/
int
{
char *value;
int rval = 0;
return (0);
/*
* we make a copy of the option string to pass to getsubopt(),
* because getsubopt() modifies the string. We also save
* the original pointer returned by strdup, because getsubopt
* changes the pointer passed into it. If strdup fails (unlikely),
* we act as if the "ignore" option isn't set rather than fail.
*/
nomem();
while (*my_opts != '\0') {
rval = 1;
}
return (rval);
}
/*
* Perform the parallel version of mount. If count == 0, mount all
* vfstab filesystems with the automnt field == "yes". Use fstype if
* supplied. If mntlist supplied, then attempt to only mount those.
*/
int
{
/*
* Process scaling. After running a series
* of tests based on the number of simultaneous processes and
* processors available, optimum performance was achieved near or
* at (PROCN * 2).
*/
maxrun = 4;
else
}
/*
* The parent needs to maintain 3 of its own fd's, plus 2 for
* each child (the stdout and stderr pipes).
*/
/* periods of open fds */
if (maxrun < 4)
if (count == 0)
else
/* ignore fstype */
/*
* Read the whole vfstab into a linked list for quick processing.
* On average, this is the most efficient way to collect and
* manipulate the vfstab data.
*/
/*
* Make an array out of the vfs linked list for sorting purposes.
*/
return (0);
return (1);
}
/*
* Sort the entries based on their resolved path names
*
* If an lofs is encountered, then the original order of the vfstab
* file needs to be maintained until we are done mounting lofs's.
*/
if (!lofscnt)
/*
* Shrink the vfsll linked list down to the new list. This will
* speed up the pid search in cleanupkid() later.
*/
/*
* Try to handle interrupts in a reasonable way.
*/
do_mounts(); /* do the mounts */
return (ALL_LOFS_FAILURES);
return (exitcode);
}
/*
* Read all vstab (fp) entries into memory if fstype == NULL.
* If fstype is specified, than read all those that match it.
*
* Returns a linked list.
*/
vfsent_t *
{
exit(1);
}
if (ret > 0) {
continue;
}
/*
* If mount points were not specified, then we ignore
* entries that aren't marked "yes".
*/
if (takeall &&
continue;
continue;
continue;
exitcode = 1;
continue;
}
else
cnt++;
}
vfsarraysize = 0;
return (NULL);
}
vfsarraysize = cnt;
return (vhead);
}
/*
* Returns an array of vfsent_t's based on vfsll & mntlist.
*/
vfsent_t **
{
return (NULL);
if (count > 0)
nomem();
/*
* No mount list specified: take all vfstab mount points.
*/
/*
* Sigh. lofs entries can complicate matters so much
* that the best way to avoid problems is to
* stop parallel mounting when an lofs is
* encountered, so we keep a count of how many
* there are.
* Fortunately this is rare.
*/
if (vp->v.vfs_fstype &&
lofscnt++;
}
return (vpp);
}
/*
* A list of mount points was specified on the command line
* and we need to search for each one.
*/
/*
* For each specified mount point:
*/
found = 0;
/*
* Circle our entire linked list, looking for *mntlist.
*/
while (vp) {
if (vp->v.vfs_fstype &&
MNTTYPE_LOFS) == 0))
lofscnt++;
found++;
break;
}
/*
* Remove it from the circular list. vpprev
* remains unchanged.
*/
/*
* Set vmark to the first elem that we check
* each time.
*/
found++;
break;
}
/* the circle */
break;
}
if (!found) {
"%s: Warning: %s not found in %s\n"),
exitcode = 1;
}
}
if (ndx == 0)
return (NULL);
return (vpp);
}
/*
* Performs the exec argument processing, all of the child forking and
* execing, and child cleanup.
* Sets exitcode to non-zero if any errors occurred.
*/
void
do_mounts(void)
{
/*
* create the arg list once; the only differences among
* the calls are the options, special and mountp fields.
*/
i = 2;
if (cflg)
newargv[i++] = "-c";
if (gflg)
newargv[i++] = "-g";
if (mflg)
newargv[i++] = "-m";
if (Oflg)
newargv[i++] = "-O";
if (qflg)
newargv[i++] = "-q";
if (rflg)
newargv[i++] = "-r";
if (dashflg)
newargv[i++] = "--";
if (oflg) {
newargv[i++] = "-o";
newargv[i++] = specific_opts;
}
isave = i;
/*
* Main loop for the mount processes
*/
cnt = vfsarraysize;
/*
* Check to see if we cross a mount level: e.g.,
* /a/b -> /a/b/c. If so, we need to wait for all current
* mounts to finish, rerun realpath on the remaining mount
* points, and resort the list.
*
* Also, we mount serially as long as there are lofs's
* to mount to avoid improper mount ordering.
*/
;
/*
* Gads! It's possible for real path mounts points to
* change after mounts are done at a lower mount
* level.
* Thus, we need to recalculate mount levels and
* resort the list from this point.
*/
/*
* Sort the remaining entries based on their newly
* resolved path names.
* Do not sort if we still have lofs's to mount.
*/
if (lofscnt == 0) {
}
}
"%s: Nonexistent mount point: %s\n"),
exitcode = 1;
continue;
}
/*
* If mount options were not specified on the command
* line, then use the ones found in the vfstab entry,
* if any.
*/
i = isave;
newargv[i++] = "-o";
}
/*
* This should never really fail.
*/
;
;
#ifdef CACHEFS_BUG
if (vp->v.vfs_fstype &&
;
cachefs_running = 1;
}
#endif
perror("fork");
cleanup(-1);
/* not reached */
}
if (child == 0) { /* child */
perror("exec");
exit(1);
}
/* parent */
nrun++;
}
/*
* Mostly done by now - wait and clean up the stragglers.
*/
cleanup(0);
}
/*
* Setup stdout and stderr pipes for the children's output.
*/
int
{
/*
* Make a stdout and stderr pipe. This should never fail.
*/
return (-1);
return (-1);
}
/*
* Don't block on an empty pipe.
*/
/*
* Don't pass extra fds into children.
*/
return (0);
}
/*
* Called by a child to attach its stdout and stderr to the write side of
* the pipes.
*/
void
{
}
/*
* Parent uses this to print any stdout or stderr output issued by
* the child.
*/
static void
{
int bytes;
}
/*
* Waits for 1 child to die.
*
* Returns -1 if no children are left to wait for.
* Returns 0 if a child died without an error.
* Returns 1 if a child died with an error.
*/
int
dowait(void)
{
return (-1);
nrun--;
}
/*
* Locates the child mount process represented by pid, outputs any io
* it may have, and returns its exit code.
* Sets the global exitcode if an error occurred.
*/
int
{
int ret;
else
if (ret) {
exitcode = 1;
failcnt++;
}
/*
* Find our child.
* This search gets smaller and smaller as children are cleaned
* up.
*/
continue;
}
/*
* Found: let's remove it from this linked list.
*/
if (prevp) {
}
break;
}
/*
* This should never happen.
*/
exitcode = 1;
return (ret);
}
if (vp->v.vfs_fstype &&
lofscnt--;
if (ret)
lofsfail++;
}
#ifdef CACHEFS_BUG
cachefs_running = 0;
#endif
return (ret);
}
vfsent_t *
{
nomem();
if (vin->vfs_special &&
nomem();
if (vin->vfs_mountp &&
nomem();
if (vin->vfs_fstype &&
nomem();
/*
* If specific mount options were specified on the command
* line, then use those. Else, use the ones on the vfstab
* line, if any. In other words, specific options on the
*/
if (oflg) {
nomem();
} else if (vin->vfs_mntopts &&
nomem();
return (new);
}
/*
* Runs realpath on vp's mount point, records success or failure,
* resets the mount level based on the new realpath, and returns
* realpath()'s return value.
*/
char *
{
char *rp;
else
nomem();
return (rp);
}
/*
* sort first by mlevel (1...N), then by vfstab order.
*/
int
mlevelcmp(const void *a, const void *b)
{
int lcmp;
if (lcmp == 0)
return (lcmp);
}
/* sort by vfstab order. 0..N */
static int
mordercmp(const void *a, const void *b)
{
}
/*
* cleanup the existing children and exit with an error
* if asig != 0.
*/
void
{
;
if (asig != 0)
exit(1);
}
int
{
gettext("%s: FSType %s exceeds %d characters\n"),
return (1);
}
gettext("%s: Mount point cannot be determined\n"),
myname);
return (1);
}
if (*mountp != '/') {
"%s: Mount point %s is not an absolute pathname.\n"),
return (1);
}
/*
* Don't do some of these checks if aflg because a mount point may
* not exist now, but will be mounted before we get to it.
* This is one of the quirks of "secondary mounting".
*/
gettext("%s: Mount point %s does not exist.\n"),
else {
gettext("%s: Cannot stat mount point %s.\n"),
}
return (1);
}
return (0);
}
void
nomem(void)
{
;
exit(1);
}