4 ms·
While the bug is in tar, the API for the read(2) system call doesn't help. Its return value can be positive, which indicates the number of bytes read; negative,
by cesarb 8y ago
While the bug is in tar, the API for the read(2) system call doesn't help. Its return value can be positive, which indicates the number of bytes read; negative, which indicates an error, unless errno is EINTR, in which case you should retry; or zero, which indicates either end of file, or the number of bytes read when the buffer length is zero (and in this case you can't distinguish end of file from a normal read). It's easy to forget to handle one of these five cases.
- saagarjha 8y agoThe issue with EINTR is pretty standard between most syscall; if you get one you should retry (or, depending on what flags you have set, this can be done for you automatically in some cases). A return value of zero is an EOF, there is no such thing as an “empty read”. Why would you pass in an empty buffer?
- jandrese 8y agoYou can get an empty read if you have setup the FD to be nonblocking.
- cesarb 8y agoThat would be a sixth case I had forgotten above (negative return value with errno EAGAIN and/or EWOULDBLOCK - these two might or might not be the same number).
- thatcks 8y agoApparently the use of an empty read is to probe to see if you get various errors or an immediate 0-size return. This is merely a 'may' requirement in the Single Unix Specification, so I'm not sure how portable it is in practice.
- davemp 8y agoIssues like this are almost entirely solved with rust-esque enum pattern matching. The compiler with fail if you don’t have every match case handled. There is ‘_’ to abuse the system but doing so should be fairly obvious. —— Rust in general is pretty exciting for systems work. I just wish there were more shops using it professionally.
- cesarb 8y agoUnfortunately, Rust kept nearly the same design for its `std::io::Read` trait. It only separates the two "error" cases from the three "success" cases, so you still have to check for `ErrorKind::Interrupted` in the error case, and for `Ok(0)` in the success case (and when the passed buffer has zero length, you still can't distinguish between end-of-file and success, though that's not Rust's fault). A better design could have used a return type with more alternatives than just `Ok(n)` or `Err(e)`.
- davemp 8y agoI'm guessing that's probably for cross-platform behavior. Maybe a linux specific std replacement crate could probably solve the problem.
- iheartpotatoes 8y ago" It's easy to forget to handle one of these five cases." /feels cold shiver wondering if i've every used read()/
- merlincorey 8y ago> or zero, which indicates either end of file, EOF is not zero. It cannot be, because it must be outside the range of `char`. On my (linux) system, EOF happens to be -1. This is explained in in the first chapter of K&R.
- cesarb 8y agoDo not confuse getchar(3) with read(2). From the manual page for read(2) (http://man7.org/linux/man-pages/man2/read.2.html http://man7.org/linux/man-pages/man2/read.2.html): On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number. [...] On error, -1 is returned, and errno is set appropriately. In this case, it is left unspecified whether the file position (if any) changes.
- merlincorey 8y agoAh yes, I did in fact mistake the function in question. I wonder why they chose not to use out of range values for EOF there, given that `getchar` surely predates `read`?
- theoh 8y agoWith read(), the return value is separate from the data read. It is arguably elegant to read as many characters as possible and to not consider hitting the end of the file as an error—just a point that can't be read beyond, like a stop on a piece of machinery. With getchar(), the decision was made to return the char directly (to have used a pointer to a single char would be daft, from the point of view of economy) and to use an in-band code to indicate end of file. This "in-band signalling" is considered a bit of a kludge by some people. So I'd say read() is a better/cleaner design. See https://en.wikipedia.org/wiki/Semipredicate_problem https://en.wikipedia.org/wiki/Semipredicate_problem for some related thoughts.
- merlincorey 8y agoAh, yes, that Semipredicate problem article is very informative, thanks for sharing.