| #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; |
| } |