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);

Reply via email to