summaryrefslogtreecommitdiffstats
path: root/rtemsbsd/fs/nfsclient/nfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'rtemsbsd/fs/nfsclient/nfs.c')
-rw-r--r--rtemsbsd/fs/nfsclient/nfs.c926
1 files changed, 846 insertions, 80 deletions
diff --git a/rtemsbsd/fs/nfsclient/nfs.c b/rtemsbsd/fs/nfsclient/nfs.c
index b2526195..fe8d8412 100644
--- a/rtemsbsd/fs/nfsclient/nfs.c
+++ b/rtemsbsd/fs/nfsclient/nfs.c
@@ -53,70 +53,872 @@
#include <sys/time.h>
#include <sys/vnode.h>
+#include <rpc/rpc.h>
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include <rpc/types.h>
+#include <rpc/auth.h>
+
#include <fs/nfsclient/nfs.h>
#include <nfs/nfsproto.h>
#include <nfsclient/nfs.h>
#include <rtems/bsd/rootfs.h>
+
#include <stdio.h>
SYSINIT_MODULE_REFERENCE(rootfs);
SYSINIT_MODULE_REFERENCE(nfs);
#ifndef RTEMS_DEBUG
-#define RTEMS_DEBUG 0
+#define RTEMS_DEBUG 1
+#endif
+#ifndef RTEMS_NFSCL_DEBUGLEVEL
+#define RTEMS_NFSCL_DEBUGLEVEL 0
#endif
-#if RTEMS_DEBUG
+#if RTEMS_NFSCL_DEBUGLEVEL
extern int nfscl_debuglevel;
#endif
+/*
+ * Map user to kernel or just provide
+ */
+#define NFS_PROGRAM NFS_PROG
+#define MOUNTPROG 100005
+#define MOUNTPROC_MNT 1
+#define MNTPATHLEN 1024
+
+#define NFS_FHSIZE NFSX_V2FH
+#define NFS3_FHSIZE NFSX_V3FHMAX
+
+#undef malloc
+#undef free
+
+/* Table for af,sotype -> netid conversions. */
+static const struct nc_protos {
+ const char *netid;
+ int af;
+ int sotype;
+} nc_protos[] = {
+ {"udp", AF_INET, SOCK_DGRAM},
+ {"tcp", AF_INET, SOCK_STREAM},
+ {"udp6", AF_INET6, SOCK_DGRAM},
+ {"tcp6", AF_INET6, SOCK_STREAM},
+ {NULL, 0, 0}
+};
+
+struct nfhret {
+ u_long stat;
+ long vers;
+ long auth;
+ long fhsize;
+ u_char nfh[NFS3_FHSIZE];
+};
+
+enum mountmode {
+ ANY,
+ V2,
+ V3,
+ V4
+};
+
+/* Return codes for nfs_tryproto. */
+enum tryret {
+ TRYRET_SUCCESS,
+ TRYRET_TIMEOUT, /* No response received. */
+ TRYRET_REMOTEERR, /* Error received from remote server. */
+ TRYRET_LOCALERR /* Local failure. */
+};
+
+/*
+ * Taken from freebsd/sbin/mount_nfs/mount_nfs.c
+ */
+struct nfs_args {
+ struct mntarg *ma;
+ char options[256 + 1];
+ char hostname[MNAMELEN + 1];
+ char dirpath[MNAMELEN + 1];
+ char hname[MAXHOSTNAMELEN + 5];
+ char pname[MAXHOSTNAMELEN + 5];
+ enum mountmode mountmode;
+ int opflags;
+ int mnttcp_ok;
+ int vers;
+ int nfsproto;
+ char portspec[16];
+ int noconn;
+ struct sockaddr *addr;
+ int addrlen;
+ u_char *fh;
+ int fhsize;
+ int secflavor;
+ int got_principal;
+ struct addrinfo *ai_nfs;
+ char errstr[256];
+};
+#define OF_NOINET4 4
+#define OF_NOINET6 8
+
+/*
+ * Options to exclude from the mount
+ */
+static const char *opts_exclude[] = {
+ "nfsv2",
+ "nfsv3",
+ "nfsv4",
+ "port",
+ "noinet4",
+ "noinet6",
+ "vers"
+};
+#define NUM_OPTS_EXCLUDES (sizeof(opts_exclude) / sizeof(opts_exclude[0]))
+
+static void
+nfs_args_defaults(struct nfs_args *args)
+{
+ memset(args, 0, sizeof(struct nfs_args));
+ args->ma = NULL;
+ args->mountmode = ANY;
+ args->vers = 4;
+ args->opflags = 0;
+ args->nfsproto = IPPROTO_TCP;
+ args->mnttcp_ok = 1;
+ args->noconn = 0;
+ args->addrlen = 0;
+ args->fh = NULL;
+ args->fhsize = 0;
+ args->secflavor = -1;
+ args->got_principal = 0;
+}
+
+/* The header for the mount arguments */
+struct mntarg {
+ struct iovec *v;
+ int len;
+ int error;
+ SLIST_HEAD(, mntaarg) list;
+};
+
+static void
+print_mount_args(struct mntarg *ma)
+{
+ if (RTEMS_DEBUG) {
+ int m;
+ printf("nfs: mount args: %d\n", ma->len / 2);
+ for (m = 0; m < ma->len; m += 2) {
+ bool string = true;
+ const char *p;
+ printf(" %3d %s%c", ma->v[m + 1].iov_len,
+ (char*) ma->v[m].iov_base,
+ ma->v[m + 1].iov_len == 0 ? ' ' : '=');
+ p = (const char*) ma->v[m + 1].iov_base;
+ while (p != NULL && *p != '\0') {
+ if (*p < ' ' || *p > '~') {
+ string = false;
+ break;
+ }
+ ++p;
+ }
+ p = (const char*) ma->v[m + 1].iov_base;
+ if (p != NULL && string) {
+ printf(p);
+ } else if (p != NULL) {
+ int i;
+ for (i = 0; i < ma->v[m + 1].iov_len; ++i) {
+ printf("%02x ", (int) *p);
+ ++p;
+ }
+ }
+ printf("\n");
+ }
+ }
+}
+
+/*
+ * Look up a netid based on an address family and socket type.
+ * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM.
+ *
+ * XXX there should be a library function for this.
+ */
+static const char *
+netidbytype(int af, int sotype)
+{
+ struct nc_protos *p;
+ for (p = nc_protos; p->netid != NULL; p++) {
+ if (af != p->af || sotype != p->sotype)
+ continue;
+ return (p->netid);
+ }
+ return (NULL);
+}
+
+static const char *
+sec_num_to_name(int flavor)
+{
+ switch (flavor) {
+ case RPCSEC_GSS_KRB5:
+ return ("krb5");
+ case RPCSEC_GSS_KRB5I:
+ return ("krb5i");
+ case RPCSEC_GSS_KRB5P:
+ return ("krb5p");
+ case AUTH_SYS:
+ return ("sys");
+ }
+ return (NULL);
+}
+
+/*
+ * Catagorise a RPC return status and error into an `enum tryret'
+ * return code.
+ */
+static enum tryret
+returncode(enum clnt_stat clntstat, struct rpc_err *rpcerr)
+{
+
+ switch (clntstat) {
+ case RPC_TIMEDOUT:
+ return (TRYRET_TIMEOUT);
+ case RPC_PMAPFAILURE:
+ case RPC_PROGNOTREGISTERED:
+ case RPC_PROGVERSMISMATCH:
+ /* XXX, these can be local or remote. */
+ case RPC_CANTSEND:
+ case RPC_CANTRECV:
+ return (TRYRET_REMOTEERR);
+ case RPC_SYSTEMERROR:
+ switch (rpcerr->re_errno) {
+ case ETIMEDOUT:
+ return (TRYRET_TIMEOUT);
+ case ENOMEM:
+ break;
+ default:
+ return (TRYRET_REMOTEERR);
+ }
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+ return (TRYRET_LOCALERR);
+}
+
+/*
+ * Look up a netconfig entry based on a netid, and cache the result so
+ * that we don't need to remember to call freenetconfigent().
+ *
+ * Otherwise it behaves just like getnetconfigent(), so nc_*error()
+ * work on failure.
+ */
+static struct netconfig *
+getnetconf_cached(const char *netid)
+{
+ static struct nc_entry {
+ struct netconfig *nconf;
+ struct nc_entry *next;
+ } *head;
+ struct nc_entry *p;
+ struct netconfig *nconf;
+
+ for (p = head; p != NULL; p = p->next)
+ if (strcmp(netid, p->nconf->nc_netid) == 0)
+ return (p->nconf);
+
+ if ((nconf = getnetconfigent(netid)) == NULL)
+ return (NULL);
+ if ((p = malloc(sizeof(*p))) == NULL)
+ err(1, "malloc");
+ p->nconf = nconf;
+ p->next = head;
+ head = p;
+
+ return (p->nconf);
+}
+
+/*
+ * xdr routines for mount rpc's
+ */
+static int
+xdr_dir(XDR *xdrsp, char *dirp)
+{
+ return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
+}
+
+static int
+xdr_fh(XDR *xdrsp, struct nfhret *np)
+{
+ int i;
+ long auth, authcnt, authfnd = 0;
+
+ if (!xdr_u_long(xdrsp, &np->stat))
+ return (0);
+ if (np->stat)
+ return (1);
+ switch (np->vers) {
+ case 1:
+ np->fhsize = NFS_FHSIZE;
+ return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFS_FHSIZE));
+ case 3:
+ if (!xdr_long(xdrsp, &np->fhsize))
+ return (0);
+ if (np->fhsize <= 0 || np->fhsize > NFS3_FHSIZE)
+ return (0);
+ if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize))
+ return (0);
+ if (!xdr_long(xdrsp, &authcnt))
+ return (0);
+ for (i = 0; i < authcnt; i++) {
+ if (!xdr_long(xdrsp, &auth))
+ return (0);
+ if (np->auth == -1) {
+ np->auth = auth;
+ authfnd++;
+ } else if (auth == np->auth) {
+ authfnd++;
+ }
+ }
+ /*
+ * Some servers, such as DEC's OSF/1 return a nil authenticator
+ * list to indicate RPCAUTH_UNIX.
+ */
+ if (authcnt == 0 && np->auth == -1)
+ np->auth = AUTH_SYS;
+ if (!authfnd && (authcnt > 0 || np->auth != AUTH_SYS))
+ np->stat = EAUTH;
+ return (1);
+ }
+ return (0);
+}
+
+static int
+getnfsargs(char *spec, struct nfs_args *args)
+{
+ struct addrinfo hints;
+ int ecode, speclen, remoteerr, offset, have_bracket = 0;
+ char *hostp, *delimp, *errstr;
+ size_t len;
+
+ if (*spec == '[' && (delimp = strchr(spec + 1, ']')) != NULL &&
+ *(delimp + 1) == ':') {
+ hostp = spec + 1;
+ spec = delimp + 2;
+ have_bracket = 1;
+ } else if ((delimp = strrchr(spec, ':')) != NULL) {
+ hostp = spec;
+ spec = delimp + 1;
+ } else {
+ if (RTEMS_DEBUG) {
+ printf("nfs: mount: no <host>:<dirpath> nfs-name\n");
+ }
+ return (-1);
+ }
+ *delimp = '\0';
+
+ /*
+ * If there has been a trailing slash at mounttime it seems
+ * that some mountd implementations fail to remove the mount
+ * entries from their mountlist while unmounting.
+ */
+ for (speclen = strlen(spec);
+ speclen > 1 && spec[speclen - 1] == '/';
+ speclen--)
+ spec[speclen - 1] = '\0';
+ if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) {
+ if (RTEMS_DEBUG) {
+ printf("nfs: mount: %s:%s: %s", hostp, spec, strerror(ENAMETOOLONG));
+ }
+ return (-1);
+ }
+ /* Make both '@' and ':' notations equal */
+ if (*hostp != '\0') {
+ len = strlen(hostp);
+ offset = 0;
+ if (have_bracket)
+ args->hostname[offset++] = '[';
+ memmove(args->hostname + offset, hostp, len);
+ if (have_bracket)
+ args->hostname[len + offset++] = ']';
+ args->hostname[len + offset++] = ':';
+ memmove(args->hostname + len + offset, spec, speclen);
+ args->hostname[len + speclen + offset] = '\0';
+ }
+
+ /*
+ * Handle an internet host address.
+ */
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_NUMERICHOST;
+ if (args->nfsproto == IPPROTO_TCP)
+ hints.ai_socktype = SOCK_STREAM;
+ else if (args->nfsproto == IPPROTO_UDP)
+ hints.ai_socktype = SOCK_DGRAM;
+
+ char* portspec = args->portspec[0] == '\0' ? NULL : args->portspec;
+ if (getaddrinfo(hostp, portspec, &hints, &args->ai_nfs) != 0) {
+ hints.ai_flags = AI_CANONNAME;
+ if ((ecode = getaddrinfo(hostp, args->portspec, &hints, &args->ai_nfs))
+ != 0) {
+ if (RTEMS_DEBUG) {
+ printf("nfs: mount: getaddrinfo: ");
+ if (args->portspec[0] == '\0')
+ printf("%s: %s\n", hostp, gai_strerror(ecode));
+ else
+ printf("%s:%s: %s]n", hostp, args->portspec,
+ gai_strerror(ecode));
+ }
+ return (-1);
+ }
+
+ /*
+ * For a Kerberized nfs mount where the "principal"
+ * argument has not been set, add it here.
+ */
+ if (args->got_principal == 0 && args->secflavor != AUTH_SYS &&
+ args->ai_nfs->ai_canonname != NULL) {
+ snprintf(args->pname, sizeof(args->pname), "nfs@%s",
+ args->ai_nfs->ai_canonname);
+ }
+ }
+
+ strlcpy(args->hname, hostp, sizeof(args->hname));
+ strlcpy(args->dirpath, spec, sizeof(args->dirpath));
+
+ return (0);
+}
+
+/*
+ * Try to set up the NFS arguments according to the address
+ * family, protocol (and possibly port) specified in `ai'.
+ *
+ * Returns TRYRET_SUCCESS if successful, or:
+ * TRYRET_TIMEOUT The server did not respond.
+ * TRYRET_REMOTEERR The server reported an error.
+ * TRYRET_LOCALERR Local failure.
+ *
+ * In all error cases, *errstr will be set to a statically-allocated string
+ * describing the error.
+ */
+static enum tryret
+nfs_tryproto(struct addrinfo *ai, struct nfs_args *args)
+{
+ #define errbuf args->errstr
+ struct sockaddr_storage nfs_ss;
+ struct netbuf nfs_nb;
+ struct nfhret nfhret;
+ struct timeval try;
+ struct rpc_err rpcerr;
+ CLIENT *clp;
+ struct netconfig *nconf, *nconf_mnt;
+ const char *netid, *netid_mnt, *secname;
+ int doconnect, nfsvers, mntvers, sotype;
+ enum clnt_stat clntstat;
+ enum mountmode trymntmode;
+
+ const char *portspec = *args->portspec == '\0' ? NULL : args->portspec;
+ const char *hostp = args->hname;
+ const char *spec = args->dirpath;
+
+ sotype = 0;
+ trymntmode = args->mountmode;
+ errbuf[0] = '\0';
+
+ if (args->nfsproto == IPPROTO_TCP)
+ sotype = SOCK_STREAM;
+ else if (args->nfsproto == IPPROTO_UDP)
+ sotype = SOCK_DGRAM;
+
+ if ((netid = netidbytype(ai->ai_family, sotype)) == NULL) {
+ snprintf(errbuf, sizeof errbuf,
+ "af %d sotype %d not supported", ai->ai_family, sotype);
+ return (TRYRET_LOCALERR);
+ }
+ if ((nconf = getnetconf_cached(netid)) == NULL) {
+ snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror());
+ return (TRYRET_LOCALERR);
+ }
+ /* The RPCPROG_MNT netid may be different. */
+ if (args->mnttcp_ok) {
+ netid_mnt = netid;
+ nconf_mnt = nconf;
+ } else {
+ if ((netid_mnt = netidbytype(ai->ai_family, SOCK_DGRAM))
+ == NULL) {
+ snprintf(errbuf, sizeof errbuf,
+ "af %d sotype SOCK_DGRAM not supported",
+ ai->ai_family);
+ return (TRYRET_LOCALERR);
+ }
+ if ((nconf_mnt = getnetconf_cached(netid_mnt)) == NULL) {
+ snprintf(errbuf, sizeof errbuf, "%s: %s", netid_mnt,
+ nc_sperror());
+ return (TRYRET_LOCALERR);
+ }
+ }
+
+tryagain:
+ if (trymntmode == V4) {
+ nfsvers = 4;
+ mntvers = 3; /* Workaround for GCC. */
+ } else if (trymntmode == V2) {
+ nfsvers = 2;
+ mntvers = 1;
+ } else {
+ nfsvers = 3;
+ mntvers = 3;
+ }
+
+ if (portspec != NULL) {
+ /* `ai' contains the complete nfsd sockaddr. */
+ nfs_nb.buf = ai->ai_addr;
+ nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen;
+ } else {
+ /* Ask the remote rpcbind. */
+ nfs_nb.buf = &nfs_ss;
+ nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss;
+
+ if (!rpcb_getaddr(NFS_PROGRAM, nfsvers, nconf, &nfs_nb,
+ hostp)) {
+ if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH &&
+ trymntmode == ANY) {
+ trymntmode = V2;
+ goto tryagain;
+ }
+ snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s",
+ netid, hostp, spec,
+ clnt_spcreateerror("RPCPROG_NFS"));
+ return (returncode(rpc_createerr.cf_stat,
+ &rpc_createerr.cf_error));
+ }
+ }
+
+ /* Check that the server (nfsd) responds on the port we have chosen. */
+ clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, NFS_PROGRAM, nfsvers,
+ 0, 0);
+ if (clp == NULL) {
+ snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid,
+ hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS"));
+ return (returncode(rpc_createerr.cf_stat,
+ &rpc_createerr.cf_error));
+ }
+ if (sotype == SOCK_DGRAM && args->noconn == 0) {
+ /*
+ * Use connect(), to match what the kernel does. This
+ * catches cases where the server responds from the
+ * wrong source address.
+ */
+ doconnect = 1;
+ if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) {
+ clnt_destroy(clp);
+ snprintf(errbuf, sizeof errbuf,
+ "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp,
+ spec);
+ return (TRYRET_LOCALERR);
+ }
+ }
+
+ try.tv_sec = 10;
+ try.tv_usec = 0;
+ clntstat = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL,
+ (xdrproc_t)xdr_void, NULL, try);
+ if (clntstat != RPC_SUCCESS) {
+ if (clntstat == RPC_PROGVERSMISMATCH && trymntmode == ANY) {
+ clnt_destroy(clp);
+ trymntmode = V2;
+ goto tryagain;
+ }
+ clnt_geterr(clp, &rpcerr);
+ snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid,
+ hostp, spec, clnt_sperror(clp, "NFSPROC_NULL"));
+ clnt_destroy(clp);
+ return (returncode(clntstat, &rpcerr));
+ }
+ clnt_destroy(clp);
+
+ /*
+ * For NFSv4, there is no mount protocol.
+ */
+ if (trymntmode == V4) {
+ /*
+ * Store the server address in nfsargsp, making
+ * sure to copy any locally allocated structures.
+ */
+ args->addrlen = nfs_nb.len;
+ args->addr = malloc(args->addrlen);
+ if (args->addr == NULL) {
+ snprintf(errbuf, sizeof errbuf, "no memory");
+ return (TRYRET_LOCALERR);
+ }
+ bcopy(nfs_nb.buf, args->addr, args->addrlen);
+ args->ma = mount_arg(args->ma, "addr", args->addr, args->addrlen);
+ secname = sec_num_to_name(args->secflavor);
+ if (secname != NULL) {
+ args->ma = mount_arg(args->ma, "sec",
+ __DECONST(void *, secname), (size_t)-1);
+ }
+ args->ma = mount_arg(args->ma, "nfsv4", NULL, 0);
+ args->ma = mount_arg(args->ma, "dirpath", spec, -1);
+
+ return (TRYRET_SUCCESS);
+ }
+
+ /* Send the MOUNTPROC_MNT RPC to get the root filehandle. */
+ try.tv_sec = 10;
+ try.tv_usec = 0;
+ clp = clnt_tp_create(hostp, MOUNTPROG, mntvers, nconf_mnt);
+ if (clp == NULL) {
+ snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
+ hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create"));
+ return (returncode(rpc_createerr.cf_stat,
+ &rpc_createerr.cf_error));
+ }
+ clp->cl_auth = authsys_create_default();
+ nfhret.auth = args->secflavor;
+ nfhret.vers = mntvers;
+ clntstat = clnt_call(clp, MOUNTPROC_MNT, (xdrproc_t)xdr_dir, spec,
+ (xdrproc_t)xdr_fh, &nfhret, try);
+ auth_destroy(clp->cl_auth);
+ if (clntstat != RPC_SUCCESS) {
+ if (clntstat == RPC_PROGVERSMISMATCH && trymntmode == ANY) {
+ clnt_destroy(clp);
+ trymntmode = V2;
+ goto tryagain;
+ }
+ clnt_geterr(clp, &rpcerr);
+ snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
+ hostp, spec, clnt_sperror(clp, "RPCPROG_MNT"));
+ clnt_destroy(clp);
+ return (returncode(clntstat, &rpcerr));
+ }
+ clnt_destroy(clp);
+
+ if (nfhret.stat != 0) {
+ snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
+ hostp, spec, strerror(nfhret.stat));
+ return (TRYRET_REMOTEERR);
+ }
+
+ /*
+ * Store the filehandle and server address in nfsargsp, making
+ * sure to copy any locally allocated structures.
+ */
+ args->addrlen = nfs_nb.len;
+ args->addr = malloc(args->addrlen);
+ args->fhsize = nfhret.fhsize;
+ args->fh = malloc(args->fhsize);
+ if (args->addr == NULL || args->fh == NULL) {
+ free(args->addr);
+ free(args->fh);
+ snprintf(errbuf, sizeof errbuf, "no memory");
+ return (TRYRET_LOCALERR);
+ }
+ bcopy(nfs_nb.buf, args->addr, args->addrlen);
+ bcopy(nfhret.nfh, args->fh, args->fhsize);
+
+ args->ma = mount_arg(args->ma, "addr", args->addr, args->addrlen);
+ args->ma = mount_arg(args->ma, "fh", args->fh, args->fhsize);
+ secname = sec_num_to_name(nfhret.auth);
+ if (secname) {
+ args->ma = mount_arg(args->ma, "sec",
+ __DECONST(void *, secname), (size_t)-1);
+ }
+ if (nfsvers == 3)
+ args->ma = mount_arg(args->ma, "nfsv3", NULL, 0);
+
+ return (TRYRET_SUCCESS);
+}
+
+static int
+nfs_trymount(
+ rtems_filesystem_mount_table_entry_t *mt_entry,
+ struct addrinfo *ai, struct nfs_args *args,
+ const char* fspath, void *data
+) {
+ struct thread *td = curthread;
+ char errmsg[255];
+ int error;
+ if (td == NULL) {
+ if (RTEMS_DEBUG)
+ printf("nfs: mount: no current thread\n");
+ return ENOMEM;
+ }
+ args->ma = mount_arg(
+ args->ma, "fstype", RTEMS_DECONST(char *, mt_entry->type), -1);
+ args->ma = mount_arg(args->ma, "fspath", RTEMS_DECONST(char *, fspath), -1);
+ args->ma = mount_arg(
+ args->ma, "hostname", RTEMS_DECONST(char *, args->hostname), -1);
+ if (mt_entry->writeable) {
+ args->ma = mount_arg(args->ma, "rw", NULL, 0);
+ } else {
+ args->ma = mount_arg(args->ma, "ro", NULL, 0);
+ }
+ if (data != NULL) {
+ char *options = args->options;
+ char *opts;
+ /*
+ * See `man mount_nfs` and the list of options.
+ */
+ strlcpy(options, (const char *)data, sizeof(args->options));
+ opts = &options[0];
+ while (opts != NULL) {
+ char *delimiter = strchr(opts, ',');
+ char *opt = opts;
+ int s;
+ if (delimiter != NULL) {
+ *delimiter = '\0';
+ opts = delimiter + 1;
+ } else {
+ opts = NULL;
+ }
+ delimiter = strchr(opt, '=');
+ if (delimiter != NULL) {
+ *delimiter = '\0';
+ }
+ for (s = 0; s < NUM_OPTS_EXCLUDES; ++s) {
+ if (strcasecmp(opt, opts_exclude[s]) == 0) {
+ break;
+ }
+ }
+ if (s < NUM_OPTS_EXCLUDES) {
+ continue;
+ }
+ if (delimiter != NULL) {
+ args->ma = mount_arg(
+ args->ma, opt, delimiter + 1, -1);
+ } else {
+ args->ma = mount_arg(args->ma, opt, NULL, 0);
+ }
+ }
+ }
+ memset(errmsg, 0, sizeof(errmsg));
+ print_mount_args(args->ma);
+ args->ma = mount_arg(args->ma, "errmsg", errmsg, sizeof(errmsg) - 1);
+ error = kernel_mount(args->ma, MNT_VERIFIED);
+ if (error == 0) {
+ struct nameidata nd;
+ vhold(rootvnode);
+ NDINIT_ATVP(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE,
+ fspath, rootvnode, td);
+ error = namei(&nd);
+ if (error == 0) {
+ rtems_bsd_libio_loc_set_vnode(
+ &mt_entry->mt_fs_root->location, nd.ni_vp);
+ rtems_bsd_vfs_clonenode(
+ &mt_entry->mt_fs_root->location);
+ NDFREE(&nd, NDF_NO_VP_RELE);
+ } else {
+ NDFREE(&nd, 0);
+ rtems_bsd_libio_loc_set_vnode(
+ &mt_entry->mt_fs_root->location, NULL);
+ rtems_bsd_vfs_freenode(
+ &mt_entry->mt_fs_root->location);
+ rtems_bsd_rootfs_rmdir(fspath);
+ }
+ } else {
+ if (RTEMS_DEBUG) {
+ if (strlen(errmsg) > 0) {
+ printf("nfs: mount: error: %s\n", errmsg);
+ }
+ }
+ }
+ return error;
+}
+
int
rtems_nfs_initialize(
rtems_filesystem_mount_table_entry_t *mt_entry, const void *data)
{
- struct thread *td = curthread;
+ struct nfs_args args;
const char *fspath = NULL;
- char options[64];
char *at;
int error;
if (RTEMS_DEBUG) {
- printf("nfsv4: mount: %s -> %s", mt_entry->type, mt_entry->dev,
+ printf("nfs: mount: %s -> %s", mt_entry->type, mt_entry->dev,
mt_entry->target);
if (data != NULL) {
printf(" (%s)", (const char *)data);
}
printf("\n");
-#ifdef RTEMS_NFSCL_DEBUGLEVEL
+#if RTEMS_NFSCL_DEBUGLEVEL
nfscl_debuglevel = RTEMS_NFSCL_DEBUGLEVEL;
#endif
}
- if (td == NULL) {
- if (RTEMS_DEBUG)
- printf("nfsv4: mount: no current thread\n");
- error = ENOMEM;
- goto out;
- }
-
at = strchr(mt_entry->dev, '@');
if (at != NULL) {
if (RTEMS_DEBUG)
printf(
- "nfsv4: mount: user/group name in path not supported\n");
+ "nfs: mount: user/group name in path not supported\n");
error = EINVAL;
goto out;
}
+ nfs_args_defaults(&args);
+
if (data != NULL) {
+ char options[64];
size_t opts_len = strnlen((const char *)data, sizeof(options));
+ char *opts;
if (opts_len >= sizeof(options)) {
if (RTEMS_DEBUG)
printf(
- "nfsv4: mount: options string too long\n");
+ "nfs: mount: options string too long\n");
error = EINVAL;
goto out;
}
+
+ /*
+ * See `man mount_nfs` and the list of options.
+ */
+ strlcpy(options, (const char *)data, sizeof(options));
+ opts = &options[0];
+ while (opts != NULL) {
+ char *delimiter = strchr(opts, ',');
+ char *opt = opts;
+ if (delimiter != NULL) {
+ *delimiter = '\0';
+ opts = delimiter + 1;
+ } else {
+ opts = NULL;
+ }
+ delimiter = strchr(opt, '=');
+ if (strcasecmp(opt, "nfsv2") == 0) {
+ args.vers = 2;
+ args.mountmode = V2;
+ } else if (strcasecmp(opt, "nfsv3") == 0) {
+ args.vers = 3;
+ args.mountmode = V3;
+ } else if (strcasecmp(opt, "nfsv4") == 0) {
+ args.vers = 4;
+ args.mountmode = V4;
+ if (args.portspec[0] == '\0')
+ strlcpy(args.portspec, "2049", sizeof(args.portspec));
+ } else if (strcasecmp(opt, "tcp") == 0) {
+ args.nfsproto = IPPROTO_TCP;
+ } else if (strcasecmp(opt, "udp") == 0) {
+ args.nfsproto = IPPROTO_UDP;
+ } else if (strcasecmp(opt, "port") == 0) {
+ if (delimiter != NULL) {
+ strlcpy(args.portspec, delimiter + 1, sizeof(args.portspec));
+ } else {
+ error = EINVAL;
+ goto out;
+ }
+ } else if (strcasecmp(opt, "noinet4") == 0) {
+ args.opflags |= OF_NOINET4;
+ } else if (strcasecmp(opt, "noinet6") == 0) {
+ args.opflags |= OF_NOINET6;
+ } else if (strcasecmp(opt, "vers") == 0) {
+ if (delimiter != NULL) {
+ args.vers = 2;
+ } else {
+ error = EINVAL;
+ goto out;
+ }
+ }
+ }
}
rtems_bsd_vfs_mount_init(mt_entry);
@@ -130,6 +932,14 @@ rtems_nfs_initialize(
goto out;
}
+ if (getnfsargs(mt_entry->dev, &args) < 0) {
+ if (RTEMS_DEBUG)
+ printf(
+ "nfs: mount: invalid device path: %s\n", mt_entry->dev);
+ error = EINVAL;
+ goto out;
+ }
+
rtems_bsd_libio_loc_set_vnode(&mt_entry->mt_fs_root->location, NULL);
rtems_bsd_libio_loc_set_vnode_dir(
&mt_entry->mt_fs_root->location, NULL);
@@ -141,79 +951,35 @@ rtems_nfs_initialize(
*/
error = rtems_bsd_rootfs_mkdir(fspath);
if (error == 0) {
- struct mntarg *ma = NULL;
- char errmsg[255];
- ma = mount_arg(
- ma, "fstype", RTEMS_DECONST(char *, mt_entry->type), -1);
- ma = mount_arg(ma, "fspath", RTEMS_DECONST(char *, fspath), -1);
- ma = mount_arg(
- ma, "from", RTEMS_DECONST(char *, mt_entry->dev), -1);
- if (mt_entry->writeable) {
- ma = mount_arg(ma, "rw", NULL, 0);
- } else {
- ma = mount_arg(ma, "ro", NULL, 0);
- }
- if (data != NULL) {
- char *opts;
- /*
- * See `man mount_nfs` and the list of options.
- */
- strlcpy(options, (const char *)data, sizeof(options));
- opts = &options[0];
- while (opts != NULL) {
- char *delimiter = strchr(opts, ',');
- char *opt = opts;
- if (delimiter != NULL) {
- *delimiter = '\0';
- opts = delimiter + 1;
- } else {
- opts = NULL;
- }
- delimiter = strchr(opt, '=');
- if (delimiter != NULL) {
- *delimiter = '\0';
- ma = mount_arg(
- ma, opt, delimiter + 1, -1);
- } else {
- ma = mount_arg(ma, opt, NULL, 0);
- }
- }
- }
- memset(errmsg, 0, sizeof(errmsg));
- ma = mount_arg(ma, "errmsg", errmsg, sizeof(errmsg) - 1);
- error = kernel_mount(ma, MNT_VERIFIED);
- if (error == 0) {
- struct nameidata nd;
- vhold(rootvnode);
- NDINIT_ATVP(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE,
- fspath, rootvnode, td);
- error = namei(&nd);
- if (error == 0) {
- rtems_bsd_libio_loc_set_vnode(
- &mt_entry->mt_fs_root->location, nd.ni_vp);
- rtems_bsd_vfs_clonenode(
- &mt_entry->mt_fs_root->location);
- NDFREE(&nd, NDF_NO_VP_RELE);
- } else {
- NDFREE(&nd, 0);
- rtems_bsd_libio_loc_set_vnode(
- &mt_entry->mt_fs_root->location, NULL);
- rtems_bsd_vfs_freenode(
- &mt_entry->mt_fs_root->location);
- rtems_bsd_rootfs_rmdir(fspath);
- }
- } else {
+ struct addrinfo *ai;
+ enum tryret tryret;
+ for (ai = args.ai_nfs; ai != NULL; ai = ai->ai_next) {
+ if ((ai->ai_family == AF_INET6) &&
+ (args.opflags & OF_NOINET6))
+ continue;
+ if ((ai->ai_family == AF_INET) &&
+ (args.opflags & OF_NOINET4))
+ continue;
+ tryret = nfs_tryproto(ai, &args);
+ if (tryret == TRYRET_SUCCESS) {
+ error = nfs_trymount(mt_entry, ai, &args, fspath, data);
if (RTEMS_DEBUG)
- printf("nfsv4: mount: error: %s\n", errmsg);
+ printf("nfs: mount: (%d) %s\n", error, strerror(error));
+ break;
+ } else {
+ error = EIO;
+ if (RTEMS_DEBUG)
+ printf("nfs: mount: %s\n", args.errstr);
}
}
+ }
+
+ freeaddrinfo(args.ai_nfs);
rtems_bsd_libio_loc_set_vnode_dir(
&mt_entry->mt_fs_root->location, NULL);
out:
- if (RTEMS_DEBUG)
- printf("nfsv4: mount: (%d) %s\n", error, strerror(error));
if (error != 0) {
if (fspath != NULL) {
rtems_bsd_rootfs_rmdir(fspath);