test: extend coverage of coreTest

This commit is contained in:
dualfroz committed 2026-09-10 10:37:33 +02:00
1 parent 02681bb9a1
commit 5f60d0e517
1 file changed
+159
+159
View File
@@ -155,3 +155,162 @@ KTEST(coreVfsMkdirGetdents)
KT_EQ(t, vfsMkdir("/tmp/cd"), 0);
KT_EQ(t, vfsMkdir("/tmp/cd"), -1); /* already exists */
int fa = vfsOpen("/tmp/cd/a", oReadWrite | oCreate);
int fb = vfsOpen("/tmp/cd/b", oReadWrite | oCreate);
KT_GE(t, fa, 0);
KT_GE(t, fb, 0);
vfsClose(fa);
vfsClose(fb);
int dd = vfsOpen("/tmp/cd", oReadOnly);
KT_GE(t, dd, 0);
FbDirent ents[8];
int64 n = vfsGetdents(dd, ents, sizeof(ents));
KT_TRUE(t, n >= (int64)(2 * sizeof(FbDirent)));
vfsClose(dd);
vfsUnlink("/tmp/cd/a");
vfsUnlink("/tmp/cd/b");
}
/* Real rmdir(2) on tmpfs: only an EMPTY directory may be removed, and the
directory must still be there (untouched) after a rejected attempt. */
KTEST(coreVfsRmdirTmpfsEmptyCheck)
{
KT_EQ(t, vfsMkdir("/tmp/rd"), 0);
int fd = vfsOpen("/tmp/rd/f", oReadWrite | oCreate);
KT_GE(t, fd, 0);
vfsClose(fd);
KT_EQ(t, vfsRmdir("/tmp/rd"), -1); /* not empty yet */
FbStat st;
KT_EQ(t, vfsStat("/tmp/rd", &st), 0); /* rejected attempt left it alone */
KT_EQ(t, vfsUnlink("/tmp/rd/f"), 0);
KT_EQ(t, vfsRmdir("/tmp/rd"), 0); /* empty now, succeeds */
KT_EQ(t, vfsStat("/tmp/rd", &st), -1); /* gone */
}
/* Real unlink(2)/rmdir(2) split on tmpfs: each refuses the other's kind of
node outright, neither silently falls back to the other's behavior. */
KTEST(coreVfsUnlinkAndRmdirRejectWrongTypeTmpfs)
{
KT_EQ(t, vfsMkdir("/tmp/rdDir"), 0);
KT_EQ(t, vfsUnlink("/tmp/rdDir"), -1); /* EISDIR-shaped refusal */
FbStat st;
KT_EQ(t, vfsStat("/tmp/rdDir", &st), 0); /* still there */
KT_EQ(t, vfsRmdir("/tmp/rdDir"), 0); /* the sanctioned way */
int fd = vfsOpen("/tmp/rdFile", oReadWrite | oCreate);
KT_GE(t, fd, 0);
vfsClose(fd);
KT_EQ(t, vfsRmdir("/tmp/rdFile"), -1); /* ENOTDIR-shaped refusal */
KT_EQ(t, vfsStat("/tmp/rdFile", &st), 0); /* still there */
KT_EQ(t, vfsUnlink("/tmp/rdFile"), 0);
}
/* Root can never be removed -- covered by the same `!node->writable` guard
every other mutating tmpfs entry point already relies on (root is
created non-writable in vfsInit). */
KTEST(coreVfsRmdirRefusesRoot)
{
KT_EQ(t, vfsRmdir("/"), -1);
}
/* Same two checks as coreVfsRmdirTmpfsEmptyCheck/
coreVfsUnlinkAndRmdirRejectWrongTypeTmpfs above, on the persistent SFS
mount instead of tmpfs -- proves vfs.c's dispatch layer enforces the
same real unlink(2)/rmdir(2) split on BOTH backing stores, not just the
in-memory one. Before this fala, vfsUnlink on an SFS path never checked
the inode's type at all, so unlinking an EMPTY directory under /mnt
silently succeeded (sfsRemove's own dir-emptiness check let it through)
-- this is the regression guard for that real, previously-shipped gap. */
KTEST(coreVfsRmdirSfsParity)
{
KT_EQ(t, vfsMkdir("/mnt/rdSfsEmpty"), 0);
KT_EQ(t, vfsUnlink("/mnt/rdSfsEmpty"), -1); /* the fixed bug */
FbStat st;
KT_EQ(t, vfsStat("/mnt/rdSfsEmpty", &st), 0); /* still there */
KT_EQ(t, vfsRmdir("/mnt/rdSfsEmpty"), 0);
KT_EQ(t, vfsStat("/mnt/rdSfsEmpty", &st), -1);
KT_EQ(t, vfsMkdir("/mnt/rdSfsFull"), 0);
int fd = vfsOpen("/mnt/rdSfsFull/f", oReadWrite | oCreate);
KT_GE(t, fd, 0);
vfsClose(fd);
KT_EQ(t, vfsRmdir("/mnt/rdSfsFull"), -1); /* not empty */
KT_EQ(t, vfsUnlink("/mnt/rdSfsFull/f"), 0);
KT_EQ(t, vfsRmdir("/mnt/rdSfsFull"), 0);
fd = vfsOpen("/mnt/rdSfsFile", oReadWrite | oCreate);
KT_GE(t, fd, 0);
vfsClose(fd);
KT_EQ(t, vfsRmdir("/mnt/rdSfsFile"), -1); /* ENOTDIR-shaped refusal */
KT_EQ(t, vfsUnlink("/mnt/rdSfsFile"), 0);
}
/* Real chmod(2) + owner rwx permission bits (fs.h's fsModeOwner*) on tmpfs:
a fresh file gets fsModeDefaultFile (0644) by default; chmod to a mode
missing the owner-write bit makes a SUBSEQUENT open-for-write fail
(EACCES-shaped) while reads keep working, and chmod back restores write
access immediately -- permission is only checked at open() time, never
re-checked against an fd already held, matching real Unix. */
KTEST(coreVfsChmodTmpfsWritePermission)
{
int fd = vfsOpen("/tmp/chmodf", oReadWrite | oCreate);
KT_GE(t, fd, 0);
KT_EQ(t, vfsWrite(fd, "x", 1), 1);
vfsClose(fd);
FbStat st;
KT_EQ(t, vfsStat("/tmp/chmodf", &st), 0);
KT_EQ(t, st.mode, fsModeDefaultFile);
KT_EQ(t, vfsChmod("/tmp/chmodf", 0444), 0);
KT_EQ(t, vfsStat("/tmp/chmodf", &st), 0);
KT_EQ(t, st.mode, 0444u);
KT_EQ(t, vfsOpen("/tmp/chmodf", oWriteOnly), -1); /* no owner-write bit */
int rfd = vfsOpen("/tmp/chmodf", oReadOnly);
KT_GE(t, rfd, 0);
char rd;
KT_EQ(t, vfsRead(rfd, &rd, 1), 1);
vfsClose(rfd);
KT_EQ(t, vfsChmod("/tmp/chmodf", fsModeDefaultFile), 0);
int wfd = vfsOpen("/tmp/chmodf", oWriteOnly);
KT_GE(t, wfd, 0);
vfsClose(wfd);
vfsUnlink("/tmp/chmodf");
}
/* Real, previously-missing enforcement found by this fala's own grouptest.c
live demo, not review: resolveOpen (vfs.c) checked fsModeOwnerWrite for a
write-mode open and fsModeOwnerRead for opendir()-ing a DIRECTORY, but
NEVER checked any read bit at all for a plain FILE opened oReadOnly or
oReadWrite -- any caller could read any readable-by-someone file's
CONTENTS regardless of mode, since nothing gated it. Proven here entirely
from KTEST (unlike the cross-uid case, which needed a live process,
see grouptest.c's own "other-fallback-denied-ok"): chmod 0000 denies even
the file's OWNER its own read, exactly like coreVfsMkdirAndCreateRequire
ParentWrite already proved for root's own directory-write bit -- no
superuser/owner bypass either way. */
KTEST(coreVfsResolveOpenRequiresFileReadPermission)
{
int fd = vfsOpen("/tmp/readf", oReadWrite | oCreate);
KT_GE(t, fd, 0);
KT_EQ(t, vfsWrite(fd, "x", 1), 1);
vfsClose(fd);
KT_EQ(t, vfsChmod("/tmp/readf", 0000), 0); /* no bits at all, even owner */
KT_EQ(t, vfsOpen("/tmp/readf", oReadOnly), -1);
KT_EQ(t, vfsOpen("/tmp/readf", oReadWrite), -1);
KT_EQ(t, vfsChmod("/tmp/readf", 0200), 0); /* owner-write only, no read */
KT_EQ(t, vfsOpen("/tmp/readf", oReadOnly), -1);
int wfd2 = vfsOpen("/tmp/readf", oWriteOnly);
KT_GE(t, wfd2, 0); /* write-only still works -- the two bits are independent */
vfsClose(wfd2);