diff --git a/arch/arm/boards/raspberry-pi/rpi.c b/arch/arm/boards/raspberry-pi/rpi.c index 0997124..03a16d7 100644 --- a/arch/arm/boards/raspberry-pi/rpi.c +++ b/arch/arm/boards/raspberry-pi/rpi.c @@ -126,7 +126,7 @@ } 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 bedb4d8..060c592 100644 --- a/arch/arm/mach-omap/omap_generic.c +++ b/arch/arm/mach-omap/omap_generic.c @@ -136,7 +136,7 @@ } 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 69e3e42..a309450 100644 --- a/arch/arm/mach-omap/xload.c +++ b/arch/arm/mach-omap/xload.c @@ -110,7 +110,7 @@ partname = asprintf("%s.0", diskdev); - ret = mount(partname, "fat", "/"); + ret = mount(partname, "fat", "/", NULL); free(partname); @@ -170,7 +170,7 @@ 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 0d958d2..6259354 100644 --- a/arch/arm/mach-socfpga/generic.c +++ b/arch/arm/mach-socfpga/generic.c @@ -97,7 +97,7 @@ } 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/boot.c b/commands/boot.c index ccf5827..bb8d07f 100644 --- a/commands/boot.c +++ b/commands/boot.c @@ -47,7 +47,7 @@ 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 bd7d54a..8d12b30 100644 --- a/commands/exec.c +++ b/commands/exec.c @@ -39,7 +39,7 @@ 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 b616bf1..d9297fa 100644 --- a/commands/login.c +++ b/commands/login.c @@ -68,7 +68,7 @@ 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/mount.c b/commands/mount.c index 2e9d4be..691bc29 100644 --- a/commands/mount.c +++ b/commands/mount.c @@ -33,26 +33,31 @@ { 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 @@ 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 @@ 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 c83d174..64cab2f 100644 --- a/commands/tftp.c +++ b/commands/tftp.c @@ -72,7 +72,7 @@ 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/commands/time.c b/commands/time.c index 987c25e..2cc3292 100644 --- a/commands/time.c +++ b/commands/time.c @@ -27,7 +27,7 @@ start = get_time_ns(); - run_command(buf, 0); + run_command(buf); end = get_time_ns(); diff --git a/common/Kconfig b/common/Kconfig index 5224838..5989502 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -596,6 +596,7 @@ select CMD_DIRNAME select FLEXIBLE_BOOTARGS select CMD_BOOT + select NET_CMD_IFUP if NET prompt "Generic environment template" config DEFAULT_ENVIRONMENT_GENERIC_NEW_MENU diff --git a/common/blspec.c b/common/blspec.c index 2244d5a..9b4b096 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 @@ -131,6 +134,107 @@ } /* + * 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 * * Given a root path collects all blspec entries found under /blspec/entries/. @@ -145,9 +249,13 @@ 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; - pr_debug("%s: %s %s\n", __func__, root, dirname); + nfspath = parse_nfs_url(root); + if (!IS_ERR(nfspath)) + root = nfspath; + + 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 @@ closedir(dir); err_out: + if (!IS_ERR(nfspath)) + free(nfspath); free(abspath); free(entry_default); free(entry_once); @@ -276,7 +386,7 @@ 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/hush.c b/common/hush.c index abe2ced..bd534c1 100644 --- a/common/hush.c +++ b/common/hush.c @@ -1824,7 +1824,7 @@ 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 4cefadb..54f2c71 100644 --- a/common/menu.c +++ b/common/menu.c @@ -468,7 +468,7 @@ 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 d390fb6..4a48210 100644 --- a/common/parser.c +++ b/common/parser.c @@ -176,7 +176,7 @@ * 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 @@ 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 9c4e995..ceb597b 100644 --- a/common/startup.c +++ b/common/startup.c @@ -47,9 +47,9 @@ #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); @@ -95,11 +95,11 @@ 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/defaultenv/defaultenv-2-base/bin/ifup b/defaultenv/defaultenv-2-base/bin/ifup deleted file mode 100644 index 37b986c..0000000 --- a/defaultenv/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/fs/Makefile b/fs/Makefile index e8347bd..d3465ed 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -8,6 +8,6 @@ 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/fs.c b/fs/fs.c index 1b43c61..b0ac918 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -435,7 +435,7 @@ setenv("automount_path", am->path); export("automount_path"); - ret = run_command(am->cmd, 0); + ret = run_command(am->cmd); setenv("automount_path", NULL); if (ret) @@ -1243,6 +1243,7 @@ } free(fsdev->path); + free(fsdev->options); if (fsdev == fs_dev_root) fs_dev_root = NULL; @@ -1317,13 +1318,18 @@ * We do this by registering a new device on which the filesystem * driver will match. */ -int mount(const char *device, const char *fsname, const char *_path) +int mount(const char *device, const char *fsname, const char *_path, + const char *fsoptions) { struct fs_device_d *fsdev; int ret; char *path = normalise_path(_path); - debug("mount: %s on %s type %s\n", device, path, fsname); + if (!fsoptions) + fsoptions = ""; + + debug("mount: %s on %s type %s, options=%s\n", + device, path, fsname, fsoptions); if (fs_dev_root) { fsdev = get_fsdevice_by_path(path); @@ -1355,6 +1361,7 @@ fsdev->dev.id = get_free_deviceid(fsdev->dev.name); fsdev->path = xstrdup(path); fsdev->dev.bus = &fs_bus; + fsdev->options = xstrdup(fsoptions); ret = register_device(&fsdev->dev); if (ret) @@ -1712,7 +1719,7 @@ * 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; @@ -1721,7 +1728,7 @@ /* * If this cdev is already mounted somewhere use this path * instead of mounting it again to avoid corruption on the - * filesystem. + * filesystem. Note this ignores eventual fsoptions though. */ path = cdev_get_mount_path(cdev); if (path) @@ -1732,7 +1739,7 @@ devpath = asprintf("/dev/%s", cdev->name); - ret = mount(devpath, NULL, newpath); + ret = mount(devpath, NULL, newpath, fsoptions); free(devpath); @@ -1762,6 +1769,6 @@ struct cdev *cdev = &bdev->cdev; list_for_each_entry(cdev, &bdev->dev->cdevs, devices_list) - cdev_mount_default(cdev); + cdev_mount_default(cdev, NULL); } } diff --git a/fs/nfs.c b/fs/nfs.c index 7173264..046cd4d 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,9 @@ #include #include #include +#include + +#include "parseopt.h" #define SUNRPC_PORT 111 @@ -48,34 +52,54 @@ #define MOUNT_ADDENTRY 1 #define MOUNT_UMOUNT 3 -#define NFS_GETATTR 1 -#define NFS_LOOKUP 4 -#define NFS_READLINK 5 -#define NFS_READ 6 -#define NFS_READDIR 16 +#define 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 +131,96 @@ 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 +231,20 @@ #define xdr_zero 0 #define XDR_QUADLEN(l) (((l) + 3) >> 2) +struct nfs_dir { + DIR dir; + + /* + * stream points to the next entry3 in the reply member of READDIR3res + * (if any, to the end indicator otherwise). + */ + struct xdr_stream stream; + struct dirent ent; + struct file_priv *priv; + uint64_t cookie; + char cookieverf[NFS3_COOKIEVERFSIZE]; +}; + static void xdr_init(struct xdr_stream *stream, void *buf, int len) { stream->p = stream->buf = buf; @@ -192,8 +275,10 @@ return p; } -static int decode_filename(struct xdr_stream *xdr, - char *name, u32 *length) +/* + * name is expected to point to a buffer with a size of at least 256 bytes. + */ +static int decode_filename(struct xdr_stream *xdr, char *name, u32 *length) { __be32 *p; u32 count; @@ -201,7 +286,7 @@ p = xdr_inline_decode(xdr, 4); if (!p) goto out_overflow; - count = be32_to_cpup(p); + count = ntoh32(net_read_uint32(p)); if (count > 255) goto out_nametoolong; p = xdr_inline_decode(xdr, count); @@ -211,11 +296,13 @@ name[count] = 0; *length = count; return 0; + out_nametoolong: - printk("NFS: returned filename too long: %u\n", count); + printf("%s: returned a too long filename: %u\n", __func__, count); return -ENAMETOOLONG; + out_overflow: - printf("%s overflow\n",__func__); + printf("%s: premature end of packet\n", __func__); return -EIO; } @@ -224,34 +311,18 @@ */ static uint32_t *rpc_add_credentials(uint32_t *p) { - int hl; - int hostnamelen = 0; - /* - * Here's the executive summary on authentication requirements of the - * various NFS server implementations: Linux accepts both AUTH_NONE - * and AUTH_UNIX authentication (also accepts an empty hostname field - * in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts - * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX - * scheme). To be safe, use AUTH_UNIX and pass the hostname if we have - * it (if the BOOTP/DHCP reply didn't give one, just use an empty - * hostname). + * *BSD refuses AUTH_NONE, so use AUTH_UNIX. An empty hostname is OK for + * both Linux and *BSD. */ - hl = (hostnamelen + 3) & ~3; - /* Provide an AUTH_UNIX credential. */ - *p++ = htonl(1); /* AUTH_UNIX */ - *p++ = htonl(hl + 20); /* auth length */ - *p++ = htonl(0); /* stamp */ - *p++ = htonl(hostnamelen); /* hostname string */ + *p++ = hton32(1); /* AUTH_UNIX */ + *p++ = hton32(20); /* auth length: 20 + strlen(hostname) */ + *p++ = hton32(0); /* stamp */ + *p++ = hton32(0); /* hostname string length */ + /* memcpy(p, "", 0); p += 0; <- empty host name */ - if (hostnamelen & 3) - *(p + hostnamelen / 4) = 0; /* add zero padding */ - - /* memcpy(p, hostname, hostnamelen); */ /* empty hostname */ - - p += hl / 4; *p++ = 0; /* uid */ *p++ = 0; /* gid */ *p++ = 0; /* auxiliary gid list */ @@ -263,7 +334,8 @@ return p; } -static int rpc_check_reply(unsigned char *pkt, int rpc_prog, unsigned long rpc_id, int *nfserr) +static int rpc_check_reply(unsigned char *pkt, + int rpc_prog, uint32_t rpc_id, int *nfserr) { uint32_t *data; struct rpc_reply rpc; @@ -275,8 +347,13 @@ memcpy(&rpc, pkt, sizeof(rpc)); - if (ntohl(rpc.id) != rpc_id) + if (ntoh32(rpc.id) != rpc_id) { + if (rpc_id - ntoh32(rpc.id) == 1) + /* stale packet, wait a bit longer */ + return 0; + return -EINVAL; + } if (rpc.rstatus || rpc.verifier || @@ -288,7 +365,7 @@ return 0; data = (uint32_t *)(pkt + sizeof(struct rpc_reply)); - *nfserr = ntohl(net_read_uint32(data)); + *nfserr = ntoh32(net_read_uint32(data)); *nfserr = -*nfserr; debug("%s: state: %d, err %d\n", __func__, nfs_state, *nfserr); @@ -303,37 +380,41 @@ 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(); @@ -368,7 +449,7 @@ /* * rpc_lookup_req - Lookup RPC Port numbers */ -static int rpc_lookup_req(struct nfs_priv *npriv, int prog, int ver) +static int rpc_lookup_req(struct nfs_priv *npriv, uint32_t prog, uint32_t ver) { uint32_t data[16]; int ret; @@ -376,33 +457,149 @@ data[0] = 0; data[1] = 0; /* auth credential */ data[2] = 0; data[3] = 0; /* auth verifier */ - data[4] = htonl(prog); - data[5] = htonl(ver); - data[6] = htonl(17); /* IP_UDP */ + data[4] = hton32(prog); + data[5] = hton32(ver); + data[6] = hton32(17); /* IP_UDP */ data[7] = 0; ret = rpc_req(npriv, PROG_PORTMAP, PORTMAP_GETPORT, data, 8); if (ret) return ret; - port = net_read_uint32((uint32_t *)(nfs_packet + sizeof(struct rpc_reply))); + port = ntoh32(net_read_uint32(nfs_packet + sizeof(struct rpc_reply))); + return port; +} - switch (prog) { - case PROG_MOUNT: - npriv->mount_port = ntohl(port); - debug("mount port: %d\n", npriv->mount_port); +static uint32_t *nfs_add_uint32(uint32_t *p, uint32_t val) +{ + *p++ = hton32(val); + return p; +} + +static uint32_t *nfs_add_uint64(uint32_t *p, uint64_t val) +{ + uint64_t nval = hton64(val); + + memcpy(p, &nval, 8); + return p + 2; +} + +static uint32_t *nfs_add_fh3(uint32_t *p, unsigned fh_len, const char *fh) +{ + *p++ = hton32(fh_len); + + /* zero padding */ + if (fh_len & 3) + p[fh_len / 4] = 0; + + memcpy(p, fh, fh_len); + p += DIV_ROUND_UP(fh_len, 4); + return p; +} + +static uint32_t *nfs_add_filename(uint32_t *p, + uint32_t filename_len, const char *filename) +{ + *p++ = hton32(filename_len); + + /* zero padding */ + if (filename_len & 3) + p[filename_len / 4] = 0; + + memcpy(p, filename, filename_len); + p += DIV_ROUND_UP(filename_len, 4); + return p; +} + +/* This is a 1:1 mapping for Linux, the compiler optimizes it out */ +static const struct { + uint32_t nfsmode; + unsigned short statmode; +} nfs3_mode_bits[] = { + { 0x00001, S_IXOTH }, + { 0x00002, S_IWOTH }, + { 0x00004, S_IROTH }, + { 0x00008, S_IXGRP }, + { 0x00010, S_IWGRP }, + { 0x00020, S_IRGRP }, + { 0x00040, S_IXUSR }, + { 0x00080, S_IWUSR }, + { 0x00100, S_IRUSR }, + { 0x00200, S_ISVTX }, + { 0x00400, S_ISGID }, + { 0x00800, S_ISUID }, +}; + +static int nfs_fattr3_to_stat(uint32_t *p, struct stat *s) +{ + uint32_t mode; + size_t i; + + /* offsetof(struct fattr3, type) = 0 */ + switch (ntoh32(net_read_uint32(p + 0))) { + case NF3REG: + s->st_mode = S_IFREG; break; - case PROG_NFS: - npriv->nfs_port = ntohl(port); - debug("nfs port: %d\n", npriv->nfs_port); + 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: - return -EINVAL; + printf("%s: invalid mode %x\n", + __func__, ntoh32(net_read_uint32(p + 0))); + return -EIO; } + /* offsetof(struct fattr3, mode) = 4 */ + mode = ntoh32(net_read_uint32(p + 1)); + for (i = 0; i < ARRAY_SIZE(nfs3_mode_bits); ++i) { + if (mode & nfs3_mode_bits[i].nfsmode) + s->st_mode |= nfs3_mode_bits[i].statmode; + } + + /* offsetof(struct fattr3, size) = 20 */ + s->st_size = ntoh64(net_read_uint64(p + 5)); + return 0; } +static uint32_t *nfs_read_post_op_attr(uint32_t *p, struct stat **s) +{ + struct stat dummy; + /* + * union post_op_attr switch (bool attributes_follow) { + * case TRUE: + * fattr3 attributes; + * case FALSE: + * void; + * }; + */ + + if (ntoh32(net_read_uint32(p++))) { + nfs_fattr3_to_stat(p, s ? *s : &dummy); + p += 21; + } else if (s) { + /* no attributes available */ + *s = NULL; + } + + return p; +} + /* * nfs_mount_req - Mount an NFS Filesystem */ @@ -421,7 +618,7 @@ p = &(data[0]); p = rpc_add_credentials(p); - *p++ = htonl(pathlen); + *p++ = hton32(pathlen); if (pathlen & 3) *(p + pathlen / 4) = 0; @@ -434,7 +631,16 @@ if (ret) return ret; - memcpy(npriv->rootfh, nfs_packet + sizeof(struct rpc_reply) + 4, NFS_FHSIZE); + p = nfs_packet + sizeof(struct rpc_reply) + 4; + + npriv->rootfh_len = ntoh32(net_read_uint32(p++)); + if (npriv->rootfh_len > NFS3_FHSIZE) { + printf("%s: file handle too big: %lu\n", __func__, + (unsigned long)npriv->rootfh_len); + return -EIO; + } + memcpy(npriv->rootfh, p, npriv->rootfh_len); + p += DIV_ROUND_UP(npriv->rootfh_len, 4); return 0; } @@ -454,12 +660,7 @@ p = &(data[0]); p = rpc_add_credentials(p); - *p++ = htonl(pathlen); - if (pathlen & 3) - *(p + pathlen / 4) = 0; - - memcpy (p, npriv->path, pathlen); - p += (pathlen + 3) / 4; + p = nfs_add_filename(p, pathlen, npriv->path); len = p - &(data[0]); @@ -468,36 +669,68 @@ /* * nfs_lookup_req - Lookup Pathname + * + * *s is set to NULL if LOOKUP3resok doesn't contain obj_attributes. */ -static int nfs_lookup_req(struct file_priv *priv, const char *filename, - int fnamelen) +static int nfs_lookup_req(struct file_priv *priv, + uint32_t filename_len, const char *filename, struct stat **s) { uint32_t data[1024]; uint32_t *p; int len; int ret; + /* + * struct LOOKUP3args { + * diropargs3 what; + * }; + * + * struct LOOKUP3resok { + * nfs_fh3 object; + * post_op_attr obj_attributes; + * post_op_attr dir_attributes; + * }; + * + * struct LOOKUP3resfail { + * post_op_attr dir_attributes; + * }; + * + * union LOOKUP3res switch (nfsstat3 status) { + * case NFS3_OK: + * LOOKUP3resok resok; + * default: + * LOOKUP3resfail resfail; + * }; + */ + p = &(data[0]); p = rpc_add_credentials(p); - memcpy(p, priv->filefh, NFS_FHSIZE); + /* what.dir */ + p = nfs_add_fh3(p, priv->filefh_len, priv->filefh); - p += (NFS_FHSIZE / 4); - *p++ = htonl(fnamelen); - - if (fnamelen & 3) - *(p + fnamelen / 4) = 0; - - memcpy(p, filename, fnamelen); - p += (fnamelen + 3) / 4; + /* what.name */ + p = nfs_add_filename(p, filename_len, filename); len = p - &(data[0]); - ret = rpc_req(priv->npriv, PROG_NFS, NFS_LOOKUP, data, len); + ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_LOOKUP, data, len); if (ret) return ret; - memcpy(priv->filefh, nfs_packet + sizeof(struct rpc_reply) + 4, NFS_FHSIZE); + p = nfs_packet + sizeof(struct rpc_reply) + 4; + + priv->filefh_len = ntoh32(net_read_uint32(p++)); + if (priv->filefh_len > NFS3_FHSIZE) { + debug("%s: file handle too big: %lu\n", __func__, + (unsigned long)priv->filefh_len); + return -EIO; + } + memcpy(priv->filefh, p, priv->filefh_len); + p += DIV_ROUND_UP(priv->filefh_len, 4); + + if (s) + nfs_read_post_op_attr(p, s); return 0; } @@ -508,33 +741,48 @@ 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; @@ -542,26 +790,79 @@ 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; } @@ -569,35 +870,74 @@ /* * 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; } @@ -636,43 +976,45 @@ return -ENOSYS; } -static struct file_priv *nfs_do_open(struct device_d *dev, const char *filename) +static struct file_priv *nfs_do_open(struct device_d *dev, + const char *filename, struct stat **s) { struct file_priv *priv; struct nfs_priv *npriv = dev->priv; int ret; - const char *fname, *tok; + const char *tok; + debug("%s: filename = %s\n", __func__, filename); priv = xzalloc(sizeof(*priv)); priv->npriv = npriv; if (!*filename) { - memcpy(priv->filefh, npriv->rootfh, NFS_FHSIZE); + priv->filefh_len = npriv->rootfh_len; + memcpy(priv->filefh, npriv->rootfh, npriv->rootfh_len); return priv; } filename++; - fname = filename; + priv->filefh_len = npriv->rootfh_len; + memcpy(priv->filefh, npriv->rootfh, NFS3_FHSIZE); - memcpy(priv->filefh, npriv->rootfh, NFS_FHSIZE); + while (*filename) { + size_t flen; - while (*fname) { - int flen; - - tok = strchr(fname, '/'); + tok = strchr(filename, '/'); if (tok) - flen = tok - fname; + flen = tok - filename; else - flen = strlen(fname); + flen = strlen(filename); - ret = nfs_lookup_req(priv, fname, flen); + ret = nfs_lookup_req(priv, flen, filename, s); if (ret) goto out; if (tok) - fname += flen + 1; + filename += flen + 1; else break; } @@ -693,19 +1035,28 @@ free(priv); } -static struct file_priv *nfs_do_stat(struct device_d *dev, const char *filename, struct stat *s) +static struct file_priv *nfs_do_stat(struct device_d *dev, + const char *filename, struct stat *s) { struct file_priv *priv; int ret; + struct stat **sptr = &s; - priv = nfs_do_open(dev, filename); + debug("%s: filename = %s\n", __func__, filename); + priv = nfs_do_open(dev, filename, sptr); if (IS_ERR(priv)) return priv; - ret = nfs_attr_req(priv, s); - if (ret) { - nfs_do_close(priv); - return ERR_PTR(ret); + if (!*sptr) { + /* + * The nfs server didn't provide obj_attributes in the lookup + * reply, so ask for them explicitly. + */ + ret = nfs_attr_req(priv, s); + if (ret) { + nfs_do_close(priv); + return ERR_PTR(ret); + } } return priv; @@ -715,35 +1066,52 @@ { uint32_t data[1024]; uint32_t *p; - int len; + uint32_t len; int ret; - char *path; - uint32_t *filedata; + /* + * struct READLINK3args { + * nfs_fh3 symlink; + * }; + * + * struct READLINK3resok { + * post_op_attr symlink_attributes; + * nfspath3 data; + * }; + * + * struct READLINK3resfail { + * post_op_attr symlink_attributes; + * } + * + * union READLINK3res switch (nfsstat3 status) { + * case NFS3_OK: + * READLINK3resok resok; + * default: + * READLINK3resfail resfail; + * }; + */ p = &(data[0]); p = rpc_add_credentials(p); - memcpy(p, priv->filefh, NFS_FHSIZE); - p += (NFS_FHSIZE / 4); + p = nfs_add_fh3(p, priv->filefh_len, priv->filefh); len = p - &(data[0]); - ret = rpc_req(priv->npriv, PROG_NFS, NFS_READLINK, data, len); + ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_READLINK, data, len); if (ret) return ret; - filedata = nfs_packet + sizeof(struct rpc_reply); - filedata++; + p = nfs_packet + sizeof(struct rpc_reply) + 4; - len = ntohl(net_read_uint32(filedata)); /* new path length */ - filedata++; + p = nfs_read_post_op_attr(p, NULL); - path = (char *)filedata; + len = ntoh32(net_read_uint32(p)); /* new path length */ + p++; if (len > size) - len = size; + return -ENOMEM; - memcpy(buf, path, len); + memcpy(buf, p, len); return 0; } @@ -753,9 +1121,8 @@ { struct file_priv *priv; int ret; - struct stat s; - priv = nfs_do_stat(dev, filename, &s); + priv = nfs_do_open(dev, filename, NULL); if (IS_ERR(priv)) return PTR_ERR(priv); @@ -807,32 +1174,17 @@ static int nfs_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) @@ -845,110 +1197,89 @@ 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) @@ -1010,17 +1341,27 @@ /* Need a priviliged source port */ net_udp_bind(npriv->con, 1000); - ret = rpc_lookup_req(npriv, PROG_MOUNT, 1); - if (ret) { - printf("lookup mount port failed with %d\n", ret); - goto err2; + parseopt_hu(fsdev->options, "mountport", &npriv->mount_port); + if (!npriv->mount_port) { + ret = rpc_lookup_req(npriv, PROG_MOUNT, 3); + if (ret < 0) { + printf("lookup mount port failed with %d\n", ret); + goto err2; + } + npriv->mount_port = ret; } + debug("mount port: %hu\n", npriv->mount_port); - ret = rpc_lookup_req(npriv, PROG_NFS, 2); - if (ret) { - printf("lookup nfs port failed with %d\n", ret); - goto err2; + parseopt_hu(fsdev->options, "port", &npriv->nfs_port); + if (!npriv->nfs_port) { + ret = rpc_lookup_req(npriv, PROG_NFS, 3); + if (ret < 0) { + printf("lookup nfs port failed with %d\n", ret); + goto err2; + } + npriv->nfs_port = ret; } + debug("nfs port: %d\n", npriv->nfs_port); ret = nfs_mount_req(npriv); if (ret) { diff --git a/fs/parseopt.c b/fs/parseopt.c new file mode 100644 index 0000000..fbe53cf --- /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 0000000..a8523b6 --- /dev/null +++ b/fs/parseopt.h @@ -0,0 +1 @@ +void parseopt_hu(const char *options, const char *opt, unsigned short *val); diff --git a/include/byteorder.h b/include/byteorder.h new file mode 100644 index 0000000..4b255a5 --- /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__ */ diff --git a/include/common.h b/include/common.h index 293f504..6987b4f 100644 --- a/include/common.h +++ b/include/common.h @@ -92,8 +92,7 @@ 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 */ diff --git a/include/fs.h b/include/fs.h index 856e00a..073641c 100644 --- a/include/fs.h +++ b/include/fs.h @@ -99,6 +99,7 @@ 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 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 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/include/net.h b/include/net.h index a4cfec7..3b800b7 100644 --- a/include/net.h +++ b/include/net.h @@ -269,11 +269,18 @@ } /* 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; +} + +static inline uint64_t net_read_uint64(void *from) +{ + uint64_t tmp; + memcpy(&tmp, from, sizeof(tmp)); + return tmp; } /* write IP *in network byteorder* */ @@ -397,7 +404,7 @@ 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 @@ -450,4 +457,9 @@ 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/lib/bootstrap/disk.c b/lib/bootstrap/disk.c index 879d331..527e430 100644 --- a/lib/bootstrap/disk.c +++ b/lib/bootstrap/disk.c @@ -20,7 +20,7 @@ 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; diff --git a/net/Kconfig b/net/Kconfig index c12193d..59b6417 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -26,4 +26,15 @@ 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 416e30a..fabb17e 100644 --- a/net/Makefile +++ b/net/Makefile @@ -5,3 +5,4 @@ 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/eth.c b/net/eth.c index 524fb89..1f48f2d 100644 --- a/net/eth.c +++ b/net/eth.c @@ -144,7 +144,7 @@ return eth_current; } -struct eth_device *eth_get_byname(char *ethname) +struct eth_device *eth_get_byname(const char *ethname) { struct eth_device *edev; diff --git a/net/ifup.c b/net/ifup.c new file mode 100644 index 0000000..3b89ce1 --- /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