test: cover vfsFifoTest
This commit is contained in:
1 parent
8fe67d9b3e
commit
4b41308da9
1 file changed
+162
@@ -0,0 +1,162 @@
|
||||
#include "ktest.h"
|
||||
#include "vfs.h"
|
||||
#include "pipe.h"
|
||||
#include "fs.h"
|
||||
#include "string.h"
|
||||
#include "klib.h"
|
||||
#include "sfs.h"
|
||||
|
||||
/*
|
||||
* Coverage for named pipes (vfsMkfifo, vfs.h/vfs.c): a real filesystem entry
|
||||
* of type fsTypeFifo that lets two processes rendezvous over a blocking
|
||||
* pipe just by agreeing on a path, with no common ancestor and no handle
|
||||
* number to pass through argv the way vfsPipeCreateHandle/vfsPipeAttach
|
||||
* (pipeShareTest.c) require. The genuine cross-process park/wake handshake
|
||||
* over such a fifo only runs for real at boot (namedreader.c/namedwriter.c,
|
||||
* two independently spawned kmain.c programs, needles "namedreader:
|
||||
* opened, blocking on read"/"namedreader: payload-ok" in tools/ci-test.sh)
|
||||
* -- what is deterministic and scheduler-free (open/read/write never
|
||||
* actually parking a thread) lives here, matching the division of labor
|
||||
* pipeShareTest.c already established for the handle-based flavor.
|
||||
*/
|
||||
|
||||
KTEST(vfsMkfifoCreatesFifoNodeVisibleViaStat)
|
||||
{
|
||||
KT_EQ(t, vfsMkfifo("/tmp/fifoBasic"), 0);
|
||||
|
||||
FbStat st;
|
||||
KT_EQ(t, vfsStat("/tmp/fifoBasic", &st), 0);
|
||||
KT_EQ(t, st.type, fsTypeFifo);
|
||||
|
||||
/* Same path again must fail, like mkdir/open(O_CREAT|O_EXCL) would --
|
||||
mkfifo never silently succeeds on an existing name. */
|
||||
KT_EQ(t, vfsMkfifo("/tmp/fifoBasic"), -1);
|
||||
|
||||
KT_EQ(t, vfsUnlink("/tmp/fifoBasic"), 0);
|
||||
KT_EQ(t, vfsStat("/tmp/fifoBasic", &st), -1);
|
||||
}
|
||||
|
||||
KTEST(vfsMkfifoRejectsReservedAndInvalidPaths)
|
||||
{
|
||||
/* The one reserved, non-tree path mkfifo never supports -- a raw byte
|
||||
device has no notion of a named directory entry at all, unlike "/mnt"
|
||||
(see the sfsCreateFifo-backed tests below), which does. */
|
||||
KT_EQ(t, vfsMkfifo("/dev/disk0"), -1);
|
||||
/* Parent directory does not exist at all. */
|
||||
KT_EQ(t, vfsMkfifo("/doesNotExist/fifo"), -1);
|
||||
/* Root itself is never writable -- only vfsMountTmpfs-created
|
||||
directories (e.g. "/tmp") are, same rule makeChildFile enforces for
|
||||
oCreate. */
|
||||
KT_EQ(t, vfsMkfifo("/rootFifo"), -1);
|
||||
}
|
||||
|
||||
KTEST(vfsFifoOpenRejectsDualDirectionModes)
|
||||
{
|
||||
KT_EQ(t, vfsMkfifo("/tmp/fifoModes"), 0);
|
||||
|
||||
/* This kernel's pipe end is inherently one-directional (fdPipeRead XOR
|
||||
fdPipeWrite) -- a fifo fd can never be both, unlike Linux's permissive
|
||||
(POSIX-undefined) O_RDWR on a FIFO. */
|
||||
KT_EQ(t, vfsOpen("/tmp/fifoModes", oReadWrite), -1);
|
||||
KT_EQ(t, vfsOpen("/tmp/fifoModes", oReadWrite | oWriteOnly), -1);
|
||||
|
||||
KT_EQ(t, vfsUnlink("/tmp/fifoModes"), 0);
|
||||
}
|
||||
|
||||
KTEST(vfsFifoRendezvousByPathAloneNoSharedHandle)
|
||||
{
|
||||
KT_EQ(t, vfsMkfifo("/tmp/fifoRendezvous"), 0);
|
||||
|
||||
/* Two independent vfsOpen calls on the SAME PATH -- no handle number, no
|
||||
argv, no common caller -- is exactly what namedreader.c/namedwriter.c
|
||||
(two kmain.c boot programs with no parent-child relationship) rely
|
||||
on. */
|
||||
int rfd = vfsOpen("/tmp/fifoRendezvous", oReadOnly);
|
||||
KT_GE(t, rfd, 0);
|
||||
|
||||
/* Nobody has opened the write end yet: a read must report "try again"
|
||||
(-1), never spurious end-of-file, exactly like a freshly
|
||||
pipeCreateDetached handle (pipeShareTest.c). */
|
||||
char probe[4];
|
||||
KT_EQ(t, vfsRead(rfd, probe, sizeof(probe)), -1);
|
||||
KT_NOTNULL(t, vfsBlockingChannel(rfd, false));
|
||||
|
||||
int wfd = vfsOpen("/tmp/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("/tmp/fifoRendezvous"), 0);
|
||||
}
|
||||
|
||||
KTEST(vfsFifoUnlinkWhileOpenLeavesLiveFdsWorking)
|
||||
{
|
||||
KT_EQ(t, vfsMkfifo("/tmp/fifoUnlinkLive"), 0);
|
||||
|
||||
int rfd = vfsOpen("/tmp/fifoUnlinkLive", oReadOnly);
|
||||
int wfd = vfsOpen("/tmp/fifoUnlinkLive", oWriteOnly);
|
||||
KT_GE(t, rfd, 0);
|
||||
KT_GE(t, wfd, 0);
|
||||
|
||||
/* Real unlink-while-open semantics: removing the name must not disturb
|
||||
fds opened before the unlink -- they keep working off their own copy
|
||||
of the raw pipe handle, never the freed vfs node. */
|
||||
KT_EQ(t, vfsUnlink("/tmp/fifoUnlinkLive"), 0);
|
||||
FbStat st;
|
||||
KT_EQ(t, vfsStat("/tmp/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(vfsUnlinkOnNeverOpenedFifoReclaimsPipeSlot)
|
||||
{
|
||||
/* Drives the pipe table (pipe.h's fixed-size `pipes[maxPipes]`, shared
|
||||
with every other pipe user in the kernel) to exhaustion using fifos
|
||||
that are never opened by anyone -- the one case pipeCloseRead/
|
||||
pipeCloseWrite's own cleanup can never reach, since neither is ever
|
||||
called for an end nobody attached. Does not assume the table starts
|
||||
fully empty (kmain.c's own vfsMkfifo("/tmp/rendezvous") already holds
|
||||
one slot for the rest of boot by the time ktests run) -- just proves
|
||||
whatever headroom exists gets used up, and reclaimed, correctly. */
|
||||
char paths[maxPipes][24];
|
||||
int madeCount = 0;
|
||||
for (int i = 0; i < (int)maxPipes; i++) {
|
||||
snprintf(paths[i], sizeof(paths[i]), "/tmp/fifoLeak%d", i);
|
||||
if (vfsMkfifo(paths[i]) != 0)
|
||||
break;
|
||||
madeCount++;
|
||||
}
|
||||
KT_GT(t, madeCount, 0);
|
||||
|
||||
/* Table is now genuinely full: one more must fail. */
|
||||
KT_EQ(t, vfsMkfifo("/tmp/fifoOverflow"), -1);
|
||||
|
||||
/* Unlinking one never-opened fifo must reclaim its slot immediately
|
||||
(pipeDestroyIfUnattached), not leave it dangling until some later,
|
||||
unrelated event. */
|
||||
KT_EQ(t, vfsUnlink(paths[0]), 0);
|
||||
KT_EQ(t, vfsMkfifo("/tmp/fifoOverflow"), 0);
|
||||
|
||||
/* Leave the shared pipe table exactly as free as it was found, so later
|
||||
boot-time pipe users (the pipeProducer/pipeConsumer demo,
|
||||
fifomaster's handle, killtest's shared pipe, namedreader/
|
||||
namedwriter's own fifo) are never starved by this test having run. */
|
||||
vfsUnlink("/tmp/fifoOverflow");
|
||||
for (int i = 1; i < madeCount; i++)
|
||||
vfsUnlink(paths[i]);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user