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