This loop was using the number of bytes read from the file as condition
to keep reading.
From Linux's fread(3) man page:
> On success, fread() and fwrite() return the number of items read or
> written. This number equals the number of bytes transferred only when
> size is 1. If an error occurs, or the end of the file is reached, the
> return value is a short item count (or zero).
>
> The file position indicator for the stream is advanced by the number
> of bytes successfully read or written.
>
> fread() does not distinguish between end-of-file and error, and
> callers must use feof(3) and ferror(3) to determine which occurred.
This means that nread!=0 doesn't make much sense as an end condition for
the loop: nread==0 doesn't necessarily mean that EOF has been reached or
an error has occured (but that is usually the case) and nread!=0 doesn't
necessarily mean that EOF has not been reached or that no read errors
have occured. feof(3) and ferror(3) should be uses when using fread(3).
Currently curl has to performs an extra fread(3) call to get a return
value equal to 0 to stop looping.
This usually "works" (even though nread==0 shouldn't be interpreted as
EOF) if stdin is a pipe because EOF usually marks the "real" end of the
stream, so the extra fread(3) call will return immediately and the extra
read syscall won't be noticeable:
bash-5.1$ strace -e read curl -s -F file=@- 0x0.st <<< a 2>&1 |
> tail -n 5
read(0, "a\n", 4096) = 2
read(0, "", 4096) = 0
read(0, "", 4096) = 0
http://0x0.st/oRs.txt
+++ exited with 0 +++
bash-5.1$
But this doesn't work if curl is reading from stdin, stdin is a
terminal, and the EOF is being emulated using a shell with ^D. Two
consecutive ^D will be required in this case to actually make curl stop
reading:
bash-5.1$ curl -F file=@- 0x0.st
a
^D^D
http://0x0.st/oRs.txt
bash-5.1$
A possible workaround to this issue is to use a program that handles EOF
correctly to indirectly send data to curl's stdin:
bash-5.1$ cat - | curl -F file=@- 0x0.st
a
^D
http://0x0.st/oRs.txt
bash-5.1$
This patch makes curl handle EOF properly when using fread(3) in
file2memory() so that the workaround is not necessary.
Since curl was previously ignoring read errors caused by this fread(3),
ferror(3) is also used in the condition of the loop: read errors and EOF
will have the same meaning; this is done to somewhat preserve the old
behaviour instead of making the command fail when a read error occurs.
Closes #8701
else { /* Not suitable for direct use, buffer stdin data. */
size_t stdinsize = 0;
- if(file2memory(&data, &stdinsize, stdin) != PARAM_OK) {
- /* Out of memory. */
+ switch(file2memory(&data, &stdinsize, stdin)) {
+ case PARAM_NO_MEM:
return m;
- }
-
- if(ferror(stdin)) {
+ case PARAM_READ_ERROR:
result = CURLE_READ_ERROR;
- Curl_safefree(data);
- data = NULL;
- }
- else if(!stdinsize) {
- /* Zero-length data has been freed. Re-create it. */
- data = strdup("");
- if(!data)
- return m;
+ break;
+ default:
+ if(!stdinsize) {
+ /* Zero-length data has been freed. Re-create it. */
+ data = strdup("");
+ if(!data)
+ return m;
+ }
+ break;
}
size = curlx_uztoso(stdinsize);
origin = 0;
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
PARAM_NO_NOT_BOOLEAN,
PARAM_CONTDISP_SHOW_HEADER, /* --include and --remote-header-name */
PARAM_CONTDISP_RESUME_FROM, /* --continue-at and --remote-header-name */
+ PARAM_READ_ERROR,
PARAM_LAST
} ParameterError;
return "showing headers and --remote-header-name cannot be combined";
case PARAM_CONTDISP_RESUME_FROM:
return "--continue-at and --remote-header-name cannot be combined";
+ case PARAM_READ_ERROR:
+ return "error encountered when reading a file";
default:
return "unknown error";
}
tool_list_engines();
else if(res == PARAM_LIBCURL_UNSUPPORTED_PROTOCOL)
result = CURLE_UNSUPPORTED_PROTOCOL;
+ else if(res == PARAM_READ_ERROR)
+ result = CURLE_READ_ERROR;
else
result = CURLE_FAILED_INIT;
}
do {
char buffer[4096];
nread = fread(buffer, 1, sizeof(buffer), file);
+ if(ferror(file)) {
+ curlx_dyn_free(&dyn);
+ *size = 0;
+ *bufp = NULL;
+ return PARAM_READ_ERROR;
+ }
if(nread)
if(curlx_dyn_addn(&dyn, buffer, nread))
return PARAM_NO_MEM;
- } while(nread);
+ } while(!feof(file));
*size = curlx_dyn_len(&dyn);
*bufp = curlx_dyn_ptr(&dyn);
}