fs: follow symlinks like Linux; Linux mknod; /proc/partitions
Symlinks are followed in namex_from() now, in the middle of a path as well as at the end, and a relative target resolves from the directory that holds the link rather than from the caller's cwd. `ln -s f d1/s; cat d1/s` said "Symbolic link loop" because it looked for ./f. A loop is ELOOP after 40 links, a dangling link ENOENT, and open(O_CREAT) through a link to a file opens the file. divesymlink() is gone. mknod from a Linux program passes (mode, dev) where the native call passes (major, minor), and was read as the native one: `mknod n c 1 2` made node 432,258, and mkfifo made a character device. Both are decoded now; a FIFO is EPERM, and an existing name is EEXIST. /proc/mounts listed devtmpfs twice: once from a fixed line and once from the mount table. The fixed line is gone, and mount_add() refuses a filesystem mounted on its own root, which is where a second devtmpfs mount lands. A read-only FAT mount says ro. /proc/partitions exists, in Linux's format, and /proc/filesystems lists vfat. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
edb560de66
commit
7a1e63a553
8 changed files with 274 additions and 136 deletions
|
|
@ -153,6 +153,11 @@ Three things worth knowing:
|
|||
length until it is generated, and generating every one on every `stat()` to
|
||||
fill in a number almost nothing reads would be absurd.
|
||||
|
||||
`/proc/mounts`, `/proc/filesystems` and `/proc/partitions` are in Linux's
|
||||
formats to the column, since what reads them (`df`, `mount`, `fdisk -l`,
|
||||
installer scripts) parses them. Every disk and partition in `/proc/partitions`
|
||||
has major 8 and its device number as the minor, matching `/dev`.
|
||||
|
||||
## devtmpfs
|
||||
|
||||
`/dev` is generated, not stored. `kernel/fs/devfs.c`, behind the same `fs_ops`
|
||||
|
|
@ -283,6 +288,23 @@ held alive by an `nlink` bump.
|
|||
Renaming a *directory* is still refused with `EISDIR`, and so is replacing one:
|
||||
both need the `..` fixup and the ancestor-loop check that are not written.
|
||||
|
||||
## Symlinks
|
||||
|
||||
Followed the way Linux follows them, in `namex_from()`. A link in the middle of
|
||||
a path (`/a/link/b`) always is; the last component only for the calls that
|
||||
follow (`open`, `stat`, `chdir`, `execve`, `access`, `truncate`, `statfs`,
|
||||
`utimensat`), not for `lstat`, `readlink`, `unlink`, `rename`, `link` and the
|
||||
create paths. A relative target resolves from the directory holding the link,
|
||||
not from the caller's cwd, and a 41st link in one lookup is `ELOOP`.
|
||||
|
||||
The walk never holds a directory lock while it reads a link, only a reference
|
||||
to the directory it is in, so following one is restarting the walk from there
|
||||
with the target spliced in front of what is left of the path.
|
||||
|
||||
`open(O_CREAT)` through a link to an existing file opens the file, so
|
||||
`echo hi > link` writes where the link points. A dangling link is not created
|
||||
through.
|
||||
|
||||
## What it does not do
|
||||
|
||||
- **No page cache.** `mmap` of a file reads the whole thing in at map time, and
|
||||
|
|
@ -290,20 +312,13 @@ both need the `..` fixup and the ancestor-loop check that are not written.
|
|||
single largest gap in the layer.
|
||||
- **No symlinks in procfs**, which is why `/proc/<pid>/exe` is a regular file
|
||||
and `ps -o comm` prints `[init]`.
|
||||
- **Symlinks resolve on the final path component only.** `open()`, `stat()` and
|
||||
friends follow a link they are pointed at; a link in the *middle* of a path —
|
||||
`/a/link/b` — is not resolved. That belongs in `namex()`, which would have to
|
||||
re-enter path lookup while holding a directory lock, and getting that wrong is
|
||||
a deadlock rather than a wrong answer.
|
||||
- **`rename()` does not move directories**, and will not replace one. `EISDIR`
|
||||
either way.
|
||||
- **No named pipes.** `mkfifo()` does not fail — it *succeeds* and produces a
|
||||
character device, because `mknod()` turns `S_IFIFO` into a `T_DEV` and keeps
|
||||
the major/minor. `ls -l` shows `crw-rw-rw- 438, 0` where a `p` belongs, and
|
||||
nothing written to it comes back out. Succeeding wrongly is worse than
|
||||
refusing: the zsh port was configured with `zsh_cv_sys_fifo=yes` on the
|
||||
strength of it, and process substitution failed at the point of use rather
|
||||
than being reported as unavailable.
|
||||
- **No named pipes.** `mkfifo()` fails with `EPERM`, Linux's answer for a
|
||||
filesystem that cannot hold one. It used to *succeed* and make a character
|
||||
device, because `mknod()` read a Linux program's `(mode, dev)` as the native
|
||||
`(major, minor)`: `ls -l` showed `crw-rw-rw- 438, 0` where a `p` belongs. The
|
||||
zsh port was configured with `zsh_cv_sys_fifo=yes` on the strength of that.
|
||||
- **No `/dev/fd`, and no `/proc/self`.** A process cannot reach its own open
|
||||
descriptors by path. `<(cmd)` needs one or the other — zsh now knows it has
|
||||
neither and says so.
|
||||
|
|
|
|||
Loading…
Reference in a new issue