2690 lines
65 KiB
C
2690 lines
65 KiB
C
/*
|
|
* Copyright (c) 1985 Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#define NFSCLIENT
|
|
/*
|
|
* mount
|
|
*/
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <dirent.h>
|
|
#include <diskinfo.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <mntent.h>
|
|
#include <netdb.h>
|
|
#include <signal.h>
|
|
#include <strings.h>
|
|
#include <ustat.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <rpc/rpc.h>
|
|
#include <sys/fs/efs_clnt.h>
|
|
#include <sys/fs/nfs.h>
|
|
#include <sys/fs/xfs_clnt.h>
|
|
#include <sys/fs/bds.h>
|
|
#include <rpcsvc/mount.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/fstyp.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/param.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/statfs.h>
|
|
#include <sys/time.h>
|
|
#include <sys/wait.h>
|
|
#include <sat.h>
|
|
#include <paths.h>
|
|
#include <mountinfo.h>
|
|
#include <sys/fs/nfs_clnt.h>
|
|
#include <sys/mac.h>
|
|
#include <sys/capability.h>
|
|
#include <sys/xlv_base.h>
|
|
|
|
static int mac_enabled;
|
|
|
|
int ro = 0;
|
|
int quota = 0;
|
|
int fake = 0;
|
|
int freq = 0;
|
|
int passno = 0;
|
|
int all = 0;
|
|
char *allbut = 0;
|
|
char *host;
|
|
int check = 0;
|
|
int verbose = 0;
|
|
int printed = 0;
|
|
int nomtab = 0;
|
|
|
|
char *type_list;
|
|
|
|
int mounttree_error = 0;
|
|
|
|
#define MNTOPTSEP ',' /* separator between mount options in fstab */
|
|
|
|
/* Longer attribute cache timeouts for "private" mounts. */
|
|
#define PRIV_ACREGMIN 30
|
|
#define PRIV_ACREGMAX (3 * ACREGMAX)
|
|
#if PRIV_ACREGMAX < PRIV_ACREGMIN
|
|
#undef PRIV_ACREGMAX
|
|
#define PRIV_ACREGMAX (2 * PRIV_ACREGMIN)
|
|
#endif
|
|
#define PRIV_ACDIRMIN (3 * ACDIRMIN)
|
|
#define PRIV_ACDIRMAX (3 * ACDIRMAX)
|
|
|
|
#define NRETRY 10000 /* number of times to retry a mount request */
|
|
#define BGSLEEP 5 /* initial sleep time for background mount in seconds */
|
|
#define MAXSLEEP 120 /* max sleep time for background mount in seconds */
|
|
#define MNTTYPE_NFS3_PREF "nfs3pref"
|
|
#define MNTTYPE_NFS2 FSID_NFS2
|
|
/*
|
|
* Fake errno for RPC failures that don't map to real errno's
|
|
*/
|
|
#define ERPC (10000)
|
|
#define OLDENFSREMOTE 135 /* compatibility with ?? */
|
|
|
|
/*
|
|
* Structure used to build a mount tree. The tree is traversed to do
|
|
* the mounts and catch dependencies.
|
|
*/
|
|
struct mnttree {
|
|
struct mntent *mt_mnt;
|
|
struct mnttree *mt_sib;
|
|
struct mnttree *mt_sib_back;
|
|
struct mnttree *mt_kid;
|
|
};
|
|
|
|
/*
|
|
* Structure to hold a list of all currently mounted points from mtab
|
|
*/
|
|
typedef struct mtab_entry_s {
|
|
char mount_point[PATH_MAX];
|
|
struct mtab_entry_s *next;
|
|
} mtab_entry_t;
|
|
|
|
#define PARALLEL_MOUNT_MINIMUM 4
|
|
int parallel_mounts = 0;
|
|
int mount_proc_limit = 16;
|
|
|
|
void addtomtab(struct mntent *);
|
|
void background(void);
|
|
void fixpath(void);
|
|
char *getnextopt(char **);
|
|
struct mnttree *maketree(struct mnttree *, struct mntent *, struct mnttree *);
|
|
void mntcp(struct mntent *, struct mntent *);
|
|
struct mntent *mntdup(struct mntent *);
|
|
int mount_efs(struct mntent *, struct efs_args *);
|
|
int mount_nfs(struct mntent *, struct nfs_args *, char *, int, int);
|
|
#ifdef PCFS
|
|
int mount_pc(struct mntent *, struct pc_args *);
|
|
#endif
|
|
int mount_xfs(struct mntent *, struct xfs_args *);
|
|
int mounted(struct mntent *);
|
|
int mountfs(int, struct mntent *);
|
|
int mounttree(struct mnttree *);
|
|
int nameinlist(char *, char *);
|
|
int nopt(struct mntent *, char *, u_int *);
|
|
int adjustnfsargs(char *, struct mntent *);
|
|
void printent(struct mntent *);
|
|
void printmtab(FILE *);
|
|
void printtree(struct mnttree *);
|
|
void replace_opts(char *, int, char *, char *);
|
|
void set_long_timeouts(void);
|
|
void set_short_timeouts(void);
|
|
int substr(char *, char *);
|
|
void usage(void);
|
|
void *xmalloc(int);
|
|
int check_mounted_overlaps(mnt_check_state_t *, struct mntent *);
|
|
|
|
/*
|
|
* mount -M /root/etc/fstab -P /root -p
|
|
* prints that fstab with /root prepended to paths
|
|
* altmtab -- set to "/root/etc/fstab", use instead of MOUNTED "/etc/mtab"
|
|
* rbase -- set to "/root", used as prefix for mount and dev paths
|
|
*/
|
|
char *altmtab = 0;
|
|
char *altfstab = 0;
|
|
char *rbase = 0;
|
|
/*
|
|
* ismntopt implements a more accurate version of hasmntopt.
|
|
* hasmntopt has no concept of tokens. hasmntopt(s,"noac")
|
|
* could return pointing to "noac," or "noacl" or "noaction"
|
|
* which causes problems because some joker decided to invent
|
|
* an option called "noacl" which conflict with noac.
|
|
* This routine makes sure that
|
|
* a) hasmntopt's return pointer is actually the beginning of
|
|
* a token.
|
|
* (if there is no previous char, or if it is a ',')
|
|
* b) hasmntopt's return pointer points to a token that is a
|
|
* exact match for opt.
|
|
(the next char is a '=' or a ',')
|
|
* If this is the case, return the pointer, otherwise return NULL.
|
|
*/
|
|
static char *
|
|
ismntopt(struct mntent *mnt, char *opt)
|
|
{
|
|
char *cp, *retp = hasmntopt(mnt, opt);
|
|
if (retp) {
|
|
/* is retp the beginning of a token?*/
|
|
if (retp == mnt->mnt_opts || *(retp-1)==',') {
|
|
/* is retp exactly equal to opt? */
|
|
cp = retp + strlen(opt);
|
|
if (*cp == '\0' || *cp == ',' || *cp == '=') {
|
|
return (retp);
|
|
} else {
|
|
char c;
|
|
while (*cp != '\0' && *cp != ',' && *cp != '=')
|
|
cp++;
|
|
c = *cp;
|
|
*cp = '\0';
|
|
(void) fprintf(stderr,
|
|
"mount: invalid option %s ignored\n",
|
|
retp);
|
|
*cp = c;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
main(int argc, char **argv)
|
|
{
|
|
int realpaths = 1;
|
|
struct mntent mnt;
|
|
struct mntent *mntp;
|
|
FILE *mnttab;
|
|
char *options;
|
|
char *colon;
|
|
struct mnttree *mtree;
|
|
char name[MNTMAXSTR];
|
|
char dir[MNTMAXSTR];
|
|
char type[MNTMAXSTR];
|
|
char opts[MNTMAXSTR];
|
|
int hflag = 0;
|
|
mnt_check_state_t *check_state = 0;
|
|
int skip_mtab_checks = 0;
|
|
mtab_entry_t *mtab_entry_list = 0;
|
|
u_int vers = 0;
|
|
int do_overlap_checks = 1;
|
|
|
|
mac_enabled = (sysconf(_SC_MAC) > 0);
|
|
|
|
if (argc == 1) {
|
|
mnttab = setmntent(MOUNTED, "r");
|
|
while ((mntp = getmntent(mnttab)) != NULL) {
|
|
if (strcmp(mntp->mnt_type, MNTTYPE_IGNORE) == 0 ||
|
|
(mntp->mnt_opts != NULL &&
|
|
strstr(mntp->mnt_opts, "ignore") != NULL)) {
|
|
continue;
|
|
}
|
|
printent(mntp);
|
|
}
|
|
(void) endmntent(mnttab);
|
|
exit(0);
|
|
}
|
|
|
|
(void) close(2);
|
|
if (fcntl (1, F_DUPFD, 2) < 0) {
|
|
perror("dup");
|
|
exit(1);
|
|
}
|
|
|
|
opts[0] = '\0';
|
|
type[0] = '\0';
|
|
|
|
/*
|
|
* Set options
|
|
*/
|
|
while (argc > 1 && argv[1][0] == '-') {
|
|
options = &argv[1][1];
|
|
while (*options) {
|
|
switch (*options) {
|
|
case 'A':
|
|
/*
|
|
* The mount is from autofs, so do not
|
|
* use realpath. Just accept the mount
|
|
* point path supplied.
|
|
*/
|
|
realpaths = 0;
|
|
break;
|
|
case 'a':
|
|
all++;
|
|
break;
|
|
case 'b':
|
|
--argc, argv++;
|
|
all++;
|
|
allbut = argv[1];
|
|
break;
|
|
case 'C':
|
|
do_overlap_checks = 0;
|
|
break;
|
|
case 'c':
|
|
check++;
|
|
break;
|
|
case 'f':
|
|
fake++;
|
|
break;
|
|
case 'h':
|
|
--argc, argv++;
|
|
all++;
|
|
hflag = 1;
|
|
host = argv[1];
|
|
break;
|
|
case 'm':
|
|
--argc, argv++;
|
|
mount_proc_limit = strtoll(argv[1], NULL, 0);
|
|
if ((mount_proc_limit <= 0) ||
|
|
(mount_proc_limit > 128)) {
|
|
usage();
|
|
}
|
|
break;
|
|
case 'n':
|
|
nomtab++;
|
|
break;
|
|
case 'o':
|
|
if (argc < 3) {
|
|
usage();
|
|
}
|
|
(void) strcpy(opts, argv[2]);
|
|
argv++;
|
|
argc--;
|
|
break;
|
|
case 'p':
|
|
if (argc != 2) {
|
|
usage();
|
|
}
|
|
printmtab(stdout);
|
|
exit(0);
|
|
case 'q':
|
|
quota++;
|
|
break;
|
|
case 'r':
|
|
ro++;
|
|
break;
|
|
case 'F': /* ABI synonym for -t */
|
|
case 't':
|
|
if (argc < 3) {
|
|
usage();
|
|
}
|
|
(void) strcpy(type, argv[2]);
|
|
type_list = type;
|
|
argv++;
|
|
argc--;
|
|
break;
|
|
case 'T':
|
|
if (argc < 3) {
|
|
usage();
|
|
}
|
|
type_list = argv[2];
|
|
all++;
|
|
argv++;
|
|
argc--;
|
|
break;
|
|
case 'v':
|
|
verbose++;
|
|
break;
|
|
case 'M': /* support alternate to /etc/mtab for -p */
|
|
--argc, argv++;
|
|
altmtab = argv[1];
|
|
break;
|
|
case 'i': /* support alternate to /etc/fstab */
|
|
--argc, argv++;
|
|
altfstab = argv[1];
|
|
break;
|
|
case 'P': /* append $rbase to emitted -p paths */
|
|
--argc, argv++;
|
|
rbase = argv[1];
|
|
break;
|
|
default:
|
|
(void) fprintf(stderr,
|
|
"mount: unknown option: %c\n", *options);
|
|
usage();
|
|
}
|
|
options++;
|
|
}
|
|
argc--, argv++;
|
|
}
|
|
|
|
if (cap_envl(0, CAP_MOUNT_MGT, 0)) {
|
|
(void) fprintf(stderr, "Insufficient privilege\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (all) {
|
|
struct stat mnttab_stat;
|
|
long mnttab_size;
|
|
int count;
|
|
int mounts;
|
|
int which_mtab_read = 0;
|
|
int max_mtab_entries = 0;
|
|
FILE *mtab_fp;
|
|
struct mntent *mtab_entry_p;
|
|
mtab_entry_t *new_mtab_entry;
|
|
mtab_entry_t *which_mtab_entry;
|
|
int continue_flag = 0;
|
|
|
|
if (argc != 1) {
|
|
usage();
|
|
}
|
|
|
|
/* read all volheaders and setup mounted partition checking */
|
|
if (do_overlap_checks && (mnt_check_init(&check_state) == -1)) {
|
|
check_state = 0;
|
|
(void) fprintf(stderr, "mount: unable to init partition "
|
|
"checking routines, suspending checks\n");
|
|
}
|
|
|
|
/* read mount points from /etc/mtab into memory
|
|
* /etc/mtab is also checked in mountfs() but we do
|
|
* the checks here to avoid fork() complexty of doing the
|
|
* partition checks in the back end.
|
|
*/
|
|
mtab_fp = setmntent(MOUNTED, "r");
|
|
if (mtab_fp == NULL) {
|
|
skip_mtab_checks = 1;
|
|
} else {
|
|
if (fstat(fileno(mtab_fp), &mnttab_stat) == -1) {
|
|
skip_mtab_checks = 1;
|
|
(void) endmntent(mtab_fp);
|
|
} else {
|
|
|
|
for (count = 0; ; count++) {
|
|
if ((mtab_entry_p = getmntent(mtab_fp)) == NULL)
|
|
break;
|
|
|
|
/* copy entry from /etc/mtab */
|
|
new_mtab_entry = xmalloc(sizeof(mtab_entry_t));
|
|
strcpy(new_mtab_entry->mount_point,
|
|
mtab_entry_p->mnt_dir);
|
|
/* add to list */
|
|
new_mtab_entry->next = mtab_entry_list;
|
|
mtab_entry_list = new_mtab_entry;
|
|
}
|
|
|
|
if (!count)
|
|
skip_mtab_checks = 1;
|
|
|
|
(void) endmntent(mtab_fp);
|
|
}
|
|
}
|
|
|
|
|
|
/* setup to read mtab */
|
|
mnttab = setmntent(altfstab ? altfstab : MNTTAB, "r");
|
|
if (mnttab == NULL) {
|
|
(void) fprintf(stderr, "mount: ");
|
|
perror(altfstab ? altfstab : MNTTAB);
|
|
exit(1);
|
|
}
|
|
if (fstat(fileno(mnttab), &mnttab_stat) == -1) {
|
|
(void) fprintf(stderr, "mount: ");
|
|
perror(altfstab ? altfstab : MNTTAB);
|
|
exit(1);
|
|
}
|
|
mnttab_size = mnttab_stat.st_size;
|
|
mtree = NULL;
|
|
mounts = 0;
|
|
|
|
for (count = 1; ; count++) {
|
|
if ((mntp = getmntent(mnttab)) == NULL) {
|
|
if (ftell(mnttab) >= mnttab_size)
|
|
break; /* it's EOF */
|
|
(void) fprintf(stderr,
|
|
"mount: %s: illegal entry on line %d\n",
|
|
altfstab ? altfstab : MNTTAB, count);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Addition of hflag is when the -h option (by itself
|
|
* and no other "all" options are turned on ) will mount
|
|
* entries that have been tagged with "noauto" in the /etc/fstab.
|
|
*
|
|
* Skip over 'ignore' 'swap' 'rawdata' 'noauto' and '/'
|
|
*/
|
|
if ((strcmp(mntp->mnt_type, MNTTYPE_IGNORE) == 0)
|
|
|| (strcmp(mntp->mnt_type, MNTTYPE_SWAP) == 0)
|
|
|| (strcmp(mntp->mnt_type, MNTTYPE_RAWDATA) == 0)
|
|
|| (hasmntopt(mntp, MNTOPT_NOAUTO)
|
|
&& (hflag == 0 || all >= 2))
|
|
|| (strcmp(mntp->mnt_dir, "/") == 0) ) {
|
|
continue;
|
|
}
|
|
if (allbut && nameinlist(mntp->mnt_dir, allbut))
|
|
continue;
|
|
if (host) {
|
|
if (strcmp(mntp->mnt_type, MNTTYPE_NFS)
|
|
&& strcmp(mntp->mnt_type, MNTTYPE_NFS2)
|
|
&& strcmp(mntp->mnt_type, MNTTYPE_NFS3)
|
|
&& strcmp(mntp->mnt_type, MNTTYPE_NFS3_PREF))
|
|
continue;
|
|
strcpy(name, mntp->mnt_fsname);
|
|
colon = strchr(name, ':');
|
|
if (colon == 0)
|
|
continue;
|
|
*colon = '\0';
|
|
if (strcmp(host, name))
|
|
continue;
|
|
}
|
|
if (type_list && !nameinlist(mntp->mnt_type, type_list))
|
|
continue;
|
|
|
|
/* check if already listed in mtab, if so skip it
|
|
* this avoids both the mtab check later and possible
|
|
* partition/mount conflicts during the mountcheck test.
|
|
*/
|
|
if (!skip_mtab_checks && !verbose) {
|
|
|
|
continue_flag = 0;
|
|
for (which_mtab_entry = mtab_entry_list;
|
|
which_mtab_entry;
|
|
which_mtab_entry = which_mtab_entry->next) {
|
|
|
|
if (!strcmp(mntp->mnt_dir,
|
|
which_mtab_entry->mount_point)) {
|
|
continue_flag++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (continue_flag)
|
|
continue;
|
|
}
|
|
|
|
/* check for conflicting mounts/overlaps */
|
|
if (check_mounted_overlaps(check_state, mntp))
|
|
continue;
|
|
|
|
if (!strncmp(mntp->mnt_type, MNTTYPE_NFS,strlen(MNTTYPE_NFS))) {
|
|
if (adjustnfsargs(NULL, mntp))
|
|
continue;
|
|
}
|
|
mtree = maketree(mtree, mntp, NULL);
|
|
mounts++;
|
|
}
|
|
(void) endmntent(mnttab);
|
|
/*
|
|
* If there are enough mounts to do, go ahead and
|
|
* do them in parallel.
|
|
*/
|
|
if (mounts >= PARALLEL_MOUNT_MINIMUM) {
|
|
parallel_mounts = 1;
|
|
}
|
|
|
|
if (check_state)
|
|
mnt_check_end(check_state);
|
|
|
|
exit(mounttree(mtree));
|
|
}
|
|
|
|
/*
|
|
* Command looks like: mount <dev>|<dir>
|
|
* we walk through /etc/fstab til we match either fsname or dir.
|
|
*/
|
|
if (argc == 2) {
|
|
struct stat mnttab_stat;
|
|
long mnttab_size;
|
|
int count;
|
|
|
|
/* read all volheaders and setup mounted partition checking */
|
|
if (do_overlap_checks && (mnt_check_init(&check_state) == -1)) {
|
|
check_state = 0;
|
|
(void) fprintf(stderr, "mount: unable to init partition "
|
|
"checking routines, suspending checks\n");
|
|
}
|
|
|
|
mnttab = setmntent(altfstab ? altfstab : MNTTAB, "r");
|
|
if (mnttab == NULL) {
|
|
(void) fprintf(stderr, "mount: ");
|
|
perror(altfstab ? altfstab : MNTTAB);
|
|
exit(1);
|
|
}
|
|
if (fstat(fileno(mnttab), &mnttab_stat) == -1) {
|
|
(void) fprintf(stderr, "mount: ");
|
|
perror(altfstab ? altfstab : MNTTAB);
|
|
exit(1);
|
|
}
|
|
mnttab_size = mnttab_stat.st_size;
|
|
|
|
for (count = 1;; count++) {
|
|
if ((mntp = getmntent(mnttab)) == NULL) {
|
|
if (ftell(mnttab) >= mnttab_size)
|
|
break; /* it's EOF */
|
|
(void) fprintf(stderr,
|
|
"mount: %s: illegal entry on line %d\n",
|
|
altfstab ? altfstab : MNTTAB, count);
|
|
continue;
|
|
}
|
|
if ((strcmp(mntp->mnt_type, MNTTYPE_IGNORE) == 0)
|
|
|| (strcmp(mntp->mnt_type, MNTTYPE_RAWDATA) == 0)
|
|
|| (strcmp(mntp->mnt_type, MNTTYPE_SWAP) == 0)) {
|
|
continue;
|
|
}
|
|
if ((strcmp(mntp->mnt_fsname, argv[1]) == 0)
|
|
|| (strcmp(mntp->mnt_dir, argv[1]) == 0)) {
|
|
|
|
if (opts[0] != '\0') {
|
|
/*
|
|
* "-o" specified; override fstab with
|
|
* command line options, unless it's
|
|
* "-o remount", in which case do
|
|
* nothing if the fstab says R/O (you
|
|
* can't remount from R/W to R/O, and
|
|
* remounting from R/O to R/O is not
|
|
* only invalid but pointless).
|
|
*/
|
|
if (strcmp(opts, MNTOPT_REMOUNT) == 0
|
|
&& hasmntopt(mntp, MNTOPT_RO))
|
|
exit(0);
|
|
|
|
mntp->mnt_opts = opts;
|
|
replace_opts(mntp->mnt_opts, ro, MNTOPT_RO,
|
|
MNTOPT_RW);
|
|
} else if (ro) {
|
|
mntp->mnt_opts = MNTOPT_RO;
|
|
}
|
|
|
|
if (strcmp(type, MNTTYPE_EFS) == 0) {
|
|
replace_opts(mntp->mnt_opts, quota, MNTOPT_QUOTA,
|
|
MNTOPT_NOQUOTA);
|
|
}
|
|
|
|
replace_opts(mntp->mnt_opts, nomtab, MNTOPT_NOMTAB,
|
|
NULL);
|
|
|
|
if (check_mounted_overlaps(check_state, mntp)) {
|
|
if (check_state)
|
|
mnt_check_end(check_state);
|
|
exit(1);
|
|
}
|
|
|
|
if (check_state)
|
|
mnt_check_end(check_state);
|
|
|
|
if (!strncmp(mntp->mnt_type,
|
|
MNTTYPE_NFS, strlen(MNTTYPE_NFS))) {
|
|
if (adjustnfsargs(NULL, mntp))
|
|
exit(1);
|
|
}
|
|
|
|
exit(mounttree(maketree(NULL, mntp, NULL)));
|
|
}
|
|
}
|
|
(void) fprintf(stderr, "mount: %s not found in %s\n", argv[1],
|
|
altfstab ? altfstab : MNTTAB);
|
|
exit(1);
|
|
}
|
|
|
|
if (argc != 3) {
|
|
usage();
|
|
}
|
|
|
|
if (realpaths) {
|
|
if (realpath(argv[2], dir) == NULL) {
|
|
(void) fprintf(stderr, "mount: ");
|
|
perror(dir);
|
|
exit(1);
|
|
}
|
|
} else {
|
|
(void) strcpy(dir, argv[2]);
|
|
}
|
|
(void) strcpy(name, argv[1]);
|
|
|
|
/*
|
|
* If the file system type is not given then
|
|
* assume "nfs" if the name is of the form
|
|
* host:path
|
|
*/
|
|
if (index(name, ':') && type[0] == '\0') {
|
|
strcpy(type, MNTTYPE_NFS);
|
|
}
|
|
if (type[0] == '\0') {
|
|
register short fstyp, nfstyp;
|
|
struct statfs sfsb;
|
|
|
|
/*
|
|
* Get filesystem type index and, from it, type name.
|
|
*/
|
|
nfstyp = sysfs(GETNFSTYP);
|
|
for (fstyp = 1; fstyp < nfstyp; fstyp++) {
|
|
if (statfs(name, &sfsb, sizeof sfsb, fstyp) == 0)
|
|
break;
|
|
}
|
|
if (fstyp != sfsb.f_fstyp
|
|
|| sysfs(GETFSTYP, fstyp, type) < 0) {
|
|
perror(name);
|
|
exit(1);
|
|
}
|
|
}
|
|
if (dir[0] != '/') {
|
|
(void) fprintf(stderr,
|
|
"mount: directory path must begin with '/'\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (opts[0] == '\0') {
|
|
strcpy(opts, ro ? MNTOPT_RO : MNTOPT_RW);
|
|
} else
|
|
replace_opts(opts, ro, MNTOPT_RO, MNTOPT_RW);
|
|
if (strcmp(type, MNTTYPE_EFS) == 0)
|
|
replace_opts(opts, quota, MNTOPT_QUOTA, MNTOPT_NOQUOTA);
|
|
replace_opts(opts, nomtab, MNTOPT_NOMTAB, NULL);
|
|
|
|
mnt.mnt_opts = opts;
|
|
|
|
if (strncmp(type, MNTTYPE_NFS, strlen(MNTTYPE_NFS)) == 0) {
|
|
if (adjustnfsargs(type, &mnt))
|
|
exit(1);
|
|
passno = 0;
|
|
freq = 0;
|
|
} else {
|
|
mnt.mnt_type = type;
|
|
}
|
|
mnt.mnt_fsname = name;
|
|
mnt.mnt_dir = dir;
|
|
mnt.mnt_opts = opts;
|
|
mnt.mnt_freq = freq;
|
|
mnt.mnt_passno = passno;
|
|
|
|
/* read all volheaders and setup mounted partition checking */
|
|
if (do_overlap_checks && (mnt_check_init(&check_state) == -1)) {
|
|
check_state = 0;
|
|
(void) fprintf(stderr, "mount: unable to init partition "
|
|
"checking routines, suspending checks\n");
|
|
}
|
|
|
|
if (check_mounted_overlaps(check_state, &mnt)) {
|
|
if (check_state)
|
|
mnt_check_end(check_state);
|
|
exit(1);
|
|
}
|
|
|
|
if (check_state)
|
|
mnt_check_end(check_state);
|
|
|
|
exit(mounttree(maketree(NULL, &mnt, NULL)));
|
|
}
|
|
|
|
/* check_mounted_overlaps()
|
|
*
|
|
* See if the filesystem we're about to mount conflicts with
|
|
* any other mounted overlapping partition or volumes.
|
|
*
|
|
* ARGUMENTS:
|
|
* check_state - valid pointer check_state_t from mnt_check_init()
|
|
* mntp - valid pointer to filled-in mountent structure
|
|
*
|
|
* RETURN VALUE:
|
|
* 0 - no conflicts
|
|
* 1 - overlapping partition conflict or partition already mounted/claimed
|
|
*/
|
|
int
|
|
check_mounted_overlaps(mnt_check_state_t *check_state, struct mntent *mntp)
|
|
{
|
|
int retval;
|
|
|
|
if (!check_state)
|
|
return 0;
|
|
|
|
/* only check local efs & xfs filesystems */
|
|
if (strcmp(mntp->mnt_type, MNTTYPE_XFS)
|
|
&& strcmp(mntp->mnt_type, MNTTYPE_EFS))
|
|
return 0;
|
|
|
|
retval = mnt_check_and_mark_mounted(check_state, mntp->mnt_fsname);
|
|
|
|
if (retval == -1) {
|
|
if (mnt_causes_test(check_state, MNT_CAUSE_MOUNTED)) {
|
|
(void) fprintf(stderr, "mount: %s is already mounted.\n",
|
|
mntp->mnt_fsname);
|
|
} else if (mnt_causes_test(check_state, MNT_CAUSE_OVERLAP)) {
|
|
(void) fprintf(stderr, "mount: %s overlaps a mounted partition.\n", mntp->mnt_fsname);
|
|
} else {
|
|
mnt_causes_show(check_state, stderr, "mount");
|
|
}
|
|
(void) fprintf(stderr, "\n");
|
|
(void) fflush(stderr);
|
|
mnt_plist_show(check_state, stderr, "mount");
|
|
(void) fprintf(stderr, "\n");
|
|
return 1;
|
|
}
|
|
|
|
/* let mount handle stat errors etc */
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Make sure that /etc and /usr/etc are in the
|
|
* path before execing user supplied mount program.
|
|
* We set the path to _PATH_ROOTPATH so no security problems can creep in.
|
|
*/
|
|
void
|
|
fixpath()
|
|
{
|
|
char *newpath;
|
|
static char prefix[] = "PATH=";
|
|
|
|
/*
|
|
* Allocate enough space for the path and the trailing null.
|
|
*/
|
|
newpath = malloc(strlen(prefix) + strlen(_PATH_ROOTPATH) + 1);
|
|
strcpy(newpath, prefix);
|
|
strcat(newpath, _PATH_ROOTPATH);
|
|
putenv(newpath);
|
|
}
|
|
|
|
/*
|
|
* attempt to mount file system, return errno or 0
|
|
*/
|
|
mountfs(int print, struct mntent *mnt)
|
|
{
|
|
pid_t waitpid;
|
|
int error;
|
|
int type = -1;
|
|
int flags = 0;
|
|
union data {
|
|
struct nfs_args nfs_args;
|
|
struct efs_args efs_args;
|
|
struct xfs_args xfs_args;
|
|
#ifdef PCFS
|
|
struct pc_args pc_args;
|
|
#endif
|
|
} data;
|
|
char *fsname = mnt->mnt_fsname;
|
|
int vhfd;
|
|
char *eagopt;
|
|
struct stat statb;
|
|
char opts[MNTMAXSTR];
|
|
cap_t ocap;
|
|
cap_value_t mount_caps[] = {CAP_MOUNT_MGT, CAP_PRIV_PORT,
|
|
CAP_MAC_READ, CAP_DAC_READ_SEARCH};
|
|
|
|
if (eagopt = hasmntopt(mnt, MNTOPT_EAG)) {
|
|
char *cp;
|
|
eagopt = strdup(eagopt);
|
|
if (cp = strchr(eagopt, ','))
|
|
*cp = '\0';
|
|
if (cp = strchr(eagopt, ' '))
|
|
*cp = '\0';
|
|
if (cp = strchr(eagopt, '\t'))
|
|
*cp = '\0';
|
|
}
|
|
|
|
if (hasmntopt(mnt, MNTOPT_REMOUNT) == 0) {
|
|
if (mounted(mnt)) {
|
|
if (print && verbose) {
|
|
struct ustat us;
|
|
struct stat sb;
|
|
|
|
/*
|
|
* only works when fsname is a device, so we
|
|
* skip NFS and DBG, but better than nothing:
|
|
* the check for a block device is so we won't
|
|
* get incorrect messages with non EFS fs's
|
|
*/
|
|
if (stat(mnt->mnt_fsname, &sb) == 0
|
|
&& (sb.st_mode & S_IFBLK) == S_IFBLK
|
|
&& ustat(sb.st_rdev, &us) < 0) {
|
|
fprintf(stderr,
|
|
"mount: %s in mount table, but not mounted\n",
|
|
mnt->mnt_fsname);
|
|
} else {
|
|
(void) fprintf(stderr,
|
|
"mount: %s already mounted\n",
|
|
mnt->mnt_fsname);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
} else if (print && verbose)
|
|
(void) fprintf (stderr, "mountfs: remount ignoring mtab\n");
|
|
if (fake) {
|
|
addtomtab(mnt);
|
|
return (0);
|
|
}
|
|
if (strcmp(mnt->mnt_type, MNTTYPE_EFS) == 0) {
|
|
flags |= MS_DATA;
|
|
error = mount_efs(mnt, &data.efs_args);
|
|
} else if (strcmp(mnt->mnt_type, MNTTYPE_XFS) == 0) {
|
|
flags |= MS_DATA;
|
|
if (hasmntopt(mnt, MNTOPT_DMI))
|
|
flags |= MS_DMI;
|
|
error = mount_xfs(mnt, &data.xfs_args);
|
|
} else if (strcmp(mnt->mnt_type, MNTTYPE_DBG) == 0) {
|
|
flags |= MS_FSS;
|
|
error = 0;
|
|
} else if (strcmp(mnt->mnt_type, MNTTYPE_NFS2) == 0) {
|
|
struct mntent tmpmnt;
|
|
|
|
tmpmnt = *mnt;
|
|
tmpmnt.mnt_type = MNTTYPE_NFS;
|
|
flags |= MS_DATA;
|
|
error = mount_nfs(&tmpmnt, &data.nfs_args, eagopt,
|
|
MOUNTVERS, MOUNTVERS_SGI_ORIG);
|
|
if (!error)
|
|
strcpy(mnt->mnt_type,tmpmnt.mnt_type);
|
|
/*
|
|
* Gross hack for AT&T 5.3.1 mount: coerce fsname to dir
|
|
* so that the mount syscall can lookup inodes for both
|
|
* fsname and dir. XXX change mount(2) to treat fsname
|
|
* as a filesystem-dependent parameter.
|
|
*/
|
|
fsname = mnt->mnt_dir;
|
|
#ifdef PCFS
|
|
} else if (strcmp(mnt->mnt_type, MNTTYPE_PC) == 0) {
|
|
error = mount_pc(mnt, &data.pc_args);
|
|
#endif
|
|
} else if (strcmp(mnt->mnt_type, MNTTYPE_NFS3) == 0) {
|
|
flags |= MS_DATA;
|
|
if (sysfs(GETFSIND, mnt->mnt_type) < 0) {
|
|
error = errno;
|
|
} else {
|
|
error = mount_nfs(mnt, &data.nfs_args, eagopt,
|
|
MOUNTVERS3, MOUNTVERS_SGI_ORIG_3);
|
|
}
|
|
fsname = mnt->mnt_dir;
|
|
} else if (strcmp(mnt->mnt_type, MNTTYPE_NFS3_PREF) == 0) {
|
|
struct mntent tmpmnt;
|
|
|
|
tmpmnt = *mnt;
|
|
tmpmnt.mnt_type = MNTTYPE_NFS;
|
|
|
|
flags |= MS_DATA;
|
|
|
|
/* Make sure V3 is installed */
|
|
tmpmnt.mnt_type = MNTTYPE_NFS3;
|
|
type = sysfs(GETFSIND, tmpmnt.mnt_type);
|
|
if (type > 0) {
|
|
/* Try to V3 mount, otherwise just fall back to V2 */
|
|
|
|
error = mount_nfs(&tmpmnt, &data.nfs_args, eagopt,
|
|
MOUNTVERS3, MOUNTVERS_SGI_ORIG_3);
|
|
if (!error)
|
|
strcpy(mnt->mnt_type,tmpmnt.mnt_type);
|
|
else
|
|
printf("NFS version 3 mount failed, trying NFS version 2\n");
|
|
} else
|
|
error = 0;
|
|
if (error || (type <= 0)) {
|
|
tmpmnt.mnt_type = MNTTYPE_NFS;
|
|
error = mount_nfs(&tmpmnt, &data.nfs_args, eagopt,
|
|
MOUNTVERS, MOUNTVERS_SGI_ORIG);
|
|
if (!error)
|
|
strcpy(mnt->mnt_type,tmpmnt.mnt_type);
|
|
}
|
|
fsname = mnt->mnt_dir;
|
|
} else if (strcmp(mnt->mnt_type, MNTTYPE_FD) == 0) {
|
|
flags |= MS_DATA;
|
|
error = 0;
|
|
} else if (strcmp(mnt->mnt_type, MNTTYPE_HWGFS) == 0) {
|
|
flags |= MS_DATA;
|
|
error = 0;
|
|
} else {
|
|
/*
|
|
* Invoke "mount" command for particular file system type.
|
|
* Use this for unknown or third-party file system types.
|
|
*/
|
|
char mountcmd[128];
|
|
int pid;
|
|
int status;
|
|
|
|
pid = fork();
|
|
switch (pid) {
|
|
|
|
case -1:
|
|
(void) fprintf(stderr,
|
|
"mount: %s on ", mnt->mnt_fsname);
|
|
perror(mnt->mnt_dir);
|
|
return (errno);
|
|
case 0:
|
|
fixpath();
|
|
(void) sprintf(mountcmd, "mount_%s", mnt->mnt_type);
|
|
execlp(mountcmd, mountcmd, mnt->mnt_fsname,
|
|
mnt->mnt_dir, mnt->mnt_type, mnt->mnt_opts,
|
|
(char *)NULL);
|
|
|
|
/*
|
|
* If the mount_* command was not found, let the UMFS
|
|
* mechanism check if the type is a registered User
|
|
* Mode filesystem.
|
|
*/
|
|
if (errno == ENOENT) {
|
|
execlp("mount_umfs", "mount_umfs", mnt->mnt_fsname,
|
|
mnt->mnt_dir, mnt->mnt_type, mnt->mnt_opts,
|
|
(char *)NULL);
|
|
}
|
|
|
|
/*
|
|
* UMFS is not installed, punt in the usual manner
|
|
*/
|
|
if (errno == ENOENT) {
|
|
(void) fprintf(stderr,
|
|
"mount: unknown filesystem type: %s\n",
|
|
mnt->mnt_type);
|
|
} else if (print) {
|
|
(void) fprintf(stderr, "%s: %s on %s ",
|
|
mountcmd, mnt->mnt_fsname,
|
|
mnt->mnt_dir); perror("exec failed");
|
|
}
|
|
exit(errno);
|
|
default:
|
|
do {
|
|
waitpid = wait(&status);
|
|
if (waitpid == -1) {
|
|
return(waitpid);
|
|
}
|
|
} while (waitpid != pid);
|
|
if (WIFEXITED(status)) {
|
|
error = WEXITSTATUS(status);
|
|
if (!error) {
|
|
goto itworked;
|
|
}
|
|
} else {
|
|
error = status;
|
|
}
|
|
}
|
|
}
|
|
if (error) {
|
|
return (error);
|
|
}
|
|
|
|
type = sysfs(GETFSIND, mnt->mnt_type);
|
|
if (type > 0)
|
|
flags |= MS_FSS;
|
|
else {
|
|
perror(mnt->mnt_type);
|
|
exit(errno);
|
|
}
|
|
|
|
if (hasmntopt(mnt, MNTOPT_RO))
|
|
flags |= MS_RDONLY;
|
|
if (hasmntopt(mnt, MNTOPT_GRPID))
|
|
flags |= MS_GRPID;
|
|
if (hasmntopt(mnt, MNTOPT_NOSUID))
|
|
flags |= MS_NOSUID;
|
|
if (hasmntopt(mnt, MNTOPT_NODEV))
|
|
flags |= MS_NODEV;
|
|
/* jws - This needs to be set by default */
|
|
flags |= MS_DEFXATTR;
|
|
if (hasmntopt(mnt, MNTOPT_NODEFXATTR))
|
|
flags &= ~(MS_DEFXATTR);
|
|
if (hasmntopt(mnt, MNTOPT_DOXATTR))
|
|
flags |= MS_DOXATTR;
|
|
|
|
/*
|
|
* Set the blocksize of the device to the blocksize
|
|
* specified in the volume header. This is necessary
|
|
* when mounting efs CDROM on the 12x Toshiba (and
|
|
* possibly newer) drives. It also SHOULD handle
|
|
* future devices that support larger than 512 byte blocks.
|
|
*
|
|
* Specifically coded the routines to not check return
|
|
* status for errors as we may be able to mount the
|
|
* device even if we can't set or detect the block size.
|
|
*/
|
|
|
|
vhfd = disk_openvh (fsname);
|
|
if (vhfd >= 0) {
|
|
{
|
|
struct volume_header vhdr;
|
|
if (getdiskheader(vhfd,&vhdr) == 0) /* okay */
|
|
(void)disk_setblksz(vhfd,vhdr.vh_dp.dp_secbytes);
|
|
}
|
|
close(vhfd);
|
|
}
|
|
|
|
/*
|
|
* Use sgi_eag_mount instead of mount if the Plan G
|
|
* attribute extension mechanism is required by the mount
|
|
* ("eag:....") options.
|
|
*/
|
|
|
|
retry:
|
|
ocap = cap_acquire (4, mount_caps);
|
|
if (eagopt || mac_enabled)
|
|
error = sgi_eag_mount(fsname, mnt->mnt_dir, flags, type,
|
|
&data, sizeof data, eagopt);
|
|
else
|
|
error = mount(fsname, mnt->mnt_dir, flags, type,
|
|
&data, sizeof data);
|
|
cap_surrender (ocap);
|
|
|
|
if (error < 0) {
|
|
error = errno;
|
|
|
|
/*
|
|
* Hack for NFS over TCP
|
|
*/
|
|
if ((strncmp(mnt->mnt_type, MNTTYPE_NFS,
|
|
strlen(MNTTYPE_NFS)) == 0) &&
|
|
(data.nfs_args.flags & NFSMNT_TCP) &&
|
|
(errno == ECONNREFUSED)) {
|
|
data.nfs_args.flags &= ~NFSMNT_TCP;
|
|
/*
|
|
* Fix up options; relies on the fact that tcp and udp
|
|
* are the same length
|
|
*/
|
|
{
|
|
char *p = strstr(mnt->mnt_opts, "proto=");
|
|
if (p) {
|
|
while (*p != '=') {
|
|
p++;
|
|
}
|
|
p++;
|
|
bcopy("udp", p, 3);
|
|
}
|
|
}
|
|
|
|
if (print) {
|
|
fprintf(stderr,
|
|
"mount: mount of %s on %s, %s: TCP unavailable, trying UDP\n",
|
|
mnt->mnt_fsname, mnt->mnt_dir,
|
|
mnt->mnt_type);
|
|
}
|
|
goto retry;
|
|
}
|
|
if (print) {
|
|
fprintf(stderr, "mount: %s on ", mnt->mnt_fsname);
|
|
if (error == ENOSPC) {
|
|
if ((flags & MS_RDONLY) &&
|
|
strncmp(mnt->mnt_type, MNTTYPE_XFS,
|
|
strlen(MNTTYPE_XFS)) == 0) {
|
|
fprintf(stderr, "%s: Dirty XFS filesystem. Cannot be mounted read-only.\n\tMount read/write, unmount, and try again.\n",
|
|
mnt->mnt_dir);
|
|
} else {
|
|
fprintf(stderr,
|
|
"%s: Dirty filesystem.\n",
|
|
mnt->mnt_dir);
|
|
}
|
|
} else if (error == E2BIG) {
|
|
fprintf(stderr,
|
|
"%s: Filesystem too large for device.\n",
|
|
mnt->mnt_dir);
|
|
} else if (error == EWRONGFS) {
|
|
fprintf(stderr,
|
|
"%s: Wrong filesystem type %s\n",
|
|
mnt->mnt_dir, mnt->mnt_type);
|
|
} else {
|
|
errno = error;
|
|
perror(mnt->mnt_dir);
|
|
}
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
itworked:
|
|
if (*mnt->mnt_opts == '\0') /* XXX ??? */
|
|
(void) strcpy(mnt->mnt_opts, MNTOPT_RW);
|
|
|
|
/*
|
|
* normalize NFS mount output to look like
|
|
* we want all nfs output to look like this
|
|
* blah:blah on /blah type nfs (vers=n,....)
|
|
* at this point MNTTYPE_NFS means nfs v2,
|
|
* and MNTTYPE_NFS3 means v3. All others were
|
|
* factored out above.
|
|
*/
|
|
|
|
if ((strcmp(mnt->mnt_type, MNTTYPE_NFS3) == 0)
|
|
&& (stat(mnt->mnt_dir, &statb) == 0)) {
|
|
char *fmtstr;
|
|
/* Note: printmtab assumes dev is last */
|
|
if (ismntopt(mnt, MNTOPT_VERS) == NULL)
|
|
fmtstr = "vers=3,%s,dev=%04x";
|
|
else
|
|
fmtstr = "%s,dev=%04x";
|
|
sprintf(opts, fmtstr, mnt->mnt_opts, statb.st_dev);
|
|
mnt->mnt_opts = opts;
|
|
mnt->mnt_type = MNTTYPE_NFS;
|
|
}
|
|
else if ((strcmp(mnt->mnt_type, MNTTYPE_NFS) == 0)
|
|
&& (stat(mnt->mnt_dir, &statb) == 0)) {
|
|
char *fmtstr;
|
|
/* Note: printmtab assumes dev is last */
|
|
if (ismntopt(mnt, MNTOPT_VERS) == NULL)
|
|
fmtstr = "vers=2,%s,dev=%04x";
|
|
else
|
|
fmtstr = "%s,dev=%04x";
|
|
sprintf(opts, fmtstr, mnt->mnt_opts, statb.st_dev);
|
|
mnt->mnt_opts = opts;
|
|
}
|
|
addtomtab(mnt);
|
|
return (0);
|
|
} /* mountfs */
|
|
|
|
mount_efs(struct mntent *mnt, struct efs_args *args)
|
|
{
|
|
int status;
|
|
char cmd[MNTMAXSTR];
|
|
char *nametocheck;
|
|
char *sep = NULL;
|
|
|
|
args->flags = 0;
|
|
|
|
if (nopt(mnt, "lbsize", &args->lbsize))
|
|
args->flags |= EFSMNT_LBSIZE;
|
|
|
|
if (!check)
|
|
return 0;
|
|
|
|
sprintf(cmd, "2>&1 /sbin/fsstat %s > /dev/null", mnt->mnt_fsname);
|
|
status = system(cmd);
|
|
if (status >> 8 != 1) {
|
|
return 0; /* filesystem clean or invalid */
|
|
}
|
|
if (strcmp(mnt->mnt_fsname, "/")
|
|
&& (nametocheck = hasmntopt(mnt, MNTOPT_RAW)) != NULL) {
|
|
/*
|
|
* Ignore the next option field for now, but we'll
|
|
* have to restore the separator for later hasmntopt calls.
|
|
*/
|
|
if (sep = strchr(nametocheck, ','))
|
|
*sep = '\0';
|
|
|
|
if ((nametocheck = strchr(nametocheck, '=')) != NULL)
|
|
nametocheck++; /* advance to option rhs */
|
|
} else {
|
|
/* Nonexistent or malformed raw spec in fstab */
|
|
nametocheck = mnt->mnt_fsname;
|
|
}
|
|
sprintf(cmd, "/sbin/fsck -y %s", nametocheck);
|
|
if (sep)
|
|
*sep = MNTOPTSEP;
|
|
status = system(cmd);
|
|
return (status >> 8);
|
|
} /* mount_efs */
|
|
|
|
mount_xfs(struct mntent *mnt, struct xfs_args *args)
|
|
{
|
|
char *slogbufs, *slogbufsize, *sep, *sunit, *swidth, *biosize;
|
|
int logbufs = -1;
|
|
int logbufsize = -1;
|
|
int dsunit, dswidth, xlv_dsunit, xlv_dswidth;
|
|
int iosize;
|
|
|
|
iosize = dsunit = dswidth = xlv_dsunit = xlv_dswidth = 0;
|
|
args->version = 3;
|
|
args->flags = 0;
|
|
args->fsname = malloc(PATH_MAX); /* OK, so I don't free it */
|
|
strcpy(args->fsname, mnt->mnt_dir);
|
|
if ((slogbufs = hasmntopt(mnt, MNTOPT_LOGBUFS)) != NULL) {
|
|
/*
|
|
* Ignore the next option field for now, but we'll
|
|
* have to restore the separator for later hasmntopt calls.
|
|
*/
|
|
if (sep = strchr(slogbufs, ','))
|
|
*sep = '\0';
|
|
|
|
if ((slogbufs = strchr(slogbufs, '=')) != NULL) {
|
|
slogbufs++; /* advance to option rhs */
|
|
if (!strcmp(slogbufs, "none")) {
|
|
logbufs = 0;
|
|
fprintf(stderr,
|
|
"mount: this FS is trash after writing to it\n");
|
|
} else {
|
|
logbufs = atoi(slogbufs);
|
|
if (logbufs < 2 || logbufs > 8) {
|
|
fprintf(stderr,
|
|
"mount: Illegal logbufs amount: %d\n",
|
|
logbufs);
|
|
if (sep)
|
|
*sep = MNTOPTSEP;
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
if (sep)
|
|
*sep = MNTOPTSEP;
|
|
}
|
|
if ((slogbufsize = hasmntopt(mnt, MNTOPT_LOGBSIZE)) != NULL) {
|
|
/*
|
|
* Ignore the next option field for now, but we'll
|
|
* have to restore the separator for later hasmntopt calls.
|
|
*/
|
|
if (sep = strchr(slogbufsize, ','))
|
|
*sep = '\0';
|
|
|
|
if ((slogbufsize = strchr(slogbufsize, '=')) != NULL) {
|
|
slogbufsize++; /* advance to option rhs */
|
|
logbufsize = atoi(slogbufsize);
|
|
if (logbufsize != 16*1024 && logbufsize != 32*1024) {
|
|
fprintf(stderr,
|
|
"mount: Illegal logbufsize: %d (x == 16k or 32k)\n",
|
|
logbufsize);
|
|
if (sep)
|
|
*sep = MNTOPTSEP;
|
|
return 1;
|
|
}
|
|
if (logbufsize % 512 != 0) {
|
|
fprintf(stderr,
|
|
"mount: logbufsize must be multiple of BBSIZE: %d",
|
|
logbufsize);
|
|
if (sep)
|
|
*sep = MNTOPTSEP;
|
|
return 1;
|
|
}
|
|
}
|
|
if (sep)
|
|
*sep = MNTOPTSEP;
|
|
}
|
|
if ((biosize = hasmntopt(mnt, MNTOPT_BIOSIZE)) != NULL) {
|
|
/*
|
|
* Ensure we have a null-terminated string to work with but
|
|
* we have to restore the separator for later hasmntopt calls.
|
|
*/
|
|
if (sep = strchr(biosize, ','))
|
|
*sep = '\0';
|
|
if ((biosize = strchr(biosize, '=')) != NULL) {
|
|
biosize++; /* advance to option rhs */
|
|
iosize = atoi(biosize);
|
|
if (iosize > 255 || iosize <= 0) {
|
|
fprintf(stderr,
|
|
"mount: illegal biosize %d, value out of bounds\n",
|
|
iosize);
|
|
if (sep)
|
|
*sep = MNTOPTSEP;
|
|
return 1;
|
|
}
|
|
args->flags |= XFSMNT_IOSIZE;
|
|
args->iosizelog = (uint8_t) iosize;
|
|
}
|
|
if (sep)
|
|
*sep = MNTOPTSEP;
|
|
}
|
|
/*
|
|
* Pass in the wsync option as a flag since it requires
|
|
* no argument.
|
|
*/
|
|
if (hasmntopt(mnt, MNTOPT_WSYNC) != NULL) {
|
|
args->flags |= XFSMNT_WSYNC;
|
|
}
|
|
if (hasmntopt(mnt, MNTOPT_NOATIME) != NULL) {
|
|
args->flags |= XFSMNT_NOATIME;
|
|
}
|
|
if (hasmntopt(mnt, MNTOPT_OSYNCISDSYNC) != NULL) {
|
|
args->flags |= XFSMNT_OSYNCISDSYNC;
|
|
}
|
|
if (hasmntopt(mnt, MNTOPT_NORECOVERY) != NULL) {
|
|
args->flags |= XFSMNT_NORECOVERY;
|
|
if (hasmntopt(mnt, MNTOPT_RO) == NULL) {
|
|
fprintf(stderr,
|
|
"mount: no-recovery XFS mounts must be read-only.\n");
|
|
return 1;
|
|
}
|
|
}
|
|
if (hasmntopt(mnt, MNTOPT_SHARED) != NULL) {
|
|
args->flags |= XFSMNT_SHARED;
|
|
if (hasmntopt(mnt, MNTOPT_RO) == NULL) {
|
|
fprintf(stderr,
|
|
"mount: shared XFS mounts must be read-only.\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Pass in ino64 option as a flag.
|
|
*/
|
|
if (hasmntopt(mnt, MNTOPT_INO64) != NULL) {
|
|
#ifdef XFS_BIG
|
|
args->flags |= XFSMNT_INO64;
|
|
#else
|
|
fprintf(stderr, "mount: ino64 option not allowed on this system\n");
|
|
return 1;
|
|
#endif
|
|
}
|
|
/*
|
|
* Pass in the quota options.
|
|
*/
|
|
if (hasmntopt(mnt, MNTOPT_UQUOTA) != NULL ||
|
|
hasmntopt(mnt, MNTOPT_QUOTA) != NULL) {
|
|
/* user quota accounting and limits enforcement */
|
|
args->flags |= XFSMNT_UQUOTA | XFSMNT_UQUOTAENF;
|
|
}
|
|
/*
|
|
* Don't enforce user quota limits, if asked not to.
|
|
* _*QUOTANOENF does _not_ depend on _*QUOTA option.
|
|
*/
|
|
if (hasmntopt(mnt, MNTOPT_UQUOTANOENF) != NULL ||
|
|
hasmntopt(mnt, MNTOPT_QUOTANOENF) != NULL) {
|
|
args->flags |= XFSMNT_UQUOTA;
|
|
args->flags &= ~XFSMNT_UQUOTAENF;
|
|
}
|
|
/*
|
|
* Typically, we turn quotas off if we weren't explicitly asked to mount
|
|
* quotas. MNTOPT_MRQUOTA tells us not to do that.
|
|
* This option is handy in the miniroot, when trying to mount /root.
|
|
* We can't really know what's in /etc/fstab until /root is already
|
|
* mounted! This mntopt stops quotas from getting turned off in the root
|
|
* filesystem everytime the system boots up a miniroot.
|
|
*/
|
|
if (hasmntopt(mnt, MNTOPT_MRQUOTA) != NULL) {
|
|
args->flags |= XFSMNT_QUOTAMAYBE;
|
|
}
|
|
|
|
/*
|
|
* Used to turn off stripe alignment
|
|
*/
|
|
if (hasmntopt(mnt, MNTOPT_NOALIGN) != NULL)
|
|
args->flags |= XFSMNT_NOALIGN;
|
|
|
|
/*
|
|
* Used to specify the stripe unit for xlv stripe volumes or raid disks
|
|
*/
|
|
if ((sunit = hasmntopt(mnt, MNTOPT_SUNIT)) != NULL) {
|
|
/*
|
|
* Ignore the next option field for now, but we'll
|
|
* have to restore the separator for later hasmntopt calls.
|
|
*/
|
|
if (sep = strchr(sunit, ','))
|
|
*sep = '\0';
|
|
if ((sunit = strchr(sunit, '=')) != NULL) {
|
|
sunit++;
|
|
dsunit = atoi(sunit);
|
|
}
|
|
if (sep)
|
|
*sep = MNTOPTSEP;
|
|
}
|
|
|
|
/*
|
|
* Used to specify the stripe width for xlv stripe volumes or raid disks
|
|
*/
|
|
if ((swidth = hasmntopt(mnt, MNTOPT_SWIDTH)) != NULL) {
|
|
/*
|
|
* Ignore the next option field for now, but we'll
|
|
* have to restore the separator for later hasmntopt calls.
|
|
*/
|
|
if (sep = strchr(swidth, ','))
|
|
*sep = '\0';
|
|
if ((swidth = strchr(swidth, '=')) != NULL) {
|
|
swidth++;
|
|
dswidth = atoi(swidth);
|
|
}
|
|
if (sep)
|
|
*sep = MNTOPTSEP;
|
|
}
|
|
|
|
if ((args->flags & XFSMNT_NOALIGN) && (dsunit || dswidth)) {
|
|
fprintf(stderr,
|
|
"mount: sunit and swidth options are incompatible with the noalign option\n");
|
|
return 1;
|
|
}
|
|
|
|
if (dsunit && !dswidth || !dsunit && dswidth) {
|
|
fprintf(stderr,
|
|
"mount: both sunit and swidth options have to be specified\n");
|
|
return 1;
|
|
}
|
|
|
|
if (dsunit && (dswidth % dsunit != 0)) {
|
|
fprintf(stderr,
|
|
"mount: stripe width (%d) has to be a multiple of the stripe unit (%d)\n",
|
|
dswidth, dsunit);
|
|
return 1;
|
|
}
|
|
|
|
if ((args->flags & XFSMNT_NOALIGN) != XFSMNT_NOALIGN) {
|
|
/*
|
|
* Extract the stripe width and stripe unit info from the
|
|
* underlying volume if applicable.
|
|
*/
|
|
xlv_get_subvol_stripe(mnt->mnt_fsname, XLV_SUBVOL_TYPE_DATA,
|
|
&xlv_dsunit, &xlv_dswidth);
|
|
|
|
if (dsunit && xlv_dsunit && xlv_dsunit != dsunit) {
|
|
fprintf(stderr,
|
|
"mount: Specified data stripe unit (%d) is not the same as the xlv stripe unit (%d)\n",
|
|
dsunit, xlv_dsunit);
|
|
return 1;
|
|
}
|
|
|
|
if (dswidth && xlv_dswidth && xlv_dswidth != dswidth) {
|
|
fprintf(stderr,
|
|
"mount: Specified data stripe width (%d) is not the same as the xlv stripe width (%d)\n",
|
|
dswidth, xlv_dswidth);
|
|
return 1;
|
|
}
|
|
|
|
if (dsunit) {
|
|
args->sunit = dsunit;
|
|
args->flags |= XFSMNT_RETERR;
|
|
} else
|
|
args->sunit = xlv_dsunit;
|
|
dswidth ? (args->swidth = dswidth) :
|
|
(args->swidth = xlv_dswidth);
|
|
} else
|
|
args->sunit = args->swidth = 0;
|
|
|
|
args->logbufs = logbufs;
|
|
args->logbufsize = logbufsize;
|
|
return 0;
|
|
} /* mount_xfs */
|
|
|
|
nameinlist(char *name, char *list)
|
|
{
|
|
char copy[BUFSIZ];
|
|
char *element;
|
|
|
|
list = strcpy(copy, list);
|
|
while ((element = strtok(list, ",")) != 0) {
|
|
if (!strcmp(element, name))
|
|
return 1;
|
|
if (!strcmp(element, MNTTYPE_NFS) &&
|
|
!strncmp(name,element,3) )
|
|
return 1;
|
|
list = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* RPC timeouts in seconds: per retransmission and total for a call.
|
|
* Set to a few seconds for the first (foreground) try if the "bg" option
|
|
* is specified, and reset after backgrounding to longer values.
|
|
*/
|
|
long mount_intertry, mount_percall;
|
|
struct timeval pmap_intertry, pmap_percall;
|
|
|
|
void
|
|
set_short_timeouts()
|
|
{
|
|
mount_intertry = 5;
|
|
mount_percall = 5;
|
|
pmap_intertry.tv_sec = 5;
|
|
pmap_percall.tv_sec = 5;
|
|
pmap_settimeouts(pmap_intertry, pmap_percall);
|
|
}
|
|
|
|
void
|
|
set_long_timeouts()
|
|
{
|
|
mount_intertry = 20;
|
|
mount_percall = 20;
|
|
pmap_intertry.tv_sec = 12;
|
|
pmap_percall.tv_sec = 60;
|
|
pmap_settimeouts(pmap_intertry, pmap_percall);
|
|
}
|
|
|
|
mount_nfs(struct mntent *mnt, struct nfs_args *args, char *eagopt,
|
|
int vers, int sgi_vers)
|
|
{
|
|
static struct sockaddr_in sin;
|
|
struct hostent *hp;
|
|
static struct fhstatus fhs;
|
|
static struct mountres3 mountres3;
|
|
static nfs_fh3 fh3;
|
|
char *cp;
|
|
char *host, *hostp;
|
|
char *path;
|
|
char *eag_mac_ip = NULL;
|
|
mac_t mac_ip = NULL, save_plabel = NULL;
|
|
char *proto;
|
|
int s;
|
|
struct timeval timeout;
|
|
CLIENT *client;
|
|
enum clnt_stat rpc_stat;
|
|
u_int port;
|
|
u_long prog;
|
|
char *msg;
|
|
cap_t ocap;
|
|
cap_value_t cap_mac_relabel_subj[] = {CAP_MAC_RELABEL_SUBJ};
|
|
|
|
if (mac_enabled && eagopt) {
|
|
if (eag_mac_ip = strstr(eagopt, MAC_MOUNT_IP)) {
|
|
eagopt = strdup(eag_mac_ip);
|
|
eag_mac_ip = eagopt + strlen(MAC_MOUNT_IP) + 1;
|
|
if (cp = strchr(eag_mac_ip, ':'))
|
|
*cp = '\0';
|
|
if (cp = strchr(eag_mac_ip, ','))
|
|
*cp = '\0';
|
|
if (cp = strchr(eag_mac_ip, ' '))
|
|
*cp = '\0';
|
|
if (cp = strchr(eag_mac_ip, '\t'))
|
|
*cp = '\0';
|
|
mac_ip = mac_from_text(eag_mac_ip);
|
|
free(eagopt);
|
|
if (mac_ip == NULL) {
|
|
(void) fprintf(stderr,
|
|
"mount: %s:%s invalid label.\n",
|
|
MAC_MOUNT_IP, eag_mac_ip);
|
|
return (EINVAL);
|
|
}
|
|
}
|
|
}
|
|
|
|
hostp = host = malloc(MNTMAXSTR);
|
|
cp = mnt->mnt_fsname;
|
|
while ((*hostp = *cp) != ':') {
|
|
if (*cp == '\0') {
|
|
(void) fprintf(stderr,
|
|
"mount: nfs file system; use host:path\n");
|
|
return (EINVAL);
|
|
}
|
|
hostp++;
|
|
cp++;
|
|
}
|
|
*hostp = '\0';
|
|
path = ++cp;
|
|
/*
|
|
* Get server's address
|
|
*/
|
|
if ((hp = gethostbyname(host)) == NULL) {
|
|
(void) fprintf(stderr,
|
|
"mount: %s not in hosts database\n", host);
|
|
return (ENOENT);
|
|
}
|
|
|
|
args->flags = 0;
|
|
if (ismntopt(mnt, MNTOPT_SOFT)) {
|
|
args->flags |= NFSMNT_SOFT;
|
|
}
|
|
/*
|
|
* Make hard mounts interruptible by default.
|
|
*/
|
|
if (ismntopt(mnt, MNTOPT_NOINTR)) {
|
|
args->flags |= NFSMNT_NOINT;
|
|
}
|
|
if (ismntopt(mnt, MNTOPT_NOAC)) {
|
|
args->flags |= NFSMNT_NOAC;
|
|
}
|
|
if (ismntopt(mnt, MNTOPT_PRIVATE)) {
|
|
args->flags |= NFSMNT_PRIVATE;
|
|
}
|
|
if (ismntopt(mnt, MNTOPT_SHORTUID)) {
|
|
args->flags |= NFSMNT_SHORTUID;
|
|
}
|
|
if (nopt(mnt, MNTOPT_SYMTTL, &args->symttl)) {
|
|
args->flags |= NFSMNT_SYMTTL;
|
|
}
|
|
|
|
/*
|
|
* get fhandle of remote path from server's mountd
|
|
*/
|
|
bzero((char *)&sin, sizeof(sin));
|
|
bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
|
|
sin.sin_family = AF_INET;
|
|
timeout.tv_usec = 0;
|
|
timeout.tv_sec = mount_intertry;
|
|
s = RPC_ANYSOCK;
|
|
|
|
if (ismntopt(mnt, "bds")) {
|
|
args->flags |= NFSMNT_BDS;
|
|
if (nopt(mnt, "bdsauto", &args->bdsauto)) {
|
|
args->flags |= NFSMNT_BDSAUTO;
|
|
}
|
|
if (nopt(mnt, "bdswindow", &args->bdswindow)) {
|
|
args->flags |= NFSMNT_BDSWINDOW;
|
|
}
|
|
if (nopt(mnt, "bdsbuffer", &args->bdsbuflen)) {
|
|
args->flags |= NFSMNT_BDSBUFLEN;
|
|
}
|
|
}
|
|
|
|
if (mac_ip) {
|
|
save_plabel = mac_get_proc ();
|
|
ocap = cap_acquire (1, cap_mac_relabel_subj);
|
|
(void) mac_set_proc (mac_ip);
|
|
cap_surrender (ocap);
|
|
}
|
|
/*
|
|
* Try and get the SGI private mount program which has
|
|
* a statvfs procedure. The clntudp_create function doesn't
|
|
* enforce the version, but remembers it in the client handle.
|
|
* Therefore, on create we ask for the lowest version number
|
|
* and then depend on rpc.mountd to respond with RPC_PROCUNAVAIL
|
|
* if it is the older version.
|
|
*/
|
|
if ((client = clntudp_create(&sin, prog = (u_long)MOUNTPROG_SGI,
|
|
(u_long)sgi_vers, timeout, &s)) == NULL &&
|
|
(rpc_createerr.cf_stat == RPC_PMAPFAILURE ||
|
|
(client = clntudp_create(&sin, prog = (u_long)MOUNTPROG,
|
|
(u_long)vers, timeout, &s)) == NULL)) {
|
|
if (mac_ip) {
|
|
ocap = cap_acquire (1, cap_mac_relabel_subj);
|
|
(void) mac_set_proc (save_plabel);
|
|
cap_surrender (ocap);
|
|
}
|
|
if (!printed) {
|
|
(void) fprintf(stderr,
|
|
"mount: %s server not responding",
|
|
mnt->mnt_fsname);
|
|
clnt_pcreateerror("");
|
|
printed = 1;
|
|
}
|
|
return (ETIMEDOUT);
|
|
}
|
|
callmount:
|
|
client->cl_auth = authunix_create_default();
|
|
timeout.tv_usec = 0;
|
|
timeout.tv_sec = mount_percall;
|
|
if (vers == MOUNTVERS) {
|
|
rpc_stat = clnt_call(client, MOUNTPROC_MNT, xdr_path, &path,
|
|
xdr_fhstatus, &fhs, timeout);
|
|
} else {
|
|
rpc_stat = clnt_call(client, MOUNTPROC_MNT, xdr_path, &path,
|
|
xdr_mountres3, (caddr_t)&mountres3, timeout);
|
|
}
|
|
errno = 0;
|
|
if (rpc_stat != RPC_SUCCESS) {
|
|
/*
|
|
* Some RPC implementations (like the one written
|
|
* in Lisp on Symbolics machines) don't check the
|
|
* program number's validity until it is actually
|
|
* used. So if the we fail here with MOUNTPROG_SGI,
|
|
* then back off to the standard mount program.
|
|
*/
|
|
if (prog == MOUNTPROG_SGI) {
|
|
CLIENT *nclient;
|
|
|
|
bzero((char *)&sin, sizeof(sin));
|
|
bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
|
|
sin.sin_family = AF_INET;
|
|
timeout.tv_usec = 0;
|
|
timeout.tv_sec = mount_intertry;
|
|
s = RPC_ANYSOCK;
|
|
|
|
if (nclient = clntudp_create(&sin, prog = (u_long)MOUNTPROG,
|
|
(u_long)vers, timeout, &s)) {
|
|
clnt_destroy(client);
|
|
client = nclient;
|
|
goto callmount;
|
|
}
|
|
}
|
|
if (!printed) {
|
|
(void) fprintf(stderr,
|
|
"mount: %s server not responding",
|
|
mnt->mnt_fsname);
|
|
clnt_perror(client, "");
|
|
printed = 1;
|
|
}
|
|
switch (rpc_stat) {
|
|
case RPC_TIMEDOUT:
|
|
case RPC_PMAPFAILURE:
|
|
case RPC_PROGNOTREGISTERED:
|
|
errno = ETIMEDOUT;
|
|
break;
|
|
case RPC_AUTHERROR:
|
|
errno = EACCES;
|
|
break;
|
|
default:
|
|
errno = ERPC;
|
|
break;
|
|
}
|
|
}
|
|
if (errno) {
|
|
clnt_destroy(client);
|
|
if (mac_ip) {
|
|
ocap = cap_acquire (1, cap_mac_relabel_subj);
|
|
(void) mac_set_proc(save_plabel);
|
|
cap_surrender (ocap);
|
|
mac_free(save_plabel);
|
|
mac_free(mac_ip);
|
|
}
|
|
return(errno);
|
|
}
|
|
if (vers == MOUNTVERS3) {
|
|
/*
|
|
* Assume here that most of the MNT3ERR_*
|
|
* codes map into E* errors.
|
|
*/
|
|
if ((errno = mountres3.fhs_status) != MNT_OK) {
|
|
switch (errno) {
|
|
case MNT3ERR_NAMETOOLONG:
|
|
msg = "path name is too long";
|
|
break;
|
|
case MNT3ERR_NOTSUPP:
|
|
msg = "operation not supported";
|
|
break;
|
|
case MNT3ERR_SERVERFAULT:
|
|
msg = "server fault";
|
|
break;
|
|
default:
|
|
msg = NULL;
|
|
break;
|
|
}
|
|
if (msg)
|
|
printf("can't mount %s:%s: ", host, path);
|
|
else
|
|
perror("");
|
|
return (EINVAL);
|
|
}
|
|
}
|
|
|
|
errno = (fhs.fhs_status == OLDENFSREMOTE) ? ENFSREMOTE : fhs.fhs_status;
|
|
if (errno) {
|
|
if (errno == EACCES) {
|
|
(void) fprintf(stderr,
|
|
"mount: access denied for %s:%s\n", host, path);
|
|
} else {
|
|
(void) fprintf(stderr, "mount: ");
|
|
perror(mnt->mnt_fsname);
|
|
}
|
|
clnt_destroy(client);
|
|
if (mac_ip) {
|
|
ocap = cap_acquire (1, cap_mac_relabel_subj);
|
|
(void) mac_set_proc (save_plabel);
|
|
cap_surrender (ocap);
|
|
mac_free(save_plabel);
|
|
mac_free(mac_ip);
|
|
}
|
|
return (errno);
|
|
}
|
|
|
|
/*
|
|
* Get statvfs from remote mount point to set static values
|
|
* that aren't supported by the nfs protocol.
|
|
*/
|
|
if (prog == MOUNTPROG_SGI) {
|
|
struct mntrpc_statvfs mntstatvfs;
|
|
|
|
rpc_stat = clnt_call(client, MOUNTPROC_STATVFS, xdr_path, &path,
|
|
xdr_statvfs, &mntstatvfs, timeout);
|
|
if (rpc_stat == RPC_SUCCESS) {
|
|
args->flags |= NFSMNT_NAMEMAX;
|
|
args->namemax = mntstatvfs.f_namemax;
|
|
}
|
|
}
|
|
clnt_destroy(client);
|
|
if (mac_ip) {
|
|
ocap = cap_acquire (1, cap_mac_relabel_subj);
|
|
(void) mac_set_proc (save_plabel);
|
|
cap_surrender (ocap);
|
|
mac_free(save_plabel);
|
|
mac_free(mac_ip);
|
|
}
|
|
|
|
if (printed) {
|
|
(void) fprintf(stderr, "mount: %s server ok\n",
|
|
mnt->mnt_fsname);
|
|
printed = 0;
|
|
}
|
|
|
|
/*
|
|
* set mount args
|
|
*/
|
|
if (vers == MOUNTVERS3) {
|
|
args->fh_len = mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len;
|
|
args->fh =
|
|
(fhandle_t *)mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val;
|
|
} else {
|
|
args->fh = &fhs.fhs_fh;
|
|
}
|
|
args->hostname = host;
|
|
args->flags |= NFSMNT_HOSTNAME;
|
|
if (nopt(mnt, MNTOPT_RSIZE, &args->rsize)) {
|
|
args->flags |= NFSMNT_RSIZE;
|
|
}
|
|
if (nopt(mnt, MNTOPT_WSIZE, &args->wsize)) {
|
|
args->flags |= NFSMNT_WSIZE;
|
|
}
|
|
if (nopt(mnt, MNTOPT_TIMEO, &args->timeo)) {
|
|
args->flags |= NFSMNT_TIMEO;
|
|
}
|
|
if (nopt(mnt, MNTOPT_RETRANS, &args->retrans)) {
|
|
args->flags |= NFSMNT_RETRANS;
|
|
}
|
|
if (args->flags & NFSMNT_PRIVATE) {
|
|
args->acregmin = PRIV_ACREGMIN;
|
|
args->acregmax = PRIV_ACREGMAX;
|
|
args->acdirmin = PRIV_ACDIRMIN;
|
|
args->acdirmax = PRIV_ACDIRMAX;
|
|
args->flags |= NFSMNT_ACREGMIN | NFSMNT_ACREGMAX |
|
|
NFSMNT_ACDIRMIN | NFSMNT_ACDIRMAX;
|
|
}
|
|
if (nopt(mnt, MNTOPT_ACTIMEO, &args->acregmin)) {
|
|
args->acregmax = args->acregmin;
|
|
args->acdirmin = args->acregmin;
|
|
args->acdirmax = args->acregmin;
|
|
args->flags |= NFSMNT_ACREGMIN | NFSMNT_ACREGMAX |
|
|
NFSMNT_ACDIRMIN | NFSMNT_ACDIRMAX;
|
|
} else {
|
|
if (nopt(mnt, MNTOPT_ACREGMIN, &args->acregmin)) {
|
|
args->flags |= NFSMNT_ACREGMIN;
|
|
}
|
|
if (nopt(mnt, MNTOPT_ACREGMAX, &args->acregmax)) {
|
|
args->flags |= NFSMNT_ACREGMAX;
|
|
}
|
|
if (nopt(mnt, MNTOPT_ACDIRMIN, &args->acdirmin)) {
|
|
args->flags |= NFSMNT_ACDIRMIN;
|
|
}
|
|
if (nopt(mnt, MNTOPT_ACDIRMAX, &args->acdirmax)) {
|
|
args->flags |= NFSMNT_ACDIRMAX;
|
|
}
|
|
}
|
|
if (nopt(mnt, MNTOPT_PORT, &port)) {
|
|
sin.sin_port = htons(port);
|
|
} else {
|
|
sin.sin_port = htons(NFS_PORT); /* XXX should use portmapper */
|
|
}
|
|
if (ismntopt(mnt, MNTOPT_ASYNCNLM)) {
|
|
args->flags |= NFSMNT_ASYNCNLM;
|
|
}
|
|
|
|
proto = malloc(MNTMAXSTR);
|
|
if (nonnopt(mnt, MNTOPT_PROTO, proto)) {
|
|
if (!strncmp(proto,"tcp",3) || !strncmp(proto,"TCP",3)) {
|
|
args->flags |= NFSMNT_TCP;
|
|
#ifdef DEBUG
|
|
printf("Using TCP");
|
|
#endif
|
|
} else if (!strncmp(proto,"udp",3) || !strncmp(proto,"UDP",3))
|
|
;
|
|
else {
|
|
(void) fprintf(stderr, "mount: unrecognized protocol %s\n",proto);
|
|
free(proto);
|
|
return(EINVAL);
|
|
}
|
|
}
|
|
free(proto);
|
|
|
|
args->addr = &sin;
|
|
|
|
/*
|
|
* should clean up mnt ops to not contain defaults
|
|
*/
|
|
return (0);
|
|
}
|
|
|
|
|
|
#ifdef PCFS
|
|
mount_pc(struct mntent *mnt, struct pc_args *args)
|
|
{
|
|
args->fspec = mnt->mnt_fsname;
|
|
return (0);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
printent(struct mntent *mnt)
|
|
{
|
|
(void) fprintf(stdout, "%s on %s type %s (%s)\n",
|
|
mnt->mnt_fsname, mnt->mnt_dir, mnt->mnt_type, mnt->mnt_opts);
|
|
}
|
|
|
|
void
|
|
printmtab(FILE *outp)
|
|
{
|
|
FILE *mnttab;
|
|
struct mntent *mntp;
|
|
int maxfsname = 0;
|
|
int maxdir = 0;
|
|
int maxtype = 0;
|
|
|
|
/*
|
|
* first go through and find the max width of each field
|
|
*/
|
|
mnttab = setmntent(altmtab?altmtab:MOUNTED, "r");
|
|
while ((mntp = getmntent(mnttab)) != NULL) {
|
|
if (strcmp(mntp->mnt_type, MNTTYPE_IGNORE) == 0)
|
|
continue;
|
|
if (strlen(mntp->mnt_fsname) > maxfsname) {
|
|
maxfsname = strlen(mntp->mnt_fsname);
|
|
}
|
|
if (strlen(mntp->mnt_dir) > maxdir) {
|
|
maxdir = strlen(mntp->mnt_dir);
|
|
}
|
|
if (strlen(mntp->mnt_type) > maxtype) {
|
|
maxtype = strlen(mntp->mnt_type);
|
|
}
|
|
}
|
|
(void) endmntent(mnttab);
|
|
|
|
/*
|
|
* now print them out in pretty format
|
|
*/
|
|
if (rbase) maxdir += strlen(rbase);
|
|
mnttab = setmntent(altmtab?altmtab:MOUNTED, "r");
|
|
while ((mntp = getmntent(mnttab)) != NULL) {
|
|
if (strcmp(mntp->mnt_type, MNTTYPE_IGNORE) == 0)
|
|
continue;
|
|
if (strcmp(mntp->mnt_type, MNTTYPE_NFS) == 0) {
|
|
char *cp;
|
|
/* Note: assumes mountfs puts dev last */
|
|
if ((cp = strstr(mntp->mnt_opts, ",dev")) != NULL)
|
|
*cp = '\0';
|
|
}
|
|
|
|
(void) fprintf(outp, "%-*s", maxfsname+1, mntp->mnt_fsname);
|
|
{
|
|
char pathbuf[PATH_MAX];
|
|
pathbuf[0] = 0;
|
|
if (rbase && mntp->mnt_dir[0] == '/')
|
|
(void) strcpy (pathbuf, rbase);
|
|
if (!rbase || mntp->mnt_dir[1]) /* avoid "/root/" */
|
|
(void) strcat (pathbuf, mntp->mnt_dir);
|
|
(void) fprintf(outp, "%-*s", maxdir+1, pathbuf);
|
|
}
|
|
(void) fprintf(outp, "%-*s", maxtype+1, mntp->mnt_type);
|
|
(void) fprintf(outp, "%-s %d %d\n",
|
|
mntp->mnt_opts, mntp->mnt_freq, mntp->mnt_passno);
|
|
}
|
|
(void) endmntent(mnttab);
|
|
}
|
|
|
|
/*
|
|
* Check to see if mntck is already mounted.
|
|
* We have to be careful because getmntent modifies its static struct.
|
|
*/
|
|
mounted(struct mntent *mntck)
|
|
{
|
|
int found = 0;
|
|
struct mntent *mnt, mntsave;
|
|
FILE *mnttab;
|
|
|
|
if (nomtab) {
|
|
return (0);
|
|
}
|
|
mnttab = setmntent(MOUNTED, "r");
|
|
if (mnttab == NULL) {
|
|
(void) fprintf(stderr, "mount: ");
|
|
perror(MOUNTED);
|
|
exit(1);
|
|
}
|
|
mntcp(mntck, &mntsave);
|
|
while ((mnt = getmntent(mnttab)) != NULL) {
|
|
if (strcmp(mnt->mnt_type, MNTTYPE_IGNORE) == 0) {
|
|
continue;
|
|
}
|
|
if ((strcmp(mntsave.mnt_fsname, mnt->mnt_fsname) == 0) &&
|
|
(strcmp(mntsave.mnt_dir, mnt->mnt_dir) == 0)) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
(void) endmntent(mnttab);
|
|
*mntck = mntsave;
|
|
return (found);
|
|
}
|
|
|
|
void
|
|
mntcp(struct mntent *mnt1, struct mntent *mnt2)
|
|
{
|
|
static char fsname[128], dir[128], type[128], opts[128];
|
|
|
|
mnt2->mnt_fsname = fsname;
|
|
(void) strcpy(fsname, mnt1->mnt_fsname);
|
|
mnt2->mnt_dir = dir;
|
|
(void) strcpy(dir, mnt1->mnt_dir);
|
|
mnt2->mnt_type = type;
|
|
(void) strcpy(type, mnt1->mnt_type);
|
|
mnt2->mnt_opts = opts;
|
|
(void) strcpy(opts, mnt1->mnt_opts);
|
|
mnt2->mnt_freq = mnt1->mnt_freq;
|
|
mnt2->mnt_passno = mnt1->mnt_passno;
|
|
}
|
|
|
|
/*
|
|
* Return the value of a non-numeric option of the form foo=string, if
|
|
* option is not found or is malformed, return 0.
|
|
*/
|
|
nonnopt(struct mntent *mnt, char *opt, char *cpy)
|
|
{
|
|
char *str;
|
|
char *equal;
|
|
|
|
str = ismntopt(mnt, opt);
|
|
if (str == NULL)
|
|
return (0);
|
|
equal = str + strlen(opt);
|
|
if (*equal != '=') {
|
|
(void) fprintf(stderr,
|
|
"mount: missing value for option '%s'\n", str);
|
|
return (0);
|
|
}
|
|
if (!sscanf(equal + 1, "%s", cpy)) {
|
|
(void) fprintf(stderr,
|
|
"mount: illegal value for option '%s'\n", str);
|
|
return (0);
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* Return the value of a numeric option of the form foo=x, if
|
|
* option is not found or is malformed, return 0.
|
|
*/
|
|
nopt(struct mntent *mnt, char *opt, u_int *val)
|
|
{
|
|
char *str;
|
|
char *equal;
|
|
|
|
str = ismntopt(mnt, opt);
|
|
if (str == NULL)
|
|
return (0);
|
|
equal = str + strlen(opt);
|
|
if (*equal != '=') {
|
|
(void) fprintf(stderr,
|
|
"mount: missing value for option '%s'\n", str);
|
|
return (0);
|
|
}
|
|
if (!sscanf(equal + 1, "%u", val)) {
|
|
(void) fprintf(stderr,
|
|
"mount: illegal value for option '%s'\n", str);
|
|
return (0);
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* update /etc/mtab
|
|
*/
|
|
void
|
|
addtomtab(struct mntent *mnt)
|
|
{
|
|
FILE *mnted;
|
|
struct mntent mget;
|
|
struct mntent mref;
|
|
int i;
|
|
cap_t ocap;
|
|
cap_value_t cap_mac_write = CAP_MAC_WRITE;
|
|
|
|
if (nomtab) {
|
|
return;
|
|
}
|
|
ocap = cap_acquire (1, &cap_mac_write);
|
|
mnted = setmntent(MOUNTED, "r+");
|
|
cap_surrender (ocap);
|
|
if (mnted == NULL) {
|
|
(void) fprintf(stderr, "mount: ");
|
|
perror(MOUNTED);
|
|
exit(1);
|
|
}
|
|
/*
|
|
* add the mount table entry if it is not already there
|
|
* use getmntany to check for the presence of a mount table entry
|
|
* check using only the mount point name and the FS type
|
|
*/
|
|
bzero( &mref, sizeof( struct mntent ) );
|
|
mref.mnt_dir = mnt->mnt_dir;
|
|
/*
|
|
* eliminate trailing blanks
|
|
*/
|
|
for (i = strlen(mnt->mnt_dir) - 1; (i >= 0) && (mnt->mnt_dir[i] == ' ');
|
|
i--) {
|
|
mnt->mnt_dir[i] = 0;
|
|
}
|
|
mref.mnt_type = mnt->mnt_type;
|
|
if ((getmntany(mnted, &mget, &mref) == -1) && addmntent(mnted, mnt)) {
|
|
(void) fprintf(stderr, "mount: ");
|
|
perror(MOUNTED);
|
|
exit(1);
|
|
}
|
|
(void) endmntent(mnted);
|
|
|
|
if (verbose) {
|
|
(void) fprintf(stdout, "%s mounted on %s\n",
|
|
mnt->mnt_fsname, mnt->mnt_dir);
|
|
}
|
|
}
|
|
|
|
void *
|
|
xmalloc(int size)
|
|
{
|
|
char *ret;
|
|
|
|
if ((ret = (char *)malloc((unsigned)size)) == NULL) {
|
|
(void) fprintf(stderr, "umount: ran out of memory!\n");
|
|
exit(1);
|
|
}
|
|
return (ret);
|
|
}
|
|
|
|
struct mntent *
|
|
mntdup(struct mntent *mnt)
|
|
{
|
|
struct mntent *new;
|
|
|
|
new = (struct mntent *)xmalloc(sizeof(*new));
|
|
|
|
new->mnt_fsname = (char *)xmalloc(strlen(mnt->mnt_fsname) + 1);
|
|
(void) strcpy(new->mnt_fsname, mnt->mnt_fsname);
|
|
|
|
new->mnt_dir = (char *)xmalloc(strlen(mnt->mnt_dir) + 1);
|
|
(void) strcpy(new->mnt_dir, mnt->mnt_dir);
|
|
|
|
new->mnt_type = (char *)xmalloc(strlen(mnt->mnt_type) + 1);
|
|
(void) strcpy(new->mnt_type, mnt->mnt_type);
|
|
|
|
new->mnt_opts = (char *)xmalloc(strlen(mnt->mnt_opts) + 1);
|
|
(void) strcpy(new->mnt_opts, mnt->mnt_opts);
|
|
|
|
new->mnt_freq = mnt->mnt_freq;
|
|
new->mnt_passno = mnt->mnt_passno;
|
|
|
|
return (new);
|
|
}
|
|
|
|
/*
|
|
* Build the mount dependency tree. All mount ordering dependencies are
|
|
* expressed as parent child relationships in the tree.
|
|
*/
|
|
struct mnttree *
|
|
maketree(struct mnttree *mt, struct mntent *mnt, struct mnttree *nmt)
|
|
{
|
|
struct mnttree *lmt;
|
|
struct mnttree *next_mt;
|
|
|
|
if (nmt == NULL) {
|
|
nmt = (struct mnttree *)
|
|
xmalloc(sizeof (struct mnttree));
|
|
nmt->mt_mnt = mntdup(mnt);
|
|
nmt->mt_sib = NULL;
|
|
nmt->mt_sib_back = NULL;
|
|
nmt->mt_kid = NULL;
|
|
}
|
|
|
|
/*
|
|
* If this is the first entry being inserted in the tree,
|
|
* just return the pointer to it.
|
|
*/
|
|
if (mt == NULL) {
|
|
nmt->mt_sib = NULL;
|
|
nmt->mt_sib_back = NULL;
|
|
nmt->mt_kid = NULL;
|
|
return nmt;
|
|
}
|
|
|
|
/*
|
|
* Always add a new entry at a level as the first entry in the
|
|
* level.
|
|
*/
|
|
lmt = mt;
|
|
while (lmt != NULL) {
|
|
next_mt = lmt->mt_sib;
|
|
/*
|
|
* If the entry is a substring of the new entry, add
|
|
* the new entry as a child of the current one.
|
|
*/
|
|
if (substr(lmt->mt_mnt->mnt_dir, mnt->mnt_dir)) {
|
|
lmt->mt_kid = maketree(lmt->mt_kid, mnt, nmt);
|
|
return mt;
|
|
}
|
|
|
|
/*
|
|
* If the current entry is a substring of the new entry,
|
|
* pull it from the tree and add it back as a child of
|
|
* the new entry. We know that the new entry will be
|
|
* inserted at this level, because otherwise the one
|
|
* we are now removing would have been a child of whatever
|
|
* the new entry would become a child of.
|
|
*/
|
|
if (substr(mnt->mnt_dir, lmt->mt_mnt->mnt_dir)) {
|
|
/*
|
|
* Keep mt pointing to the head of this level.
|
|
*/
|
|
if (lmt == mt) {
|
|
mt = lmt->mt_sib;
|
|
}
|
|
/*
|
|
* Fix up the forward and backward pointers of
|
|
* our siblings.
|
|
*/
|
|
if (lmt->mt_sib != NULL) {
|
|
lmt->mt_sib->mt_sib_back = lmt->mt_sib_back;
|
|
}
|
|
if (lmt->mt_sib_back != NULL) {
|
|
lmt->mt_sib_back->mt_sib = lmt->mt_sib;
|
|
}
|
|
/*
|
|
* Insert lmt as a child of nmt.
|
|
*/
|
|
nmt->mt_kid = maketree(nmt->mt_kid, lmt->mt_mnt, lmt);
|
|
}
|
|
lmt = next_mt;
|
|
}
|
|
nmt->mt_sib = mt;
|
|
if (mt != NULL) {
|
|
mt->mt_sib_back = nmt;
|
|
}
|
|
nmt->mt_sib_back = NULL;
|
|
return (nmt);
|
|
}
|
|
|
|
void
|
|
printtree(struct mnttree *mt)
|
|
{
|
|
if (mt) {
|
|
printtree(mt->mt_sib);
|
|
(void) printf(" %s\n", mt->mt_mnt->mnt_dir);
|
|
printtree(mt->mt_kid);
|
|
}
|
|
}
|
|
|
|
int keepconsoleclosed = 0;
|
|
char consolepath[] = "/dev/console";
|
|
|
|
void
|
|
background()
|
|
{
|
|
struct stat ss, cs;
|
|
|
|
/* ignore hup so init can't kill us */
|
|
(void) signal(SIGHUP, SIG_IGN);
|
|
|
|
/* now we can afford to wait a while for NFS retries */
|
|
set_long_timeouts();
|
|
|
|
/*
|
|
* Check whether stdout and stderr are the console. Unfortunately,
|
|
* the window management system can't cope with processes holding the
|
|
* old console open when it switches to a new console pseudodevice.
|
|
*/
|
|
keepconsoleclosed =
|
|
fstat(fileno(stdout), &ss) == 0
|
|
&& stat(consolepath, &cs) == 0
|
|
&& ss.st_dev == cs.st_dev
|
|
&& ss.st_ino == cs.st_ino;
|
|
if (keepconsoleclosed) {
|
|
register int d = getdtablesize();
|
|
|
|
while (--d > 2)
|
|
(void) close(d);
|
|
(void) freopen("/dev/null", "r", stdin);
|
|
d = open("/dev/tty", 2);
|
|
(void) ioctl(d, TIOCNOTTY, 0);
|
|
(void) close(d);
|
|
(void) fclose(stdout);
|
|
(void) fclose(stderr);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This is the counter used to synchronize with our child processes
|
|
* during a parallel mount.
|
|
*/
|
|
volatile int mount_procs;
|
|
|
|
void
|
|
mount_proc_done(void)
|
|
{
|
|
pid_t pid;
|
|
int status;
|
|
struct mnttree *lmt;
|
|
int subtree_error;
|
|
|
|
/*
|
|
* Reap the child proc.
|
|
*/
|
|
pid = wait(&status);
|
|
|
|
/*
|
|
* The child processes return errors via their exit status.
|
|
* If a child process fails, set the global mounttree_error
|
|
* so that we can return a proper error to the user.
|
|
*/
|
|
subtree_error = WEXITSTATUS(status);
|
|
if (subtree_error) {
|
|
mounttree_error = subtree_error;
|
|
}
|
|
|
|
mount_procs--;
|
|
|
|
sigset(SIGCLD, mount_proc_done);
|
|
|
|
}
|
|
|
|
/*
|
|
* Mount the file systems described by the given mount dependency
|
|
* tree. Make sure to mount all parents before their children.
|
|
*
|
|
* If there are a sufficient number of mounts to warrant it, we
|
|
* do the mounts in parallel. To do this we fork a process for
|
|
* each entry at each level. If there is only 1 mount point at
|
|
* a given level, then we don't bother to fork. These are
|
|
* dependency levels, not directory levels.
|
|
*
|
|
* This routine is called recursively from mountsubtree(). It is
|
|
* always passed a pointer to the first element in a list of tree
|
|
* entries to be mounted. The elements in the list do not depend
|
|
* on each other, so they can be processed in parallel.
|
|
*
|
|
* mounttree_error is used to store any errors encountered
|
|
* during the mount process. It is a global variable so
|
|
* that we can set it from the child process cleanup signal
|
|
* handler when the child process exits with an error.
|
|
*/
|
|
int
|
|
mounttree(struct mnttree *mt)
|
|
{
|
|
struct mnttree *lmt; /* local 'mt' pointer */
|
|
pid_t forkret;
|
|
int subtree_error;
|
|
|
|
if (mt == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if ((!parallel_mounts) || (mt->mt_sib == NULL)) {
|
|
/*
|
|
* Do it serially if there are not enough total
|
|
* mounts to bother with parallelism or if there
|
|
* is only one mount point at this level.
|
|
*/
|
|
for (lmt = mt; lmt != NULL; lmt = lmt->mt_sib) {
|
|
subtree_error = mountsubtree(lmt);
|
|
if (subtree_error) {
|
|
mounttree_error = subtree_error;
|
|
}
|
|
}
|
|
} else {
|
|
sigset(SIGCLD, mount_proc_done);
|
|
sighold(SIGCLD);
|
|
mount_procs = 0;
|
|
for (lmt = mt; lmt != NULL; lmt = lmt->mt_sib) {
|
|
forkret = fork();
|
|
if (forkret == 0) {
|
|
/*
|
|
* Mount the subtree in the child process.
|
|
*/
|
|
sigset(SIGCLD, SIG_DFL);
|
|
sigrelse(SIGCLD);
|
|
subtree_error = mountsubtree(lmt);
|
|
exit(subtree_error);
|
|
} else if (forkret > 0) {
|
|
/*
|
|
* Keep count of the children at this level.
|
|
* If we have too many children at this
|
|
* level, then wait for some to complete
|
|
* before creating more of them.
|
|
*/
|
|
mount_procs++;
|
|
while (mount_procs > mount_proc_limit) {
|
|
sigpause(SIGCLD);
|
|
sighold(SIGCLD);
|
|
}
|
|
} else {
|
|
/*
|
|
* The fork failed. Break out of the loop
|
|
* and process the rest of the tree in this
|
|
* proc.
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
/*
|
|
* Wait for the processes that we've started to finish.
|
|
*/
|
|
while (mount_procs > 0) {
|
|
sigpause(SIGCLD);
|
|
sighold(SIGCLD);
|
|
}
|
|
/*
|
|
* If one of the forks above failed, then there may be
|
|
* more mounts to do at this level. Do those now
|
|
* serially. Waiting until after the forked off procs
|
|
* have finished simplifies the process accounting since
|
|
* we don't get a mix of forked and non-forked processing.
|
|
*/
|
|
sigset(SIGCLD, SIG_DFL);
|
|
sigrelse(SIGCLD);
|
|
if (lmt != NULL) {
|
|
mt = lmt;
|
|
parallel_mounts = 0;
|
|
for (lmt = mt; lmt != NULL; lmt = lmt->mt_sib) {
|
|
subtree_error = mountsubtree(lmt);
|
|
if (subtree_error) {
|
|
mounttree_error = subtree_error;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return mounttree_error;
|
|
}
|
|
|
|
int
|
|
mountsubtree(struct mnttree *mt)
|
|
{
|
|
int error;
|
|
int slptime;
|
|
int forked;
|
|
int retry;
|
|
int firsttry;
|
|
int status = 0;
|
|
cap_t ocap;
|
|
cap_value_t cap_audit_write = CAP_AUDIT_WRITE;
|
|
|
|
forked = 0;
|
|
printed = 0;
|
|
firsttry = 1;
|
|
slptime = BGSLEEP;
|
|
if (!nopt(mt->mt_mnt, "retry", (uint *)&retry)) {
|
|
retry = NRETRY;
|
|
}
|
|
if (ismntopt(mt->mt_mnt, "bg")) {
|
|
set_short_timeouts();
|
|
} else {
|
|
set_long_timeouts();
|
|
}
|
|
|
|
do {
|
|
error = mountfs(!forked, mt->mt_mnt);
|
|
if (error != ETIMEDOUT && error != ENETDOWN &&
|
|
error != ENETUNREACH && error != ENOBUFS &&
|
|
error != ECONNREFUSED && error != ECONNABORTED) {
|
|
break;
|
|
}
|
|
/*
|
|
* mount failed due to network problems, reset options
|
|
* string and retry (maybe)
|
|
*/
|
|
if (!forked && ismntopt(mt->mt_mnt, "bg")) {
|
|
(void) fprintf(stderr,
|
|
"mount: backgrounding\n");
|
|
(void) fprintf(stderr, " %s\n",
|
|
mt->mt_mnt->mnt_dir);
|
|
printtree(mt->mt_kid);
|
|
if (fork())
|
|
return 0;
|
|
background();
|
|
forked = 1;
|
|
}
|
|
/* don't print retrying if we aren't */
|
|
if (!forked && firsttry && retry) {
|
|
(void) fprintf(stderr, "mount: retrying\n");
|
|
(void) fprintf(stderr, " %s\n",
|
|
mt->mt_mnt->mnt_dir);
|
|
printtree(mt->mt_kid);
|
|
firsttry = 0;
|
|
}
|
|
if (keepconsoleclosed) {
|
|
(void) fclose(stdout);
|
|
(void) fclose(stderr);
|
|
}
|
|
sleep(slptime);
|
|
slptime = MIN(slptime << 1, MAXSLEEP);
|
|
if (keepconsoleclosed) {
|
|
(void) freopen(consolepath, "w", stdout);
|
|
(void) freopen(consolepath, "w", stderr);
|
|
}
|
|
} while (--retry >= 0);
|
|
|
|
/* audit the mount (does nothing if no auditing) */
|
|
ocap = cap_acquire(1, &cap_audit_write);
|
|
if (error)
|
|
satvwrite(SAT_AE_MOUNT, SAT_FAILURE,
|
|
"mount: failure to mount %s on %s: %s",
|
|
mt->mt_mnt->mnt_fsname, mt->mt_mnt->mnt_dir,
|
|
strerror(error));
|
|
else
|
|
satvwrite(SAT_AE_MOUNT, SAT_SUCCESS,
|
|
"mount: mounted %s on %s", mt->mt_mnt->mnt_fsname,
|
|
mt->mt_mnt->mnt_dir);
|
|
cap_surrender(ocap);
|
|
|
|
if (!error) {
|
|
error = mounttree(mt->mt_kid);
|
|
if (error) {
|
|
status = error;
|
|
}
|
|
} else {
|
|
status = error;
|
|
(void) fprintf(stderr, "mount: giving up on:\n");
|
|
(void) fprintf(stderr, " %s\n", mt->mt_mnt->mnt_dir);
|
|
printtree(mt->mt_kid);
|
|
}
|
|
if (forked) {
|
|
exit(0);
|
|
}
|
|
return (status);
|
|
}
|
|
|
|
/*
|
|
* Returns true if s1 is a pathname substring of s2.
|
|
*/
|
|
substr(char *s1, char *s2)
|
|
{
|
|
while (*s1 == *s2) {
|
|
s1++;
|
|
s2++;
|
|
}
|
|
if (*s1 == '\0' && *s2 == '/') {
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
usage()
|
|
{
|
|
fprintf(stdout,
|
|
"Usage: mount [-acfnrpv [-b list] [-h host] [-i altfstab] [-o option] [-t|F type] [-T type1,type2...] [-m numprocs (1 to 128)] [-M altmtab] [-P prefix] ... [fsname] [dir]]\n");
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/*
|
|
* Returns the next option in the option string.
|
|
*/
|
|
char *
|
|
getnextopt(char **p)
|
|
{
|
|
char *cp = *p;
|
|
char *retstr;
|
|
|
|
while (*cp && isspace(*cp))
|
|
cp++;
|
|
retstr = cp;
|
|
while (*cp && *cp != ',')
|
|
cp++;
|
|
if (*cp) {
|
|
*cp = '\0';
|
|
cp++;
|
|
}
|
|
*p = cp;
|
|
return (retstr);
|
|
}
|
|
|
|
/*
|
|
* "trueopt" and "falseopt" are two settings of a Boolean option.
|
|
* If "flag" is true, forcibly set the option to the "true" setting; otherwise,
|
|
* if the option isn't present, set it to the false setting.
|
|
*/
|
|
void
|
|
replace_opts(char *options, int flag, char *trueopt, char *falseopt)
|
|
{
|
|
register char *f;
|
|
char tmptopts[MNTMAXSTR];
|
|
char *tmpoptsp;
|
|
register int found;
|
|
|
|
(void) strcpy(tmptopts, options);
|
|
tmpoptsp = tmptopts;
|
|
(void) strcpy(options, "");
|
|
|
|
found = 0;
|
|
for (f = getnextopt(&tmpoptsp); *f; f = getnextopt(&tmpoptsp)) {
|
|
if (strcmp(f, trueopt) == 0) {
|
|
if (options[0] != '\0')
|
|
(void) strcat(options, ",");
|
|
(void) strcat(options, f);
|
|
found++;
|
|
} else if (falseopt && strcmp(f, falseopt) == 0) {
|
|
if (options[0] != '\0')
|
|
(void) strcat(options, ",");
|
|
if (flag)
|
|
(void) strcat(options, trueopt);
|
|
else
|
|
(void) strcat(options, f);
|
|
found++;
|
|
} else {
|
|
if (options[0] != '\0')
|
|
(void) strcat(options, ",");
|
|
(void) strcat(options, f);
|
|
}
|
|
}
|
|
if (!found) {
|
|
if ( flag ) {
|
|
if (options[0] != '\0')
|
|
(void) strcat(options, ",");
|
|
(void) strcat(options, trueopt);
|
|
} else if ( falseopt ) {
|
|
if (options[0] != '\0')
|
|
(void) strcat(options, ",");
|
|
(void) strcat(options, falseopt);
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
adjustnfsargs(char *type, struct mntent *mntp)
|
|
{
|
|
|
|
u_int vers = 0;
|
|
char *origp, origtype[MNTMAXSTR];
|
|
|
|
if (type)
|
|
strcpy(origtype, type);
|
|
else
|
|
strcpy(origtype, mntp->mnt_type);
|
|
|
|
/*
|
|
* look for vers options and adjust type :
|
|
* vers=2 ==> MNTTYPE_NFS2
|
|
* vers=3 ==> MNTTYPE_NFS3
|
|
* no vers ==> MNTTYPE_NFS3_PREF
|
|
* invalid vers ==> error
|
|
* vers & MNTTYPE mismatch ==> error
|
|
*/
|
|
if (nopt(mntp, MNTOPT_VERS, &vers)) {
|
|
switch (vers) {
|
|
case 2:
|
|
mntp->mnt_type = MNTTYPE_NFS2;
|
|
break;
|
|
case 3:
|
|
mntp->mnt_type = MNTTYPE_NFS3;
|
|
break;
|
|
default:
|
|
(void) fprintf (stderr,
|
|
"mount: invalid option vers=%d\n",
|
|
vers);
|
|
return (1);
|
|
}
|
|
|
|
} else if (type) {
|
|
mntp->mnt_type = type;
|
|
}
|
|
|
|
/*
|
|
* if origtype is nfs, then any valid vers= option is fine.
|
|
* if origtype is not (strcmp return != 0), then if the
|
|
* vers options changed the type we have a mismatch.
|
|
*/
|
|
|
|
if (strcmp(origtype, MNTTYPE_NFS) && vers && strcmp(origtype, mntp->mnt_type)) {
|
|
(void) fprintf (stderr,
|
|
"mount: mismatched options 'vers=%d' and '-t %s'\n",
|
|
vers, origtype);
|
|
return (1);
|
|
}
|
|
|
|
if (strcmp(mntp->mnt_type,MNTTYPE_NFS) == 0) {
|
|
mntp->mnt_type = MNTTYPE_NFS3_PREF;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|