From f43f827bb4bc03526cbfa4ac58fac6747e3caddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 7 Feb 2014 22:28:11 +0100 Subject: [PATCH] nfs: switch to nfs3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was tested against nfs-kernel-server and unfs3. Signed-off-by: Uwe Kleine-König Signed-off-by: Sascha Hauer --- fs/nfs.c | 900 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 628 insertions(+), 272 deletions(-) diff --git a/fs/nfs.c b/fs/nfs.c index 79df6a8e4..ab33c91ef 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 , Pengutronix * Copyright (c) Masami Komiya 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 @@ -33,6 +34,7 @@ #include #include #include +#include #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 NFSPROC3_GETATTR 1 +#define NFSPROC3_LOOKUP 3 +#define NFSPROC3_READLINK 5 +#define NFSPROC3_READ 6 +#define NFSPROC3_READDIR 16 -#define NFS_FHSIZE 32 +#define NFS3_FHSIZE 64 +#define NFS3_COOKIEVERFSIZE 8 -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, -}; +/* 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; + * } + * + * 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); + + debug("%s: prog: %d, proc: %d\n", __func__, rpc_prog, rpc_proc); + + if (rpc_prog == PROG_PORTMAP) { + dport = SUNRPC_PORT; + pkt.vers = hton32(2); + } else if (rpc_prog == PROG_MOUNT) { + dport = npriv->mount_port; + 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)); - if (rpc_prog == PROG_PORTMAP) - dport = SUNRPC_PORT; - else if (rpc_prog == PROG_MOUNT) - dport = npriv->mount_port; - else - dport = npriv->nfs_port; - - 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); + /* 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; } @@ -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; + p = nfs_packet + sizeof(struct rpc_reply) + 4; - type = ntohl(net_read_uint32(&fattr->type)); - - 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; - ret = rpc_req(priv->npriv, PROG_NFS, NFS_READDIR, data, len); + p = nfs_add_uint32(p, 1024); /* count */ + + 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; - rlen = ntohl(net_read_uint32(filedata + 18)); + p = nfs_read_post_op_attr(p, NULL); - kfifo_put(priv->fifo, (char *)(filedata + 19), rlen); + rlen = ntoh32(net_read_uint32(p)); + + /* skip over count */ + p += 1; + + 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; } @@ -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 > 1024) + insize = 1024; - 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 && !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++ == xdr_zero) { - 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. - */ - - free(ndir->stream.buf); - - buf = nfs_readdirattr_req(ndir->priv, &len, ndir->cookie); - if (!buf) - return NULL; - - xdr_init(&ndir->stream, buf, len); - - goto again; - } - return NULL; /* -EINVAL */ + if (!p) { + printf("%s: premature end of packet\n", __func__); + return NULL; } - p = xdr_inline_decode(xdr, 4); - if (!p) - goto out_overflow; + if (!net_read_uint32(p)) { + /* eof? */ + p = xdr_inline_decode(xdr, 4); + if (!p) { + printf("%s: premature end of packet\n", __func__); + return NULL; + } + if (net_read_uint32(p)) + return NULL; + + if (!nfs_readdirattr_req(ndir->priv, ndir)) { + printf("%s: nfs_readdirattr_req failed\n", __func__); + return NULL; + } + + goto again; + } + + /* there is another entry available in the last reply */ + + /* skip over fileid */ + p = xdr_inline_decode(xdr, 8); + if (!p) { + printf("%s: premature end of packet\n", __func__); + return NULL; + } 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;