summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/Makefile2
-rw-r--r--fs/bpkfs.c3
-rw-r--r--fs/devfs-core.c34
-rw-r--r--fs/fs.c21
-rw-r--r--fs/nfs.c963
-rw-r--r--fs/parseopt.c34
-rw-r--r--fs/parseopt.h1
7 files changed, 719 insertions, 339 deletions
diff --git a/fs/Makefile b/fs/Makefile
index e8347bd670..d3465edfa5 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -8,6 +8,6 @@ obj-y += fs.o
obj-$(CONFIG_FS_UBIFS) += ubifs/
obj-$(CONFIG_FS_TFTP) += tftp.o
obj-$(CONFIG_FS_OMAP4_USBBOOT) += omap4_usbbootfs.o
-obj-$(CONFIG_FS_NFS) += nfs.o
+obj-$(CONFIG_FS_NFS) += nfs.o parseopt.o
obj-$(CONFIG_FS_BPKFS) += bpkfs.o
obj-$(CONFIG_FS_UIMAGEFS) += uimagefs.o
diff --git a/fs/bpkfs.c b/fs/bpkfs.c
index b3b45be76d..8352307e59 100644
--- a/fs/bpkfs.c
+++ b/fs/bpkfs.c
@@ -12,7 +12,6 @@
#include <fs.h>
#include <errno.h>
#include <fcntl.h>
-#include <fs.h>
#include <malloc.h>
#include <init.h>
#include <linux/stat.h>
@@ -438,7 +437,7 @@ static int bpkfs_probe(struct device_d *dev)
dev_dbg(dev, "%d: type = 0x%x => %s\n", i, d->type, d->name);
dev_dbg(dev, "%d: size = %llu\n", i, d->size);
- dev_dbg(dev, "%d: offset = %d\n", i, d->offset);
+ dev_dbg(dev, "%d: offset = %zu\n", i, d->offset);
dev_dbg(dev, "%d: hw_id = 0x%x => %s\n", i, h->hw_id, h->name);
diff --git a/fs/devfs-core.c b/fs/devfs-core.c
index 193cd10234..eafdc878ac 100644
--- a/fs/devfs-core.c
+++ b/fs/devfs-core.c
@@ -296,10 +296,21 @@ static struct cdev *__devfs_add_partition(struct cdev *cdev,
return ERR_PTR(-EINVAL);
}
+ if (IS_ENABLED(CONFIG_PARTITION_NEED_MTD) && cdev->mtd) {
+ struct mtd_info *mtd;
+
+ mtd = mtd_add_partition(cdev->mtd, offset, size,
+ partinfo->flags, partinfo->name);
+ if (IS_ERR(mtd))
+ return (void *)mtd;
+ return 0;
+ }
+
new = xzalloc(sizeof(*new));
new->name = strdup(partinfo->name);
if (!strncmp(cdev->name, partinfo->name, strlen(cdev->name)))
new->partname = xstrdup(partinfo->name + strlen(cdev->name) + 1);
+
new->ops = cdev->ops;
new->priv = cdev->priv;
new->size = size;
@@ -308,19 +319,6 @@ static struct cdev *__devfs_add_partition(struct cdev *cdev,
new->dev = cdev->dev;
new->flags = partinfo->flags | DEVFS_IS_PARTITION;
-#ifdef CONFIG_PARTITION_NEED_MTD
- if (cdev->mtd) {
- new->mtd = mtd_add_partition(cdev->mtd, offset, size,
- partinfo->flags, partinfo->name);
- if (IS_ERR(new->mtd)) {
- int ret = PTR_ERR(new->mtd);
- free(new->partname);
- free(new);
- return ERR_PTR(ret);
- }
- }
-#endif
-
devfs_create(new);
return new;
@@ -354,16 +352,16 @@ int devfs_del_partition(const char *name)
if (!cdev)
return -ENOENT;
+ if (IS_ENABLED(CONFIG_PARTITION_NEED_MTD) && cdev->mtd) {
+ ret = mtd_del_partition(cdev->mtd);
+ return ret;
+ }
+
if (!(cdev->flags & DEVFS_IS_PARTITION))
return -EINVAL;
if (cdev->flags & DEVFS_PARTITION_FIXED)
return -EPERM;
-#ifdef CONFIG_PARTITION_NEED_MTD
- if (cdev->mtd)
- mtd_del_partition(cdev->mtd);
-#endif
-
ret = devfs_remove(cdev);
if (ret)
return ret;
diff --git a/fs/fs.c b/fs/fs.c
index 1b43c616ba..b0ac918acc 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -435,7 +435,7 @@ static void automount_mount(const char *path, int instat)
setenv("automount_path", am->path);
export("automount_path");
- ret = run_command(am->cmd, 0);
+ ret = run_command(am->cmd);
setenv("automount_path", NULL);
if (ret)
@@ -1243,6 +1243,7 @@ static void fs_remove(struct device_d *dev)
}
free(fsdev->path);
+ free(fsdev->options);
if (fsdev == fs_dev_root)
fs_dev_root = NULL;
@@ -1317,13 +1318,18 @@ int fsdev_open_cdev(struct fs_device_d *fsdev)
* We do this by registering a new device on which the filesystem
* driver will match.
*/
-int mount(const char *device, const char *fsname, const char *_path)
+int mount(const char *device, const char *fsname, const char *_path,
+ const char *fsoptions)
{
struct fs_device_d *fsdev;
int ret;
char *path = normalise_path(_path);
- debug("mount: %s on %s type %s\n", device, path, fsname);
+ if (!fsoptions)
+ fsoptions = "";
+
+ debug("mount: %s on %s type %s, options=%s\n",
+ device, path, fsname, fsoptions);
if (fs_dev_root) {
fsdev = get_fsdevice_by_path(path);
@@ -1355,6 +1361,7 @@ int mount(const char *device, const char *fsname, const char *_path)
fsdev->dev.id = get_free_deviceid(fsdev->dev.name);
fsdev->path = xstrdup(path);
fsdev->dev.bus = &fs_bus;
+ fsdev->options = xstrdup(fsoptions);
ret = register_device(&fsdev->dev);
if (ret)
@@ -1712,7 +1719,7 @@ const char *cdev_get_mount_path(struct cdev *cdev)
* mount it to /mnt/<cdevname> and return the path. Returns an error pointer
* on failure.
*/
-const char *cdev_mount_default(struct cdev *cdev)
+const char *cdev_mount_default(struct cdev *cdev, const char *fsoptions)
{
const char *path;
char *newpath, *devpath;
@@ -1721,7 +1728,7 @@ const char *cdev_mount_default(struct cdev *cdev)
/*
* If this cdev is already mounted somewhere use this path
* instead of mounting it again to avoid corruption on the
- * filesystem.
+ * filesystem. Note this ignores eventual fsoptions though.
*/
path = cdev_get_mount_path(cdev);
if (path)
@@ -1732,7 +1739,7 @@ const char *cdev_mount_default(struct cdev *cdev)
devpath = asprintf("/dev/%s", cdev->name);
- ret = mount(devpath, NULL, newpath);
+ ret = mount(devpath, NULL, newpath, fsoptions);
free(devpath);
@@ -1762,6 +1769,6 @@ void mount_all(void)
struct cdev *cdev = &bdev->cdev;
list_for_each_entry(cdev, &bdev->dev->cdevs, devices_list)
- cdev_mount_default(cdev);
+ cdev_mount_default(cdev, NULL);
}
}
diff --git a/fs/nfs.c b/fs/nfs.c
index 7173264351..046cd4d76c 100644
--- a/fs/nfs.c
+++ b/fs/nfs.c
@@ -1,10 +1,12 @@
/*
* nfs.c - barebox NFS driver
*
+ * Copyright (c) 2014 Uwe Kleine-König, Pengutronix
* Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
* Copyright (c) Masami Komiya <mkomiya@sonare.it> 2004
*
- * Based on U-Boot NFS code which is based on NetBSD code
+ * Based on U-Boot NFS code which is based on NetBSD code with
+ * major changes to support nfs3.
*
* See file CREDITS for list of people who contributed to this
* project.
@@ -17,7 +19,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
*/
#include <common.h>
@@ -33,6 +34,9 @@
#include <linux/err.h>
#include <kfifo.h>
#include <sizes.h>
+#include <byteorder.h>
+
+#include "parseopt.h"
#define SUNRPC_PORT 111
@@ -48,34 +52,54 @@
#define MOUNT_ADDENTRY 1
#define MOUNT_UMOUNT 3
-#define NFS_GETATTR 1
-#define NFS_LOOKUP 4
-#define NFS_READLINK 5
-#define NFS_READ 6
-#define NFS_READDIR 16
-
-#define NFS_FHSIZE 32
-
-enum nfs_stat {
- NFS_OK = 0,
- NFSERR_PERM = 1,
- NFSERR_NOENT = 2,
- NFSERR_IO = 5,
- NFSERR_NXIO = 6,
- NFSERR_ACCES = 13,
- NFSERR_EXIST = 17,
- NFSERR_NODEV = 19,
- NFSERR_NOTDIR = 20,
- NFSERR_ISDIR = 21,
- NFSERR_FBIG = 27,
- NFSERR_NOSPC = 28,
- NFSERR_ROFS = 30,
- NFSERR_NAMETOOLONG=63,
- NFSERR_NOTEMPTY = 66,
- NFSERR_DQUOT = 69,
- NFSERR_STALE = 70,
- NFSERR_WFLUSH = 99,
-};
+#define NFSPROC3_GETATTR 1
+#define NFSPROC3_LOOKUP 3
+#define NFSPROC3_READLINK 5
+#define NFSPROC3_READ 6
+#define NFSPROC3_READDIR 16
+
+#define NFS3_FHSIZE 64
+#define NFS3_COOKIEVERFSIZE 8
+
+/* values of enum ftype3 */
+#define NF3REG 1
+#define NF3DIR 2
+#define NF3BLK 3
+#define NF3CHR 4
+#define NF3LNK 5
+#define NF3SOCK 6
+#define NF3FIFO 7
+
+/* values for enum nfsstat3 */
+#define NFS3_OK 0
+#define NFS3ERR_PERM 1
+#define NFS3ERR_NOENT 2
+#define NFS3ERR_IO 5
+#define NFS3ERR_NXIO 6
+#define NFS3ERR_ACCES 13
+#define NFS3ERR_EXIST 17
+#define NFS3ERR_XDEV 18
+#define NFS3ERR_NODEV 19
+#define NFS3ERR_NOTDIR 20
+#define NFS3ERR_ISDIR 21
+#define NFS3ERR_INVAL 22
+#define NFS3ERR_FBIG 27
+#define NFS3ERR_NOSPC 28
+#define NFS3ERR_ROFS 30
+#define NFS3ERR_MLINK 31
+#define NFS3ERR_NAMETOOLONG 63
+#define NFS3ERR_NOTEMPTY 66
+#define NFS3ERR_DQUOT 69
+#define NFS3ERR_STALE 70
+#define NFS3ERR_REMOTE 71
+#define NFS3ERR_BADHANDLE 10001
+#define NFS3ERR_NOT_SYNC 10002
+#define NFS3ERR_BAD_COOKIE 10003
+#define NFS3ERR_NOTSUPP 10004
+#define NFS3ERR_TOOSMALL 10005
+#define NFS3ERR_SERVERFAULT 10006
+#define NFS3ERR_BADTYPE 10007
+#define NFS3ERR_JUKEBOX 10008
static void *nfs_packet;
static int nfs_len;
@@ -107,51 +131,96 @@ struct nfs_priv {
struct net_connection *con;
IPaddr_t server;
char *path;
- int mount_port;
- int nfs_port;
- unsigned long rpc_id;
- char rootfh[NFS_FHSIZE];
+ unsigned short mount_port;
+ unsigned short nfs_port;
+ uint32_t rpc_id;
+ uint32_t rootfh_len;
+ char rootfh[NFS3_FHSIZE];
};
struct file_priv {
struct kfifo *fifo;
void *buf;
- char filefh[NFS_FHSIZE];
+ uint32_t filefh_len;
+ char filefh[NFS3_FHSIZE];
struct nfs_priv *npriv;
};
static uint64_t nfs_timer_start;
-static int nfs_state;
+static int nfs_state;
#define STATE_DONE 1
#define STATE_START 2
-enum ftype {
- NFNON = 0,
- NFREG = 1,
- NFDIR = 2,
- NFBLK = 3,
- NFCHR = 4,
- NFLNK = 5
-};
-
-struct fattr {
- uint32_t type;
- uint32_t mode;
- uint32_t nlink;
- uint32_t uid;
- uint32_t gid;
- uint32_t size;
- uint32_t blocksize;
- uint32_t rdev;
- uint32_t blocks;
-};
-
-struct readdirargs {
- char filefh[NFS_FHSIZE];
- uint32_t cookie;
- uint32_t count;
-};
+/*
+ * common types used in more than one request:
+ *
+ * typedef uint32 count3;
+ * typedef uint32 gid3;
+ * typedef uint32 mode3;
+ * typedef uint32 uid3;
+ *
+ * typedef uint64 cookie3;
+ * typedef uint64 fileid3;
+ * typedef uint64 size3;
+ *
+ * typedef string filename3<>;
+ * typedef string nfspath3<>;
+ *
+ * typedef opaque cookieverf3[NFS3_COOKIEVERFSIZE];
+ *
+ * enum ftype3 {
+ * NF3REG = 1,
+ * NF3DIR = 2,
+ * NF3BLK = 3,
+ * NF3CHR = 4,
+ * NF3LNK = 5,
+ * NF3SOCK = 6,
+ * NF3FIFO = 7
+ * };
+ *
+ * struct specdata3 {
+ * uint32 specdata1;
+ * uint32 specdata2;
+ * };
+ *
+ * struct nfs_fh3 {
+ * opaque data<NFS3_FHSIZE>;
+ * }
+ *
+ * struct nfstime3 {
+ * uint32 seconds;
+ * uint32 nseconds;
+ * };
+ *
+ * struct fattr3 {
+ * ftype3 type;
+ * mode3 mode;
+ * uint32_t nlink;
+ * uid3 uid;
+ * gid3 gid;
+ * size3 size;
+ * size3 used;
+ * specdata3 rdev;
+ * uint64_t fsid;
+ * fileid3 fileid;
+ * nfstime3 atime;
+ * nfstime3 mtime;
+ * nfstime3 ctime;
+ * };
+ *
+ * struct diropargs3 {
+ * nfs_fh3 dir;
+ * filename3 name;
+ * }
+ *
+ * union post_op_attr switch (bool attributes_follow) {
+ * case TRUE:
+ * fattr3 attributes;
+ * case FALSE:
+ * void;
+ * };
+ */
struct xdr_stream {
__be32 *p;
@@ -162,6 +231,20 @@ struct xdr_stream {
#define xdr_zero 0
#define XDR_QUADLEN(l) (((l) + 3) >> 2)
+struct nfs_dir {
+ DIR dir;
+
+ /*
+ * stream points to the next entry3 in the reply member of READDIR3res
+ * (if any, to the end indicator otherwise).
+ */
+ struct xdr_stream stream;
+ struct dirent ent;
+ struct file_priv *priv;
+ uint64_t cookie;
+ char cookieverf[NFS3_COOKIEVERFSIZE];
+};
+
static void xdr_init(struct xdr_stream *stream, void *buf, int len)
{
stream->p = stream->buf = buf;
@@ -192,8 +275,10 @@ static __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
return p;
}
-static int decode_filename(struct xdr_stream *xdr,
- char *name, u32 *length)
+/*
+ * name is expected to point to a buffer with a size of at least 256 bytes.
+ */
+static int decode_filename(struct xdr_stream *xdr, char *name, u32 *length)
{
__be32 *p;
u32 count;
@@ -201,7 +286,7 @@ static int decode_filename(struct xdr_stream *xdr,
p = xdr_inline_decode(xdr, 4);
if (!p)
goto out_overflow;
- count = be32_to_cpup(p);
+ count = ntoh32(net_read_uint32(p));
if (count > 255)
goto out_nametoolong;
p = xdr_inline_decode(xdr, count);
@@ -211,11 +296,13 @@ static int decode_filename(struct xdr_stream *xdr,
name[count] = 0;
*length = count;
return 0;
+
out_nametoolong:
- printk("NFS: returned filename too long: %u\n", count);
+ printf("%s: returned a too long filename: %u\n", __func__, count);
return -ENAMETOOLONG;
+
out_overflow:
- printf("%s overflow\n",__func__);
+ printf("%s: premature end of packet\n", __func__);
return -EIO;
}
@@ -224,34 +311,18 @@ out_overflow:
*/
static uint32_t *rpc_add_credentials(uint32_t *p)
{
- int hl;
- int hostnamelen = 0;
-
/*
- * Here's the executive summary on authentication requirements of the
- * various NFS server implementations: Linux accepts both AUTH_NONE
- * and AUTH_UNIX authentication (also accepts an empty hostname field
- * in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts
- * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
- * scheme). To be safe, use AUTH_UNIX and pass the hostname if we have
- * it (if the BOOTP/DHCP reply didn't give one, just use an empty
- * hostname).
+ * *BSD refuses AUTH_NONE, so use AUTH_UNIX. An empty hostname is OK for
+ * both Linux and *BSD.
*/
- hl = (hostnamelen + 3) & ~3;
-
/* Provide an AUTH_UNIX credential. */
- *p++ = htonl(1); /* AUTH_UNIX */
- *p++ = htonl(hl + 20); /* auth length */
- *p++ = htonl(0); /* stamp */
- *p++ = htonl(hostnamelen); /* hostname string */
+ *p++ = hton32(1); /* AUTH_UNIX */
+ *p++ = hton32(20); /* auth length: 20 + strlen(hostname) */
+ *p++ = hton32(0); /* stamp */
+ *p++ = hton32(0); /* hostname string length */
+ /* memcpy(p, "", 0); p += 0; <- empty host name */
- if (hostnamelen & 3)
- *(p + hostnamelen / 4) = 0; /* add zero padding */
-
- /* memcpy(p, hostname, hostnamelen); */ /* empty hostname */
-
- p += hl / 4;
*p++ = 0; /* uid */
*p++ = 0; /* gid */
*p++ = 0; /* auxiliary gid list */
@@ -263,7 +334,8 @@ static uint32_t *rpc_add_credentials(uint32_t *p)
return p;
}
-static int rpc_check_reply(unsigned char *pkt, int rpc_prog, unsigned long rpc_id, int *nfserr)
+static int rpc_check_reply(unsigned char *pkt,
+ int rpc_prog, uint32_t rpc_id, int *nfserr)
{
uint32_t *data;
struct rpc_reply rpc;
@@ -275,8 +347,13 @@ static int rpc_check_reply(unsigned char *pkt, int rpc_prog, unsigned long rpc_i
memcpy(&rpc, pkt, sizeof(rpc));
- if (ntohl(rpc.id) != rpc_id)
+ if (ntoh32(rpc.id) != rpc_id) {
+ if (rpc_id - ntoh32(rpc.id) == 1)
+ /* stale packet, wait a bit longer */
+ return 0;
+
return -EINVAL;
+ }
if (rpc.rstatus ||
rpc.verifier ||
@@ -288,7 +365,7 @@ static int rpc_check_reply(unsigned char *pkt, int rpc_prog, unsigned long rpc_i
return 0;
data = (uint32_t *)(pkt + sizeof(struct rpc_reply));
- *nfserr = ntohl(net_read_uint32(data));
+ *nfserr = ntoh32(net_read_uint32(data));
*nfserr = -*nfserr;
debug("%s: state: %d, err %d\n", __func__, nfs_state, *nfserr);
@@ -303,37 +380,41 @@ static int rpc_req(struct nfs_priv *npriv, int rpc_prog, int rpc_proc,
uint32_t *data, int datalen)
{
struct rpc_call pkt;
- unsigned long id;
- int dport;
+ unsigned short dport;
int ret;
unsigned char *payload = net_udp_get_payload(npriv->con);
int nfserr;
int tries = 0;
npriv->rpc_id++;
- id = npriv->rpc_id;
- pkt.id = htonl(id);
- pkt.type = htonl(MSG_CALL);
- pkt.rpcvers = htonl(2); /* use RPC version 2 */
- pkt.prog = htonl(rpc_prog);
- pkt.vers = htonl(2); /* portmapper is version 2 */
- pkt.proc = htonl(rpc_proc);
+ pkt.id = hton32(npriv->rpc_id);
+ pkt.type = hton32(MSG_CALL);
+ pkt.rpcvers = hton32(2); /* use RPC version 2 */
+ pkt.prog = hton32(rpc_prog);
+ pkt.proc = hton32(rpc_proc);
- memcpy(payload, &pkt, sizeof(pkt));
- memcpy(payload + sizeof(pkt), data, datalen * sizeof(uint32_t));
+ debug("%s: prog: %d, proc: %d\n", __func__, rpc_prog, rpc_proc);
- if (rpc_prog == PROG_PORTMAP)
+ if (rpc_prog == PROG_PORTMAP) {
dport = SUNRPC_PORT;
- else if (rpc_prog == PROG_MOUNT)
+ pkt.vers = hton32(2);
+ } else if (rpc_prog == PROG_MOUNT) {
dport = npriv->mount_port;
- else
+ pkt.vers = hton32(3);
+ } else {
dport = npriv->nfs_port;
+ pkt.vers = hton32(3);
+ }
- npriv->con->udp->uh_dport = htons(dport);
+ memcpy(payload, &pkt, sizeof(pkt));
+ memcpy(payload + sizeof(pkt), data, datalen * sizeof(uint32_t));
+
+ npriv->con->udp->uh_dport = hton16(dport);
again:
- ret = net_udp_send(npriv->con, sizeof(pkt) + datalen * sizeof(uint32_t));
+ ret = net_udp_send(npriv->con,
+ sizeof(pkt) + datalen * sizeof(uint32_t));
nfs_timer_start = get_time_ns();
@@ -368,7 +449,7 @@ again:
/*
* rpc_lookup_req - Lookup RPC Port numbers
*/
-static int rpc_lookup_req(struct nfs_priv *npriv, int prog, int ver)
+static int rpc_lookup_req(struct nfs_priv *npriv, uint32_t prog, uint32_t ver)
{
uint32_t data[16];
int ret;
@@ -376,33 +457,149 @@ static int rpc_lookup_req(struct nfs_priv *npriv, int prog, int ver)
data[0] = 0; data[1] = 0; /* auth credential */
data[2] = 0; data[3] = 0; /* auth verifier */
- data[4] = htonl(prog);
- data[5] = htonl(ver);
- data[6] = htonl(17); /* IP_UDP */
+ data[4] = hton32(prog);
+ data[5] = hton32(ver);
+ data[6] = hton32(17); /* IP_UDP */
data[7] = 0;
ret = rpc_req(npriv, PROG_PORTMAP, PORTMAP_GETPORT, data, 8);
if (ret)
return ret;
- port = net_read_uint32((uint32_t *)(nfs_packet + sizeof(struct rpc_reply)));
+ port = ntoh32(net_read_uint32(nfs_packet + sizeof(struct rpc_reply)));
+ return port;
+}
- switch (prog) {
- case PROG_MOUNT:
- npriv->mount_port = ntohl(port);
- debug("mount port: %d\n", npriv->mount_port);
+static uint32_t *nfs_add_uint32(uint32_t *p, uint32_t val)
+{
+ *p++ = hton32(val);
+ return p;
+}
+
+static uint32_t *nfs_add_uint64(uint32_t *p, uint64_t val)
+{
+ uint64_t nval = hton64(val);
+
+ memcpy(p, &nval, 8);
+ return p + 2;
+}
+
+static uint32_t *nfs_add_fh3(uint32_t *p, unsigned fh_len, const char *fh)
+{
+ *p++ = hton32(fh_len);
+
+ /* zero padding */
+ if (fh_len & 3)
+ p[fh_len / 4] = 0;
+
+ memcpy(p, fh, fh_len);
+ p += DIV_ROUND_UP(fh_len, 4);
+ return p;
+}
+
+static uint32_t *nfs_add_filename(uint32_t *p,
+ uint32_t filename_len, const char *filename)
+{
+ *p++ = hton32(filename_len);
+
+ /* zero padding */
+ if (filename_len & 3)
+ p[filename_len / 4] = 0;
+
+ memcpy(p, filename, filename_len);
+ p += DIV_ROUND_UP(filename_len, 4);
+ return p;
+}
+
+/* This is a 1:1 mapping for Linux, the compiler optimizes it out */
+static const struct {
+ uint32_t nfsmode;
+ unsigned short statmode;
+} nfs3_mode_bits[] = {
+ { 0x00001, S_IXOTH },
+ { 0x00002, S_IWOTH },
+ { 0x00004, S_IROTH },
+ { 0x00008, S_IXGRP },
+ { 0x00010, S_IWGRP },
+ { 0x00020, S_IRGRP },
+ { 0x00040, S_IXUSR },
+ { 0x00080, S_IWUSR },
+ { 0x00100, S_IRUSR },
+ { 0x00200, S_ISVTX },
+ { 0x00400, S_ISGID },
+ { 0x00800, S_ISUID },
+};
+
+static int nfs_fattr3_to_stat(uint32_t *p, struct stat *s)
+{
+ uint32_t mode;
+ size_t i;
+
+ /* offsetof(struct fattr3, type) = 0 */
+ switch (ntoh32(net_read_uint32(p + 0))) {
+ case NF3REG:
+ s->st_mode = S_IFREG;
+ break;
+ case NF3DIR:
+ s->st_mode = S_IFDIR;
+ break;
+ case NF3BLK:
+ s->st_mode = S_IFBLK;
+ break;
+ case NF3CHR:
+ s->st_mode = S_IFCHR;
+ break;
+ case NF3LNK:
+ s->st_mode = S_IFLNK;
+ break;
+ case NF3SOCK:
+ s->st_mode = S_IFSOCK;
break;
- case PROG_NFS:
- npriv->nfs_port = ntohl(port);
- debug("nfs port: %d\n", npriv->nfs_port);
+ case NF3FIFO:
+ s->st_mode = S_IFIFO;
break;
default:
- return -EINVAL;
+ printf("%s: invalid mode %x\n",
+ __func__, ntoh32(net_read_uint32(p + 0)));
+ return -EIO;
+ }
+
+ /* offsetof(struct fattr3, mode) = 4 */
+ mode = ntoh32(net_read_uint32(p + 1));
+ for (i = 0; i < ARRAY_SIZE(nfs3_mode_bits); ++i) {
+ if (mode & nfs3_mode_bits[i].nfsmode)
+ s->st_mode |= nfs3_mode_bits[i].statmode;
}
+ /* offsetof(struct fattr3, size) = 20 */
+ s->st_size = ntoh64(net_read_uint64(p + 5));
+
return 0;
}
+static uint32_t *nfs_read_post_op_attr(uint32_t *p, struct stat **s)
+{
+ struct stat dummy;
+ /*
+ * union post_op_attr switch (bool attributes_follow) {
+ * case TRUE:
+ * fattr3 attributes;
+ * case FALSE:
+ * void;
+ * };
+ */
+
+ if (ntoh32(net_read_uint32(p++))) {
+ nfs_fattr3_to_stat(p, s ? *s : &dummy);
+ p += 21;
+ } else if (s) {
+ /* no attributes available */
+ *s = NULL;
+ }
+
+ return p;
+}
+
/*
* nfs_mount_req - Mount an NFS Filesystem
*/
@@ -421,7 +618,7 @@ static int nfs_mount_req(struct nfs_priv *npriv)
p = &(data[0]);
p = rpc_add_credentials(p);
- *p++ = htonl(pathlen);
+ *p++ = hton32(pathlen);
if (pathlen & 3)
*(p + pathlen / 4) = 0;
@@ -434,7 +631,16 @@ static int nfs_mount_req(struct nfs_priv *npriv)
if (ret)
return ret;
- memcpy(npriv->rootfh, nfs_packet + sizeof(struct rpc_reply) + 4, NFS_FHSIZE);
+ p = nfs_packet + sizeof(struct rpc_reply) + 4;
+
+ npriv->rootfh_len = ntoh32(net_read_uint32(p++));
+ if (npriv->rootfh_len > NFS3_FHSIZE) {
+ printf("%s: file handle too big: %lu\n", __func__,
+ (unsigned long)npriv->rootfh_len);
+ return -EIO;
+ }
+ memcpy(npriv->rootfh, p, npriv->rootfh_len);
+ p += DIV_ROUND_UP(npriv->rootfh_len, 4);
return 0;
}
@@ -454,12 +660,7 @@ static void nfs_umount_req(struct nfs_priv *npriv)
p = &(data[0]);
p = rpc_add_credentials(p);
- *p++ = htonl(pathlen);
- if (pathlen & 3)
- *(p + pathlen / 4) = 0;
-
- memcpy (p, npriv->path, pathlen);
- p += (pathlen + 3) / 4;
+ p = nfs_add_filename(p, pathlen, npriv->path);
len = p - &(data[0]);
@@ -468,36 +669,68 @@ static void nfs_umount_req(struct nfs_priv *npriv)
/*
* nfs_lookup_req - Lookup Pathname
+ *
+ * *s is set to NULL if LOOKUP3resok doesn't contain obj_attributes.
*/
-static int nfs_lookup_req(struct file_priv *priv, const char *filename,
- int fnamelen)
+static int nfs_lookup_req(struct file_priv *priv,
+ uint32_t filename_len, const char *filename, struct stat **s)
{
uint32_t data[1024];
uint32_t *p;
int len;
int ret;
+ /*
+ * struct LOOKUP3args {
+ * diropargs3 what;
+ * };
+ *
+ * struct LOOKUP3resok {
+ * nfs_fh3 object;
+ * post_op_attr obj_attributes;
+ * post_op_attr dir_attributes;
+ * };
+ *
+ * struct LOOKUP3resfail {
+ * post_op_attr dir_attributes;
+ * };
+ *
+ * union LOOKUP3res switch (nfsstat3 status) {
+ * case NFS3_OK:
+ * LOOKUP3resok resok;
+ * default:
+ * LOOKUP3resfail resfail;
+ * };
+ */
+
p = &(data[0]);
p = rpc_add_credentials(p);
- memcpy(p, priv->filefh, NFS_FHSIZE);
+ /* what.dir */
+ p = nfs_add_fh3(p, priv->filefh_len, priv->filefh);
- p += (NFS_FHSIZE / 4);
- *p++ = htonl(fnamelen);
-
- if (fnamelen & 3)
- *(p + fnamelen / 4) = 0;
-
- memcpy(p, filename, fnamelen);
- p += (fnamelen + 3) / 4;
+ /* what.name */
+ p = nfs_add_filename(p, filename_len, filename);
len = p - &(data[0]);
- ret = rpc_req(priv->npriv, PROG_NFS, NFS_LOOKUP, data, len);
+ ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_LOOKUP, data, len);
if (ret)
return ret;
- memcpy(priv->filefh, nfs_packet + sizeof(struct rpc_reply) + 4, NFS_FHSIZE);
+ p = nfs_packet + sizeof(struct rpc_reply) + 4;
+
+ priv->filefh_len = ntoh32(net_read_uint32(p++));
+ if (priv->filefh_len > NFS3_FHSIZE) {
+ debug("%s: file handle too big: %lu\n", __func__,
+ (unsigned long)priv->filefh_len);
+ return -EIO;
+ }
+ memcpy(priv->filefh, p, priv->filefh_len);
+ p += DIV_ROUND_UP(priv->filefh_len, 4);
+
+ if (s)
+ nfs_read_post_op_attr(p, s);
return 0;
}
@@ -508,33 +741,48 @@ static int nfs_attr_req(struct file_priv *priv, struct stat *s)
uint32_t *p;
int len;
int ret;
- struct fattr *fattr;
- uint32_t type;
+
+ /*
+ * struct GETATTR3args {
+ * nfs_fh3 object;
+ * }
+ *
+ * struct GETATTR3resok {
+ * fattr3 obj_attributes;
+ * };
+ *
+ * union GETATTR3res switch (nfsstat3 status) {
+ * case NFS3_OK:
+ * GETATTR3resok resok;
+ * default:
+ * void;
+ * }
+ */
p = &(data[0]);
p = rpc_add_credentials(p);
- memcpy(p, priv->filefh, NFS_FHSIZE);
- p += (NFS_FHSIZE / 4);
- *p++ = 0;
+ /* object */
+ p = nfs_add_fh3(p, priv->filefh_len, priv->filefh);
len = p - &(data[0]);
- ret = rpc_req(priv->npriv, PROG_NFS, NFS_GETATTR, data, len);
+ ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_GETATTR, data, len);
if (ret)
return ret;
- fattr = nfs_packet + sizeof(struct rpc_reply) + 4;
-
- type = ntohl(net_read_uint32(&fattr->type));
+ p = nfs_packet + sizeof(struct rpc_reply) + 4;
- s->st_size = ntohl(net_read_uint32(&fattr->size));
- s->st_mode = ntohl(net_read_uint32(&fattr->mode));
+ nfs_fattr3_to_stat(p, s);
return 0;
}
-static void *nfs_readdirattr_req(struct file_priv *priv, int *plen, uint32_t cookie)
+/*
+ * returns with dir->stream pointing to the first entry
+ * of dirlist3 res.resok.reply
+ */
+static void *nfs_readdirattr_req(struct file_priv *priv, struct nfs_dir *dir)
{
uint32_t data[1024];
uint32_t *p;
@@ -542,26 +790,79 @@ static void *nfs_readdirattr_req(struct file_priv *priv, int *plen, uint32_t coo
int ret;
void *buf;
+ /*
+ * struct READDIR3args {
+ * nfs_fh3 dir;
+ * cookie3 cookie;
+ * cookieverf3 cookieverf;
+ * count3 count;
+ * };
+ *
+ * struct entry3 {
+ * fileid3 fileid;
+ * filename3 name;
+ * cookie3 cookie;
+ * entry3 *nextentry;
+ * };
+ *
+ * struct dirlist3 {
+ * entry3 *entries;
+ * bool eof;
+ * };
+ *
+ * struct READDIR3resok {
+ * post_op_attr dir_attributes;
+ * cookieverf3 cookieverf;
+ * dirlist3 reply;
+ * };
+ *
+ * struct READDIR3resfail {
+ * post_op_attr dir_attributes;
+ * };
+ *
+ * union READDIR3res switch (nfsstat3 status) {
+ * case NFS3_OK:
+ * READDIR3resok resok;
+ * default:
+ * READDIR3resfail resfail;
+ * };
+ */
+
p = &(data[0]);
p = rpc_add_credentials(p);
- memcpy(p, priv->filefh, NFS_FHSIZE);
- p += (NFS_FHSIZE / 4);
- *p++ = htonl(cookie); /* cookie */
- *p++ = htonl(1024); /* count */
- *p++ = 0;
+ p = nfs_add_fh3(p, priv->filefh_len, priv->filefh);
+ p = nfs_add_uint64(p, dir->cookie);
- len = p - &(data[0]);
+ memcpy(p, dir->cookieverf, NFS3_COOKIEVERFSIZE);
+ p += NFS3_COOKIEVERFSIZE / 4;
+
+ p = nfs_add_uint32(p, 1024); /* count */
- ret = rpc_req(priv->npriv, PROG_NFS, NFS_READDIR, data, len);
+ ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_READDIR, data, p - data);
if (ret)
return NULL;
- *plen = nfs_len - sizeof(struct rpc_reply) + 4;
+ p = nfs_packet + sizeof(struct rpc_reply) + 4;
+ p = nfs_read_post_op_attr(p, NULL);
- buf = xzalloc(*plen);
+ /* update cookieverf */
+ memcpy(dir->cookieverf, p, NFS3_COOKIEVERFSIZE);
+ p += NFS3_COOKIEVERFSIZE / 4;
- memcpy(buf, nfs_packet + sizeof(struct rpc_reply) + 4, *plen);
+ len = nfs_packet + nfs_len - (void *)p;
+ if (!len) {
+ printf("%s: huh, no payload left\n", __func__);
+ return NULL;
+ }
+
+ buf = xzalloc(len);
+
+ memcpy(buf, p, len);
+
+ xdr_init(&dir->stream, buf, len);
+
+ /* now xdr points to dirlist3 res.resok.reply */
return buf;
}
@@ -569,35 +870,74 @@ static void *nfs_readdirattr_req(struct file_priv *priv, int *plen, uint32_t coo
/*
* nfs_read_req - Read File on NFS Server
*/
-static int nfs_read_req(struct file_priv *priv, int offset, int readlen)
+static int nfs_read_req(struct file_priv *priv, uint64_t offset,
+ uint32_t readlen)
{
uint32_t data[1024];
uint32_t *p;
- uint32_t *filedata;
int len;
int ret;
- int rlen;
+ uint32_t rlen, eof;
+ /*
+ * struct READ3args {
+ * nfs_fh3 file;
+ * offset3 offset;
+ * count3 count;
+ * };
+ *
+ * struct READ3resok {
+ * post_op_attr file_attributes;
+ * count3 count;
+ * bool eof;
+ * opaque data<>;
+ * };
+ *
+ * struct READ3resfail {
+ * post_op_attr file_attributes;
+ * };
+ *
+ * union READ3res switch (nfsstat3 status) {
+ * case NFS3_OK:
+ * READ3resok resok;
+ * default:
+ * READ3resfail resfail;
+ * };
+ */
p = &(data[0]);
p = rpc_add_credentials(p);
- memcpy (p, priv->filefh, NFS_FHSIZE);
- p += (NFS_FHSIZE / 4);
- *p++ = htonl(offset);
- *p++ = htonl(readlen);
- *p++ = 0;
+ p = nfs_add_fh3(p, priv->filefh_len, priv->filefh);
+ p = nfs_add_uint64(p, offset);
+ p = nfs_add_uint32(p, readlen);
len = p - &(data[0]);
- ret = rpc_req(priv->npriv, PROG_NFS, NFS_READ, data, len);
+ ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_READ, data, len);
if (ret)
return ret;
- filedata = (uint32_t *)(nfs_packet + sizeof(struct rpc_reply));
+ p = nfs_packet + sizeof(struct rpc_reply) + 4;
+
+ p = nfs_read_post_op_attr(p, NULL);
+
+ rlen = ntoh32(net_read_uint32(p));
- rlen = ntohl(net_read_uint32(filedata + 18));
+ /* skip over count */
+ p += 1;
- kfifo_put(priv->fifo, (char *)(filedata + 19), rlen);
+ eof = ntoh32(net_read_uint32(p));
+
+ /*
+ * skip over eof and count embedded in the representation of data
+ * assuming it equals rlen above.
+ */
+ p += 2;
+
+ if (readlen && !rlen && !eof)
+ return -EIO;
+
+ kfifo_put(priv->fifo, (char *)p, rlen);
return 0;
}
@@ -636,43 +976,45 @@ static int nfs_truncate(struct device_d *dev, FILE *f, ulong size)
return -ENOSYS;
}
-static struct file_priv *nfs_do_open(struct device_d *dev, const char *filename)
+static struct file_priv *nfs_do_open(struct device_d *dev,
+ const char *filename, struct stat **s)
{
struct file_priv *priv;
struct nfs_priv *npriv = dev->priv;
int ret;
- const char *fname, *tok;
+ const char *tok;
+ debug("%s: filename = %s\n", __func__, filename);
priv = xzalloc(sizeof(*priv));
priv->npriv = npriv;
if (!*filename) {
- memcpy(priv->filefh, npriv->rootfh, NFS_FHSIZE);
+ priv->filefh_len = npriv->rootfh_len;
+ memcpy(priv->filefh, npriv->rootfh, npriv->rootfh_len);
return priv;
}
filename++;
- fname = filename;
+ priv->filefh_len = npriv->rootfh_len;
+ memcpy(priv->filefh, npriv->rootfh, NFS3_FHSIZE);
- memcpy(priv->filefh, npriv->rootfh, NFS_FHSIZE);
+ while (*filename) {
+ size_t flen;
- while (*fname) {
- int flen;
-
- tok = strchr(fname, '/');
+ tok = strchr(filename, '/');
if (tok)
- flen = tok - fname;
+ flen = tok - filename;
else
- flen = strlen(fname);
+ flen = strlen(filename);
- ret = nfs_lookup_req(priv, fname, flen);
+ ret = nfs_lookup_req(priv, flen, filename, s);
if (ret)
goto out;
if (tok)
- fname += flen + 1;
+ filename += flen + 1;
else
break;
}
@@ -693,19 +1035,28 @@ static void nfs_do_close(struct file_priv *priv)
free(priv);
}
-static struct file_priv *nfs_do_stat(struct device_d *dev, const char *filename, struct stat *s)
+static struct file_priv *nfs_do_stat(struct device_d *dev,
+ const char *filename, struct stat *s)
{
struct file_priv *priv;
int ret;
+ struct stat **sptr = &s;
- priv = nfs_do_open(dev, filename);
+ debug("%s: filename = %s\n", __func__, filename);
+ priv = nfs_do_open(dev, filename, sptr);
if (IS_ERR(priv))
return priv;
- ret = nfs_attr_req(priv, s);
- if (ret) {
- nfs_do_close(priv);
- return ERR_PTR(ret);
+ if (!*sptr) {
+ /*
+ * The nfs server didn't provide obj_attributes in the lookup
+ * reply, so ask for them explicitly.
+ */
+ ret = nfs_attr_req(priv, s);
+ if (ret) {
+ nfs_do_close(priv);
+ return ERR_PTR(ret);
+ }
}
return priv;
@@ -715,35 +1066,52 @@ static int nfs_readlink_req(struct file_priv *priv, char* buf, size_t size)
{
uint32_t data[1024];
uint32_t *p;
- int len;
+ uint32_t len;
int ret;
- char *path;
- uint32_t *filedata;
+ /*
+ * struct READLINK3args {
+ * nfs_fh3 symlink;
+ * };
+ *
+ * struct READLINK3resok {
+ * post_op_attr symlink_attributes;
+ * nfspath3 data;
+ * };
+ *
+ * struct READLINK3resfail {
+ * post_op_attr symlink_attributes;
+ * }
+ *
+ * union READLINK3res switch (nfsstat3 status) {
+ * case NFS3_OK:
+ * READLINK3resok resok;
+ * default:
+ * READLINK3resfail resfail;
+ * };
+ */
p = &(data[0]);
p = rpc_add_credentials(p);
- memcpy(p, priv->filefh, NFS_FHSIZE);
- p += (NFS_FHSIZE / 4);
+ p = nfs_add_fh3(p, priv->filefh_len, priv->filefh);
len = p - &(data[0]);
- ret = rpc_req(priv->npriv, PROG_NFS, NFS_READLINK, data, len);
+ ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_READLINK, data, len);
if (ret)
return ret;
- filedata = nfs_packet + sizeof(struct rpc_reply);
- filedata++;
+ p = nfs_packet + sizeof(struct rpc_reply) + 4;
- len = ntohl(net_read_uint32(filedata)); /* new path length */
- filedata++;
+ p = nfs_read_post_op_attr(p, NULL);
- path = (char *)filedata;
+ len = ntoh32(net_read_uint32(p)); /* new path length */
+ p++;
if (len > size)
- len = size;
+ return -ENOMEM;
- memcpy(buf, path, len);
+ memcpy(buf, p, len);
return 0;
}
@@ -753,9 +1121,8 @@ static int nfs_readlink(struct device_d *dev, const char *filename,
{
struct file_priv *priv;
int ret;
- struct stat s;
- priv = nfs_do_stat(dev, filename, &s);
+ priv = nfs_do_open(dev, filename, NULL);
if (IS_ERR(priv))
return PTR_ERR(priv);
@@ -807,32 +1174,17 @@ static int nfs_write(struct device_d *_dev, FILE *file, const void *inbuf,
static int nfs_read(struct device_d *dev, FILE *file, void *buf, size_t insize)
{
struct file_priv *priv = file->inode;
- int now, outsize = 0, ret, pos = file->pos;
-
- while (insize) {
- now = kfifo_get(priv->fifo, buf, insize);
- outsize += now;
- buf += now;
- insize -= now;
-
- if (insize) {
- /* do not use min as insize is a size_t */
- if (insize < 1024)
- now = insize;
- else
- now = 1024;
-
- if (pos + now > file->size)
- now = file->size - pos;
-
- ret = nfs_read_req(priv, pos, now);
- if (ret)
- return ret;
- pos += now;
- }
+
+ if (insize > 1024)
+ insize = 1024;
+
+ if (insize && !kfifo_len(priv->fifo)) {
+ int ret = nfs_read_req(priv, file->pos, insize);
+ if (ret)
+ return ret;
}
- return outsize;
+ return kfifo_get(priv->fifo, buf, insize);
}
static loff_t nfs_lseek(struct device_d *dev, FILE *file, loff_t pos)
@@ -845,110 +1197,89 @@ static loff_t nfs_lseek(struct device_d *dev, FILE *file, loff_t pos)
return file->pos;
}
-struct nfs_dir {
- DIR dir;
- struct xdr_stream stream;
- struct dirent ent;
- struct file_priv *priv;
- uint32_t cookie;
-};
-
static DIR *nfs_opendir(struct device_d *dev, const char *pathname)
{
struct file_priv *priv;
- struct stat s;
- int ret;
- void *buf;
+ void *buf = NULL;
struct nfs_dir *dir;
- int len;
- priv = nfs_do_open(dev, pathname);
+ priv = nfs_do_open(dev, pathname, NULL);
if (IS_ERR(priv))
return NULL;
- ret = nfs_attr_req(priv, &s);
- if (ret)
- return NULL;
-
- if (!S_ISDIR(s.st_mode))
- return NULL;
-
dir = xzalloc(sizeof(*dir));
dir->priv = priv;
- buf = nfs_readdirattr_req(priv, &len, 0);
- if (!buf)
- return NULL;
-
- xdr_init(&dir->stream, buf, len);
+ /* cookie == 0 and cookieverf == 0 means start of dir */
+ buf = nfs_readdirattr_req(priv, dir);
+ if (!buf) {
+ debug("%s: nfs_readdirattr_req failed\n", __func__);
+ goto err;
+ }
return &dir->dir;
+
+err:
+ free(buf);
+ free(dir);
+ nfs_do_close(priv);
+ return NULL;
}
static struct dirent *nfs_readdir(struct device_d *dev, DIR *dir)
{
- struct nfs_dir *ndir = (void *)dir;
- __be32 *p;
+ struct nfs_dir *ndir = container_of(dir, struct nfs_dir, dir);
+ uint32_t *p;
int ret;
int len;
struct xdr_stream *xdr = &ndir->stream;
again:
p = xdr_inline_decode(xdr, 4);
- if (!p)
- goto out_overflow;
+ if (!p) {
+ printf("%s: premature end of packet\n", __func__);
+ return NULL;
+ }
- if (*p++ == xdr_zero) {
+ if (!net_read_uint32(p)) {
+ /* eof? */
p = xdr_inline_decode(xdr, 4);
- if (!p)
- goto out_overflow;
- if (*p++ == xdr_zero) {
- void *buf;
- int len;
-
- /*
- * End of current entries, read next chunk.
- */
+ if (!p) {
+ printf("%s: premature end of packet\n", __func__);
+ return NULL;
+ }
+ if (net_read_uint32(p))
+ return NULL;
- free(ndir->stream.buf);
+ if (!nfs_readdirattr_req(ndir->priv, ndir)) {
+ printf("%s: nfs_readdirattr_req failed\n", __func__);
+ return NULL;
+ }
- buf = nfs_readdirattr_req(ndir->priv, &len, ndir->cookie);
- if (!buf)
- return NULL;
+ goto again;
+ }
- xdr_init(&ndir->stream, buf, len);
+ /* there is another entry available in the last reply */
- goto again;
- }
- return NULL; /* -EINVAL */
+ /* skip over fileid */
+ p = xdr_inline_decode(xdr, 8);
+ if (!p) {
+ printf("%s: premature end of packet\n", __func__);
+ return NULL;
}
- p = xdr_inline_decode(xdr, 4);
- if (!p)
- goto out_overflow;
-
ret = decode_filename(xdr, ndir->ent.d_name, &len);
if (ret)
return NULL;
- /*
- * The type (size and byte order) of nfscookie isn't defined in
- * RFC 1094. This implementation assumes that it's an XDR uint32.
- */
- p = xdr_inline_decode(xdr, 4);
- if (!p)
- goto out_overflow;
-
- ndir->cookie = be32_to_cpup(p);
+ p = xdr_inline_decode(xdr, 8);
+ if (!p) {
+ printf("%s: premature end of packet\n", __func__);
+ return NULL;
+ }
+ ndir->cookie = ntoh64(net_read_uint64(p));
return &ndir->ent;
-
-out_overflow:
-
- printf("nfs: overflow error\n");
-
- return NULL;
-
}
static int nfs_closedir(struct device_d *dev, DIR *dir)
@@ -1010,17 +1341,27 @@ static int nfs_probe(struct device_d *dev)
/* Need a priviliged source port */
net_udp_bind(npriv->con, 1000);
- ret = rpc_lookup_req(npriv, PROG_MOUNT, 1);
- if (ret) {
- printf("lookup mount port failed with %d\n", ret);
- goto err2;
+ parseopt_hu(fsdev->options, "mountport", &npriv->mount_port);
+ if (!npriv->mount_port) {
+ ret = rpc_lookup_req(npriv, PROG_MOUNT, 3);
+ if (ret < 0) {
+ printf("lookup mount port failed with %d\n", ret);
+ goto err2;
+ }
+ npriv->mount_port = ret;
}
-
- ret = rpc_lookup_req(npriv, PROG_NFS, 2);
- if (ret) {
- printf("lookup nfs port failed with %d\n", ret);
- goto err2;
+ debug("mount port: %hu\n", npriv->mount_port);
+
+ parseopt_hu(fsdev->options, "port", &npriv->nfs_port);
+ if (!npriv->nfs_port) {
+ ret = rpc_lookup_req(npriv, PROG_NFS, 3);
+ if (ret < 0) {
+ printf("lookup nfs port failed with %d\n", ret);
+ goto err2;
+ }
+ npriv->nfs_port = ret;
}
+ debug("nfs port: %d\n", npriv->nfs_port);
ret = nfs_mount_req(npriv);
if (ret) {
diff --git a/fs/parseopt.c b/fs/parseopt.c
new file mode 100644
index 0000000000..fbe53cfb02
--- /dev/null
+++ b/fs/parseopt.c
@@ -0,0 +1,34 @@
+#include <common.h>
+
+#include "parseopt.h"
+
+void parseopt_hu(const char *options, const char *opt, unsigned short *val)
+{
+ const char *start;
+ size_t optlen = strlen(opt);
+ ulong v;
+ char *endp;
+
+again:
+ start = strstr(options, opt);
+
+ if (!start)
+ return;
+
+ if (start > options && start[-1] != ',') {
+ options = start;
+ goto again;
+ }
+
+ if (start[optlen] != '=') {
+ options = start;
+ goto again;
+ }
+
+ v = simple_strtoul(start + optlen + 1, &endp, 0);
+ if (v > USHORT_MAX)
+ return;
+
+ if (*endp == ',' || *endp == '\0')
+ *val = v;
+}
diff --git a/fs/parseopt.h b/fs/parseopt.h
new file mode 100644
index 0000000000..a8523b6a10
--- /dev/null
+++ b/fs/parseopt.h
@@ -0,0 +1 @@
+void parseopt_hu(const char *options, const char *opt, unsigned short *val);