From 3b72ea3aee26352250bd00d98bb295c830c2701c Mon Sep 17 00:00:00 2001 From: DualFroz Date: Sat, 12 Sep 2026 18:18:12 +0200 Subject: [PATCH] test: extend coverage of vfsFifoTest --- src/vfsFifoTest.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/src/vfsFifoTest.c b/src/vfsFifoTest.c index 21d75c5..e4329c6 100644 --- a/src/vfsFifoTest.c +++ b/src/vfsFifoTest.c @@ -160,3 +160,162 @@ KTEST(vfsUnlinkOnNeverOpenedFifoReclaimsPipeSlot) 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); +