casaroli opened a new pull request, #21143:
URL: https://github.com/apache/nuttx/pull/21143

   ## Summary
   
   This PR fixes two results of the VFS that POSIX specifies and that programs 
use to choose a fallback: `open()` with `O_TMPFILE` or with `O_DIRECTORY` and 
write access, and `lseek()` of a pipe, a FIFO or a socket.
   
   - **fs/vfs: Fail an open of a directory for writing or with O_TMPFILE.** 
`open()` with `O_DIRECTORY` opened the directory whatever the access mode. 
`O_TMPFILE` includes `O_DIRECTORY`, so `open(dir, O_RDWR | O_TMPFILE)` gave a 
directory descriptor instead of an unnamed file: `open()` returned success and 
every `write()` to it failed. A program that uses `mkstemp()` only when 
`O_TMPFILE` fails (apt does) could not write its temporary file. Now an 
`O_DIRECTORY` open with write access closes the directory again and fails: with 
`EOPNOTSUPP` for `O_TMPFILE`, as Linux does for a file system without it (no 
NuttX file system makes an unnamed file), else with `EISDIR`, as POSIX 
specifies for a directory opened with `O_WRONLY` or `O_RDWR`. A path that is 
not a directory still fails with `ENOTDIR`.
   - **fs/vfs: Add the blank lines after declarations in fs_sendfile.c.** 
Whitespace only: two older nxstyle errors in a file that the last commit edits.
   - **fs/aio: Read and write a file that cannot seek at its position.** The 
AIO workers used `file_pread()` and `file_pwrite()` at `aio_offset`. These 
seek, so after the next commit they fail with `ESPIPE` for a pipe. POSIX 
ignores `aio_offset` for a file that cannot seek: the workers now read and 
write at the current position when they get `ESPIPE`. This commit comes first 
so that no commit breaks AIO on a pipe.
   - **fs/vfs: Fail lseek() of a pipe, a FIFO or a socket with ESPIPE.** These 
drivers have no seek method, so `lseek()` set the common file position and 
returned it: `lseek(fd, 10, SEEK_CUR)` of a pipe returned 10, and `SEEK_END` 
failed with `ENOSYS`. POSIX specifies `ESPIPE`. A program that skips data with 
`lseek()` and reads only when `lseek()` fails lost the data (dpkg does this 
with the tar stream from dpkg-deb). `file_seek()` now returns `-ESPIPE` for 
these inodes; other drivers without a seek method keep the common file 
position. `pread()`, `pwrite()` and `sendfile()` with an offset then also fail 
with `ESPIPE` for them, as POSIX and Linux specify. The `sendfile()` path of a 
TCP socket (`CONFIG_NET_SENDFILE`) seeks its input to send data again: for an 
input that cannot seek, `sendfile()` now uses the read and write copy, as it 
does when that path returns `ENOSYS`.
   
   ## Impact
   
   Possible breaking change (results and `errno` values of the flat API too, 
every build mode and architecture):
   
   | Call | before | after |
   |---|---|---|
   | `open(dir, O_RDWR \| O_TMPFILE)` | a directory descriptor that cannot be 
written | -1, `EOPNOTSUPP` |
   | `open(dir, O_WRONLY or O_RDWR \| O_DIRECTORY)` | a directory descriptor | 
-1, `EISDIR` |
   | `lseek()` of a pipe, FIFO or socket | a made-up position (`SEEK_END`: 
`ENOSYS`) | -1, `ESPIPE` |
   | `pread()`/`pwrite()` of a pipe, FIFO or socket | read or write, the offset 
ignored | -1, `ESPIPE` |
   | `ftell()` of a stream on a pipe | a made-up position | -1, `ESPIPE` |
   | `sendfile()` from a pipe with an offset | the offset ignored | -1, 
`ESPIPE` |
   
   `O_RDONLY | O_DIRECTORY` (`opendir()`) does not change; reads and writes of 
pipes, FIFOs and sockets do not change; `sendfile()` from a pipe without an 
offset still works (also to a TCP socket with `CONFIG_NET_SENDFILE`).
   
   In-tree users, nuttx and nuttx-apps master: no code uses `O_TMPFILE`, and 
every `O_DIRECTORY` open is read-only. Of the `lseek()`, `fseek()`, `ftell()`, 
`pread()`, `pwrite()` and `file_seek()` callers, these can see a pipe, a FIFO 
or a socket:
   
   - `fs/aio` and the TCP `sendfile()`: changed in this PR (see above).
   - `fcntl(F_SETFL)` with `O_APPEND` seeks to the end: on a pipe it failed 
before (`ENOSYS`) and fails now (`ESPIPE`).
   - stdio: `fwrite()` after buffered reads and `fseek()` seek back over the 
read-ahead; on a pipe this failed before (`EINVAL`: the position became 
negative) and fails now (`ESPIPE`).
   - `dd skip=N` with a pipe as input: before, `dd` copied from the start 
without skipping; now it stops with "failed to lseek".
   - NSH loops (`while`/`until`) in a script that is a FIFO: never worked; now 
NSH prints an `lseek` error for each line.
   
   All other callers seek regular files or block and character devices, which 
do not change.
   
   ## Testing
   
   Tested on master 192ac6d3fbc and on this PR rebased on it, with QEMU (HVF): 
`qemu-armv8a:knsh` (kernel build, programs on v9fs) and `qemu-armv8a:nsh` (flat 
build, the programs as NSH built-ins), with `CONFIG_FS_TMPFS` and 
`CONFIG_PIPES`. The flat build also has `CONFIG_FS_AIO` and TCP over the 
loopback device with `CONFIG_NET_SENDFILE`, for the socket cases.
   
   `tmpfile.c`: `open()` of a tmpfs and of a v9fs directory (the same result on 
both):
   
   | Case | master | this PR |
   |---|---|---|
   | `open(dir, O_RDWR \| O_TMPFILE \| O_EXCL, 0600)` | a descriptor; `write()` 
fails with `EINVAL`: FAIL | -1, `EOPNOTSUPP`: PASS |
   | `open(dir, O_RDWR \| O_DIRECTORY)` | a descriptor: FAIL | -1, `EISDIR`: 
PASS |
   | `open(dir, O_RDONLY \| O_DIRECTORY)` | a descriptor: PASS | a descriptor: 
PASS |
   
   `pipeseek.c`:
   
   | Case | master | this PR |
   |---|---|---|
   | `lseek(read end, 10, SEEK_CUR)` | 10: FAIL | -1, `ESPIPE`: PASS |
   | `lseek(write end, 0, SEEK_END)` | -1, `ENOSYS`: FAIL | -1, `ESPIPE`: PASS |
   | the 16 bytes in the pipe are read after the `lseek()` | PASS | PASS |
   | `lseek()` of a FIFO | 0: FAIL | -1, `ESPIPE`: PASS |
   | `lseek()` of a socket (flat build) | 0: FAIL | -1, `ESPIPE`: PASS |
   
   `g8extra.c`, more cases:
   
   | Case | master | this PR |
   |---|---|---|
   | `open(regular file, O_RDWR \| O_DIRECTORY)` | -1, `ENOTDIR` | -1, 
`ENOTDIR` |
   | `open(regular file, O_RDWR \| O_TMPFILE)` | -1, `ENOTDIR` | -1, `ENOTDIR` |
   | `open("/dev", O_WRONLY \| O_DIRECTORY)` | a descriptor | -1, `EISDIR` |
   | 200 failed directory opens on a tmpfs, then `umount()` | the opens 
succeed; `umount()` 0 | the opens fail; `umount()` 0 (no reference leaked) |
   | `pread()` / `pwrite()` of a pipe | 5 / 1 | -1, `ESPIPE` |
   | `ftell()` of a stream on a pipe | 0 | -1, `ESPIPE` |
   | `sendfile(file, pipe, NULL, 5)` | 5 | 5 |
   | `sendfile(file, pipe, &off, 5)` | 5 | -1, `ESPIPE` |
   | `sendfile(TCP socket, pipe, NULL, 5)` (flat build) | 5 | 5 (the read and 
write copy) |
   
   `aiotest.c` (flat build; AIO writes and reads, `lio_listio()`, and requests 
on a pipe): the same result on master and with this PR (3 of 6 cases pass on 
both; the other three fail with the same values on master).
   
   ostest exits with status 0 on `qemu-armv8a:knsh` and `qemu-armv8a:nsh`, on 
master and with this PR.
   
   Builds, each OK and without new warnings: `lm3s6965-ek:nsh` (flat, 
Cortex-M3) with `CONFIG_FS_AIO`, `CONFIG_NET_SENDFILE`, `CONFIG_PIPES` and 
`CONFIG_FS_TMPFS`; `lm3s6965-ek:qemu-protected` (protected, Cortex-M3); 
`rv-virt:pnsh64` (protected, RISC-V) and `rv-virt:knsh64` (kernel, RISC-V) with 
`CONFIG_PIPES` and `CONFIG_FS_TMPFS`.
   
   `tools/checkpatch.sh -c -u -m -g` passes.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to