blob: 36b29a5f9805f94ebc54d94cc80aed30e8ebbaf0 [file] [log] [blame]
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <setjmp.h>
#include <sys/wait.h>
#include <unistd.h>
#include <klibc/sysconfig.h> /* For _KLIBC_NO_MMU */
#include <linux/nfs_mount.h>
#include "nfsmount.h"
#include "sunrpc.h"
#include "dummypmap.h"
const char *progname;
static jmp_buf abort_buf;
static struct nfs_mount_data mount_data = {
.version = NFS_MOUNT_VERSION,
.flags = NFS_MOUNT_NONLM | NFS_MOUNT_VER3 | NFS_MOUNT_TCP,
.rsize = 0, /* Server's choice */
.wsize = 0, /* Server's choice */
.timeo = 7,
.retrans = 3,
.acregmin = 3,
.acregmax = 60,
.acdirmin = 30,
.acdirmax = 60,
.namlen = NAME_MAX,
};
int nfs_port;
static struct int_opts {
char *name;
int *val;
} int_opts[] = {
{"port", &nfs_port},
{"rsize", &mount_data.rsize},
{"wsize", &mount_data.wsize},
{"timeo", &mount_data.timeo},
{"retrans", &mount_data.retrans},
{"acregmin", &mount_data.acregmin},
{"acregmax", &mount_data.acregmax},
{"acdirmin", &mount_data.acdirmin},
{"acdirmax", &mount_data.acdirmax},
{NULL, NULL}
};
static struct bool_opts {
char *name;
int and_mask;
int or_mask;
} bool_opts[] = {
{"soft", ~NFS_MOUNT_SOFT, NFS_MOUNT_SOFT},
{"hard", ~NFS_MOUNT_SOFT, 0},
{"intr", ~NFS_MOUNT_INTR, NFS_MOUNT_INTR},
{"nointr", ~NFS_MOUNT_INTR, 0},
{"posix", ~NFS_MOUNT_POSIX, NFS_MOUNT_POSIX},
{"noposix", ~NFS_MOUNT_POSIX, 0},
{"cto", ~NFS_MOUNT_NOCTO, 0},
{"nocto", ~NFS_MOUNT_NOCTO, NFS_MOUNT_NOCTO},
{"ac", ~NFS_MOUNT_NOAC, 0},
{"noac", ~NFS_MOUNT_NOAC, NFS_MOUNT_NOAC},
{"lock", ~NFS_MOUNT_NONLM, 0},
{"nolock", ~NFS_MOUNT_NONLM, NFS_MOUNT_NONLM},
{"acl", ~NFS_MOUNT_NOACL, 0},
{"noacl", ~NFS_MOUNT_NOACL, NFS_MOUNT_NOACL},
{"v2", ~NFS_MOUNT_VER3, 0},
{"v3", ~NFS_MOUNT_VER3, NFS_MOUNT_VER3},
{"udp", ~NFS_MOUNT_TCP, 0},
{"tcp", ~NFS_MOUNT_TCP, NFS_MOUNT_TCP},
{"broken_suid", ~NFS_MOUNT_BROKEN_SUID, NFS_MOUNT_BROKEN_SUID},
{"ro", ~NFS_MOUNT_KLIBC_RONLY, NFS_MOUNT_KLIBC_RONLY},
{"rw", ~NFS_MOUNT_KLIBC_RONLY, 0},
{NULL, 0, 0}
};
static int parse_int(const char *val, const char *ctx)
{
char *end;
int ret;
ret = (int)strtoul(val, &end, 0);
if (*val == '\0' || *end != '\0') {
fprintf(stderr, "%s: invalid value for %s\n", val, ctx);
longjmp(abort_buf, 1);
}
return ret;
}
static void parse_opts(char *opts)
{
char *cp, *val;
while ((cp = strsep(&opts, ",")) != NULL) {
if (*cp == '\0')
continue;
val = strchr(cp, '=');
if (val != NULL) {
struct int_opts *opts = int_opts;
*val++ = '\0';
while (opts->name && strcmp(opts->name, cp) != 0)
opts++;
if (opts->name)
*(opts->val) = parse_int(val, opts->name);
else {
fprintf(stderr, "%s: bad option '%s'\n",
progname, cp);
longjmp(abort_buf, 1);
}
} else {
struct bool_opts *opts = bool_opts;
while (opts->name && strcmp(opts->name, cp) != 0)
opts++;
if (opts->name) {
mount_data.flags &= opts->and_mask;
mount_data.flags |= opts->or_mask;
} else {
fprintf(stderr, "%s: bad option '%s'\n",
progname, cp);
longjmp(abort_buf, 1);
}
}
}
}
static uint32_t parse_addr(const char *ip)
{
struct in_addr in;
if (inet_aton(ip, &in) == 0) {
fprintf(stderr, "%s: can't parse IP address '%s'\n",
progname, ip);
longjmp(abort_buf, 1);
}
return in.s_addr;
}
static void check_path(const char *path)
{
struct stat st;
if (stat(path, &st) == -1) {
perror("stat");
longjmp(abort_buf, 1);
} else if (!S_ISDIR(st.st_mode)) {
fprintf(stderr, "%s: '%s' not a directory\n", progname, path);
longjmp(abort_buf, 1);
}
}
int main(int argc, char *argv[])
__attribute__ ((weak, alias("nfsmount_main")));
int nfsmount_main(int argc, char *argv[])
{
uint32_t server;
char *rem_name;
char *rem_path;
char *hostname;
char *path;
int c;
const char *portmap_file;
pid_t spoof_portmap;
int err, ret;
if ((err = setjmp(abort_buf)))
return err;
/* Set these here to avoid longjmp warning */
portmap_file = NULL;
spoof_portmap = 0;
server = 0;
/* If progname is set we're invoked from another program */
if (!progname) {
struct timeval now;
progname = argv[0];
gettimeofday(&now, NULL);
srand48(now.tv_usec ^ (now.tv_sec << 24));
}
while ((c = getopt(argc, argv, "o:p:")) != EOF) {
switch (c) {
case 'o':
parse_opts(optarg);
break;
case 'p':
portmap_file = optarg;
break;
case '?':
fprintf(stderr, "%s: invalid option -%c\n",
progname, optopt);
return 1;
}
}
if (optind == argc) {
fprintf(stderr, "%s: need a path\n", progname);
return 1;
}
hostname = rem_path = argv[optind];
rem_name = strdup(rem_path);
if (rem_name == NULL) {
perror("strdup");
return 1;
}
rem_path = strchr(rem_path, ':');
if (rem_path == NULL) {
fprintf(stderr, "%s: need a server\n", progname);
free(rem_name);
return 1;
}
*rem_path++ = '\0';
if (*rem_path != '/') {
fprintf(stderr, "%s: need a path\n", progname);
free(rem_name);
return 1;
}
server = parse_addr(hostname);
if (optind <= argc - 2)
path = argv[optind + 1];
else
path = "/nfs_root";
check_path(path);
#if !_KLIBC_NO_MMU
/* Note: uClinux can't fork(), so the spoof portmapper is not
available on uClinux. */
if (portmap_file)
spoof_portmap = start_dummy_portmap(portmap_file);
if (spoof_portmap == -1) {
free(rem_name);
return 1;
}
#endif
ret = 0;
if (nfs_mount(rem_name, hostname, server, rem_path, path,
&mount_data) != 0)
ret = 1;
/* If we set up the spoofer, tear it down now */
if (spoof_portmap) {
kill(spoof_portmap, SIGTERM);
while (waitpid(spoof_portmap, NULL, 0) == -1
&& errno == EINTR)
;
}
free(rem_name);
return ret;
}