test: extend coverage of coreTest
This commit is contained in:
1 parent
02681bb9a1
commit
5f60d0e517
1 file changed
+159
+159
@@ -155,3 +155,162 @@ KTEST(coreVfsMkdirGetdents)
|
|||||||
KT_EQ(t, vfsMkdir("/tmp/cd"), 0);
|
KT_EQ(t, vfsMkdir("/tmp/cd"), 0);
|
||||||
KT_EQ(t, vfsMkdir("/tmp/cd"), -1); /* already exists */
|
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);
|
||||||
|
|
||||||
Reference in new issue
Block a user