mount: helper support + improvements

- helper support (mount.type).
  - helpers need to be in $PATH, if needed we can add a check for
    /sbin/mount.XXXX
  - pass -B, -M, -R to helper, its more reliable to pass these named
    options with -o however.
- allow prefix "no" for which type no action should be taken:
  mount -a -t nonfs,ext4

fix bugs:
- dont modify me->mnt_opts (used strtok).
This commit is contained in:
Hiltjo Posthuma 2015-05-10 18:19:15 +02:00 committed by sin
parent ee5b04a7a3
commit 71da5628d1
2 changed files with 139 additions and 32 deletions

18
mount.8
View File

@ -36,7 +36,23 @@ This is the default action.
.It Fl o Ar options
Specify a comma separated string of filesystem specific options.
.It Fl t Ar fstype
Set the filesystem type.
Set the filesystem type. More than one type may be specified in a comma
separated list. The list of file system types can be prefixed with "no" to
specify the file system types for which action should not be taken. For
example, the
.Nm
command:
.Bd -literal
# mount -a -t nonfs,ext4
.Ed
mounts all file systems except those of type NFS and EXT4.
.Nm
will attempt to execute a program in your
.Ev PATH
mount.XXX where XXX is replaced by the type name. For example, NFS file
systems are mounted by the program
.Pa mount.nfs .
.El
.Sh SEE ALSO
.Xr mount 2 ,

153
mount.c
View File

@ -2,7 +2,9 @@
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <limits.h>
#include <mntent.h>
#include <stdio.h>
@ -34,41 +36,117 @@ struct {
{ NULL, NULL, 0 }
};
static unsigned long argflags = 0;
static char *argopts = NULL;
static char *
findtype(const char *types, const char *t)
{
const char *p;
size_t len;
for (len = strlen(t); (p = strstr(types, t)); types = p + len) {
if (!strncmp(p, t, len) && (p[len] == '\0' || p[len] == ','))
return (char *)p;
}
return NULL;
}
static void
parseopts(char *popts, unsigned long *flags, char *data, size_t datasiz)
parseopts(const char *popts, unsigned long *flags, char *data, size_t datasiz)
{
unsigned int i, validopt;
size_t optlen, dlen = 0;
char *name;
const char *name, *e;
name = popts;
data[0] = '\0';
for (name = strtok(popts, ","); name; name = strtok(NULL, ",")) {
do {
if ((e = strstr(name, ",")))
optlen = e - name;
else
optlen = strlen(name);
validopt = 0;
for (i = 0; optnames[i].opt; i++) {
if (optnames[i].opt && strcmp(name, optnames[i].opt) == 0) {
if (optnames[i].opt &&
!strncmp(name, optnames[i].opt, optlen)) {
*flags |= optnames[i].v;
validopt = 1;
break;
}
if (optnames[i].notopt && strcmp(name, optnames[i].notopt) == 0) {
if (optnames[i].notopt &&
!strncmp(name, optnames[i].notopt, optlen)) {
*flags &= ~optnames[i].v;
validopt = 1;
break;
}
}
if (!validopt) {
if (!validopt && optlen > 0) {
/* unknown option, pass as data option to mount() */
if ((optlen = strlen(name))) {
if (dlen + optlen + 2 >= datasiz)
return; /* prevent overflow */
if (dlen)
data[dlen++] = ',';
memcpy(&data[dlen], name, optlen);
dlen += optlen;
data[dlen] = '\0';
}
if (dlen + optlen + 2 >= datasiz)
return; /* prevent overflow */
if (dlen)
data[dlen++] = ',';
memcpy(&data[dlen], name, optlen);
dlen += optlen;
data[dlen] = '\0';
}
name = e + 1;
} while (e);
}
static int
mounthelper(const char *fsname, const char *dir, const char *fstype)
{
pid_t pid;
char eprog[PATH_MAX];
char const *eargv[10];
int status, i;
pid = fork();
switch(pid) {
case -1:
break;
case 0:
snprintf(eprog, sizeof(eprog), "mount.%s", fstype);
i = 0;
eargv[i++] = eprog;
if (argflags & MS_BIND)
eargv[i++] = "-B";
if (argflags & MS_MOVE)
eargv[i++] = "-M";
if (argflags & MS_REC)
eargv[i++] = "-R";
if (argopts) {
eargv[i++] = "-o";
eargv[i++] = argopts;
}
eargv[i++] = fsname;
eargv[i++] = dir;
eargv[i] = NULL;
execvp(eprog, (char * const *)eargv);
if (errno == ENOENT)
_exit(1);
weprintf("execvp:");
_exit(1);
break;
default:
if (waitpid(pid, &status, 0) < 0) {
weprintf("waitpid:");
return -1;
}
if (WIFEXITED(status))
return WEXITSTATUS(status);
else if (WIFSIGNALED(status))
return 1;
break;
}
return 0;
}
static int
@ -96,6 +174,7 @@ mounted(const char *dir)
return 1;
}
endmntent(fp);
return 0;
}
@ -103,37 +182,37 @@ static void
usage(void)
{
eprintf("usage: %s [-BMRan] [-t fstype] [-o options] [source] [target]\n",
argv0);
argv0);
}
int
main(int argc, char *argv[])
{
int aflag = 0, oflag = 0, status = 0, i;
unsigned long flags = 0;
char *types = NULL, data[512] = "", *resolvpath = NULL;
char *files[] = { "/proc/mounts", "/etc/fstab", NULL };
size_t datasiz = sizeof(data);
const char *source, *target;
struct mntent *me = NULL;
int aflag = 0, oflag = 0, status = 0, i, r;
unsigned long flags = 0;
FILE *fp;
ARGBEGIN {
case 'B':
flags |= MS_BIND;
argflags |= MS_BIND;
break;
case 'M':
flags |= MS_MOVE;
argflags |= MS_MOVE;
break;
case 'R':
flags |= MS_REC;
argflags |= MS_REC;
break;
case 'a':
aflag = 1;
break;
case 'o':
oflag = 1;
parseopts(EARGF(usage()), &flags, data, datasiz);
argopts = EARGF(usage());
parseopts(argopts, &flags, data, sizeof(data));
break;
case 't':
types = EARGF(usage());
@ -182,7 +261,7 @@ main(int argc, char *argv[])
source = me->mnt_fsname;
}
if (!oflag)
parseopts(me->mnt_opts, &flags, data, datasiz);
parseopts(me->mnt_opts, &flags, data, sizeof(data));
if (!types)
types = me->mnt_type;
goto mountsingle;
@ -195,7 +274,10 @@ main(int argc, char *argv[])
eprintf("can't find %s in /etc/fstab\n", target);
mountsingle:
if (mount(source, target, types, flags, data) < 0) {
r = mounthelper(source, target, types);
if (r == -1)
status = 1;
if (r > 0 && mount(source, target, types, argflags | flags, data) < 0) {
weprintf("mount: %s:", source);
status = 1;
}
@ -208,14 +290,23 @@ mountall:
if (!(fp = setmntent("/etc/fstab", "r")))
eprintf("setmntent %s:", "/etc/fstab");
while ((me = getmntent(fp))) {
if (hasmntopt(me, MNTOPT_NOAUTO))
continue;
/* already mounted, skip */
if (mounted(me->mnt_dir))
/* has "noauto" option or already mounted: skip */
if (hasmntopt(me, MNTOPT_NOAUTO) || mounted(me->mnt_dir))
continue;
flags = 0;
parseopts(me->mnt_opts, &flags, data, datasiz);
if (mount(me->mnt_fsname, me->mnt_dir, me->mnt_type, flags, data) < 0) {
parseopts(me->mnt_opts, &flags, data, sizeof(data));
/* if -t types specified:
* if non-match, skip
* if match and prefixed with "no", skip */
if (types &&
((types[0] == 'n' && types[1] == 'o' &&
findtype(types + 2, me->mnt_type)) ||
(!findtype(types, me->mnt_type))))
continue;
r = mounthelper(me->mnt_fsname, me->mnt_dir, me->mnt_type);
if (r > 0 && mount(me->mnt_fsname, me->mnt_dir, me->mnt_type,
argflags | flags, data) < 0) {
weprintf("mount: %s:", me->mnt_fsname);
status = 1;
}