test: test more of coreTest

This commit is contained in:
dualfroz committed 2026-09-10 14:03:42 +02:00
1 parent fdeb30948c
commit e6f36d52d3
1 file changed
+188
+188
View File
@@ -630,3 +630,191 @@ KTEST(coreVfsChgrpRootReassignsArbitraryGroup)
vfsClose(fd); vfsClose(fd);
KT_EQ(t, vfsChown("/tmp/chgrpForeign", 4242), 0); KT_EQ(t, vfsChown("/tmp/chgrpForeign", 4242), 0);
KT_EQ(t, vfsChgrp("/tmp/chgrpForeign", 9999), 0);
FbStat st;
KT_EQ(t, vfsStat("/tmp/chgrpForeign", &st), 0);
KT_EQU(t, st.gid, 9999u);
vfsUnlink("/tmp/chgrpForeign");
}
/* Same round trip on the persistent SFS mount, PLUS the "survives
unmount+remount" proof every other SFS metadata field already has --
mirrors coreVfsChownSfsPersistsAcrossRemount exactly, one field over. */
KTEST(coreVfsChgrpSfsPersistsAcrossRemount)
{
KT_EQ(t, vfsMkdir("/mnt/chgrpd"), 0);
int fd = vfsOpen("/mnt/chgrpd/f", oReadWrite | oCreate);
KT_GE(t, fd, 0);
vfsClose(fd);
FbStat st;
KT_EQ(t, vfsStat("/mnt/chgrpd/f", &st), 0);
KT_EQU(t, st.gid, 0u);
KT_EQ(t, vfsChgrp("/mnt/chgrpd/f", 6666), 0);
KT_EQ(t, vfsStat("/mnt/chgrpd/f", &st), 0);
KT_EQU(t, st.gid, 6666u);
sfsUnmount();
KT_TRUE(t, sfsMount());
KT_EQ(t, vfsStat("/mnt/chgrpd/f", &st), 0);
KT_EQU(t, st.gid, 6666u);
vfsUnlink("/mnt/chgrpd/f");
vfsRmdir("/mnt/chgrpd");
}
/* Real directory-write enforcement (vfs.c's vfsMkdir/resolveOpen create
branch), the gap FALA 44 explicitly flagged as stored-but-unenforced --
creating a NEW entry (file or subdirectory) needs the owner-write bit on
the PARENT directory, checked against the CALLING thread's own uid, not
just "the parent is a writable tmpfs node". KTEST always runs as the
permanent idle thread's own uid 0 (coreVfsTmpfsNodeStampsCreatorAsOwner's
own doc comment), and vfsPermitted has no uid-0 bypass
(coreVfsPermittedUidZeroIsNotASuperuser), so removing root's OWN
owner-write bit on a directory it owns genuinely denies root itself --
the same "no superuser bypass" story chmod's own tests already prove for
files, now proven for directories too, entirely from KTEST without
needing a live non-root caller. */
KTEST(coreVfsMkdirAndCreateRequireParentWrite)
{
KT_EQ(t, vfsMkdir("/tmp/dwParent"), 0);
KT_EQ(t, vfsChmod("/tmp/dwParent", 0555), 0); /* r-x, no owner-write */
KT_EQ(t, vfsMkdir("/tmp/dwParent/child"), -1);
KT_EQ(t, vfsOpen("/tmp/dwParent/f", oWriteOnly | oCreate), -1);
KT_EQ(t, vfsChmod("/tmp/dwParent", fsModeDefaultDir), 0);
KT_EQ(t, vfsMkdir("/tmp/dwParent/child"), 0);
int fd = vfsOpen("/tmp/dwParent/f", oWriteOnly | oCreate);
KT_GE(t, fd, 0);
vfsClose(fd);
vfsUnlink("/tmp/dwParent/f");
vfsRmdir("/tmp/dwParent/child");
vfsRmdir("/tmp/dwParent");
}
/* Real unlink(2)/rmdir(2) semantics: removing an entry needs write
permission on the PARENT directory, never the entry's own mode -- proven
here by a file that is itself still perfectly writable (0644) but whose
PARENT has lost its own write bit, matching real Unix's own "you can
delete a read-only file if you can write its directory, and you canNOT
delete a writable file if you can't" rule the file-level mode never
governs on its own. */
KTEST(coreVfsUnlinkAndRmdirRequireParentWrite)
{
KT_EQ(t, vfsMkdir("/tmp/dwParent2"), 0);
int fd = vfsOpen("/tmp/dwParent2/f", oReadWrite | oCreate);
KT_GE(t, fd, 0);
vfsClose(fd);
KT_EQ(t, vfsMkdir("/tmp/dwParent2/sub"), 0);
KT_EQ(t, vfsChmod("/tmp/dwParent2", 0555), 0);
KT_EQ(t, vfsUnlink("/tmp/dwParent2/f"), -1);
KT_EQ(t, vfsRmdir("/tmp/dwParent2/sub"), -1);
KT_EQ(t, vfsChmod("/tmp/dwParent2", fsModeDefaultDir), 0);
KT_EQ(t, vfsUnlink("/tmp/dwParent2/f"), 0);
KT_EQ(t, vfsRmdir("/tmp/dwParent2/sub"), 0);
KT_EQ(t, vfsRmdir("/tmp/dwParent2"), 0);
}
/* Real POSIX search permission (vfs.c's lookupNode, checked on every
directory ANCESTOR a path walks through): removing the exec bit on a
directory makes every path THROUGH it fail, even though the target file
inside it never changed at all -- proven by chmod'ing the ancestor away
and back around an otherwise-untouched file. */
KTEST(coreVfsLookupRequiresAncestorExec)
{
KT_EQ(t, vfsMkdir("/tmp/dxAncestor"), 0);
int fd = vfsOpen("/tmp/dxAncestor/leaf", oReadWrite | oCreate);
KT_GE(t, fd, 0);
KT_EQ(t, vfsWrite(fd, "x", 1), 1);
vfsClose(fd);
KT_EQ(t, vfsChmod("/tmp/dxAncestor", 0600), 0); /* rw-, no exec at all */
FbStat st;
KT_EQ(t, vfsStat("/tmp/dxAncestor/leaf", &st), -1);
KT_EQ(t, vfsOpen("/tmp/dxAncestor/leaf", oReadOnly), -1);
KT_EQ(t, vfsChmod("/tmp/dxAncestor", fsModeDefaultDir), 0);
KT_EQ(t, vfsStat("/tmp/dxAncestor/leaf", &st), 0);
int rfd = vfsOpen("/tmp/dxAncestor/leaf", oReadOnly);
KT_GE(t, rfd, 0);
vfsClose(rfd);
vfsUnlink("/tmp/dxAncestor/leaf");
vfsRmdir("/tmp/dxAncestor");
}
/* Real distinction between search (exec) and list (read) on a directory --
the subtlest part of this feature, and the one most likely to be
accidentally conflated: a directory with exec but no read lets a caller
open a KNOWN child by name (resolveOpen never needs read on `cur` for
that, only exec on it as an ANCESTOR of the leaf) while refusing to open
the directory itself for listing (vfsGetdents). Mirrors dirpermtest.c's
own live list-denied-ok/search-without-list-ok pair, but here entirely
from KTEST since both sides are root-vs-its-own-directory, not root-vs-
another-uid. */
KTEST(coreVfsOpendirRequiresDirReadNotJustExec)
{
KT_EQ(t, vfsMkdir("/tmp/dxListable"), 0);
int fd = vfsOpen("/tmp/dxListable/known", oReadWrite | oCreate);
KT_GE(t, fd, 0);
vfsClose(fd);
KT_EQ(t, vfsChmod("/tmp/dxListable", 0311), 0); /* -wx, no owner-read */
KT_EQ(t, vfsOpen("/tmp/dxListable", oReadOnly), -1);
int kfd = vfsOpen("/tmp/dxListable/known", oReadOnly);
KT_GE(t, kfd, 0);
vfsClose(kfd);
KT_EQ(t, vfsChmod("/tmp/dxListable", fsModeDefaultDir), 0);
int dd = vfsOpen("/tmp/dxListable", oReadOnly);
KT_GE(t, dd, 0);
FbDirent ents[8];
KT_TRUE(t, vfsGetdents(dd, ents, sizeof(ents)) >= (int64)sizeof(FbDirent));
vfsClose(dd);
vfsUnlink("/tmp/dxListable/known");
vfsRmdir("/tmp/dxListable");
}
/* Same three checks as the four tmpfs tests directly above (parent-write
for create/remove, ancestor-exec for traversal, dir-read-vs-exec for
listing), run once through the SFS dispatch branch of vfs.c's own
choke points (resolveOpen/vfsMkdir/vfsUnlink/vfsRmdir/lookupNode's
isSfsPath fork) instead of sfs.c's raw primitives directly -- sfsTest.c's
sfsCreateNodeRequiresParentWrite/sfsRemoveRequiresParentWrite/
sfsLookupRequiresAncestorExec already cover the on-disk layer itself;
this is the "does the /mnt dispatch actually reach it" parity proof, the
same relationship coreVfsChmodSfsPersistsAcrossRemount already has to
its own tmpfs sibling above. */
KTEST(coreVfsSfsDirectoryPermissionParity)
{
KT_EQ(t, vfsMkdir("/mnt/dpTop"), 0);
int fd = vfsOpen("/mnt/dpTop/leaf", oReadWrite | oCreate);
KT_GE(t, fd, 0);
vfsClose(fd);
KT_EQ(t, vfsChmod("/mnt/dpTop", 0555), 0);
KT_EQ(t, vfsOpen("/mnt/dpTop/newfile", oWriteOnly | oCreate), -1);
KT_EQ(t, vfsUnlink("/mnt/dpTop/leaf"), -1);
KT_EQ(t, vfsChmod("/mnt/dpTop", 0600), 0);
FbStat st;
KT_EQ(t, vfsStat("/mnt/dpTop/leaf", &st), -1);
KT_EQ(t, vfsChmod("/mnt/dpTop", 0311), 0);
KT_EQ(t, vfsOpen("/mnt/dpTop", oReadOnly), -1);
int kfd = vfsOpen("/mnt/dpTop/leaf", oReadOnly);
KT_GE(t, kfd, 0);
vfsClose(kfd);
KT_EQ(t, vfsChmod("/mnt/dpTop", fsModeDefaultDir), 0);
KT_EQ(t, vfsUnlink("/mnt/dpTop/leaf"), 0);
KT_EQ(t, vfsRmdir("/mnt/dpTop"), 0);
}