Hi Marek, On 29/01/2026 00:43, Marek Vasut wrote: > The TFTP server can report the size of the entire file that is about to > be received in the Transfer Size Option, this is described in RFC 2349. > This functionality is optional and the server may not report tsize in > case it is not supported. > > Always send tsize request to the server to query the transfer size, > and in case the server does respond, cache that information locally > in tftp_state.tsize, otherwise cache size 0. Introduce new function > tftp_client_get_tsize() which returns the cached tftp_state.tsize so > clients can determine the transfer size and use it. > > Update net/lwip/tftp.c to make use of tftp_client_get_tsize() and > avoid excessive printing of '#' during TFTP transfers in case the > transfer size is reported by the server. > > Submitted upstream: https://savannah.nongnu.org/patch/index.php?item_id=10557 > > Signed-off-by: Marek Vasut <[email protected]>
Nice feature! One minor comment below. Acked-by: Jerome Forissier <[email protected]> > --- > Cc: Andrew Goodbody <[email protected]> > Cc: Heinrich Schuchardt <[email protected]> > Cc: Ilias Apalodimas <[email protected]> > Cc: Jerome Forissier <[email protected]> > Cc: Joe Hershberger <[email protected]> > Cc: Ramon Fried <[email protected]> > Cc: Tim Harvey <[email protected]> > Cc: Tom Rini <[email protected]> > Cc: Wolfgang Wallner <[email protected]> > Cc: [email protected] > --- > lib/lwip/lwip/src/apps/tftp/tftp.c | 51 +++++++++++++++---- > .../lwip/src/include/lwip/apps/tftp_client.h | 1 + > net/lwip/tftp.c | 35 +++++++++++-- > 3 files changed, 74 insertions(+), 13 deletions(-) > > diff --git a/lib/lwip/lwip/src/apps/tftp/tftp.c > b/lib/lwip/lwip/src/apps/tftp/tftp.c > index ecb6c55ae11..5061446b1cf 100644 > --- a/lib/lwip/lwip/src/apps/tftp/tftp.c > +++ b/lib/lwip/lwip/src/apps/tftp/tftp.c > @@ -98,6 +98,7 @@ struct tftp_state { > int last_pkt; > u16_t blknum; > u16_t blksize; > + u32_t tsize; > u8_t retries; > u8_t mode_write; > u8_t tftp_mode; > @@ -167,6 +168,7 @@ send_request(const ip_addr_t *addr, u16_t port, u16_t > opcode, const char* fname, > { > size_t fname_length = strlen(fname)+1; > size_t mode_length = strlen(mode)+1; > + size_t tsize_length = strlen("tsize")+3; /* "tsize\0\0\0" */ > size_t blksize_length = 0; > int blksize = tftp_state.blksize; > struct pbuf* p; > @@ -182,7 +184,7 @@ send_request(const ip_addr_t *addr, u16_t port, u16_t > opcode, const char* fname, > } > } > > - p = init_packet(opcode, 0, fname_length + mode_length + blksize_length - > 2); > + p = init_packet(opcode, 0, fname_length + mode_length + blksize_length + > tsize_length - 2); > if (p == NULL) { > return ERR_MEM; > } > @@ -190,8 +192,9 @@ send_request(const ip_addr_t *addr, u16_t port, u16_t > opcode, const char* fname, > payload = (char*) p->payload; > MEMCPY(payload+2, fname, fname_length); > MEMCPY(payload+2+fname_length, mode, mode_length); > + sprintf(payload+2+fname_length+mode_length, "tsize%c%u%c", 0, 0, 0); > if (tftp_state.blksize) > - sprintf(payload+2+fname_length+mode_length, "blksize%c%d%c", 0, > tftp_state.blksize, 0); > + sprintf(payload+2+fname_length+mode_length+tsize_length, > "blksize%c%d%c", 0, tftp_state.blksize, 0); > > tftp_state.wait_oack = true; > ret = udp_sendto(tftp_state.upcb, p, addr, port); > @@ -450,14 +453,24 @@ tftp_recv(void *arg, struct udp_pcb *upcb, struct pbuf > *p, const ip_addr_t *addr > } > > blknum = lwip_ntohs(sbuf[1]); > - if (tftp_state.blksize && tftp_state.wait_oack) { > + if (tftp_state.wait_oack) { > /* > - * Data received while we are expecting an OACK for our blksize > option. > + * Data received while we are expecting an OACK for our tsize option. > * This means the server doesn't support it, let's switch back to the > * default block size. > */ > - tftp_state.blksize = 0; > - tftp_state.wait_oack = false; > + tftp_state.tsize = 0; > + tftp_state.wait_oack = false; > + > + if (tftp_state.blksize) { > + /* > + * Data received while we are expecting an OACK for our blksize > option. > + * This means the server doesn't support it, let's switch back to > the > + * default block size. > + */ > + tftp_state.blksize = 0; > + tftp_state.wait_oack = false; > + } > } > if (blknum == tftp_state.blknum) { > pbuf_remove_header(p, TFTP_HEADER_LENGTH); > @@ -527,21 +540,31 @@ tftp_recv(void *arg, struct udp_pcb *upcb, struct pbuf > *p, const ip_addr_t *addr > } > break; > case PP_HTONS(TFTP_OACK): { > - const char *optval = find_option(p, "blksize"); > + const char *blksizeoptval = find_option(p, "blksize"); > + const char *tsizeoptval = find_option(p, "tsize"); > u16_t srv_blksize = 0; > + u32_t srv_tsize = 0; > tftp_state.wait_oack = false; > - if (optval) { > + if (blksizeoptval) { > if (!tftp_state.blksize) { > /* We did not request this option */ > send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "blksize > unexpected"); > } > - srv_blksize = atoi(optval); > + srv_blksize = atoi(blksizeoptval); > if (srv_blksize <= 0 || srv_blksize > tftp_state.blksize) { > send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Invalid > blksize"); > } > LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: accepting > blksize=%d\n", srv_blksize)); > tftp_state.blksize = srv_blksize; > } > + if (tsizeoptval) { > + srv_tsize = atoi(tsizeoptval); > + if (srv_tsize <= 0) { > + srv_tsize = 0; /* tsize is optional */ > + } > + LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: accepting tsize=%d\n", > srv_tsize)); > + tftp_state.tsize = srv_tsize; > + } > send_ack(addr, port, 0); > break; > } > @@ -655,6 +678,16 @@ tftp_init_client(const struct tftp_context *ctx) > return tftp_init_common(LWIP_TFTP_MODE_CLIENT, ctx); > } > > +/** @ingroup tftp > + * Get the transfer size used by the TFTP client. The server may > + * report zero in case this is unsupported. > + */ > +u32_t > +tftp_client_get_tsize(void) > +{ > + return tftp_state.tsize; > +} > + > /** @ingroup tftp > * Set the block size to be used by the TFTP client. The server may choose to > * accept a lower value. > diff --git a/lib/lwip/lwip/src/include/lwip/apps/tftp_client.h > b/lib/lwip/lwip/src/include/lwip/apps/tftp_client.h > index e1e21d06b67..78de50f4924 100644 > --- a/lib/lwip/lwip/src/include/lwip/apps/tftp_client.h > +++ b/lib/lwip/lwip/src/include/lwip/apps/tftp_client.h > @@ -45,6 +45,7 @@ enum tftp_transfer_mode { > > err_t tftp_init_client(const struct tftp_context* ctx); > void tftp_client_set_blksize(u16_t blksize); > +u32_t tftp_client_get_tsize(void); > err_t tftp_get(void* handle, const ip_addr_t *addr, u16_t port, const char* > fname, enum tftp_transfer_mode mode); > err_t tftp_put(void* handle, const ip_addr_t *addr, u16_t port, const char* > fname, enum tftp_transfer_mode mode); > > diff --git a/net/lwip/tftp.c b/net/lwip/tftp.c > index 6c7ffba661e..213c1292eb1 100644 > --- a/net/lwip/tftp.c > +++ b/net/lwip/tftp.c > @@ -31,6 +31,7 @@ struct tftp_ctx { > ulong daddr; > ulong size; > ulong block_count; > + ulong hash_count; > ulong start_time; > enum done_state done; > }; > @@ -49,6 +50,8 @@ struct tftp_ctx { > static int store_block(struct tftp_ctx *ctx, void *src, u16_t len) > { > ulong store_addr = ctx->daddr; > + ulong tftp_tsize; > + ulong pos; > void *ptr; > > if (CONFIG_IS_ENABLED(LMB)) { > @@ -67,10 +70,21 @@ static int store_block(struct tftp_ctx *ctx, void *src, > u16_t len) > ctx->daddr += len; > ctx->size += len; > ctx->block_count++; > - if (ctx->block_count % 10 == 0) { > - putc('#'); > - if (ctx->block_count % (65 * 10) == 0) > - puts("\n\t "); > + > + tftp_tsize = tftp_client_get_tsize(); > + if (tftp_tsize) { > + pos = clamp(ctx->size, 0UL, tftp_tsize); > + > + while (ctx->hash_count < pos * 50 / tftp_tsize) { > + putc('#'); > + ctx->hash_count++; > + } Nit: with this format being the same as the non-tsize one, the user has no way to know how many hashes to expect. How about printing percentages instead? 0%....10%....20%.... etc. (functional tests may need adjusting though) > + } else { > + if (ctx->block_count % 10 == 0) { > + putc('#'); > + if (ctx->block_count % (65 * 10) == 0) > + puts("\n\t "); > + } > } > > return 0; > @@ -84,6 +98,7 @@ static void *tftp_open(const char *fname, const char *mode, > u8_t is_write) > static void tftp_close(void *handle) > { > struct tftp_ctx *ctx = handle; > + ulong tftp_tsize; > ulong elapsed; > > if (ctx->done == FAILURE || ctx->done == ABORTED) { > @@ -92,6 +107,17 @@ static void tftp_close(void *handle) > } > ctx->done = SUCCESS; > > + tftp_tsize = tftp_client_get_tsize(); > + if (tftp_tsize) { > + /* Print hash marks for the last packet received */ > + while (ctx->hash_count < 49) { > + putc('#'); > + ctx->hash_count++; > + } > + puts(" "); > + print_size(tftp_tsize, ""); > + } > + > elapsed = get_timer(ctx->start_time); > if (elapsed > 0) { > puts("\n\t "); /* Line up with "Loading: " */ > @@ -176,6 +202,7 @@ static int tftp_loop(struct udevice *udev, ulong addr, > char *fname, > ctx.done = NOT_DONE; > ctx.size = 0; > ctx.block_count = 0; > + ctx.hash_count = 0; > ctx.daddr = addr; > > printf("Using %s device\n", udev->name);

