* 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
* 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
* [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
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