]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
tool_operate: when retrying, only truncate regular files
authorDaniel Stenberg <daniel@haxx.se>
Fri, 16 May 2025 22:17:11 +0000 (00:17 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 19 May 2025 07:42:54 +0000 (09:42 +0200)
If /dev/null or another character device etc is used for output, trying
to truncate that only causes errors.

Add test 1497 to verify

Fixes #17371
Reported-by: Brendan Dolan-Gavitt
Closes #17374

src/tool_operate.c
tests/data/Makefile.am
tests/data/test1497 [new file with mode: 0644]

index b533e625746b2e77dbb0f8e7ff417a66e10b81ab..91b3e97ac9ad30b5ac60870ba9ff4f7871218557 100644 (file)
@@ -587,37 +587,49 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
         if(per->retry_sleep > RETRY_SLEEP_MAX)
           per->retry_sleep = RETRY_SLEEP_MAX;
       }
+
       if(outs->bytes && outs->filename && outs->stream) {
-        /* We have written data to an output file, we truncate file
-         */
-        notef(config->global,
-              "Throwing away %"  CURL_FORMAT_CURL_OFF_T " bytes",
-              outs->bytes);
-        fflush(outs->stream);
-        /* truncate file at the position where we started appending */
+#ifndef __MINGW32CE__
+        struct_stat fileinfo;
+
+        /* The output can be a named pipe or a character device etc that
+           cannot be truncated. Only truncate regular files. */
+        if(!fstat(fileno(outs->stream), &fileinfo) &&
+           S_ISREG(fileinfo.st_mode))
+#else
+          /* Windows CE's fileno() is bad so just skip the check */
+#endif
+        {
+          /* We have written data to an output file, we truncate file */
+          fflush(outs->stream);
+          notef(config->global,
+                "Throwing away %"  CURL_FORMAT_CURL_OFF_T " bytes",
+                outs->bytes);
+          /* truncate file at the position where we started appending */
 #if defined(HAVE_FTRUNCATE) && !defined(__DJGPP__) && !defined(__AMIGA__) && \
   !defined(__MINGW32CE__)
-        if(ftruncate(fileno(outs->stream), outs->init)) {
-          /* when truncate fails, we cannot just append as then we will
-             create something strange, bail out */
-          errorf(config->global, "Failed to truncate file");
-          return CURLE_WRITE_ERROR;
-        }
-        /* now seek to the end of the file, the position where we
-           just truncated the file in a large file-safe way */
-        rc = fseek(outs->stream, 0, SEEK_END);
+          if(ftruncate(fileno(outs->stream), outs->init)) {
+            /* when truncate fails, we cannot just append as then we will
+               create something strange, bail out */
+            errorf(config->global, "Failed to truncate file");
+            return CURLE_WRITE_ERROR;
+          }
+          /* now seek to the end of the file, the position where we
+             just truncated the file in a large file-safe way */
+          rc = fseek(outs->stream, 0, SEEK_END);
 #else
-        /* ftruncate is not available, so just reposition the file
-           to the location we would have truncated it. This will not
-           work properly with large files on 32-bit systems, but
-           most of those will have ftruncate. */
-        rc = fseek(outs->stream, (long)outs->init, SEEK_SET);
+          /* ftruncate is not available, so just reposition the file
+             to the location we would have truncated it. This will not
+             work properly with large files on 32-bit systems, but
+             most of those will have ftruncate. */
+          rc = fseek(outs->stream, (long)outs->init, SEEK_SET);
 #endif
-        if(rc) {
-          errorf(config->global, "Failed seeking to end of file");
-          return CURLE_WRITE_ERROR;
+          if(rc) {
+            errorf(config->global, "Failed seeking to end of file");
+            return CURLE_WRITE_ERROR;
+          }
+          outs->bytes = 0; /* clear for next round */
         }
-        outs->bytes = 0; /* clear for next round */
       }
       *retryp = TRUE;
       per->num_retries++;
index 33ba9b5b17125bb7607fa801ce66dbe857ffd5d4..c718b4461518db0d9acd8946e6291b7ae3d63157 100644 (file)
@@ -196,7 +196,7 @@ test1460 test1461 test1462 test1463 test1464 test1465 test1466 test1467 \
 test1468 test1469 test1470 test1471 test1472 test1473 test1474 test1475 \
 test1476 test1477 test1478 test1479 test1480 test1481 test1482 test1483 \
 test1484 test1485 test1486 test1487 test1488 test1489 test1490 test1491 \
-test1492 test1493 test1494 test1495 test1496 \
+test1492 test1493 test1494 test1495 test1496 test1497 \
 \
 test1500 test1501 test1502 test1503 test1504 test1505 test1506 test1507 \
 test1508 test1509 test1510 test1511 test1512 test1513 test1514 test1515 \
diff --git a/tests/data/test1497 b/tests/data/test1497
new file mode 100644 (file)
index 0000000..94b19a2
--- /dev/null
@@ -0,0 +1,61 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+retry
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 503 BAD swsbounce
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Content-Length: 21
+
+server not available
+</data>
+<data1 nocheck="yes">
+HTTP/1.1 200 OK
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Content-Length: 3
+Connection: close
+
+ok
+</data1>
+
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+http
+</server>
+<name>
+HTTP GET --retry on 503 error with output to /dev/null
+</name>
+<command>
+http://%HOSTIP:%HTTPPORT/%TESTNUMBER --retry 3 -o /dev/null
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol>
+GET /%TESTNUMBER HTTP/1.1\r
+Host: %HOSTIP:%HTTPPORT\r
+User-Agent: curl/%VERSION\r
+Accept: */*\r
+\r
+GET /%TESTNUMBER HTTP/1.1\r
+Host: %HOSTIP:%HTTPPORT\r
+User-Agent: curl/%VERSION\r
+Accept: */*\r
+\r
+</protocol>
+
+</verify>
+</testcase>