]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
net: lwip: tftp: add support of tsize option to client
authorMarek Vasut <marek.vasut+renesas@mailbox.org>
Wed, 28 Jan 2026 23:43:45 +0000 (00:43 +0100)
committerJerome Forissier <jerome.forissier@arm.com>
Fri, 6 Feb 2026 15:42:37 +0000 (16:42 +0100)
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 <marek.vasut+renesas@mailbox.org>
Acked-by: Jerome Forissier <jerome.forissier@arm.com>
lib/lwip/lwip/src/apps/tftp/tftp.c
lib/lwip/lwip/src/include/lwip/apps/tftp_client.h
net/lwip/tftp.c

index 25da952e92566cbca1c64bc89c89102e74d0a42c..e73bea20e634254f98acc2e2259344f549a6de41 100644 (file)
@@ -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", 0, tftp_state.blksize);
+    sprintf(payload+2+fname_length+mode_length+tsize_length, "blksize%c%d", 0, tftp_state.blksize);
 
   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.
index e1e21d06b675c4e222b6a12ce1a01b6c8adbd09f..78de50f49244f3bffac3901114f70c45f025d1d9 100644 (file)
@@ -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);
 
index 86516e662732e6ca84aabb8be1fb35ae19f7f8eb..5c3becc68c6cfdcde63087d7ff76abc6a67d5fa9 100644 (file)
@@ -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++;
+               }
+       } 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);