Your message dated Sun, 27 Sep 2026 15:00:07 +0200
with message-id <[email protected]>
and subject line Re: Bug#979209: handle interrupted and partial uploads
has caused the Debian Bug report #979209,
regarding handle interrupted and partial uploads
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact [email protected]
immediately.)


-- 
979209: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=979209
Debian Bug Tracking System
Contact [email protected] with problems
--- Begin Message ---
Package: tftpd-hpa
Version: 5.2+20150808-1.2

We have discovered that tftpd-hpa does not detect a partial upload nor
does it remove the file in case of an interrupted upload. The attached
patch fixes both of these issues. Specifically, it does the following:

1. If the client sent the upload tsize, then verify that it matches
   the final file size on disk.

2. And, if the sizes do not match or any other error was encountered
   during upload, log a warning and remove the file.

We have tested the patch both manually with curl to confirm the above
error cases are indeed handled as expected, and on our CI system (where
tftpd-hpa is used to communicate result from the VM to the host) for
any regressions.

I am also happy to answer any questions about the issues or the patch.
--- tftp-hpa-5.2+20150808.orig/tftpd/tftpd.c	2015-08-08 06:24:45.000000000 +0200
+++ tftp-hpa-5.2+20150808/tftpd/tftpd.c	2020-09-30 16:00:50.512458328 +0200
@@ -151,13 +151,13 @@
     exit_signal = sig;
 }
 
-/* Handle timeout signal or timeout event */
+/* Handle timeout event */
 void timer(int sig)
 {
     (void)sig;                  /* Suppress unused warning */
     timeout <<= 1;
     if (timeout >= maxtimeout || timeout_quit)
-        exit(0);
+        return;
     siglongjmp(timeoutbuf, 1);
 }
 
@@ -264,7 +264,8 @@
         } while (rv == -1 && err == EINTR);
 
         if (rv == 0) {
-            timer(0);           /* Should not return */
+            timer(0);           /* If returns, fail. */
+            errno = ETIMEDOUT;
             return -1;
         }
 
@@ -1245,8 +1246,20 @@
     if (!tsize_ok)
         return 0;
 
+    if (tsize_ok == 1) { /* RRQ */
     if (sz == 0)
         sz = tsize;
+    }
+    else {               /* WRQ */
+        /* Note that a client may send 0 tsize both when the size is unknown
+           and when uploading 0 bytes (at least this is what curl does). But
+           this ambiguity doesn't matter in our case since 0 bytes cannot be
+           truncated to anything less. So we treat both cases as unknown. */
+        if (sz != 0) {
+            tsize = sz;
+            tsize_ok = 1; /* ok to get */
+        }
+    }
 
     *vp = sz;
     return 1;
@@ -1418,6 +1431,7 @@
 #endif
 
 static FILE *file;
+static const char *filename;
 /*
  * Validate file access.  Since we
  * have no uid or gid, for now require
@@ -1429,18 +1443,19 @@
  * Note also, full path name must be
  * given as we have no login directory.
  */
-static int validate_access(char *filename, int mode,
+static int validate_access(char *fname, int mode,
 			   const struct formats *pf, const char **errmsg)
 {
     struct stat stbuf;
     int i, len;
     int fd, wmode, rmode;
-    char *cp;
+    const char *cp;
     const char **dirp;
     char stdio_mode[3];
 
     tsize_ok = 0;
     *errmsg = NULL;
+    filename = fname;
 
     if (!secure) {
         if (*filename != '/') {
@@ -1510,7 +1525,7 @@
         }
         tsize = stbuf.st_size;
         /* We don't know the tsize if conversion is needed */
-        tsize_ok = !pf->f_convert;
+        tsize_ok = pf->f_convert ? 0 : 1 /* ok to get */;
     } else {
         if (!unixperms) {
             if ((stbuf.st_mode & (S_IWRITE >> 6)) == 0) {
@@ -1527,7 +1542,7 @@
 	}
 #endif
         tsize = 0;
-        tsize_ok = 1;
+        tsize_ok = 2; /* ok to set */
     }
 
     stdio_mode[0] = (mode == RRQ) ? 'r' : 'w';
@@ -1652,6 +1667,7 @@
     static int acksize;
     u_short dp_opcode, dp_block;
     unsigned long r_timeout;
+    struct stat stbuf;
 
     dp = w_init();
     do {
@@ -1711,6 +1727,17 @@
         }
     } while (size == segsize);
     write_behind(file, pf->f_convert);
+
+    /* Verify the file size if it was sent by the client. */
+    if (tsize_ok == 1) {
+        (void)fflush(file);
+        if (fstat(fileno(file), &stbuf) < 0)
+            exit(EX_OSERR); /* This shouldn't happen */
+        if (stbuf.st_size != tsize) {
+            goto abort;
+        }
+    }
+
     (void)fclose(file);         /* close data file */
 
     ap->th_opcode = htons((u_short) ACK);       /* send the "final" ack */
@@ -1726,7 +1753,11 @@
         block == dp_block) {    /* then my last ack was lost */
         (void)send(peer, ackbuf, 4, 0); /* resend final ack */
     }
+    return;
   abort:
+    /* Remove the file in case of a partial upload. */
+    syslog(LOG_WARNING, "Partial/interrupted upload, removing %s", filename);
+    (void)unlink(filename);
     return;
 }
 

--- End Message ---
--- Begin Message ---
Source: tftp-hpa
Source-Version: 7.1-1

On Mon, Jan 04, 2021 at 10:15:46AM +0200, Boris Kolpackov wrote:
> Package: tftpd-hpa
> Version: 5.2+20150808-1.2
> 
> We have discovered that tftpd-hpa does not detect a partial upload nor
> does it remove the file in case of an interrupted upload.

Upstream's 7.1 changelog says:

    If --create is specified, the server will now delete a failed
        upload file.

I hope this is what you mean.

Best,
Chris

--- End Message ---

Reply via email to