test: extend coverage of vfsFifoTest

This commit is contained in:
dualfroz committed 2026-09-12 18:18:12 +02:00
1 parent 4b41308da9
commit 3b72ea3aee
1 file changed
+159
+159
View File
@@ -160,3 +160,162 @@ KTEST(vfsUnlinkOnNeverOpenedFifoReclaimsPipeSlot)
vfsUnlink(paths[i]); vfsUnlink(paths[i]);
} }
/*
* Coverage for PERSISTENT named pipes: vfsMkfifo under "/mnt" now creates a
* real, on-disk sfs.h entry (sfsTypeFifo) instead of being rejected, so the
* special file itself -- unlike everything above, which lives only in this
* process's kmalloc heap -- survives sfsUnmount()+sfsMount(). The live
* pipe.h byte stream two openers exchange data through is still purely
* in-memory (vfs.c's sfsFifoHandleFor), created lazily on first open, since
* buffered bytes never persist across a real mkfifo(2)'s reboot either --
* only the rendezvous point's name and type do. Every test below starts
* with its own sfsFormat() (same discipline sfsTest.c uses) so results
* never depend on whatever an earlier test (here or in sfsTest.c, same
* boot, same disk) left behind.
*/
KTEST(sfsBackedMkfifoCreatesPersistentFifoVisibleViaStat)
{
KT_TRUE(t, sfsFormat());
KT_EQ(t, vfsMkfifo("/mnt/fifoBasic"), 0);
FbStat st;
KT_EQ(t, vfsStat("/mnt/fifoBasic", &st), 0);
KT_EQ(t, st.type, fsTypeFifo);
KT_EQU(t, st.size, 0u);
/* Same path again must fail, exactly like the tmpfs case above. */
KT_EQ(t, vfsMkfifo("/mnt/fifoBasic"), -1);
KT_EQ(t, vfsUnlink("/mnt/fifoBasic"), 0);
KT_EQ(t, vfsStat("/mnt/fifoBasic", &st), -1);
}
KTEST(sfsBackedFifoRendezvousByPathAloneNoSharedHandle)
{
KT_TRUE(t, sfsFormat());
KT_EQ(t, vfsMkfifo("/mnt/fifoRendezvous"), 0);
int rfd = vfsOpen("/mnt/fifoRendezvous", oReadOnly);
KT_GE(t, rfd, 0);
/* Nobody has opened the write end yet: "try again" (-1), never spurious
end-of-file, exactly like the tmpfs fifo and a freshly
pipeCreateDetached handle both already guarantee. */
char probe[4];
KT_EQ(t, vfsRead(rfd, probe, sizeof(probe)), -1);
KT_NOTNULL(t, vfsBlockingChannel(rfd, false));
int wfd = vfsOpen("/mnt/fifoRendezvous", oWriteOnly);
KT_GE(t, wfd, 0);
KT_NE(t, rfd, wfd);
KT_EQ(t, vfsWrite(wfd, "hi", 2), 2);
char buffer[4];
memset(buffer, 0, sizeof(buffer));
KT_EQ(t, vfsRead(rfd, buffer, sizeof(buffer)), 2);
KT_TRUE(t, memcmp(buffer, "hi", 2) == 0);
vfsClose(rfd);
vfsClose(wfd);
KT_EQ(t, vfsUnlink("/mnt/fifoRendezvous"), 0);
}
KTEST(sfsBackedFifoUnlinkWhileOpenLeavesLiveFdsWorking)
{
KT_TRUE(t, sfsFormat());
KT_EQ(t, vfsMkfifo("/mnt/fifoUnlinkLive"), 0);
int rfd = vfsOpen("/mnt/fifoUnlinkLive", oReadOnly);
int wfd = vfsOpen("/mnt/fifoUnlinkLive", oWriteOnly);
KT_GE(t, rfd, 0);
KT_GE(t, wfd, 0);
/* Real unlink-while-open semantics, same guarantee as the tmpfs fifo:
removing the directory entry must not disturb fds opened before the
unlink -- they keep working off their own copy of the raw pipe
handle number. */
KT_EQ(t, vfsUnlink("/mnt/fifoUnlinkLive"), 0);
FbStat st;
KT_EQ(t, vfsStat("/mnt/fifoUnlinkLive", &st), -1);
KT_EQ(t, vfsWrite(wfd, "ok", 2), 2);
char buffer[4];
memset(buffer, 0, sizeof(buffer));
KT_EQ(t, vfsRead(rfd, buffer, sizeof(buffer)), 2);
KT_TRUE(t, memcmp(buffer, "ok", 2) == 0);
vfsClose(rfd);
vfsClose(wfd);
}
KTEST(sfsBackedFifoEntryPersistsAcrossUnmountRemount)
{
KT_TRUE(t, sfsFormat());
KT_EQ(t, vfsMkfifo("/mnt/fifoPersist"), 0);
/* The real point of this test (mirrors sfsTest.c's own
sfsMountReloadsExistingFilesystem-style coverage): drop EVERY
in-memory sfs.c structure (bitmaps, superblock) and reconstruct them
purely from what sfsFormat/sfsCreateFifo actually wrote to disk --
proving the fifo is a genuine on-disk entry, not a kmalloc node that
would evaporate the instant this process's heap did (like the tmpfs
fifo above, which vfsMkfifo("/mnt/...") deliberately never was). */
sfsUnmount();
KT_TRUE(t, sfsMount());
FbStat st;
KT_EQ(t, vfsStat("/mnt/fifoPersist", &st), 0);
KT_EQ(t, st.type, fsTypeFifo);
/* Still fully functional after the remount, not just present in a
directory listing: a fresh open/write/read round trip works exactly
as it would have before the remount. */
int rfd = vfsOpen("/mnt/fifoPersist", oReadOnly);
int wfd = vfsOpen("/mnt/fifoPersist", oWriteOnly);
KT_GE(t, rfd, 0);
KT_GE(t, wfd, 0);
KT_EQ(t, vfsWrite(wfd, "yo", 2), 2);
char buffer[4];
memset(buffer, 0, sizeof(buffer));
KT_EQ(t, vfsRead(rfd, buffer, sizeof(buffer)), 2);
KT_TRUE(t, memcmp(buffer, "yo", 2) == 0);
vfsClose(rfd);
vfsClose(wfd);
KT_EQ(t, vfsUnlink("/mnt/fifoPersist"), 0);
}
KTEST(sfsBackedFifoReopenAfterCompletedRoundTripGetsFreshWorkingSession)
{
/* Ordinary fifo usage, not a corner case: open both ends, exchange
data, close both, then reopen the SAME persistent path later for a
second, unrelated conversation -- exactly what a real named pipe is
for. The first round trip fully releases pipe.h's underlying slot
back to its general pool (pipeReleaseIfUnused, once both readers and
writers hit 0), so the second round's vfs.c-level binding
(sfsFifoHandleFor) MUST detect that its remembered handle number no
longer refers to the same pipe (pipeIncarnation, pipe.h) and mint a
genuinely fresh one -- otherwise this second write would silently
target a slot nobody has attached to (or, worse, one some unrelated
pipe elsewhere in the kernel grabbed in between), and would report
-1 forever instead of actually delivering the byte. */
KT_TRUE(t, sfsFormat());
KT_EQ(t, vfsMkfifo("/mnt/fifoRoundTrip"), 0);
int rfd1 = vfsOpen("/mnt/fifoRoundTrip", oReadOnly);
int wfd1 = vfsOpen("/mnt/fifoRoundTrip", oWriteOnly);
KT_GE(t, rfd1, 0);
KT_GE(t, wfd1, 0);
KT_EQ(t, vfsWrite(wfd1, "one", 3), 3);
char first[4];
memset(first, 0, sizeof(first));
KT_EQ(t, vfsRead(rfd1, first, sizeof(first)), 3);
KT_TRUE(t, memcmp(first, "one", 3) == 0);
vfsClose(rfd1);
vfsClose(wfd1);
/* Second, independent session on the same path. */
int rfd2 = vfsOpen("/mnt/fifoRoundTrip", oReadOnly);
KT_GE(t, rfd2, 0);