From 47eb6b50bbcd5dad9718afd5f495a83e37944217 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 7 Feb 2014 22:28:03 +0100 Subject: net: net_read_uint32: assert that only 32 bit are read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On some architectures (e.g. alpha, amd64, arm64, ia64, s390x, mips64) sizeof(ulong) is 8 which made net_read_uint32 actually read too much and even returned the wrong value on big endian machines. (Note the second issue isn't that critical though, because the only architecture from the list above that uses big endian byte order is s390x ...) Also change the argument to void * because the pointer is not necessarily properly aligned. Signed-off-by: Uwe Kleine-König Signed-off-by: Sascha Hauer --- include/net.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/net.h b/include/net.h index a4cfec7123..8388e2f12e 100644 --- a/include/net.h +++ b/include/net.h @@ -269,11 +269,11 @@ static inline IPaddr_t net_read_ip(void *from) } /* return uint32 *in network byteorder* */ -static inline uint32_t net_read_uint32(uint32_t *from) +static inline uint32_t net_read_uint32(void *from) { - ulong l; - memcpy((void*)&l, (void*)from, sizeof(l)); - return l; + uint32_t tmp; + memcpy(&tmp, from, sizeof(tmp)); + return tmp; } /* write IP *in network byteorder* */ -- cgit v1.2.3 From e6745134480680e2163dfe16cb5798bcd20e8ef4 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 7 Feb 2014 22:28:04 +0100 Subject: nfs: fix mount prog version in portmap lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're speaking rpc mount version 2 (i.e. the same version as the implemented nfs protocol), so also specify that in the lookup. Signed-off-by: Uwe Kleine-König Signed-off-by: Sascha Hauer --- fs/nfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfs.c b/fs/nfs.c index 7173264351..6582bae7db 100644 --- a/fs/nfs.c +++ b/fs/nfs.c @@ -1010,7 +1010,7 @@ 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); + ret = rpc_lookup_req(npriv, PROG_MOUNT, 2); if (ret) { printf("lookup mount port failed with %d\n", ret); goto err2; -- cgit v1.2.3 From f91c16b8e28daa725a11fea1b011a35e3c5b6721 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 7 Feb 2014 22:28:05 +0100 Subject: nfs: skip over stale rpc packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a former transaction was aborted by hitting Ctrl-C the old reply might still hang in the ethernet controller making all further transactions fail. So just skip over old replies. Signed-off-by: Uwe Kleine-König Signed-off-by: Sascha Hauer --- fs/nfs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/nfs.c b/fs/nfs.c index 6582bae7db..76d8c0595d 100644 --- a/fs/nfs.c +++ b/fs/nfs.c @@ -275,8 +275,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 (ntohl(rpc.id) != rpc_id) { + if (rpc_id - ntohl(rpc.id) == 1) + /* stale packet, wait a bit longer */ + return 0; + return -EINVAL; + } if (rpc.rstatus || rpc.verifier || -- cgit v1.2.3 From 2897531de67756b1b359cc5c91a27970cd189086 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 7 Feb 2014 22:28:06 +0100 Subject: nfs: shorten and simplify rpc_add_credentials a bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Signed-off-by: Sascha Hauer --- fs/nfs.c | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/fs/nfs.c b/fs/nfs.c index 76d8c0595d..54dda261c0 100644 --- a/fs/nfs.c +++ b/fs/nfs.c @@ -224,34 +224,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(20); /* auth length: 20 + strlen(hostname) */ *p++ = htonl(0); /* stamp */ - *p++ = htonl(hostnamelen); /* hostname string */ - - if (hostnamelen & 3) - *(p + hostnamelen / 4) = 0; /* add zero padding */ - - /* memcpy(p, hostname, hostnamelen); */ /* empty hostname */ + *p++ = htonl(0); /* hostname string length */ + /* memcpy(p, "", 0); p += 0; <- empty host name */ - p += hl / 4; *p++ = 0; /* uid */ *p++ = 0; /* gid */ *p++ = 0; /* auxiliary gid list */ -- cgit v1.2.3 From 0f2c0a7edd5073162f0206ccde18304b7876f1b1 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 7 Feb 2014 22:28:07 +0100 Subject: nfs: simplify rpc_lookup_req MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of letting rpc_lookup_req set mount_port and nfs_port, let it return the port found and let the caller use that information. Signed-off-by: Uwe Kleine-König Signed-off-by: Sascha Hauer --- fs/nfs.c | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/fs/nfs.c b/fs/nfs.c index 54dda261c0..761bfd6267 100644 --- a/fs/nfs.c +++ b/fs/nfs.c @@ -374,22 +374,8 @@ static int rpc_lookup_req(struct nfs_priv *npriv, int prog, int ver) if (ret) return ret; - port = net_read_uint32((uint32_t *)(nfs_packet + sizeof(struct rpc_reply))); - - switch (prog) { - case PROG_MOUNT: - npriv->mount_port = ntohl(port); - debug("mount port: %d\n", npriv->mount_port); - break; - case PROG_NFS: - npriv->nfs_port = ntohl(port); - debug("nfs port: %d\n", npriv->nfs_port); - break; - default: - return -EINVAL; - } - - return 0; + port = ntohl(net_read_uint32(nfs_packet + sizeof(struct rpc_reply))); + return port; } /* @@ -1000,16 +986,18 @@ static int nfs_probe(struct device_d *dev) net_udp_bind(npriv->con, 1000); ret = rpc_lookup_req(npriv, PROG_MOUNT, 2); - if (ret) { + 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) { + if (ret < 0) { printf("lookup nfs port failed with %d\n", ret); goto err2; } + npriv->nfs_port = ret; ret = nfs_mount_req(npriv); if (ret) { -- cgit v1.2.3 From e668dc53365116f5ae57d79a4e4819e7dcf38131 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 7 Feb 2014 22:28:08 +0100 Subject: nfs: drop an unneeded variable from nfs_do_open() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While at it also fix the type of flen holding a string length (int -> size_t). Signed-off-by: Uwe Kleine-König Signed-off-by: Sascha Hauer --- fs/nfs.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/fs/nfs.c b/fs/nfs.c index 761bfd6267..79df6a8e4f 100644 --- a/fs/nfs.c +++ b/fs/nfs.c @@ -616,7 +616,7 @@ static struct file_priv *nfs_do_open(struct device_d *dev, const char *filename) struct file_priv *priv; struct nfs_priv *npriv = dev->priv; int ret; - const char *fname, *tok; + const char *tok; priv = xzalloc(sizeof(*priv)); @@ -629,25 +629,23 @@ static struct file_priv *nfs_do_open(struct device_d *dev, const char *filename) filename++; - fname = filename; - memcpy(priv->filefh, npriv->rootfh, NFS_FHSIZE); - while (*fname) { - int flen; + while (*filename) { + size_t 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, filename, flen); if (ret) goto out; if (tok) - fname += flen + 1; + filename += flen + 1; else break; } -- cgit v1.2.3 From b104688cbb9c4d1e04b8a97e64b65a2bca614697 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 7 Feb 2014 22:28:09 +0100 Subject: net: new function net_read_uint64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is needed for nfs3 support as some types became bigger compared to nfs2. Signed-off-by: Uwe Kleine-König Signed-off-by: Sascha Hauer --- include/net.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/net.h b/include/net.h index 8388e2f12e..6c86947d9c 100644 --- a/include/net.h +++ b/include/net.h @@ -276,6 +276,13 @@ static inline uint32_t net_read_uint32(void *from) return tmp; } +static inline uint64_t net_read_uint64(void *from) +{ + uint64_t tmp; + memcpy(&tmp, from, sizeof(tmp)); + return tmp; +} + /* write IP *in network byteorder* */ static inline void net_write_ip(void *to, IPaddr_t ip) { -- cgit v1.2.3 From 8abaf1ad7137e0802761d85b456591de9eaa7a5f Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 7 Feb 2014 22:28:10 +0100 Subject: net: provide alternatives to {ntoh, hton}[sl] funtions with cleaner semantics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ntohl always converts 32 bits even on archs where sizeof(long) == 8. "ntoh32" is a much more intuitive name here. Also the name of the 64 bit variant that is also added isn't questionable. Signed-off-by: Uwe Kleine-König Signed-off-by: Sascha Hauer --- include/byteorder.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 include/byteorder.h diff --git a/include/byteorder.h b/include/byteorder.h new file mode 100644 index 0000000000..4b255a5fab --- /dev/null +++ b/include/byteorder.h @@ -0,0 +1,24 @@ +#ifndef __BYTEORDER_H__ +#define __BYTEORDER_H__ + +/* + * The standard macros for converting between host and network byte order are + * badly named. So ntohl converts 32 bits even on architectures where a long is + * 64 bit wide although the 'l' suffix suggests that it's working on longs. + * + * So this file introduces variants that use the bitcount as suffix instead of + * 's' or 'l'. + */ + +#include + +#define ntoh16(x) __be16_to_cpu(x) +#define hton16(x) __cpu_to_be16(x) + +#define ntoh32(x) __be32_to_cpu(x) +#define hton32(x) __cpu_to_be32(x) + +#define ntoh64(x) __be64_to_cpu(x) +#define hton64(x) __cpu_to_be64(x) + +#endif /* __BYTEORDER_H__ */ -- cgit v1.2.3 From f43f827bb4bc03526cbfa4ac58fac6747e3caddb Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 7 Feb 2014 22:28:11 +0100 Subject: 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 | 894 ++++++++++++++++++++++++++++++++++++++++++++------------------- 1 file 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 , 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 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; + * } + * + * 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; -- cgit v1.2.3 From f97f4b6571d1297973f08b9f921778e0b2e7f064 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 7 Feb 2014 22:28:12 +0100 Subject: mount: support filesystem options passed via -o MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to mount(8) the barebox command mount now supports passing a string to the file system driver via -o. This is used in the next commit to let the user specify port numbers for nfs mounts. Signed-off-by: Uwe Kleine-König Signed-off-by: Sascha Hauer --- arch/arm/boards/raspberry-pi/rpi.c | 2 +- arch/arm/mach-omap/omap_generic.c | 2 +- arch/arm/mach-omap/xload.c | 4 ++-- arch/arm/mach-socfpga/generic.c | 2 +- commands/mount.c | 19 ++++++++++++------- commands/tftp.c | 2 +- common/blspec.c | 2 +- common/startup.c | 4 ++-- fs/fs.c | 19 +++++++++++++------ include/fs.h | 6 ++++-- lib/bootstrap/disk.c | 2 +- 11 files changed, 39 insertions(+), 25 deletions(-) diff --git a/arch/arm/boards/raspberry-pi/rpi.c b/arch/arm/boards/raspberry-pi/rpi.c index 0997124c01..03a16d7561 100644 --- a/arch/arm/boards/raspberry-pi/rpi.c +++ b/arch/arm/boards/raspberry-pi/rpi.c @@ -126,7 +126,7 @@ static int rpi_env_init(void) } mkdir("/boot", 0666); - ret = mount(diskdev, "fat", "/boot"); + ret = mount(diskdev, "fat", "/boot", NULL); if (ret) { printf("failed to mount %s\n", diskdev); return 0; diff --git a/arch/arm/mach-omap/omap_generic.c b/arch/arm/mach-omap/omap_generic.c index bedb4d8d26..060c59277f 100644 --- a/arch/arm/mach-omap/omap_generic.c +++ b/arch/arm/mach-omap/omap_generic.c @@ -136,7 +136,7 @@ static int omap_env_init(void) } mkdir("/boot", 0666); - ret = mount(diskdev, "fat", "/boot"); + ret = mount(diskdev, "fat", "/boot", NULL); if (ret) { printf("failed to mount %s\n", diskdev); return 0; diff --git a/arch/arm/mach-omap/xload.c b/arch/arm/mach-omap/xload.c index 69e3e42df6..a309450109 100644 --- a/arch/arm/mach-omap/xload.c +++ b/arch/arm/mach-omap/xload.c @@ -110,7 +110,7 @@ static void *omap_xload_boot_mmc(void) partname = asprintf("%s.0", diskdev); - ret = mount(partname, "fat", "/"); + ret = mount(partname, "fat", "/", NULL); free(partname); @@ -170,7 +170,7 @@ static void *omap4_xload_boot_usb(void){ void *buf; int len; - ret = mount("omap4_usbboot", "omap4_usbbootfs", "/"); + ret = mount("omap4_usbboot", "omap4_usbbootfs", "/", NULL); if (ret) { printf("Unable to mount omap4_usbbootfs (%d)\n", ret); return NULL; diff --git a/arch/arm/mach-socfpga/generic.c b/arch/arm/mach-socfpga/generic.c index 0d958d23af..62593549d6 100644 --- a/arch/arm/mach-socfpga/generic.c +++ b/arch/arm/mach-socfpga/generic.c @@ -97,7 +97,7 @@ static int socfpga_env_init(void) } mkdir("/boot", 0666); - ret = mount(partname, "fat", "/boot"); + ret = mount(partname, "fat", "/boot", NULL); if (ret) { printf("failed to mount %s\n", diskdev); goto out_free; diff --git a/commands/mount.c b/commands/mount.c index 2e9d4bef5e..691bc2911e 100644 --- a/commands/mount.c +++ b/commands/mount.c @@ -33,26 +33,31 @@ static int do_mount(int argc, char *argv[]) { int opt; int ret = 0, verbose = 0; - struct fs_device_d *fsdev; struct driver_d *drv; const char *type = NULL; const char *mountpoint, *dev; + const char *fsoptions = NULL; - while ((opt = getopt(argc, argv, "t:va")) > 0) { + while ((opt = getopt(argc, argv, "ao:t:v")) > 0) { switch (opt) { + case 'a': + mount_all(); + break; case 't': type = optarg; break; + case 'o': + fsoptions = optarg; + break; case 'v': verbose++; break; - case 'a': - mount_all(); - break; } } if (argc == optind) { + struct fs_device_d *fsdev; + for_each_fs_device(fsdev) { printf("%s on %s type %s\n", fsdev->backingstore ? fsdev->backingstore : "none", @@ -84,7 +89,7 @@ static int do_mount(int argc, char *argv[]) if (!cdev) return -ENOENT; - path = cdev_mount_default(cdev); + path = cdev_mount_default(cdev, fsoptions); if (IS_ERR(path)) return PTR_ERR(path); @@ -108,7 +113,7 @@ static int do_mount(int argc, char *argv[]) mountpoint = argv[optind + 1]; } - if ((ret = mount(dev, type, mountpoint))) { + if ((ret = mount(dev, type, mountpoint, fsoptions))) { perror("mount"); return 1; } diff --git a/commands/tftp.c b/commands/tftp.c index c83d1740e2..64cab2f045 100644 --- a/commands/tftp.c +++ b/commands/tftp.c @@ -72,7 +72,7 @@ static int do_tftpb(int argc, char *argv[]) goto err_free; ip = net_get_serverip(); - ret = mount(ip_to_string(ip), "tftp", TFTP_MOUNT_PATH); + ret = mount(ip_to_string(ip), "tftp", TFTP_MOUNT_PATH, NULL); if (ret) goto err_rmdir; diff --git a/common/blspec.c b/common/blspec.c index 2244d5a8a8..df3c9c3c65 100644 --- a/common/blspec.c +++ b/common/blspec.c @@ -276,7 +276,7 @@ static int blspec_scan_cdev(struct blspec *blspec, struct cdev *cdev) if (type == filetype_mbr || type == filetype_gpt) return -EINVAL; - rootpath = cdev_mount_default(cdev); + rootpath = cdev_mount_default(cdev, NULL); if (IS_ERR(rootpath)) return PTR_ERR(rootpath); diff --git a/common/startup.c b/common/startup.c index e8b9ea0216..0b5fe46adb 100644 --- a/common/startup.c +++ b/common/startup.c @@ -89,9 +89,9 @@ device_initcall(register_default_env); #if defined CONFIG_FS_RAMFS && defined CONFIG_FS_DEVFS static int mount_root(void) { - mount("none", "ramfs", "/"); + mount("none", "ramfs", "/", NULL); mkdir("/dev", 0); - mount("none", "devfs", "/dev"); + mount("none", "devfs", "/dev", NULL); return 0; } fs_initcall(mount_root); diff --git a/fs/fs.c b/fs/fs.c index 32dba8cf0a..7a57bc0670 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -1242,6 +1242,7 @@ static void fs_remove(struct device_d *dev) } free(fsdev->path); + free(fsdev->options); if (fsdev == fs_dev_root) fs_dev_root = NULL; @@ -1316,13 +1317,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); @@ -1354,6 +1360,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) @@ -1711,7 +1718,7 @@ const char *cdev_get_mount_path(struct cdev *cdev) * mount it to /mnt/ 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; @@ -1720,7 +1727,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) @@ -1731,7 +1738,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); @@ -1761,6 +1768,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/include/fs.h b/include/fs.h index 856e00abb0..073641c747 100644 --- a/include/fs.h +++ b/include/fs.h @@ -99,6 +99,7 @@ struct fs_device_d { char *path; struct device_d *parent_device; struct list_head list; + char *options; }; #define drv_to_fs_driver(d) container_of(d, struct fs_driver_d, drv) @@ -140,7 +141,8 @@ int closedir(DIR *dir); int symlink(const char *pathname, const char *newpath); int readlink(const char *path, char *buf, size_t bufsiz); -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); int umount(const char *pathname); /* not-so-standard functions */ @@ -197,7 +199,7 @@ int unlink_recursive(const char *path, char **failedpath); int fsdev_open_cdev(struct fs_device_d *fsdev); const char *cdev_get_mount_path(struct cdev *cdev); -const char *cdev_mount_default(struct cdev *cdev); +const char *cdev_mount_default(struct cdev *cdev, const char *fsoptions); void mount_all(void); #endif /* __FS_H */ diff --git a/lib/bootstrap/disk.c b/lib/bootstrap/disk.c index 879d3315e8..527e430897 100644 --- a/lib/bootstrap/disk.c +++ b/lib/bootstrap/disk.c @@ -20,7 +20,7 @@ void* bootstrap_read_disk(char *dev, char *fstype) int len; char *path = "/"; - ret = mount(dev, fstype, path); + ret = mount(dev, fstype, path, NULL); if (ret) { bootstrap_err("mounting %s failed with %d\n", dev, ret); return NULL; -- cgit v1.2.3 From 3ea30d9ce892a66d5161acd8afb24b755e3f2045 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 7 Feb 2014 22:28:13 +0100 Subject: nfs: parse nfsport and mount port from file system options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows to use unfs3 on the server side which doesn't integrate into portmap/rpcbind which results in the port not being impossible to lookup via rpc calls to the portmap program. Use it like: mount -t nfs -o port=2703,mountport=2703 192.168.77.157:/root /mnt/nfs Signed-off-by: Uwe Kleine-König Signed-off-by: Sascha Hauer --- fs/Makefile | 2 +- fs/nfs.c | 32 +++++++++++++++++++++----------- fs/parseopt.c | 34 ++++++++++++++++++++++++++++++++++ fs/parseopt.h | 1 + 4 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 fs/parseopt.c create mode 100644 fs/parseopt.h 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/nfs.c b/fs/nfs.c index ab33c91ef0..046cd4d76c 100644 --- a/fs/nfs.c +++ b/fs/nfs.c @@ -36,6 +36,8 @@ #include #include +#include "parseopt.h" + #define SUNRPC_PORT 111 #define PROG_PORTMAP 100000 @@ -1339,19 +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, 3); - if (ret < 0) { - 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; } - npriv->mount_port = ret; - - ret = rpc_lookup_req(npriv, PROG_NFS, 3); - if (ret < 0) { - 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; } - 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 + +#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); -- cgit v1.2.3 From 93578fb2333bba3d1c2d0fa41de11ba4f92f9aed Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 27 Feb 2014 12:23:29 +0100 Subject: run_command: remove unused flag parameter Signed-off-by: Sascha Hauer --- commands/boot.c | 2 +- commands/exec.c | 2 +- commands/login.c | 2 +- commands/time.c | 2 +- common/hush.c | 2 +- common/menu.c | 2 +- common/parser.c | 7 +++---- common/startup.c | 4 ++-- fs/fs.c | 2 +- include/common.h | 3 +-- 10 files changed, 13 insertions(+), 15 deletions(-) diff --git a/commands/boot.c b/commands/boot.c index c4b49a9035..fea7b54332 100644 --- a/commands/boot.c +++ b/commands/boot.c @@ -47,7 +47,7 @@ static int boot_script(char *path) globalvar_set_match("linux.bootargs.dyn.", ""); globalvar_set_match("bootm.", ""); - ret = run_command(path, 0); + ret = run_command(path); if (ret) { printf("Running %s failed\n", path); goto out; diff --git a/commands/exec.c b/commands/exec.c index bd7d54afd2..8d12b30ec9 100644 --- a/commands/exec.c +++ b/commands/exec.c @@ -39,7 +39,7 @@ static int do_exec(int argc, char *argv[]) if (!script) return 1; - if (run_command (script, 0) == -1) + if (run_command(script) == -1) goto out; free(script); } diff --git a/commands/login.c b/commands/login.c index b616bf15fc..d9297fa370 100644 --- a/commands/login.c +++ b/commands/login.c @@ -68,7 +68,7 @@ static int do_login(int argc, char *argv[]) if (passwd_len < 0) { console_allow_input(false); - run_command(timeout_cmd, 0); + run_command(timeout_cmd); } if (check_passwd(passwd, passwd_len)) diff --git a/commands/time.c b/commands/time.c index 987c25ef73..2cc3292d7b 100644 --- a/commands/time.c +++ b/commands/time.c @@ -27,7 +27,7 @@ static int do_time(int argc, char *argv[]) start = get_time_ns(); - run_command(buf, 0); + run_command(buf); end = get_time_ns(); diff --git a/common/hush.c b/common/hush.c index abe2ceda07..bd534c12f5 100644 --- a/common/hush.c +++ b/common/hush.c @@ -1824,7 +1824,7 @@ static char * make_string(char ** inp) return str; } -int run_command (const char *cmd, int flag) +int run_command(const char *cmd) { struct p_context ctx; int ret; diff --git a/common/menu.c b/common/menu.c index ef56190604..114c36efcc 100644 --- a/common/menu.c +++ b/common/menu.c @@ -468,7 +468,7 @@ static void menu_action_command(struct menu *m, struct menu_entry *me) if (!s) s = e->command; - ret = run_command (s, 0); + ret = run_command(s); if (ret < 0) udelay(1000000); diff --git a/common/parser.c b/common/parser.c index d390fb6afe..4a48210c4d 100644 --- a/common/parser.c +++ b/common/parser.c @@ -176,7 +176,7 @@ static void process_macros (const char *input, char *output) * creates or modifies environment variables (like "bootp" does). */ -int run_command (const char *cmd, int flag) +int run_command(const char *cmd) { char cmdbuf[CONFIG_CBSIZE]; /* working copy of cmd */ char *token; /* start of token in cmdbuf */ @@ -265,18 +265,17 @@ int run_shell(void) static char lastcommand[CONFIG_CBSIZE] = { 0, }; int len; int rc = 1; - int flag; + for (;;) { len = readline (CONFIG_PROMPT, console_buffer, CONFIG_CBSIZE); - flag = 0; /* assume no special flags for now */ if (len > 0) strcpy (lastcommand, console_buffer); if (len == -1) puts ("\n"); else - rc = run_command (lastcommand, flag); + rc = run_command(lastcommand); if (rc <= 0) { /* invalid command or not repeatable, forget it */ diff --git a/common/startup.c b/common/startup.c index 0b5fe46adb..04f8cf06e3 100644 --- a/common/startup.c +++ b/common/startup.c @@ -137,11 +137,11 @@ void __noreturn start_barebox(void) pr_info("running /env/bin/init...\n"); if (!stat("/env/bin/init", &s)) { - run_command("source /env/bin/init", 0); + run_command("source /env/bin/init"); } else { pr_err("/env/bin/init not found\n"); if (IS_ENABLED(CONFIG_CMD_LOGIN)) - while(run_command("login -t 0", 0)); + while(run_command("login -t 0")); } } diff --git a/fs/fs.c b/fs/fs.c index 7a57bc0670..bb849cb3b9 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -434,7 +434,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) diff --git a/include/common.h b/include/common.h index 293f5041af..6987b4f16d 100644 --- a/include/common.h +++ b/include/common.h @@ -92,8 +92,7 @@ void __noreturn panic(const char *fmt, ...); char *size_human_readable(unsigned long long size); -/* common/main.c */ -int run_command (const char *cmd, int flag); +int run_command(const char *cmd); int readline (const char *prompt, char *buf, int len); /* common/memsize.c */ -- cgit v1.2.3 From 72dfc499c8ae272614d5b572b86b776e70778bbd Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 27 Feb 2014 15:57:49 +0100 Subject: net: constify eth_get_byname argument Signed-off-by: Sascha Hauer --- include/net.h | 2 +- net/eth.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/net.h b/include/net.h index 6c86947d9c..a680f972ed 100644 --- a/include/net.h +++ b/include/net.h @@ -404,7 +404,7 @@ typedef void rx_handler_f(void *ctx, char *packet, unsigned int len); void eth_set_current(struct eth_device *eth); struct eth_device *eth_get_current(void); -struct eth_device *eth_get_byname(char *name); +struct eth_device *eth_get_byname(const char *name); /** * net_receive - Pass a received packet from an ethernet driver to the protocol stack diff --git a/net/eth.c b/net/eth.c index 37dd9e087d..ba9e40b3f6 100644 --- a/net/eth.c +++ b/net/eth.c @@ -144,7 +144,7 @@ struct eth_device * eth_get_current(void) return eth_current; } -struct eth_device *eth_get_byname(char *ethname) +struct eth_device *eth_get_byname(const char *ethname) { struct eth_device *edev; -- cgit v1.2.3 From a162dfe50345d3461010759f8a0e79f7e388c140 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 28 Feb 2014 08:39:27 +0100 Subject: net: Add ifup support The defaultenv-2 has ifup support as a shell script. This patch replaces it with a command which is more robust, can be called from C and now can also bring up all configured interfaces. Signed-off-by: Sascha Hauer --- common/Kconfig | 1 + defaultenv-2/base/bin/ifup | 67 ----------------- include/net.h | 5 ++ net/Kconfig | 11 +++ net/Makefile | 1 + net/ifup.c | 179 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 197 insertions(+), 67 deletions(-) delete mode 100644 defaultenv-2/base/bin/ifup create mode 100644 net/ifup.c diff --git a/common/Kconfig b/common/Kconfig index 8af7ec1a8d..2e91cd7a6c 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -582,6 +582,7 @@ config DEFAULT_ENVIRONMENT_GENERIC_NEW select CMD_DIRNAME select FLEXIBLE_BOOTARGS select CMD_BOOT + select NET_CMD_IFUP if NET prompt "Generic environment template" config DEFAULT_ENVIRONMENT_GENERIC diff --git a/defaultenv-2/base/bin/ifup b/defaultenv-2/base/bin/ifup deleted file mode 100644 index 37b986c44b..0000000000 --- a/defaultenv-2/base/bin/ifup +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/sh - -mkdir -p /tmp/network - -if [ $# != 1 ]; then - echo "usage: ifup " - exit 1 -fi - -interface="$1" - -if [ -f /tmp/network/$interface ]; then - exit 0 -fi - -cmd=/env/network/$interface - -if [ ! -e $cmd ]; then - echo "$f: no such file" - exit 1 -fi - -ip= -ipaddr= -netmask= -gateway= -serverip= -ethaddr= - -. $cmd - -if [ $? != 0 ]; then - echo "failed to bring up $interface" - exit 1 -fi - -if [ -f /env/network/${interface}-discover ]; then - /env/network/${interface}-discover - if [ $? != 0 ]; then - echo "failed to discover eth0" - exit 1 - fi -fi - -if [ -n "$ethaddr" ]; then - ${interface}.ethaddr=$ethaddr -fi - -if [ "$ip" = static ]; then - ${interface}.ipaddr=$ipaddr - ${interface}.netmask=$netmask - ${interface}.serverip=$serverip - ${interface}.gateway=$gateway - ret=0 -elif [ "$ip" = dhcp ]; then - dhcp - ret=$? - if [ $ret = 0 -a -n "$serverip" ]; then - ${interface}.serverip=$serverip - fi -fi - -if [ $ret = 0 ]; then - echo -o /tmp/network/$interface up -fi - -exit $ret diff --git a/include/net.h b/include/net.h index a680f972ed..3b800b78f0 100644 --- a/include/net.h +++ b/include/net.h @@ -457,4 +457,9 @@ int net_icmp_send(struct net_connection *con, int len); void led_trigger_network(enum led_trigger trigger); +#define IFUP_FLAG_FORCE (1 << 0) + +int ifup(const char *name, unsigned flags); +int ifup_all(unsigned flags); + #endif /* __NET_H__ */ diff --git a/net/Kconfig b/net/Kconfig index c12193db5c..59b64175de 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -26,4 +26,15 @@ config NET_RESOLV bool prompt "dns support" +config NET_IFUP + default y + bool + +config NET_CMD_IFUP + bool + prompt "ifup support" + help + This enables the 'ifup' command which is used to bring up network + interfaces based on config files under /env/network/ + endif diff --git a/net/Makefile b/net/Makefile index 416e30ac35..fabb17e4a8 100644 --- a/net/Makefile +++ b/net/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_NET_NFS) += nfs.o obj-$(CONFIG_NET_PING) += ping.o obj-$(CONFIG_NET_RESOLV)+= dns.o obj-$(CONFIG_NET_NETCONSOLE) += netconsole.o +obj-$(CONFIG_NET_IFUP) += ifup.o diff --git a/net/ifup.c b/net/ifup.c new file mode 100644 index 0000000000..3b89ce1bc2 --- /dev/null +++ b/net/ifup.c @@ -0,0 +1,179 @@ +/* + * ifup.c - bring up network interfaces + * + * Copyright (c) 2014 Sascha Hauer , Pengutronix + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * 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 detaiifup. + * + */ +#define pr_fmt(fmt) "ifup: " fmt + +#include +#include +#include +#include +#include +#include +#include + +static char *vars[] = { + "ipaddr", + "netmask", + "gateway", + "serverip", + "ethaddr", +}; + +static int eth_set_param(struct device_d *dev, const char *param) +{ + const char *value = getenv(param); + + if (!value) + return 0; + if (!*value) + return 0; + + return dev_set_param(dev, param, value); +} + +int ifup(const char *name, unsigned flags) +{ + int ret; + char *cmd, *cmd_discover; + const char *ip; + struct stat s; + int i; + struct device_d *dev; + struct eth_device *edev = eth_get_byname(name); + + if (edev && edev->ipaddr && !(flags & IFUP_FLAG_FORCE)) + return 0; + + env_push_context(); + + setenv("ip", ""); + + for (i = 0; i < ARRAY_SIZE(vars); i++) + setenv(vars[i], ""); + + cmd = asprintf("source /env/network/%s", name); + cmd_discover = asprintf("/env/network/%s-discover", name); + + ret = run_command(cmd); + if (ret) + goto out; + + ret = stat(cmd_discover, &s); + if (!ret) { + ret = run_command(cmd_discover); + if (ret) + goto out; + } + + dev = get_device_by_name(name); + if (!dev) { + pr_err("Cannot find device %s\n", name); + goto out; + } + + ret = eth_set_param(dev, "ethaddr"); + if (ret) + goto out; + + ip = getenv("ip"); + if (!strcmp(ip, "dhcp")) { + ret = run_command("dhcp"); + if (ret) + goto out; + } else if (!strcmp(ip, "static")) { + for (i = 0; i < ARRAY_SIZE(vars); i++) { + ret = eth_set_param(dev, vars[i]); + if (ret) + goto out; + } + } else { + pr_err("unknown ip type: %s\n", ip); + ret = -EINVAL; + goto out; + } + + ret = 0; +out: + env_pop_context(); + free(cmd); + free(cmd_discover); + + return ret; +} + +int ifup_all(unsigned flags) +{ + DIR *dir; + struct dirent *d; + + dir = opendir("/env/network"); + if (!dir) + return -ENOENT; + + while ((d = readdir(dir))) { + if (*d->d_name == '.') + continue; + ifup(d->d_name, flags); + } + + closedir(dir); + + return 0; +} + +#if IS_ENABLED(CONFIG_NET_CMD_IFUP) + +static int do_ifup(int argc, char *argv[]) +{ + int opt; + unsigned flags = 0; + int all = 0; + + while ((opt = getopt(argc, argv, "af")) > 0) { + switch (opt) { + case 'f': + flags |= IFUP_FLAG_FORCE; + break; + case 'a': + all = 1; + break; + } + } + + if (all) + return ifup_all(flags); + + if (argc == optind) + return COMMAND_ERROR_USAGE; + + return ifup(argv[optind], flags); +} + +BAREBOX_CMD_HELP_START(ifup) +BAREBOX_CMD_HELP_USAGE("ifup [OPTIONS] \n") +BAREBOX_CMD_HELP_OPT ("-a", "bring up all interfaces\n") +BAREBOX_CMD_HELP_OPT ("-f", "Force. Configure even if ip already set\n") +BAREBOX_CMD_HELP_END + +BAREBOX_CMD_START(ifup) + .cmd = do_ifup, + .usage = "Bring up network interfaces", + BAREBOX_CMD_HELP(cmd_ifup_help) +BAREBOX_CMD_END + +#endif -- cgit v1.2.3 From 86f681abec4f8485d2e95537e0d59a8d9ab6b546 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 13 Feb 2014 10:38:23 +0100 Subject: blspec: Add NFS support With this barebox can start root filesystems containing bootloader spec entries via NFS. It is used as: boot nfs://[: --- common/blspec.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 2 deletions(-) diff --git a/common/blspec.c b/common/blspec.c index df3c9c3c65..9b4b096a0a 100644 --- a/common/blspec.c +++ b/common/blspec.c @@ -10,6 +10,8 @@ * GNU General Public License for more details. * */ +#define pr_fmt(fmt) "blspec: " fmt + #include #include #include @@ -22,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -130,6 +133,107 @@ static int blspec_have_entry(struct blspec *blspec, const char *path) return 0; } +/* + * nfs_find_mountpath - Check if a given url is already mounted + */ +static const char *nfs_find_mountpath(const char *nfshostpath) +{ + struct fs_device_d *fsdev; + + for_each_fs_device(fsdev) { + if (fsdev->backingstore && !strcmp(fsdev->backingstore, nfshostpath)) + return fsdev->path; + } + + return NULL; +} + +/* + * parse_nfs_url - check for nfs:// style url + * + * Check if the passed string is a NFS url and if yes, mount the + * NFS and return the path we have mounted to. + */ +static char *parse_nfs_url(const char *url) +{ + char *sep, *str, *host, *port, *path; + char *mountpath = NULL, *hostpath = NULL, *options = NULL; + const char *prevpath; + IPaddr_t ip; + int ret; + + if (!IS_ENABLED(CONFIG_FS_NFS)) + return ERR_PTR(-ENOSYS); + + if (strncmp(url, "nfs://", 6)) + return ERR_PTR(-EINVAL); + + url += 6; + + str = xstrdup(url); + + host = str; + + sep = strchr(str, '/'); + if (!sep) { + ret = -EINVAL; + goto out; + } + + *sep++ = 0; + + path = sep; + + port = strchr(host, ':'); + if (port) + *port++ = 0; + + ret = ifup_all(0); + if (ret) { + pr_err("Failed to bring up networking\n"); + goto out; + } + + ip = resolv(host); + if (ip == 0) + goto out; + + hostpath = asprintf("%s:%s", ip_to_string(ip), path); + + prevpath = nfs_find_mountpath(hostpath); + + if (prevpath) { + mountpath = xstrdup(prevpath); + } else { + mountpath = asprintf("/mnt/nfs-%s-blspec-%08x", host, rand()); + if (port) + options = asprintf("mountport=%s,port=%s", port, port); + + ret = make_directory(mountpath); + if (ret) + goto out; + + pr_debug("host: %s port: %s path: %s\n", host, port, path); + pr_debug("hostpath: %s mountpath: %s options: %s\n", hostpath, mountpath, options); + + ret = mount(hostpath, "nfs", mountpath, options); + if (ret) + goto out; + } + + ret = 0; + +out: + free(str); + free(hostpath); + free(options); + + if (ret) + free(mountpath); + + return ret ? ERR_PTR(ret) : mountpath; +} + /* * blspec_scan_directory - scan over a directory * @@ -145,9 +249,13 @@ int blspec_scan_directory(struct blspec *blspec, const char *root) char *abspath; int ret, found = 0; const char *dirname = "loader/entries"; - char *entry_default = NULL, *entry_once = NULL, *name; + char *entry_default = NULL, *entry_once = NULL, *name, *nfspath = NULL; + + nfspath = parse_nfs_url(root); + if (!IS_ERR(nfspath)) + root = nfspath; - pr_debug("%s: %s %s\n", __func__, root, dirname); + pr_info("%s: %s %s\n", __func__, root, dirname); entry_default = read_file_line("%s/default", root); entry_once = read_file_line("%s/once", root); @@ -239,6 +347,8 @@ int blspec_scan_directory(struct blspec *blspec, const char *root) closedir(dir); err_out: + if (!IS_ERR(nfspath)) + free(nfspath); free(abspath); free(entry_default); free(entry_once); -- cgit v1.2.3