#include <ctype.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <poll.h>
#include <sys/mkdev.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/mntent.h>
#include <stdlib.h>
#define bcopy(f, t, n) memcpy(t, f, n)
#define bzero(s, n) memset(s, 0, n)
#define bcmp(s, d, n) memcmp(s, d, n)
#define index(s, r) strchr(s, r)
#define rindex(s, r) strrchr(s, r)
#include <errno.h>
#include <sys/vfs.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mnttab.h>
#include <sys/mount.h>
#include <sys/mntio.h>
#include <sys/wait.h>
#include <sys/fstyp.h>
#include <sys/fsid.h>
#include <sys/vfstab.h>
#include <sys/filio.h>
#include <sys/fs/ufs_fs.h>
#include <sys/fs/ufs_mount.h>
#include <sys/fs/ufs_filio.h>
#include <locale.h>
#include <fslib.h>
static int ro = 0;
static int largefiles = 0;
static int gflg = 0;
static int mflg = 0;
static int Oflg = 0;
static int qflg = 0;
#define NAME_MAX 64
static int checkislog(char *);
static void disable_logging(char *, char *);
static int eatmntopt(struct mnttab *, char *);
static void enable_logging(char *, char *);
static void fixopts(struct mnttab *, char *);
static void mountfs(struct mnttab *);
static void replace_opts(char *, int, char *, char *);
static int replace_opts_dflt(char *, int, const char *, const char *);
static void rmopt(struct mnttab *, char *);
static void rpterr(char *, char *);
static void usage(void);
static char fstype[] = MNTTYPE_UFS;
static char opts[MAX_MNTOPT_STR];
static char typename[NAME_MAX], *myname;
static char *fop_subopts[] = { MNTOPT_ONERROR, NULL };
#define NOMATCH (-1)
#define ONERROR (0)
static struct fop_subopt {
char *str;
int flag;
} fop_subopt_list[] = {
{ UFSMNT_ONERROR_PANIC_STR, UFSMNT_ONERROR_PANIC },
{ UFSMNT_ONERROR_LOCK_STR, UFSMNT_ONERROR_LOCK },
{ UFSMNT_ONERROR_UMOUNT_STR, UFSMNT_ONERROR_UMOUNT },
{ NULL, UFSMNT_ONERROR_DEFAULT }
};
static boolean_t
in_mnttab(char *mountp)
{
FILE *file;
int found = B_FALSE;
struct mnttab mntent;
if ((file = fopen(MNTTAB, "r")) == NULL)
return (B_FALSE);
while (getmntent(file, &mntent) == 0) {
if (mntent.mnt_mountp != NULL &&
strcmp(mntent.mnt_mountp, mountp) == 0 &&
mntent.mnt_fstype != NULL &&
strcmp(mntent.mnt_fstype, MNTTYPE_UFS) == 0) {
found = B_TRUE;
break;
}
}
(void) fclose(file);
return (found);
}
static char *
findopt(char *mntopt, char *opt)
{
int nc, optlen = strlen(opt);
while (*mntopt) {
nc = strcspn(mntopt, ", =");
if (strncmp(mntopt, opt, nc) == 0)
if (optlen == nc)
return (mntopt);
mntopt += nc;
mntopt += strspn(mntopt, ", =");
}
return (NULL);
}
int
main(int argc, char *argv[])
{
struct mnttab mnt;
int c;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
myname = strrchr(argv[0], '/');
if (myname)
myname++;
else
myname = argv[0];
(void) snprintf(typename, sizeof (typename), "%s %s", fstype, myname);
argv[0] = typename;
opts[0] = '\0';
while ((c = getopt(argc, argv, "gmo:pqrVO")) != EOF) {
switch (c) {
case 'g':
gflg++;
break;
case 'o':
if (strlcpy(opts, optarg, sizeof (opts)) >=
sizeof (opts)) {
(void) fprintf(stderr, gettext("option string "
"argument too long\n"));
}
break;
case 'O':
Oflg++;
break;
case 'r':
ro++;
break;
case 'm':
mflg++;
break;
case 'q':
qflg++;
break;
default:
usage();
}
}
if ((argc - optind) != 2)
usage();
mnt.mnt_special = argv[optind];
mnt.mnt_mountp = argv[optind+1];
mnt.mnt_fstype = fstype;
mnt.mnt_mntopts = opts;
if (findopt(mnt.mnt_mntopts, "m"))
mflg++;
if ((gflg || findopt(mnt.mnt_mntopts, MNTOPT_GLOBAL)) &&
findopt(mnt.mnt_mntopts, MNTOPT_NBMAND)) {
(void) fprintf(stderr, gettext("NBMAND option not supported on"
" global filesystem\n"));
exit(32);
}
replace_opts(opts, ro, MNTOPT_RO, MNTOPT_RW);
replace_opts(opts, largefiles, MNTOPT_NOLARGEFILES, MNTOPT_LARGEFILES);
gflg = replace_opts_dflt(opts, gflg, MNTOPT_GLOBAL, MNTOPT_NOGLOBAL);
if (findopt(mnt.mnt_mntopts, MNTOPT_RQ)) {
rmopt(&mnt, MNTOPT_RQ);
replace_opts(opts, 1, MNTOPT_QUOTA, MNTOPT_NOQUOTA);
}
mountfs(&mnt);
return (0);
}
static void
reportlogerror(int ret, char *mp, char *special, char *cmd, fiolog_t *flp)
{
if ((ret != -1) && (flp->error == FIOLOG_ENONE))
return;
if (ret == -1 || flp->error != FIOLOG_ENONE)
(void) fprintf(stderr, gettext("Could not %s logging"
" for %s on %s.\n"), cmd, mp, special);
if (ret == -1)
return;
switch (flp->error) {
case FIOLOG_ENONE :
if (flp->nbytes_requested &&
(flp->nbytes_requested != flp->nbytes_actual)) {
(void) fprintf(stderr, gettext("The log has been"
" resized from %d bytes to %d bytes.\n"),
flp->nbytes_requested,
flp->nbytes_actual);
}
return;
case FIOLOG_ETRANS :
(void) fprintf(stderr, gettext("Solaris Volume Manager logging"
" is already enabled.\n"));
(void) fprintf(stderr, gettext("Please see the"
" commands metadetach(8)"
" or metaclear(8).\n"));
break;
case FIOLOG_EROFS :
(void) fprintf(stderr, gettext("File system is mounted read "
"only.\n"));
(void) fprintf(stderr, gettext("Please see the remount "
"option described in mount_ufs(8).\n"));
break;
case FIOLOG_EULOCK :
(void) fprintf(stderr, gettext("File system is locked.\n"));
(void) fprintf(stderr, gettext("Please see the -u option "
"described in lockfs(8).\n"));
break;
case FIOLOG_EWLOCK :
(void) fprintf(stderr, gettext("The file system could not be"
" write locked.\n"));
(void) fprintf(stderr, gettext("Please see the -w option "
"described in lockfs(8).\n"));
break;
case FIOLOG_ECLEAN :
(void) fprintf(stderr, gettext("The file system may not be"
" stable.\n"));
(void) fprintf(stderr, gettext("Please see the -n option"
" for fsck(8).\n"));
break;
case FIOLOG_ENOULOCK :
(void) fprintf(stderr, gettext("The file system could not be"
" unlocked.\n"));
(void) fprintf(stderr, gettext("Please see the -u option "
"described in lockfs(8).\n"));
break;
default :
(void) fprintf(stderr, gettext("Unknown internal error"
" %d.\n"), flp->error);
break;
}
}
static int
checkislog(char *mp)
{
int fd;
uint32_t islog;
fd = open(mp, O_RDONLY);
islog = 0;
(void) ioctl(fd, _FIOISLOG, &islog);
(void) close(fd);
return ((int)islog);
}
static void
enable_logging(char *mp, char *special)
{
int fd, ret, islog;
fiolog_t fl;
fd = open(mp, O_RDONLY);
if (fd == -1) {
perror(mp);
return;
}
fl.nbytes_requested = 0;
fl.nbytes_actual = 0;
fl.error = FIOLOG_ENONE;
ret = ioctl(fd, _FIOLOGENABLE, &fl);
if (ret == -1)
perror(mp);
(void) close(fd);
islog = checkislog(mp);
if (ret == -1 || !islog)
reportlogerror(ret, mp, special, "enable", &fl);
}
static void
disable_logging(char *mp, char *special)
{
int fd, ret, islog;
fiolog_t fl;
fd = open(mp, O_RDONLY);
if (fd == -1) {
perror(mp);
return;
}
fl.error = FIOLOG_ENONE;
ret = ioctl(fd, _FIOLOGDISABLE, &fl);
if (ret == -1)
perror(mp);
(void) close(fd);
islog = checkislog(mp);
if (ret == -1 || islog)
reportlogerror(ret, mp, special, "disable", &fl);
}
void
mountfs(struct mnttab *mnt)
{
char opt[MAX_MNTOPT_STR];
char opt2[MAX_MNTOPT_STR];
char *opts = opt;
int flags = MS_OPTIONSTR;
struct ufs_args args;
int need_separator = 0;
int mount_attempts = 5;
(void) bzero((char *)&args, sizeof (args));
(void) strcpy(opts, mnt->mnt_mntopts);
opt2[0] = '\0';
flags |= Oflg ? MS_OVERLAY : 0;
flags |= eatmntopt(mnt, MNTOPT_RO) ? MS_RDONLY : 0;
flags |= eatmntopt(mnt, MNTOPT_REMOUNT) ? MS_REMOUNT : 0;
flags |= eatmntopt(mnt, MNTOPT_GLOBAL) ? MS_GLOBAL : 0;
if (eatmntopt(mnt, MNTOPT_NOINTR))
args.flags |= UFSMNT_NOINTR;
if (eatmntopt(mnt, MNTOPT_INTR))
args.flags &= ~UFSMNT_NOINTR;
if (eatmntopt(mnt, MNTOPT_SYNCDIR))
args.flags |= UFSMNT_SYNCDIR;
if (eatmntopt(mnt, MNTOPT_FORCEDIRECTIO)) {
args.flags |= UFSMNT_FORCEDIRECTIO;
args.flags &= ~UFSMNT_NOFORCEDIRECTIO;
}
if (eatmntopt(mnt, MNTOPT_NOFORCEDIRECTIO)) {
args.flags |= UFSMNT_NOFORCEDIRECTIO;
args.flags &= ~UFSMNT_FORCEDIRECTIO;
}
if (eatmntopt(mnt, MNTOPT_NOSETSEC))
args.flags |= UFSMNT_NOSETSEC;
if (eatmntopt(mnt, MNTOPT_LARGEFILES))
args.flags |= UFSMNT_LARGEFILES;
if (eatmntopt(mnt, MNTOPT_NOLARGEFILES))
args.flags &= ~UFSMNT_LARGEFILES;
args.flags |= UFSMNT_LOGGING;
(void) eatmntopt(mnt, MNTOPT_LOGGING);
if (eatmntopt(mnt, MNTOPT_NOLOGGING))
args.flags &= ~UFSMNT_LOGGING;
if (eatmntopt(mnt, MNTOPT_NOATIME))
args.flags |= UFSMNT_NOATIME;
if (eatmntopt(mnt, MNTOPT_DFRATIME))
args.flags &= ~UFSMNT_NODFRATIME;
if (eatmntopt(mnt, MNTOPT_NODFRATIME))
args.flags |= UFSMNT_NODFRATIME;
while (*opts != '\0') {
char *argval;
switch (getsubopt(&opts, fop_subopts, &argval)) {
case ONERROR:
if (argval) {
struct fop_subopt *s;
int found = 0;
for (s = fop_subopt_list;
s->str && !found;
s++) {
if (strcmp(argval, s->str) == 0) {
args.flags |= s->flag;
found = 1;
}
}
if (!found) {
usage();
}
if (need_separator)
(void) strcat(opt2, ",");
(void) strcat(opt2, MNTOPT_ONERROR);
(void) strcat(opt2, "=");
(void) strcat(opt2, argval);
need_separator = 1;
} else {
args.flags |= UFSMNT_ONERROR_DEFAULT;
}
break;
case NOMATCH:
default:
if (argval) {
if (need_separator)
(void) strcat(opt2, ",");
(void) strcat(opt2, argval);
need_separator = 1;
}
break;
}
}
if (*opt2 != '\0')
(void) strcpy(opt, opt2);
opts = opt;
if ((args.flags & UFSMNT_ONERROR_FLGMASK) == 0)
args.flags |= UFSMNT_ONERROR_DEFAULT;
(void) signal(SIGHUP, SIG_IGN);
(void) signal(SIGQUIT, SIG_IGN);
(void) signal(SIGINT, SIG_IGN);
errno = 0;
flags |= MS_DATA | MS_OPTIONSTR;
if (mflg)
flags |= MS_NOMNTTAB;
if (flags & MS_REMOUNT) {
replace_opts(mnt->mnt_mntopts, 1, MNTOPT_RW, MNTOPT_RO);
}
fixopts(mnt, opts);
if (gflg || findopt(mnt->mnt_mntopts, MNTOPT_GLOBAL)) {
if (!(flags & MS_RDONLY)) {
if (mnt->mnt_mntopts[0] != '\0')
(void) strcat(mnt->mnt_mntopts, ",");
(void) strcat(mnt->mnt_mntopts, MNTOPT_LOGGING);
args.flags |= UFSMNT_LOGGING;
} else {
if (mnt->mnt_mntopts[0] != '\0')
(void) strcat(mnt->mnt_mntopts, ",");
(void) strcat(mnt->mnt_mntopts, MNTOPT_NOLOGGING);
args.flags &= ~UFSMNT_LOGGING;
}
}
again: if (mount(mnt->mnt_special, mnt->mnt_mountp, flags, fstype,
&args, sizeof (args), mnt->mnt_mntopts, MAX_MNTOPT_STR) != 0) {
if (errno == EBUSY && !(flags & MS_OVERLAY)) {
if (!in_mnttab(mnt->mnt_mountp) &&
mount_attempts-- > 0) {
(void) poll(NULL, 0, 50);
goto again;
}
}
rpterr(mnt->mnt_special, mnt->mnt_mountp);
exit(32);
}
if (!(flags & MS_RDONLY)) {
if (args.flags & UFSMNT_LOGGING)
enable_logging(mnt->mnt_mountp, mnt->mnt_special);
else
disable_logging(mnt->mnt_mountp, mnt->mnt_special);
}
if (!qflg) {
cmp_requested_to_actual_options(opts, mnt->mnt_mntopts,
mnt->mnt_special, mnt->mnt_mountp);
}
if (checkislog(mnt->mnt_mountp)) {
if (!mflg) {
struct stat64 statb;
struct mnttagdesc mtdesc;
int fd;
if (stat64(mnt->mnt_mountp, &statb) != 0)
exit(32);
mtdesc.mtd_major = major(statb.st_dev);
mtdesc.mtd_minor = minor(statb.st_dev);
mtdesc.mtd_mntpt = mnt->mnt_mountp;
mtdesc.mtd_tag = MNTOPT_LOGGING;
if ((fd = open(MNTTAB, O_RDONLY, 0)) < 0)
exit(32);
if (ioctl(fd, MNTIOC_SETTAG, &mtdesc) != 0) {
(void) close(fd);
exit(32);
}
(void) close(fd);
}
}
exit(0);
}
static int
eatmntopt(struct mnttab *mnt, char *opt)
{
int has;
has = (findopt(mnt->mnt_mntopts, opt) != NULL);
rmopt(mnt, opt);
return (has);
}
static void
rmopt(struct mnttab *mnt, char *opt)
{
char *str;
char *optstart;
while (optstart = findopt(mnt->mnt_mntopts, opt)) {
for (str = optstart;
*str != ',' && *str != '\0' && *str != ' ';
str++)
;
if (*str == ',') {
str++;
} else if (optstart != mnt->mnt_mntopts) {
optstart--;
}
while (*optstart++ = *str++)
;
}
}
static void
fixopts(struct mnttab *mnt, char *opts)
{
struct mnttab omnt;
omnt.mnt_mntopts = opts;
rmopt(&omnt, MNTOPT_LOGGING);
rmopt(&omnt, MNTOPT_NOLOGGING);
(void) strlcpy(mnt->mnt_mntopts, opts, MAX_MNTOPT_STR);
rmopt(mnt, "f");
rmopt(mnt, MNTOPT_REMOUNT);
rmopt(&omnt, MNTOPT_GLOBAL);
rmopt(&omnt, MNTOPT_NOGLOBAL);
rmopt(&omnt, MNTOPT_QUOTA);
}
static void
usage(void)
{
(void) fprintf(stdout, gettext(
"ufs usage:\n"
"mount [-F ufs] [generic options] [-o suboptions] {special | mount_point}\n"));
(void) fprintf(stdout, gettext(
"\tsuboptions are: \n"
"\t ro,rw,nosuid,remount,f,m,\n"
"\t global,noglobal,\n"
"\t largefiles,nolargefiles,\n"
"\t forcedirectio,noforcedirectio\n"
"\t logging,nologging,\n"
"\t nbmand,nonbmand,\n"
"\t onerror[={panic | lock | umount}]\n"));
exit(32);
}
static char *
getnextopt(char **p)
{
char *cp = *p;
char *retstr;
while (*cp && isspace(*cp))
cp++;
retstr = cp;
while (*cp && *cp != ',')
cp++;
while (*cp == ',') {
*cp = '\0';
cp++;
}
*p = cp;
return (retstr);
}
static void
replace_opts(char *options, int flag, char *trueopt, char *falseopt)
{
char *f;
char *tmpoptsp;
int found;
char tmptopts[MNTMAXSTR];
(void) strcpy(tmptopts, options);
tmpoptsp = tmptopts;
(void) strcpy(options, "");
found = 0;
for (f = getnextopt(&tmpoptsp); *f; f = getnextopt(&tmpoptsp)) {
if (options[0] != '\0')
(void) strcat(options, ",");
if (strcmp(f, trueopt) == 0) {
(void) strcat(options, f);
found++;
} else if (strcmp(f, falseopt) == 0) {
if (flag)
(void) strcat(options, trueopt);
else
(void) strcat(options, f);
found++;
} else
(void) strcat(options, f);
}
if (!found) {
if (options[0] != '\0')
(void) strcat(options, ",");
(void) strcat(options, flag ? trueopt : falseopt);
}
}
static int
replace_opts_dflt(
char *options,
int dflt,
const char *trueopt,
const char *falseopt)
{
char *f;
char *tmpoptsp;
int last;
char tmptopts[MNTMAXSTR];
(void) strcpy(tmptopts, options);
tmpoptsp = tmptopts;
(void) strcpy(options, "");
last = dflt;
for (f = getnextopt(&tmpoptsp); *f; f = getnextopt(&tmpoptsp)) {
if (strcmp(f, trueopt) == 0) {
last = 1;
} else if (strcmp(f, falseopt) == 0) {
last = 0;
} else {
if (options[0] != '\0')
(void) strcat(options, ",");
(void) strcat(options, f);
}
}
if (options[0] != '\0')
(void) strcat(options, ",");
(void) strcat(options, last ? trueopt : falseopt);
return (last);
}
static void
rpterr(char *bs, char *mp)
{
switch (errno) {
case EPERM:
(void) fprintf(stderr, gettext("%s: Insufficient privileges\n"),
myname);
break;
case ENXIO:
(void) fprintf(stderr, gettext("%s: %s no such device\n"),
myname, bs);
break;
case ENOTDIR:
(void) fprintf(stderr,
gettext(
"%s: %s not a directory\n\tor a component of %s is not a directory\n"),
myname, mp, bs);
break;
case ENOENT:
(void) fprintf(stderr, gettext(
"%s: %s or %s, no such file or directory\n"),
myname, bs, mp);
break;
case EINVAL:
(void) fprintf(stderr, gettext("%s: %s is not this fstype\n"),
myname, bs);
break;
case EBUSY:
(void) fprintf(stderr,
gettext("%s: %s is already mounted or %s is busy\n"),
myname, bs, mp);
break;
case ENOTBLK:
(void) fprintf(stderr, gettext(
"%s: %s not a block device\n"), myname, bs);
break;
case EROFS:
(void) fprintf(stderr, gettext("%s: %s write-protected\n"),
myname, bs);
break;
case ENOSPC:
(void) fprintf(stderr, gettext(
"%s: The state of %s is not okay\n"
"\tand it was attempted to be mounted read/write\n"),
myname, bs);
(void) printf(gettext(
"mount: Please run fsck and try again\n"));
break;
case EFBIG:
(void) fprintf(stderr, gettext(
"%s: Large files may be present on %s,\n"
"\tand it was attempted to be mounted nolargefiles\n"),
myname, bs);
break;
default:
perror(myname);
(void) fprintf(stderr, gettext("%s: Cannot mount %s\n"),
myname, bs);
}
}