user/forphan.c
About this file
forphan sets up an orphaned file so that test-xv6.py can check
that the kernel cleans it up after a crash. It creates a file, keeps it open, deletes
its name, and then sleeps forever. The file now has no name (link count (nlink) 0) but
still exists, because an open file keeps its inode alive.
The test script (test-xv6.py:149) waits for the line wait for kill and reclaim N,
then kills QEMU, which is like pulling the power plug: the final iput that
would have freed the inode on close never runs. On the next boot, fsinit
calls ireclaim (kernel/fs.c:48), which finds the inode allocated but
with no links and frees it, printing ireclaim: orphaned inode N. The script passes if
it sees that line within 30 seconds. Without ireclaim, the inode and its blocks would
stay allocated and unreachable forever.
Both this program and ireclaim were added on the same day, 6 August 2025: ireclaim
in commit db26774, and this test, with user/dorphan.c, in commit 52efd73
(“Crash tests for reclaiming an orphaned file and directory”). Run by hand in QEMU at
this commit, it printed wait for kill and reclaim 24, and the next boot printed
ireclaim: orphaned inode 24.
Read before: iput and ireclaim in kernel/fs.c.
Read next: user/dorphan.c, the same test with a directory.
Headers, purpose and an unused buffer
The comment states the purpose; the “recovery” is done by the kernel at boot, and
test-xv6.py only checks for it. BUFSZ and buf are not used: they are left
over from user/logstress.c, which has the same headers and the same
two lines and was apparently the starting point.
main(): locals
s is the program’s name, for error messages. st receives the file’s metadata
from fstat, so the program can print the inode number. ff is the file
name.
Create the file and learn its inode number
open with O_CREATE (sys_open → create) allocates an inode
with link count 1 and enters the name file0 in the current directory. The open
file descriptor holds a reference to the inode (its in-memory ref count),
which is what keeps the inode alive after the name is gone.
fstat (sys_fstat → filestat) fills st, including ino,
the inode number printed later. The error message on line 25 prints the literal
text ff instead of the file name: "ff" is passed where ff was meant.
Create file0 and keep it open for writing. This open file is what keeps the inode
alive.
Get the inode number, to print it later.
Remove the name; the file lives on
unlink (sys_unlink) clears the directory entry and drops the inode’s
link count from 1 to 0, inside one transaction, so after a crash the name is
either still there or gone with the count at 0. It then calls iunlockput, but
iput frees an inode only when it drops the last reference
(ref == 1) and nlink == 0. The open file holds another reference, so the inode,
still marked allocated on disk, survives.
The second open must fail: there is no name file0 any more. If it succeeded,
unlink would not have removed the entry. (“successed” is a typo for
“succeeded”.)
Delete the name. The inode’s link count becomes 0, but the open file still references it.
The name must be gone.
Announce the orphan and wait to be killed
The printed line is the signal test-xv6.py waits for (it matches on wait).
The program then sleeps forever: pause(1000) (sys_pause) sleeps for
1000 clock ticks, about 100 seconds, and the loop repeats.
The process must not exit. If it did, kexit would close the file, the
final iput would truncate and free the inode, and there would be no
orphan left for the crash to strand. The same is true if you kill the process
inside xv6 (for example, run it as forphan & and use kill): the file is freed at
once and the next boot reports nothing. Only killing QEMU itself leaves the inode
allocated with no links on the disk.
What failure looks like: if ireclaim did not run or missed the inode, the next
boot prints no ireclaim: line, the script times out after 30 seconds and prints
FAIL. The leaked inode would also be visible as a missing inode: ialloc
would never hand out that inode number again.
Tell the test script the orphan is ready, with its inode number (24 in a fresh image).
Sleep about 100 seconds, forever, holding the file open.