mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* tftp filesystem support
@ 2012-02-19 17:58 Sascha Hauer
  2012-02-19 17:58 ` [PATCH 1/5] Add suport for tftp as a filesystem Sascha Hauer
                   ` (4 more replies)
  0 siblings, 5 replies; 8+ messages in thread
From: Sascha Hauer @ 2012-02-19 17:58 UTC (permalink / raw)
  To: barebox

Handling tftp files has been special in barebox since it needs a special
tftp command. This series implements tftp as a regular filesystem, so no
special handling for tftp anymore.

Unfortunately there is no directory read support in tftp, so a mounted
tftp directory will only show an empty directory, but despite what 'ls'
says the files are really there and can (mostly) be accessed like every
other file. The exception to this is that lseek is not possible on tftp
files which means that bootm needs a quirk.

Sascha

Sascha Hauer (5):
      Add suport for tftp as a filesystem
      make uimages work on tftpfs
      net: let net_udp_get_payload return void *
      add string for -ETIMEDOUT
      copy_file: limit progress bar to sensible limits

 common/misc.c   |    2 +-
 common/uimage.c |   25 +++
 fs/Kconfig      |    4 +
 fs/Makefile     |    1 +
 fs/tftp.c       |  644 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/net.h   |    2 +-
 lib/copy_file.c |    5 +-
 7 files changed, 678 insertions(+), 5 deletions(-)
 create mode 100644 fs/tftp.c

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/5] Add suport for tftp as a filesystem
  2012-02-19 17:58 tftp filesystem support Sascha Hauer
@ 2012-02-19 17:58 ` Sascha Hauer
  2012-02-20  4:54   ` Jean-Christophe PLAGNIOL-VILLARD
  2012-02-19 17:58 ` [PATCH 2/5] make uimages work on tftpfs Sascha Hauer
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 8+ messages in thread
From: Sascha Hauer @ 2012-02-19 17:58 UTC (permalink / raw)
  To: barebox

This adds tftp filesystem support. It currently duplicates
significant amounts of the tftp (command) support. This is ok
since we can eventually drop the original tftp command later.

tftp is not really suitable to be handled as a filesystem. It lacks
support for stat, reading directories and other things. Handling
it as a filesystem has one big advantage though: tftp is no special
case for boot scripts and/or commands anymore which makes them simpler.

This implementation has some improvements to the original tftp command.
It supports blocksize negotiation which speeds up transfers if the tftp
server supports it. Also we can determine the filesize to transfer if
the remote end supports it.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/Kconfig  |    4 +
 fs/Makefile |    1 +
 fs/tftp.c   |  644 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 649 insertions(+), 0 deletions(-)
 create mode 100644 fs/tftp.c

diff --git a/fs/Kconfig b/fs/Kconfig
index 56c02da..6208cd2 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -16,6 +16,10 @@ config FS_DEVFS
 	default y
 	prompt "devfs support"
 
+config FS_TFTP
+	bool
+	prompt "tftp support"
+
 source fs/fat/Kconfig
 
 config PARTITION_NEED_MTD
diff --git a/fs/Makefile b/fs/Makefile
index 7cae2b6..d204093 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -4,3 +4,4 @@ obj-y			+= devfs-core.o
 obj-$(CONFIG_FS_DEVFS)	+= devfs.o
 obj-$(CONFIG_FS_FAT)	+= fat/
 obj-y	+= fs.o
+obj-$(CONFIG_FS_TFTP)	+= tftp.o
diff --git a/fs/tftp.c b/fs/tftp.c
new file mode 100644
index 0000000..512da03
--- /dev/null
+++ b/fs/tftp.c
@@ -0,0 +1,644 @@
+/*
+ * tftp.c
+ *
+ * Copyright (c) 2011 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <common.h>
+#include <command.h>
+#include <net.h>
+#include <driver.h>
+#include <clock.h>
+#include <fs.h>
+#include <errno.h>
+#include <libgen.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <fs.h>
+#include <init.h>
+#include <linux/stat.h>
+#include <linux/err.h>
+#include <kfifo.h>
+#include <sizes.h>
+
+#define TFTP_PORT	69	/* Well known TFTP port #		*/
+#define TIMEOUT		5	/* Seconds to timeout for a lost pkt	*/
+
+/* After this time without a response from the server we will resend a packet */
+#define TFTP_RESEND_TIMEOUT	SECOND
+
+/* After this time without progress we will bail out */
+#define TFTP_TIMEOUT		(5 * SECOND)
+
+/*
+ *	TFTP operations.
+ */
+#define TFTP_RRQ	1
+#define TFTP_WRQ	2
+#define TFTP_DATA	3
+#define TFTP_ACK	4
+#define TFTP_ERROR	5
+#define TFTP_OACK	6
+
+#define STATE_RRQ	1
+#define STATE_WRQ	2
+#define STATE_RDATA	3
+#define STATE_WDATA	4
+#define STATE_OACK	5
+#define STATE_WAITACK	6
+#define STATE_LAST	7
+#define STATE_DONE	8
+
+#define TFTP_BLOCK_SIZE		512	/* default TFTP block size */
+
+#define TFTP_ERR_RESEND	1
+
+struct file_priv {
+	struct net_connection *tftp_con;
+	int push;
+	uint16_t block;
+	uint16_t last_block;
+	int state;
+	int err;
+	int server_port;
+	const char *filename;
+	int filesize;
+	uint64_t resend_timeout;
+	uint64_t progress_timeout;
+	struct kfifo *fifo;
+	void *buf;
+	int blocksize;
+};
+
+struct tftp_priv {
+	IPaddr_t server;
+};
+
+static int tftp_create(struct device_d *dev, const char *pathname, mode_t mode)
+{
+	return 0;
+}
+
+static int tftp_unlink(struct device_d *dev, const char *pathname)
+{
+	return -ENOSYS;
+}
+
+static int tftp_mkdir(struct device_d *dev, const char *pathname)
+{
+	return -ENOSYS;
+}
+
+static int tftp_rmdir(struct device_d *dev, const char *pathname)
+{
+	return -ENOSYS;
+}
+
+static int tftp_truncate(struct device_d *dev, FILE *f, ulong size)
+{
+	return -ENOSYS;
+}
+
+static int tftp_send(struct file_priv *priv)
+{
+	unsigned char *xp;
+	int len = 0;
+	uint16_t *s;
+	unsigned char *pkt = net_udp_get_payload(priv->tftp_con);
+	int ret;
+
+	debug("%s: state %d\n", __func__, priv->state);
+
+	switch (priv->state) {
+	case STATE_RRQ:
+	case STATE_WRQ:
+		xp = pkt;
+		s = (uint16_t *)pkt;
+		if (priv->state == STATE_RRQ)
+			*s++ = htons(TFTP_RRQ);
+		else
+			*s++ = htons(TFTP_WRQ);
+		pkt = (unsigned char *)s;
+		pkt += sprintf((unsigned char *)pkt,
+				"%s%c"
+				"octet%c"
+				"timeout%c"
+				"%d%c"
+				"tsize%c"
+				"%d%c"
+				"blksize%c"
+				"1432",
+				priv->filename, 0,
+				0,
+				0,
+				TIMEOUT, 0,
+				0,
+				priv->filesize, 0,
+				0);
+		pkt++;
+		len = pkt - xp;
+		break;
+
+	case STATE_RDATA:
+	case STATE_OACK:
+		xp = pkt;
+		s = (uint16_t *)pkt;
+		*s++ = htons(TFTP_ACK);
+		*s++ = htons(priv->block);
+		pkt = (unsigned char *)s;
+		len = pkt - xp;
+		break;
+	}
+
+	ret = net_udp_send(priv->tftp_con, len);
+
+	return ret;
+}
+
+static int tftp_send_write(struct file_priv *priv, void *buf, int len)
+{
+	uint16_t *s;
+	unsigned char *pkt = net_udp_get_payload(priv->tftp_con);
+	int ret;
+
+	s = (uint16_t *)pkt;
+	*s++ = htons(TFTP_DATA);
+	*s++ = htons(priv->block);
+	memcpy((void *)s, buf, len);
+	if (len < priv->blocksize)
+		priv->state = STATE_LAST;
+	len += 4;
+
+	ret = net_udp_send(priv->tftp_con, len);
+	priv->last_block = priv->block;
+	priv->state = STATE_WAITACK;
+
+	return ret;
+}
+
+static int tftp_poll(struct file_priv *priv)
+{
+	if (ctrlc()) {
+		priv->state = STATE_DONE;
+		priv->err = -EINTR;
+		return -EINTR;
+	}
+
+	if (is_timeout(priv->resend_timeout, TFTP_RESEND_TIMEOUT)) {
+		printf("T ");
+		priv->resend_timeout = get_time_ns();
+		return TFTP_ERR_RESEND;
+	}
+
+	if (is_timeout(priv->progress_timeout, TFTP_TIMEOUT)) {
+		priv->state = STATE_DONE;
+		priv->err = -ETIMEDOUT;
+		return -ETIMEDOUT;
+	}
+
+	net_poll();
+
+	return 0;
+}
+
+static void tftp_parse_oack(struct file_priv *priv, unsigned char *pkt, int len)
+{
+	unsigned char *opt, *val, *s;
+
+	pkt[len - 1] = 0;
+
+	debug("got OACK\n");
+#ifdef DEBUG
+	memory_display(pkt, 0, len, 1);
+#endif
+
+	s = pkt;
+
+	while (s < pkt + len) {
+		opt = s;
+		val = s + strlen(s) + 1;
+		if (val > s + len)
+			return;
+		if (!strcmp(opt, "tsize"))
+			priv->filesize = simple_strtoul(val, NULL, 10);
+		if (!strcmp(opt, "blksize"))
+			priv->blocksize = simple_strtoul(val, NULL, 10);
+		debug("OACK opt: %s val: %s\n", opt, val);
+		s = val + strlen(val) + 1;
+	}
+}
+
+static void tftp_handler(void *ctx, char *packet, unsigned len)
+{
+	struct file_priv *priv = ctx;
+	uint16_t proto;
+	uint16_t *s;
+	char *pkt = net_eth_to_udp_payload(packet);
+	struct udphdr *udp = net_eth_to_udphdr(packet);
+
+	len = net_eth_to_udplen(packet);
+	if (len < 2)
+		return;
+
+	len -= 2;
+
+	s = (uint16_t *)pkt;
+	proto = *s++;
+	pkt = (unsigned char *)s;
+
+	debug("%s: proto 0x%04x\n", __func__, proto);
+
+	switch (ntohs(proto)) {
+	case TFTP_RRQ:
+	case TFTP_WRQ:
+	default:
+		break;
+	case TFTP_ACK:
+		if (!priv->push)
+			break;
+
+		priv->block = ntohs(*(uint16_t *)pkt);
+		if (priv->block != priv->last_block) {
+			debug("ack %d != %d\n", priv->block, priv->last_block);
+			break;
+		}
+
+		priv->block++;
+		if (priv->state == STATE_LAST) {
+			priv->state = STATE_DONE;
+			break;
+		}
+		priv->tftp_con->udp->uh_dport = udp->uh_sport;
+		priv->state = STATE_WDATA;
+		break;
+
+	case TFTP_OACK:
+		tftp_parse_oack(priv, pkt, len);
+		priv->server_port = ntohs(udp->uh_sport);
+		priv->tftp_con->udp->uh_dport = udp->uh_sport;
+
+		if (priv->push) {
+			/* send first block */
+			priv->state = STATE_WDATA;
+			priv->block = 1;
+		} else {
+			/* send ACK */
+			priv->state = STATE_OACK;
+			priv->block = 0;
+			tftp_send(priv);
+		}
+
+		break;
+	case TFTP_DATA:
+		if (len < 2)
+			return;
+		len -= 2;
+		priv->block = ntohs(*(uint16_t *)pkt);
+
+		if (priv->state == STATE_RRQ || priv->state == STATE_OACK) {
+			/* first block received */
+			priv->state = STATE_RDATA;
+			priv->tftp_con->udp->uh_dport = udp->uh_sport;
+			priv->server_port = ntohs(udp->uh_sport);
+			priv->last_block = 0;
+
+			if (priv->block != 1) {	/* Assertion */
+				printf("error: First block is not block 1 (%d)\n",
+					priv->block);
+				priv->err = -EINVAL;
+				priv->state = STATE_DONE;
+				break;
+			}
+		}
+
+		if (priv->block == priv->last_block)
+			/* Same block again; ignore it. */
+			break;
+
+		priv->last_block = priv->block;
+
+		kfifo_put(priv->fifo, pkt + 2, len);
+
+		if (len < priv->blocksize) {
+			tftp_send(priv);
+			priv->state = STATE_DONE;
+		}
+
+		break;
+
+	case TFTP_ERROR:
+		debug("\nTFTP error: '%s' (%d)\n",
+				pkt + 2, ntohs(*(uint16_t *)pkt));
+		switch (ntohs(*(uint16_t *)pkt)) {
+		case 1:
+			priv->err = -ENOENT;
+			break;
+		case 2:
+			priv->err = -EACCES;
+			break;
+		default:
+			priv->err = -EINVAL;
+			break;
+		}
+		priv->state = STATE_DONE;
+		break;
+	}
+}
+
+static void tftp_timer_reset(struct file_priv *priv)
+{
+	priv->progress_timeout = priv->resend_timeout = get_time_ns();
+}
+
+static struct file_priv *tftp_do_open(struct device_d *dev,
+		int accmode, const char *filename)
+{
+	struct file_priv *priv;
+	struct tftp_priv *tpriv = dev->priv;
+	int ret;
+
+	priv = xzalloc(sizeof(*priv));
+
+	filename++;
+
+	switch (accmode & O_ACCMODE) {
+	case O_RDONLY:
+		priv->push = 0;
+		priv->state = STATE_RRQ;
+		break;
+	case O_WRONLY:
+		priv->push = 1;
+		priv->state = STATE_WRQ;
+		break;
+	case O_RDWR:
+		ret = -ENOSYS;
+		goto out;
+	}
+
+	priv->block = 1;
+	priv->err = -EINVAL;
+	priv->filename = filename;
+	priv->blocksize = TFTP_BLOCK_SIZE;
+
+	priv->fifo = kfifo_alloc(4096);
+	if (!priv->fifo) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	priv->tftp_con = net_udp_new(tpriv->server, TFTP_PORT, tftp_handler,
+			priv);
+	if (IS_ERR(priv->tftp_con)) {
+		ret = PTR_ERR(priv->tftp_con);
+		goto out1;
+	}
+
+	ret = tftp_send(priv);
+	if (ret)
+		goto out2;
+
+	tftp_timer_reset(priv);
+	while (priv->state != STATE_RDATA &&
+			priv->state != STATE_DONE &&
+			priv->state != STATE_WDATA) {
+		ret = tftp_poll(priv);
+		if (ret == TFTP_ERR_RESEND)
+			tftp_send(priv);
+		if (ret < 0)
+			goto out2;
+	}
+
+	if (priv->state == STATE_DONE) {
+		ret = priv->err;
+		goto out2;
+	}
+
+	priv->buf = xmalloc(priv->blocksize);
+
+	return priv;
+out2:
+	net_unregister(priv->tftp_con);
+out1:
+	kfifo_free(priv->fifo);
+out:
+	free(priv);
+
+	return ERR_PTR(ret);
+}
+
+static int tftp_open(struct device_d *dev, FILE *file, const char *filename)
+{
+	struct file_priv *priv;
+
+	priv = tftp_do_open(dev, file->flags, filename);
+	if (IS_ERR(priv))
+		return PTR_ERR(priv);
+
+	file->inode = priv;
+	file->size = SZ_2G;
+
+	return 0;
+}
+
+static int tftp_do_close(struct file_priv *priv)
+{
+	int ret;
+
+	if (priv->push && priv->state != STATE_DONE) {
+		int len;
+
+		len = kfifo_get(priv->fifo, priv->buf, priv->blocksize);
+		tftp_send_write(priv, priv->buf, len);
+		priv->state = STATE_LAST;
+
+		tftp_timer_reset(priv);
+
+		while (priv->state != STATE_DONE) {
+			ret = tftp_poll(priv);
+			if (ret == TFTP_ERR_RESEND)
+				tftp_send_write(priv, priv->buf, len);
+			if (ret < 0)
+				break;
+		}
+	}
+
+	if (!priv->push && priv->state != STATE_DONE) {
+		uint16_t *pkt = net_udp_get_payload(priv->tftp_con);
+		*pkt++ = htons(TFTP_ERROR);
+		*pkt++ = 0;
+		*pkt++ = 0;
+		net_udp_send(priv->tftp_con, 6);
+	}
+
+	net_unregister(priv->tftp_con);
+	kfifo_free(priv->fifo);
+	free(priv->buf);
+	free(priv);
+
+	return 0;
+}
+
+static int tftp_close(struct device_d *dev, FILE *f)
+{
+	struct file_priv *priv = f->inode;
+
+	return tftp_do_close(priv);
+}
+
+static int tftp_write(struct device_d *_dev, FILE *f, const void *inbuf,
+		size_t insize)
+{
+	struct file_priv *priv = f->inode;
+	size_t size, now;
+	int ret;
+
+	debug("%s: %d\n", __func__, insize);
+
+	size = insize;
+
+	while (size) {
+		now = kfifo_put(priv->fifo, inbuf, size);
+
+		while (kfifo_len(priv->fifo) >= priv->blocksize) {
+			kfifo_get(priv->fifo, priv->buf, priv->blocksize);
+
+			tftp_send_write(priv, priv->buf, priv->blocksize);
+			tftp_timer_reset(priv);
+
+			while (priv->state == STATE_WAITACK) {
+				ret = tftp_poll(priv);
+				if (ret == TFTP_ERR_RESEND)
+					tftp_send_write(priv, priv->buf,
+							priv->blocksize);
+				if (ret < 0)
+					return ret;
+			}
+		}
+		size -= now;
+		inbuf += now;
+	}
+
+	return insize;
+}
+
+static int tftp_read(struct device_d *dev, FILE *f, void *buf, size_t insize)
+{
+	struct file_priv *priv = f->inode;
+	size_t outsize = 0, now;
+	int ret;
+
+	debug("%s %d\n", __func__, insize);
+
+	tftp_timer_reset(priv);
+
+	while (insize) {
+		now = kfifo_get(priv->fifo, buf, insize);
+		if (priv->state == STATE_DONE)
+			return outsize + now;
+		if (now) {
+			outsize += now;
+			buf += now;
+			insize -= now;
+			tftp_send(priv);
+			tftp_timer_reset(priv);
+		}
+
+		ret = tftp_poll(priv);
+		if (ret == TFTP_ERR_RESEND)
+			tftp_send(priv);
+		if (ret < 0)
+			return ret;
+	}
+
+	return outsize;
+}
+
+static off_t tftp_lseek(struct device_d *dev, FILE *f, off_t pos)
+{
+	/* not implemented in tftp protocol */
+	return -ENOSYS;
+}
+
+static DIR* tftp_opendir(struct device_d *dev, const char *pathname)
+{
+	/* not implemented in tftp protocol */
+	return NULL;
+}
+
+static int tftp_stat(struct device_d *dev, const char *filename, struct stat *s)
+{
+	struct file_priv *priv;
+
+	priv = tftp_do_open(dev, O_RDONLY, filename);
+	if (IS_ERR(priv))
+		return PTR_ERR(priv);
+
+	s->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO;
+	s->st_size = priv->filesize;
+
+	tftp_do_close(priv);
+
+	return 0;
+}
+
+static int tftp_probe(struct device_d *dev)
+{
+	struct fs_device_d *fsdev = dev->type_data;
+	struct tftp_priv *priv = xzalloc(sizeof(struct tftp_priv));
+
+	dev->priv = priv;
+
+	string_to_ip(fsdev->backingstore, &priv->server);
+
+	return 0;
+}
+
+static void tftp_remove(struct device_d *dev)
+{
+	struct tftp_priv *priv = dev->priv;
+
+	free(priv);
+}
+
+static struct fs_driver_d tftp_driver = {
+	.open      = tftp_open,
+	.close     = tftp_close,
+	.read      = tftp_read,
+	.lseek     = tftp_lseek,
+	.opendir   = tftp_opendir,
+	.stat      = tftp_stat,
+	.create    = tftp_create,
+	.unlink    = tftp_unlink,
+	.mkdir     = tftp_mkdir,
+	.rmdir     = tftp_rmdir,
+	.write     = tftp_write,
+	.truncate  = tftp_truncate,
+	.flags     = 0,
+	.drv = {
+		.probe  = tftp_probe,
+		.remove = tftp_remove,
+		.name = "tftp",
+		.type_data = &tftp_driver,
+	}
+};
+
+static int tftp_init(void)
+{
+	return register_fs_driver(&tftp_driver);
+}
+coredevice_initcall(tftp_init);
-- 
1.7.9


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 2/5] make uimages work on tftpfs
  2012-02-19 17:58 tftp filesystem support Sascha Hauer
  2012-02-19 17:58 ` [PATCH 1/5] Add suport for tftp as a filesystem Sascha Hauer
@ 2012-02-19 17:58 ` Sascha Hauer
  2012-02-19 17:58 ` [PATCH 3/5] net: let net_udp_get_payload return void * Sascha Hauer
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Sascha Hauer @ 2012-02-19 17:58 UTC (permalink / raw)
  To: barebox

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 common/uimage.c |   25 +++++++++++++++++++++++++
 1 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/common/uimage.c b/common/uimage.c
index 28791b5..4933c40 100644
--- a/common/uimage.c
+++ b/common/uimage.c
@@ -94,6 +94,8 @@ size_t uimage_get_size(struct uimage_handle *handle, unsigned int image_no)
 }
 EXPORT_SYMBOL(uimage_get_size);
 
+static const char uimage_tmp[] = "/.uImage_tmp";
+
 /*
  * open a uimage. This will check the header contents and
  * return a handle to the uImage
@@ -106,13 +108,29 @@ struct uimage_handle *uimage_open(const char *filename)
 	struct image_header *header;
 	int i;
 	int ret;
+	struct stat s;
 
+again:
 	fd = open(filename, O_RDONLY);
 	if (fd < 0) {
 		printf("could not open: %s\n", errno_str());
 		return NULL;
 	}
 
+	/*
+	 * Hack around tftp fs. We need lseek for uImage support, but
+	 * this cannot be implemented in tftp fs, so we detect this
+	 * by doing a test lseek and copy the file to ram if it fails
+	 */
+	if (IS_BUILTIN(CONFIG_FS_TFTP) && lseek(fd, 0, SEEK_SET)) {
+		close(fd);
+		ret = copy_file(filename, uimage_tmp, 0);
+		if (ret)
+			return NULL;
+		filename = uimage_tmp;
+		goto again;
+	}
+
 	handle = xzalloc(sizeof(struct uimage_handle));
 	header = &handle->header;
 
@@ -194,6 +212,8 @@ struct uimage_handle *uimage_open(const char *filename)
 err_out:
 	close(fd);
 	free(handle);
+	if (IS_BUILTIN(CONFIG_FS_TFTP) && !stat(uimage_tmp, &s))
+		unlink(uimage_tmp);
 	return NULL;
 }
 EXPORT_SYMBOL(uimage_open);
@@ -203,9 +223,14 @@ EXPORT_SYMBOL(uimage_open);
  */
 void uimage_close(struct uimage_handle *handle)
 {
+	struct stat s;
+
 	close(handle->fd);
 	free(handle->name);
 	free(handle);
+
+	if (IS_BUILTIN(CONFIG_FS_TFTP) && !stat("/.uimage_tmp", &s))
+		unlink("/.uimage_tmp");
 }
 EXPORT_SYMBOL(uimage_close);
 
-- 
1.7.9


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 3/5] net: let net_udp_get_payload return void *
  2012-02-19 17:58 tftp filesystem support Sascha Hauer
  2012-02-19 17:58 ` [PATCH 1/5] Add suport for tftp as a filesystem Sascha Hauer
  2012-02-19 17:58 ` [PATCH 2/5] make uimages work on tftpfs Sascha Hauer
@ 2012-02-19 17:58 ` Sascha Hauer
  2012-02-19 17:58 ` [PATCH 4/5] add string for -ETIMEDOUT Sascha Hauer
  2012-02-19 17:58 ` [PATCH 5/5] copy_file: limit progress bar to sensible limits Sascha Hauer
  4 siblings, 0 replies; 8+ messages in thread
From: Sascha Hauer @ 2012-02-19 17:58 UTC (permalink / raw)
  To: barebox

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 include/net.h |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/include/net.h b/include/net.h
index d0f8341..1026793 100644
--- a/include/net.h
+++ b/include/net.h
@@ -415,7 +415,7 @@ static inline int net_udp_bind(struct net_connection *con, int sport)
 	return 0;
 }
 
-static inline unsigned char *net_udp_get_payload(struct net_connection *con)
+static inline void *net_udp_get_payload(struct net_connection *con)
 {
 	return con->packet + sizeof(struct ethernet) + sizeof(struct iphdr) +
 		sizeof(struct udphdr);
-- 
1.7.9


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 4/5] add string for -ETIMEDOUT
  2012-02-19 17:58 tftp filesystem support Sascha Hauer
                   ` (2 preceding siblings ...)
  2012-02-19 17:58 ` [PATCH 3/5] net: let net_udp_get_payload return void * Sascha Hauer
@ 2012-02-19 17:58 ` Sascha Hauer
  2012-02-19 17:58 ` [PATCH 5/5] copy_file: limit progress bar to sensible limits Sascha Hauer
  4 siblings, 0 replies; 8+ messages in thread
From: Sascha Hauer @ 2012-02-19 17:58 UTC (permalink / raw)
  To: barebox

-ETIMEDOUT is a common error value, so print the string rather
than just the number.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 common/misc.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/common/misc.c b/common/misc.c
index 7edf536..a0059c1 100644
--- a/common/misc.c
+++ b/common/misc.c
@@ -60,6 +60,7 @@ const char *strerror(int errnum)
 	case	EINTR		: str = "Interrupted system call"; break;
 	case	ENETUNREACH	: str = "Network is unreachable"; break;
 	case	ENETDOWN	: str = "Network is down"; break;
+	case	ETIMEDOUT	: str = "Connection timed out"; break;
 #if 0 /* These are probably not needed */
 	case	ENOTBLK		: str = "Block device required"; break;
 	case	EFBIG		: str = "File too large"; break;
@@ -87,7 +88,6 @@ const char *strerror(int errnum)
 	case	ECONNABORTED	: str = "Software caused connection abort"; break;
 	case	ECONNRESET	: str = "Connection reset by peer"; break;
 	case	ENOBUFS		: str = "No buffer space available"; break;
-	case	ETIMEDOUT	: str = "Connection timed out"; break;
 	case	ECONNREFUSED	: str = "Connection refused"; break;
 	case	EHOSTDOWN	: str = "Host is down"; break;
 	case	EALREADY	: str = "Operation already in progress"; break;
-- 
1.7.9


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 5/5] copy_file: limit progress bar to sensible limits
  2012-02-19 17:58 tftp filesystem support Sascha Hauer
                   ` (3 preceding siblings ...)
  2012-02-19 17:58 ` [PATCH 4/5] add string for -ETIMEDOUT Sascha Hauer
@ 2012-02-19 17:58 ` Sascha Hauer
  4 siblings, 0 replies; 8+ messages in thread
From: Sascha Hauer @ 2012-02-19 17:58 UTC (permalink / raw)
  To: barebox

When copying in verbose mode from a tftp server we might
not know the size of the file being transfered. In this
case print one progress item per 16k instead of one for
each single byte.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 lib/copy_file.c |    5 ++---
 1 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/lib/copy_file.c b/lib/copy_file.c
index 967806c..484b2ca 100644
--- a/lib/copy_file.c
+++ b/lib/copy_file.c
@@ -21,6 +21,7 @@ int copy_file(const char *src, const char *dst, int verbose)
 	int ret = 1;
 	void *buf;
 	int total = 0;
+	struct stat statbuf;
 
 	rw_buf = xmalloc(RW_BUF_SIZE);
 
@@ -37,8 +38,6 @@ int copy_file(const char *src, const char *dst, int verbose)
 	}
 
 	if (verbose) {
-		struct stat statbuf;
-
 		if (stat(src, &statbuf) < 0)
 			statbuf.st_size = 0;
 
@@ -67,7 +66,7 @@ int copy_file(const char *src, const char *dst, int verbose)
 		}
 
 		if (verbose)
-			show_progress(total);
+			show_progress(statbuf.st_size ? total : total / 16384);
 	}
 
 	ret = 0;
-- 
1.7.9


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/5] Add suport for tftp as a filesystem
  2012-02-19 17:58 ` [PATCH 1/5] Add suport for tftp as a filesystem Sascha Hauer
@ 2012-02-20  4:54   ` Jean-Christophe PLAGNIOL-VILLARD
  2012-02-20 18:16     ` Sascha Hauer
  0 siblings, 1 reply; 8+ messages in thread
From: Jean-Christophe PLAGNIOL-VILLARD @ 2012-02-20  4:54 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: barebox

On 18:58 Sun 19 Feb     , Sascha Hauer wrote:
> This adds tftp filesystem support. It currently duplicates
> significant amounts of the tftp (command) support. This is ok
> since we can eventually drop the original tftp command later.
> 
> tftp is not really suitable to be handled as a filesystem. It lacks
> support for stat, reading directories and other things. Handling
> it as a filesystem has one big advantage though: tftp is no special
> case for boot scripts and/or commands anymore which makes them simpler.
> 
> This implementation has some improvements to the original tftp command.
> It supports blocksize negotiation which speeds up transfers if the tftp
> server supports it. Also we can determine the filesize to transfer if
> the remote end supports it.
> 
look good do you have seach patch for nfs?

Best Regards,
J.
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  fs/Kconfig  |    4 +
>  fs/Makefile |    1 +
>  fs/tftp.c   |  644 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 649 insertions(+), 0 deletions(-)
>  create mode 100644 fs/tftp.c
> 
> diff --git a/fs/Kconfig b/fs/Kconfig
> index 56c02da..6208cd2 100644
> --- a/fs/Kconfig
> +++ b/fs/Kconfig
> @@ -16,6 +16,10 @@ config FS_DEVFS
>  	default y
>  	prompt "devfs support"
>  
> +config FS_TFTP
> +	bool
> +	prompt "tftp support"
> +
>  source fs/fat/Kconfig
>  
>  config PARTITION_NEED_MTD
> diff --git a/fs/Makefile b/fs/Makefile
> index 7cae2b6..d204093 100644
> --- a/fs/Makefile
> +++ b/fs/Makefile
> @@ -4,3 +4,4 @@ obj-y			+= devfs-core.o
>  obj-$(CONFIG_FS_DEVFS)	+= devfs.o
>  obj-$(CONFIG_FS_FAT)	+= fat/
>  obj-y	+= fs.o
> +obj-$(CONFIG_FS_TFTP)	+= tftp.o
> diff --git a/fs/tftp.c b/fs/tftp.c
> new file mode 100644
> index 0000000..512da03
> --- /dev/null
> +++ b/fs/tftp.c
> @@ -0,0 +1,644 @@
> +/*
> + * tftp.c
> + *
> + * Copyright (c) 2011 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 details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +#include <common.h>
> +#include <command.h>
> +#include <net.h>
> +#include <driver.h>
> +#include <clock.h>
> +#include <fs.h>
> +#include <errno.h>
> +#include <libgen.h>
> +#include <fcntl.h>
> +#include <getopt.h>
> +#include <fs.h>
> +#include <init.h>
> +#include <linux/stat.h>
> +#include <linux/err.h>
> +#include <kfifo.h>
> +#include <sizes.h>
> +
> +#define TFTP_PORT	69	/* Well known TFTP port #		*/
> +#define TIMEOUT		5	/* Seconds to timeout for a lost pkt	*/
> +
> +/* After this time without a response from the server we will resend a packet */
> +#define TFTP_RESEND_TIMEOUT	SECOND
> +
> +/* After this time without progress we will bail out */
> +#define TFTP_TIMEOUT		(5 * SECOND)
> +
> +/*
> + *	TFTP operations.
> + */
> +#define TFTP_RRQ	1
> +#define TFTP_WRQ	2
> +#define TFTP_DATA	3
> +#define TFTP_ACK	4
> +#define TFTP_ERROR	5
> +#define TFTP_OACK	6
> +
> +#define STATE_RRQ	1
> +#define STATE_WRQ	2
> +#define STATE_RDATA	3
> +#define STATE_WDATA	4
> +#define STATE_OACK	5
> +#define STATE_WAITACK	6
> +#define STATE_LAST	7
> +#define STATE_DONE	8
> +
> +#define TFTP_BLOCK_SIZE		512	/* default TFTP block size */
> +
> +#define TFTP_ERR_RESEND	1
> +
> +struct file_priv {
> +	struct net_connection *tftp_con;
> +	int push;
> +	uint16_t block;
> +	uint16_t last_block;
> +	int state;
> +	int err;
> +	int server_port;
> +	const char *filename;
> +	int filesize;
> +	uint64_t resend_timeout;
> +	uint64_t progress_timeout;
> +	struct kfifo *fifo;
> +	void *buf;
> +	int blocksize;
> +};
> +
> +struct tftp_priv {
> +	IPaddr_t server;
> +};
> +
> +static int tftp_create(struct device_d *dev, const char *pathname, mode_t mode)
> +{
> +	return 0;
> +}
> +
> +static int tftp_unlink(struct device_d *dev, const char *pathname)
> +{
> +	return -ENOSYS;
> +}
> +
> +static int tftp_mkdir(struct device_d *dev, const char *pathname)
> +{
> +	return -ENOSYS;
> +}
> +
> +static int tftp_rmdir(struct device_d *dev, const char *pathname)
> +{
> +	return -ENOSYS;
> +}
> +
> +static int tftp_truncate(struct device_d *dev, FILE *f, ulong size)
> +{
> +	return -ENOSYS;
> +}
> +
> +static int tftp_send(struct file_priv *priv)
> +{
> +	unsigned char *xp;
> +	int len = 0;
> +	uint16_t *s;
> +	unsigned char *pkt = net_udp_get_payload(priv->tftp_con);
> +	int ret;
> +
> +	debug("%s: state %d\n", __func__, priv->state);
> +
> +	switch (priv->state) {
> +	case STATE_RRQ:
> +	case STATE_WRQ:
> +		xp = pkt;
> +		s = (uint16_t *)pkt;
> +		if (priv->state == STATE_RRQ)
> +			*s++ = htons(TFTP_RRQ);
> +		else
> +			*s++ = htons(TFTP_WRQ);
> +		pkt = (unsigned char *)s;
> +		pkt += sprintf((unsigned char *)pkt,
> +				"%s%c"
> +				"octet%c"
> +				"timeout%c"
> +				"%d%c"
> +				"tsize%c"
> +				"%d%c"
> +				"blksize%c"
> +				"1432",
> +				priv->filename, 0,
> +				0,
> +				0,
> +				TIMEOUT, 0,
> +				0,
> +				priv->filesize, 0,
> +				0);
> +		pkt++;
> +		len = pkt - xp;
> +		break;
> +
> +	case STATE_RDATA:
> +	case STATE_OACK:
> +		xp = pkt;
> +		s = (uint16_t *)pkt;
> +		*s++ = htons(TFTP_ACK);
> +		*s++ = htons(priv->block);
> +		pkt = (unsigned char *)s;
> +		len = pkt - xp;
> +		break;
> +	}
> +
> +	ret = net_udp_send(priv->tftp_con, len);
> +
> +	return ret;
> +}
> +
> +static int tftp_send_write(struct file_priv *priv, void *buf, int len)
> +{
> +	uint16_t *s;
> +	unsigned char *pkt = net_udp_get_payload(priv->tftp_con);
> +	int ret;
> +
> +	s = (uint16_t *)pkt;
> +	*s++ = htons(TFTP_DATA);
> +	*s++ = htons(priv->block);
> +	memcpy((void *)s, buf, len);
> +	if (len < priv->blocksize)
> +		priv->state = STATE_LAST;
> +	len += 4;
> +
> +	ret = net_udp_send(priv->tftp_con, len);
> +	priv->last_block = priv->block;
> +	priv->state = STATE_WAITACK;
> +
> +	return ret;
> +}
> +
> +static int tftp_poll(struct file_priv *priv)
> +{
> +	if (ctrlc()) {
> +		priv->state = STATE_DONE;
> +		priv->err = -EINTR;
> +		return -EINTR;
> +	}
> +
> +	if (is_timeout(priv->resend_timeout, TFTP_RESEND_TIMEOUT)) {
> +		printf("T ");
> +		priv->resend_timeout = get_time_ns();
> +		return TFTP_ERR_RESEND;
> +	}
> +
> +	if (is_timeout(priv->progress_timeout, TFTP_TIMEOUT)) {
> +		priv->state = STATE_DONE;
> +		priv->err = -ETIMEDOUT;
> +		return -ETIMEDOUT;
> +	}
> +
> +	net_poll();
> +
> +	return 0;
> +}
> +
> +static void tftp_parse_oack(struct file_priv *priv, unsigned char *pkt, int len)
> +{
> +	unsigned char *opt, *val, *s;
> +
> +	pkt[len - 1] = 0;
> +
> +	debug("got OACK\n");
> +#ifdef DEBUG
> +	memory_display(pkt, 0, len, 1);
> +#endif
> +
> +	s = pkt;
> +
> +	while (s < pkt + len) {
> +		opt = s;
> +		val = s + strlen(s) + 1;
> +		if (val > s + len)
> +			return;
> +		if (!strcmp(opt, "tsize"))
> +			priv->filesize = simple_strtoul(val, NULL, 10);
> +		if (!strcmp(opt, "blksize"))
> +			priv->blocksize = simple_strtoul(val, NULL, 10);
> +		debug("OACK opt: %s val: %s\n", opt, val);
> +		s = val + strlen(val) + 1;
> +	}
> +}
> +
> +static void tftp_handler(void *ctx, char *packet, unsigned len)
> +{
> +	struct file_priv *priv = ctx;
> +	uint16_t proto;
> +	uint16_t *s;
> +	char *pkt = net_eth_to_udp_payload(packet);
> +	struct udphdr *udp = net_eth_to_udphdr(packet);
> +
> +	len = net_eth_to_udplen(packet);
> +	if (len < 2)
> +		return;
> +
> +	len -= 2;
> +
> +	s = (uint16_t *)pkt;
> +	proto = *s++;
> +	pkt = (unsigned char *)s;
> +
> +	debug("%s: proto 0x%04x\n", __func__, proto);
> +
> +	switch (ntohs(proto)) {
> +	case TFTP_RRQ:
> +	case TFTP_WRQ:
> +	default:
> +		break;
> +	case TFTP_ACK:
> +		if (!priv->push)
> +			break;
> +
> +		priv->block = ntohs(*(uint16_t *)pkt);
> +		if (priv->block != priv->last_block) {
> +			debug("ack %d != %d\n", priv->block, priv->last_block);
> +			break;
> +		}
> +
> +		priv->block++;
> +		if (priv->state == STATE_LAST) {
> +			priv->state = STATE_DONE;
> +			break;
> +		}
> +		priv->tftp_con->udp->uh_dport = udp->uh_sport;
> +		priv->state = STATE_WDATA;
> +		break;
> +
> +	case TFTP_OACK:
> +		tftp_parse_oack(priv, pkt, len);
> +		priv->server_port = ntohs(udp->uh_sport);
> +		priv->tftp_con->udp->uh_dport = udp->uh_sport;
> +
> +		if (priv->push) {
> +			/* send first block */
> +			priv->state = STATE_WDATA;
> +			priv->block = 1;
> +		} else {
> +			/* send ACK */
> +			priv->state = STATE_OACK;
> +			priv->block = 0;
> +			tftp_send(priv);
> +		}
> +
> +		break;
> +	case TFTP_DATA:
> +		if (len < 2)
> +			return;
> +		len -= 2;
> +		priv->block = ntohs(*(uint16_t *)pkt);
> +
> +		if (priv->state == STATE_RRQ || priv->state == STATE_OACK) {
> +			/* first block received */
> +			priv->state = STATE_RDATA;
> +			priv->tftp_con->udp->uh_dport = udp->uh_sport;
> +			priv->server_port = ntohs(udp->uh_sport);
> +			priv->last_block = 0;
> +
> +			if (priv->block != 1) {	/* Assertion */
> +				printf("error: First block is not block 1 (%d)\n",
> +					priv->block);
> +				priv->err = -EINVAL;
> +				priv->state = STATE_DONE;
> +				break;
> +			}
> +		}
> +
> +		if (priv->block == priv->last_block)
> +			/* Same block again; ignore it. */
> +			break;
> +
> +		priv->last_block = priv->block;
> +
> +		kfifo_put(priv->fifo, pkt + 2, len);
> +
> +		if (len < priv->blocksize) {
> +			tftp_send(priv);
> +			priv->state = STATE_DONE;
> +		}
> +
> +		break;
> +
> +	case TFTP_ERROR:
> +		debug("\nTFTP error: '%s' (%d)\n",
> +				pkt + 2, ntohs(*(uint16_t *)pkt));
> +		switch (ntohs(*(uint16_t *)pkt)) {
> +		case 1:
> +			priv->err = -ENOENT;
> +			break;
> +		case 2:
> +			priv->err = -EACCES;
> +			break;
> +		default:
> +			priv->err = -EINVAL;
> +			break;
> +		}
> +		priv->state = STATE_DONE;
> +		break;
> +	}
> +}
> +
> +static void tftp_timer_reset(struct file_priv *priv)
> +{
> +	priv->progress_timeout = priv->resend_timeout = get_time_ns();
> +}
> +
> +static struct file_priv *tftp_do_open(struct device_d *dev,
> +		int accmode, const char *filename)
> +{
> +	struct file_priv *priv;
> +	struct tftp_priv *tpriv = dev->priv;
> +	int ret;
> +
> +	priv = xzalloc(sizeof(*priv));
> +
> +	filename++;
> +
> +	switch (accmode & O_ACCMODE) {
> +	case O_RDONLY:
> +		priv->push = 0;
> +		priv->state = STATE_RRQ;
> +		break;
> +	case O_WRONLY:
> +		priv->push = 1;
> +		priv->state = STATE_WRQ;
> +		break;
> +	case O_RDWR:
> +		ret = -ENOSYS;
> +		goto out;
> +	}
> +
> +	priv->block = 1;
> +	priv->err = -EINVAL;
> +	priv->filename = filename;
> +	priv->blocksize = TFTP_BLOCK_SIZE;
> +
> +	priv->fifo = kfifo_alloc(4096);
> +	if (!priv->fifo) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	priv->tftp_con = net_udp_new(tpriv->server, TFTP_PORT, tftp_handler,
> +			priv);
> +	if (IS_ERR(priv->tftp_con)) {
> +		ret = PTR_ERR(priv->tftp_con);
> +		goto out1;
> +	}
> +
> +	ret = tftp_send(priv);
> +	if (ret)
> +		goto out2;
> +
> +	tftp_timer_reset(priv);
> +	while (priv->state != STATE_RDATA &&
> +			priv->state != STATE_DONE &&
> +			priv->state != STATE_WDATA) {
> +		ret = tftp_poll(priv);
> +		if (ret == TFTP_ERR_RESEND)
> +			tftp_send(priv);
> +		if (ret < 0)
> +			goto out2;
> +	}
> +
> +	if (priv->state == STATE_DONE) {
> +		ret = priv->err;
> +		goto out2;
> +	}
> +
> +	priv->buf = xmalloc(priv->blocksize);
> +
> +	return priv;
> +out2:
> +	net_unregister(priv->tftp_con);
> +out1:
> +	kfifo_free(priv->fifo);
> +out:
> +	free(priv);
> +
> +	return ERR_PTR(ret);
> +}
> +
> +static int tftp_open(struct device_d *dev, FILE *file, const char *filename)
> +{
> +	struct file_priv *priv;
> +
> +	priv = tftp_do_open(dev, file->flags, filename);
> +	if (IS_ERR(priv))
> +		return PTR_ERR(priv);
> +
> +	file->inode = priv;
> +	file->size = SZ_2G;
> +
> +	return 0;
> +}
> +
> +static int tftp_do_close(struct file_priv *priv)
> +{
> +	int ret;
> +
> +	if (priv->push && priv->state != STATE_DONE) {
> +		int len;
> +
> +		len = kfifo_get(priv->fifo, priv->buf, priv->blocksize);
> +		tftp_send_write(priv, priv->buf, len);
> +		priv->state = STATE_LAST;
> +
> +		tftp_timer_reset(priv);
> +
> +		while (priv->state != STATE_DONE) {
> +			ret = tftp_poll(priv);
> +			if (ret == TFTP_ERR_RESEND)
> +				tftp_send_write(priv, priv->buf, len);
> +			if (ret < 0)
> +				break;
> +		}
> +	}
> +
> +	if (!priv->push && priv->state != STATE_DONE) {
> +		uint16_t *pkt = net_udp_get_payload(priv->tftp_con);
> +		*pkt++ = htons(TFTP_ERROR);
> +		*pkt++ = 0;
> +		*pkt++ = 0;
> +		net_udp_send(priv->tftp_con, 6);
> +	}
> +
> +	net_unregister(priv->tftp_con);
> +	kfifo_free(priv->fifo);
> +	free(priv->buf);
> +	free(priv);
> +
> +	return 0;
> +}
> +
> +static int tftp_close(struct device_d *dev, FILE *f)
> +{
> +	struct file_priv *priv = f->inode;
> +
> +	return tftp_do_close(priv);
> +}
> +
> +static int tftp_write(struct device_d *_dev, FILE *f, const void *inbuf,
> +		size_t insize)
> +{
> +	struct file_priv *priv = f->inode;
> +	size_t size, now;
> +	int ret;
> +
> +	debug("%s: %d\n", __func__, insize);
> +
> +	size = insize;
> +
> +	while (size) {
> +		now = kfifo_put(priv->fifo, inbuf, size);
> +
> +		while (kfifo_len(priv->fifo) >= priv->blocksize) {
> +			kfifo_get(priv->fifo, priv->buf, priv->blocksize);
> +
> +			tftp_send_write(priv, priv->buf, priv->blocksize);
> +			tftp_timer_reset(priv);
> +
> +			while (priv->state == STATE_WAITACK) {
> +				ret = tftp_poll(priv);
> +				if (ret == TFTP_ERR_RESEND)
> +					tftp_send_write(priv, priv->buf,
> +							priv->blocksize);
> +				if (ret < 0)
> +					return ret;
> +			}
> +		}
> +		size -= now;
> +		inbuf += now;
> +	}
> +
> +	return insize;
> +}
> +
> +static int tftp_read(struct device_d *dev, FILE *f, void *buf, size_t insize)
> +{
> +	struct file_priv *priv = f->inode;
> +	size_t outsize = 0, now;
> +	int ret;
> +
> +	debug("%s %d\n", __func__, insize);
> +
> +	tftp_timer_reset(priv);
> +
> +	while (insize) {
> +		now = kfifo_get(priv->fifo, buf, insize);
> +		if (priv->state == STATE_DONE)
> +			return outsize + now;
> +		if (now) {
> +			outsize += now;
> +			buf += now;
> +			insize -= now;
> +			tftp_send(priv);
> +			tftp_timer_reset(priv);
> +		}
> +
> +		ret = tftp_poll(priv);
> +		if (ret == TFTP_ERR_RESEND)
> +			tftp_send(priv);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return outsize;
> +}
> +
> +static off_t tftp_lseek(struct device_d *dev, FILE *f, off_t pos)
> +{
> +	/* not implemented in tftp protocol */
> +	return -ENOSYS;
> +}
> +
> +static DIR* tftp_opendir(struct device_d *dev, const char *pathname)
> +{
> +	/* not implemented in tftp protocol */
> +	return NULL;
> +}
> +
> +static int tftp_stat(struct device_d *dev, const char *filename, struct stat *s)
> +{
> +	struct file_priv *priv;
> +
> +	priv = tftp_do_open(dev, O_RDONLY, filename);
> +	if (IS_ERR(priv))
> +		return PTR_ERR(priv);
> +
> +	s->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO;
> +	s->st_size = priv->filesize;
> +
> +	tftp_do_close(priv);
> +
> +	return 0;
> +}
> +
> +static int tftp_probe(struct device_d *dev)
> +{
> +	struct fs_device_d *fsdev = dev->type_data;
> +	struct tftp_priv *priv = xzalloc(sizeof(struct tftp_priv));
> +
> +	dev->priv = priv;
> +
> +	string_to_ip(fsdev->backingstore, &priv->server);
> +
> +	return 0;
> +}
> +
> +static void tftp_remove(struct device_d *dev)
> +{
> +	struct tftp_priv *priv = dev->priv;
> +
> +	free(priv);
> +}
> +
> +static struct fs_driver_d tftp_driver = {
> +	.open      = tftp_open,
> +	.close     = tftp_close,
> +	.read      = tftp_read,
> +	.lseek     = tftp_lseek,
> +	.opendir   = tftp_opendir,
> +	.stat      = tftp_stat,
> +	.create    = tftp_create,
> +	.unlink    = tftp_unlink,
> +	.mkdir     = tftp_mkdir,
> +	.rmdir     = tftp_rmdir,
> +	.write     = tftp_write,
> +	.truncate  = tftp_truncate,
> +	.flags     = 0,
> +	.drv = {
> +		.probe  = tftp_probe,
> +		.remove = tftp_remove,
> +		.name = "tftp",
> +		.type_data = &tftp_driver,
> +	}
> +};
> +
> +static int tftp_init(void)
> +{
> +	return register_fs_driver(&tftp_driver);
> +}
> +coredevice_initcall(tftp_init);
> -- 
> 1.7.9
> 
> 
> _______________________________________________
> barebox mailing list
> barebox@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/barebox

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/5] Add suport for tftp as a filesystem
  2012-02-20  4:54   ` Jean-Christophe PLAGNIOL-VILLARD
@ 2012-02-20 18:16     ` Sascha Hauer
  0 siblings, 0 replies; 8+ messages in thread
From: Sascha Hauer @ 2012-02-20 18:16 UTC (permalink / raw)
  To: Jean-Christophe PLAGNIOL-VILLARD; +Cc: barebox

On Mon, Feb 20, 2012 at 05:54:37AM +0100, Jean-Christophe PLAGNIOL-VILLARD wrote:
> On 18:58 Sun 19 Feb     , Sascha Hauer wrote:
> > This adds tftp filesystem support. It currently duplicates
> > significant amounts of the tftp (command) support. This is ok
> > since we can eventually drop the original tftp command later.
> > 
> > tftp is not really suitable to be handled as a filesystem. It lacks
> > support for stat, reading directories and other things. Handling
> > it as a filesystem has one big advantage though: tftp is no special
> > case for boot scripts and/or commands anymore which makes them simpler.
> > 
> > This implementation has some improvements to the original tftp command.
> > It supports blocksize negotiation which speeds up transfers if the tftp
> > server supports it. Also we can determine the filesize to transfer if
> > the remote end supports it.
> > 
> look good do you have seach patch for nfs?

No, everyone is invited to implement it ;)

NFS is a bit more complicated than tftp though. The implementation we
have lacks readdir support, so we have to figure out how to do this
first.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2012-02-20 18:17 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-02-19 17:58 tftp filesystem support Sascha Hauer
2012-02-19 17:58 ` [PATCH 1/5] Add suport for tftp as a filesystem Sascha Hauer
2012-02-20  4:54   ` Jean-Christophe PLAGNIOL-VILLARD
2012-02-20 18:16     ` Sascha Hauer
2012-02-19 17:58 ` [PATCH 2/5] make uimages work on tftpfs Sascha Hauer
2012-02-19 17:58 ` [PATCH 3/5] net: let net_udp_get_payload return void * Sascha Hauer
2012-02-19 17:58 ` [PATCH 4/5] add string for -ETIMEDOUT Sascha Hauer
2012-02-19 17:58 ` [PATCH 5/5] copy_file: limit progress bar to sensible limits Sascha Hauer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox