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