test: test more of sfsTest

This commit is contained in:
dualfroz committed 2026-09-11 18:57:05 +02:00
1 parent f99e8eeed4
commit 28a39f4234
1 file changed
+157
+157
View File
@@ -624,3 +624,160 @@ KTEST(sfsChownReassignsOwnerAndBumpsCtime)
KT_FALSE(t, sfsChown(9999, 1000)); KT_FALSE(t, sfsChown(9999, 1000));
} }
/* Same real point as sfsPersistsUidAcrossUnmountRemount above, but for a
uid REASSIGNED by sfsChown after creation, not just the creation-time
default -- proves the chown write path itself lands on disk, not just
that createNode's own initial stamp does. */
KTEST(sfsPersistsChownedUidAcrossUnmountRemount)
{
if (!KT_TRUE(t, sfsFormat()))
return;
int32 f = sfsCreateFile("chownpersist.txt");
if (!KT_TRUE(t, f > 0))
return;
KT_TRUE(t, sfsChown(f, 8123));
sfsUnmount();
KT_TRUE(t, sfsMount());
int32 reloaded = sfsLookup("chownpersist.txt");
KT_EQ(t, reloaded, f);
KT_EQU(t, sfsUidOf(reloaded), 8123u);
}
/* Real chgrp(2)'s on-disk half (sfsChgrp), mirroring
sfsChownReassignsOwnerAndBumpsCtime exactly one field over -- same "no
privilege check of its own, ctime bumps, invalid inode fails the same
way" shape. */
KTEST(sfsChgrpReassignsGroupAndBumpsCtime)
{
if (!KT_TRUE(t, sfsFormat()))
return;
int32 f = sfsCreateFile("chgrpfile.txt");
if (!KT_TRUE(t, f > 0))
return;
KT_EQU(t, sfsGidOf(f), 0u);
uint64 ctimeBefore = sfsCtimeOf(f);
int64 before = rtcNow();
KT_TRUE(t, sfsChgrp(f, 9002));
int64 after = rtcNow();
KT_EQU(t, sfsGidOf(f), 9002u);
KT_GE(t, sfsCtimeOf(f), ctimeBefore);
KT_GE(t, sfsCtimeOf(f), before);
KT_LE(t, sfsCtimeOf(f), after);
KT_FALSE(t, sfsChgrp(-1, 1000));
KT_FALSE(t, sfsChgrp(9999, 1000));
}
/* Same real point as sfsPersistsChownedUidAcrossUnmountRemount above, one
field over: proves the chgrp write path itself lands on disk. */
KTEST(sfsPersistsChgrpedGidAcrossUnmountRemount)
{
if (!KT_TRUE(t, sfsFormat()))
return;
int32 f = sfsCreateFile("chgrppersist.txt");
if (!KT_TRUE(t, f > 0))
return;
KT_TRUE(t, sfsChgrp(f, 7124));
sfsUnmount();
KT_TRUE(t, sfsMount());
int32 reloaded = sfsLookup("chgrppersist.txt");
KT_EQ(t, reloaded, f);
KT_EQU(t, sfsGidOf(reloaded), 7124u);
}
/* Real directory-write enforcement at the on-disk choke point (sfs.c's
createNode, shared by sfsCreateFile/sfsCreateDir/sfsCreateFifo/
sfsCreateSymlink) -- mirrors coreTest.c's tmpfs
coreVfsMkdirAndCreateRequireParentWrite for the SFS backing store.
ktestRunAll always runs as uid 0, and vfsPermitted has no uid-0 bypass,
so removing the parent's own owner-write bit denies even its own
creator/owner. */
KTEST(sfsCreateNodeRequiresParentWrite)
{
if (!KT_TRUE(t, sfsFormat()))
return;
KT_TRUE(t, sfsCreateDir("dwparent") > 0);
KT_TRUE(t, sfsChmod(sfsLookup("dwparent"), 0555)); /* r-x, no owner-write */
KT_EQ(t, sfsCreateFile("dwparent/f"), -1);
KT_EQ(t, sfsCreateDir("dwparent/sub"), -1);
KT_TRUE(t, sfsChmod(sfsLookup("dwparent"), fsModeDefaultDir));
KT_TRUE(t, sfsCreateFile("dwparent/f") > 0);
KT_TRUE(t, sfsCreateDir("dwparent/sub") > 0);
}
/* Same parent-write rule applied to sfsLink -- adding a new hardlink NAME
is a directory-entry creation exactly like sfsCreateFile, so it needs
write permission on the PARENT of the new name, not any permission on
the existing target being linked to. */
KTEST(sfsLinkRequiresParentWriteOnNewName)
{
if (!KT_TRUE(t, sfsFormat()))
return;
KT_TRUE(t, sfsCreateFile("lwtarget.txt") > 0);
KT_TRUE(t, sfsCreateDir("lwparent") > 0);
KT_TRUE(t, sfsChmod(sfsLookup("lwparent"), 0555));
KT_EQ(t, sfsLink("lwtarget.txt", "lwparent/alias"), -1);
KT_TRUE(t, sfsChmod(sfsLookup("lwparent"), fsModeDefaultDir));
KT_TRUE(t, sfsLink("lwtarget.txt", "lwparent/alias") > 0);
}
/* Real unlink(2)/rmdir(2) semantics at the on-disk choke point (sfs.c's
sfsRemove) -- mirrors coreTest.c's tmpfs
coreVfsUnlinkAndRmdirRequireParentWrite: removing an entry needs write
permission on the PARENT, never the entry's own mode. */
KTEST(sfsRemoveRequiresParentWrite)
{
if (!KT_TRUE(t, sfsFormat()))
return;
KT_TRUE(t, sfsCreateDir("rwparent") > 0);
KT_TRUE(t, sfsCreateFile("rwparent/f") > 0);
KT_TRUE(t, sfsChmod(sfsLookup("rwparent"), 0555));
KT_FALSE(t, sfsRemove("rwparent/f"));
KT_TRUE(t, sfsChmod(sfsLookup("rwparent"), fsModeDefaultDir));
KT_TRUE(t, sfsRemove("rwparent/f"));
}
/* Real POSIX search permission at the on-disk choke point (sfs.c's
lookupFrom, checked on every directory ANCESTOR a path walks through) --
mirrors coreTest.c's tmpfs coreVfsLookupRequiresAncestorExec. Removing
the exec bit on an ancestor makes every lookup THROUGH it fail even
though the target inode itself never changed. */
KTEST(sfsLookupRequiresAncestorExec)
{
if (!KT_TRUE(t, sfsFormat()))
return;
KT_TRUE(t, sfsCreateDir("xancestor") > 0);
int32 leaf = sfsCreateFile("xancestor/leaf");
KT_TRUE(t, leaf > 0);
KT_TRUE(t, sfsChmod(sfsLookup("xancestor"), 0600)); /* rw-, no exec */
KT_EQ(t, sfsLookup("xancestor/leaf"), -1);
KT_TRUE(t, sfsChmod(sfsLookup("xancestor"), fsModeDefaultDir));
KT_EQ(t, sfsLookup("xancestor/leaf"), leaf);
}
KTEST(sfsSymlinkResolvesToTargetFile)
{
if (!KT_TRUE(t, sfsFormat()))
return;