summaryrefslogtreecommitdiffstats
path: root/fs/nfs.c
diff options
context:
space:
mode:
authorUwe Kleine-König <u.kleine-koenig@pengutronix.de>2014-02-07 22:28:11 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2014-02-10 09:02:20 +0100
commitf43f827bb4bc03526cbfa4ac58fac6747e3caddb (patch)
tree9f6c711d94a17867387f83332eba84a84731d7d6 /fs/nfs.c
parent8abaf1ad7137e0802761d85b456591de9eaa7a5f (diff)
downloadbarebox-f43f827bb4bc03526cbfa4ac58fac6747e3caddb.tar.gz
barebox-f43f827bb4bc03526cbfa4ac58fac6747e3caddb.tar.xz
nfs: switch to nfs3
This was tested against nfs-kernel-server and unfs3. Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'fs/nfs.c')
-rw-r--r--fs/nfs.c894
1 files changed, 625 insertions, 269 deletions
diff --git a/fs/nfs.c b/fs/nfs.c
index 79df6a8e4f..ab33c91ef0 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,7 @@
#include <linux/err.h>
#include <kfifo.h>
#include <sizes.h>
+#include <byteorder.h>
#define SUNRPC_PORT 111
@@ -48,34 +50,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 +129,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 +229,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 +273,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 +284,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 +294,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;
}
@@ -230,10 +315,10 @@ static uint32_t *rpc_add_credentials(uint32_t *p)
*/
/* Provide an AUTH_UNIX credential. */
- *p++ = htonl(1); /* AUTH_UNIX */
- *p++ = htonl(20); /* auth length: 20 + strlen(hostname) */
- *p++ = htonl(0); /* stamp */
- *p++ = htonl(0); /* hostname string length */
+ *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 */
*p++ = 0; /* uid */
@@ -247,7 +332,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;
@@ -259,8 +345,8 @@ 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 (rpc_id - ntohl(rpc.id) == 1)
+ if (ntoh32(rpc.id) != rpc_id) {
+ if (rpc_id - ntoh32(rpc.id) == 1)
/* stale packet, wait a bit longer */
return 0;
@@ -277,7 +363,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);
@@ -292,37 +378,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);
+ }
+
+ memcpy(payload, &pkt, sizeof(pkt));
+ memcpy(payload + sizeof(pkt), data, datalen * sizeof(uint32_t));
- npriv->con->udp->uh_dport = htons(dport);
+ 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();
@@ -357,7 +447,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;
@@ -365,19 +455,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 = ntohl(net_read_uint32(nfs_packet + sizeof(struct rpc_reply)));
+ port = ntoh32(net_read_uint32(nfs_packet + sizeof(struct rpc_reply)));
return 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 NF3FIFO:
+ s->st_mode = S_IFIFO;
+ break;
+ default:
+ 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
*/
@@ -396,7 +616,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;
@@ -409,7 +629,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;
}
@@ -429,12 +658,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]);
@@ -443,36 +667,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);
-
- p += (NFS_FHSIZE / 4);
- *p++ = htonl(fnamelen);
+ /* what.dir */
+ p = nfs_add_fh3(p, priv->filefh_len, priv->filefh);
- 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;
}
@@ -483,33 +739,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;
@@ -517,26 +788,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;
}
@@ -544,35 +868,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));
+
+ /* skip over count */
+ p += 1;
- rlen = ntohl(net_read_uint32(filedata + 18));
+ eof = ntoh32(net_read_uint32(p));
- kfifo_put(priv->fifo, (char *)(filedata + 19), rlen);
+ /*
+ * 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;
}
@@ -611,25 +974,29 @@ 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 *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++;
- memcpy(priv->filefh, npriv->rootfh, NFS_FHSIZE);
+ priv->filefh_len = npriv->rootfh_len;
+ memcpy(priv->filefh, npriv->rootfh, NFS3_FHSIZE);
while (*filename) {
size_t flen;
@@ -640,7 +1007,7 @@ static struct file_priv *nfs_do_open(struct device_d *dev, const char *filename)
else
flen = strlen(filename);
- ret = nfs_lookup_req(priv, filename, flen);
+ ret = nfs_lookup_req(priv, flen, filename, s);
if (ret)
goto out;
@@ -666,19 +1033,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;
@@ -688,35 +1064,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;
}
@@ -726,9 +1119,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);
@@ -780,32 +1172,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)
@@ -818,110 +1195,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)
@@ -983,14 +1339,14 @@ 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, 2);
+ 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);
+ ret = rpc_lookup_req(npriv, PROG_NFS, 3);
if (ret < 0) {
printf("lookup nfs port failed with %d\n", ret);
goto err2;