test: add more cases for sfsTest
This commit is contained in:
1 parent
ae47c8258f
commit
b9f6ee3643
1 file changed
+159
+159
@@ -1105,3 +1105,162 @@ KTEST(sfsFileGrowsThroughTripleIndirectBlock)
|
|||||||
KT_TRUE(t, sfsTruncate(f, startOffset));
|
KT_TRUE(t, sfsTruncate(f, startOffset));
|
||||||
KT_EQ(t, sfsSizeOf(f), startOffset);
|
KT_EQ(t, sfsSizeOf(f), startOffset);
|
||||||
|
|
||||||
|
uint8* pattern = (uint8*)kmalloc(sfsTripleSpan);
|
||||||
|
uint8* verify = (uint8*)kmalloc(sfsTripleSpan);
|
||||||
|
KT_NOTNULL(t, pattern);
|
||||||
|
KT_NOTNULL(t, verify);
|
||||||
|
if (!pattern || !verify) {
|
||||||
|
kfree(pattern);
|
||||||
|
kfree(verify);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < sfsTripleSpan; i++)
|
||||||
|
pattern[i] = (uint8)(i * 29 + 7);
|
||||||
|
|
||||||
|
/* Six blocks land in the double-indirect region (16516..16521), the
|
||||||
|
other six only exist through the new triple-indirect block
|
||||||
|
(16522..16527) -- one write straddles both. */
|
||||||
|
KT_EQ(t, sfsWriteAt(f, startOffset, pattern, sfsTripleSpan), sfsTripleSpan);
|
||||||
|
KT_EQ(t, sfsSizeOf(f), startOffset + sfsTripleSpan);
|
||||||
|
|
||||||
|
memset(verify, 0, sfsTripleSpan);
|
||||||
|
KT_EQ(t, sfsReadAt(f, startOffset, verify, sfsTripleSpan), sfsTripleSpan);
|
||||||
|
KT_EQ(t, memcmp(verify, pattern, sfsTripleSpan), 0);
|
||||||
|
|
||||||
|
/* Patch a window straddling the double-indirect/triple-indirect
|
||||||
|
boundary itself (logical block 16521/16522) -- same "boundary patch"
|
||||||
|
shape as sfsFileGrowsThroughDoubleIndirectBlock, one level deeper. */
|
||||||
|
uint8 patch[48];
|
||||||
|
memset(patch, 0xAB, sizeof(patch));
|
||||||
|
uint64 patchOffset = (uint64)sfsTripleBoundaryBlock * sfsBlockSize - 24;
|
||||||
|
KT_EQ(t, sfsWriteAt(f, patchOffset, patch, sizeof(patch)), (int64)sizeof(patch));
|
||||||
|
|
||||||
|
KT_EQ(t, sfsReadAt(f, startOffset, verify, sfsTripleSpan), sfsTripleSpan);
|
||||||
|
uint64 patchInSpan = patchOffset - startOffset;
|
||||||
|
KT_EQ(t, memcmp(verify + patchInSpan, patch, sizeof(patch)), 0);
|
||||||
|
KT_EQ(t, memcmp(verify, pattern, patchInSpan), 0);
|
||||||
|
|
||||||
|
kfree(pattern);
|
||||||
|
kfree(verify);
|
||||||
|
#undef sfsTripleSpan
|
||||||
|
#undef sfsTripleStartBlock
|
||||||
|
#undef sfsTripleSpanBlocks
|
||||||
|
#undef sfsTripleBoundaryBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(sfsTruncateFreesTripleIndirectBlocks)
|
||||||
|
{
|
||||||
|
if (!KT_TRUE(t, sfsFormat()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
int32 f = sfsCreateFile("hugershrink.bin");
|
||||||
|
KT_TRUE(t, f > 0);
|
||||||
|
|
||||||
|
/* Baseline taken after creating the file, same reasoning as
|
||||||
|
sfsTruncateFreesDoubleIndirectBlocks: the root directory's own data
|
||||||
|
block is unrelated bookkeeping this test must not conflate with
|
||||||
|
hugershrink.bin's own content. */
|
||||||
|
uint64 baseline = sfsFreeDataBlocks();
|
||||||
|
KT_GT(t, baseline, 0);
|
||||||
|
|
||||||
|
/* Grow straight to the triple-indirect boundary via sfsTruncate (O(1),
|
||||||
|
a genuine sparse hole -- see sfsFileGrowsThroughTripleIndirectBlock
|
||||||
|
above) and write ENTIRELY inside the triple-indirect region, never
|
||||||
|
touching a single/double-indirect block. That keeps inode->indirect
|
||||||
|
and inode->doubleIndirect at 0, so the shrink-to-0 below only ever
|
||||||
|
does a cheap in-memory "still unallocated" check across the whole
|
||||||
|
single+double logical range instead of re-reading real holder blocks
|
||||||
|
for it -- proportional to the actual work, not to how far into the
|
||||||
|
address space the file happens to reach. */
|
||||||
|
#define sfsTripleBoundaryBlock 16522
|
||||||
|
#define sfsShrink2SpanBlocks 8
|
||||||
|
#define sfsShrink2Span (sfsShrink2SpanBlocks * sfsBlockSize)
|
||||||
|
uint64 offset = (uint64)sfsTripleBoundaryBlock * sfsBlockSize;
|
||||||
|
KT_TRUE(t, sfsTruncate(f, offset));
|
||||||
|
|
||||||
|
uint8* chunk = (uint8*)kmalloc(sfsShrink2Span);
|
||||||
|
KT_NOTNULL(t, chunk);
|
||||||
|
if (!chunk)
|
||||||
|
return;
|
||||||
|
memset(chunk, 0x62, sfsShrink2Span);
|
||||||
|
KT_EQ(t, sfsWriteAt(f, offset, chunk, sfsShrink2Span), sfsShrink2Span);
|
||||||
|
kfree(chunk);
|
||||||
|
#undef sfsShrink2Span
|
||||||
|
#undef sfsShrink2SpanBlocks
|
||||||
|
#undef sfsTripleBoundaryBlock
|
||||||
|
|
||||||
|
/* This file now owns: 1 triple-indirect holder + 1 mid holder + 1
|
||||||
|
leaf-pointer holder + a handful of triple-indirect data blocks.
|
||||||
|
Every one of those must come back. */
|
||||||
|
KT_LT(t, sfsFreeDataBlocks(), baseline);
|
||||||
|
|
||||||
|
KT_TRUE(t, sfsTruncate(f, 0));
|
||||||
|
KT_EQ(t, sfsSizeOf(f), 0);
|
||||||
|
KT_EQU(t, sfsFreeDataBlocks(), baseline);
|
||||||
|
|
||||||
|
/* And the inode itself must still be usable afterward. */
|
||||||
|
KT_EQ(t, sfsWriteAt(f, 0, "reused", 6), 6);
|
||||||
|
KT_EQ(t, sfsSizeOf(f), 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(sfsHardlinkSharesInodeAndData)
|
||||||
|
{
|
||||||
|
if (!KT_TRUE(t, sfsFormat()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
int32 f = sfsCreateFile("original.txt");
|
||||||
|
KT_TRUE(t, f > 0);
|
||||||
|
const char* msg = "shared bytes";
|
||||||
|
uint64 len = strlen(msg);
|
||||||
|
KT_EQ(t, sfsWriteAt(f, 0, msg, len), (int64)len);
|
||||||
|
KT_EQ(t, sfsLinkCountOf(f), 1);
|
||||||
|
|
||||||
|
int32 linked = sfsLink("original.txt", "alias.txt");
|
||||||
|
KT_EQ(t, linked, f);
|
||||||
|
KT_EQ(t, sfsLinkCountOf(f), 2);
|
||||||
|
KT_EQ(t, sfsLookup("alias.txt"), f);
|
||||||
|
|
||||||
|
/* A write through the alias must be visible through the original name:
|
||||||
|
they are not two files that happen to match, they are the same
|
||||||
|
inode. */
|
||||||
|
KT_EQ(t, sfsWriteAt(sfsLookup("alias.txt"), len, " too", 4), 4);
|
||||||
|
char back[32];
|
||||||
|
memset(back, 0, sizeof(back));
|
||||||
|
KT_EQ(t, sfsReadAt(f, 0, back, len + 4), (int64)(len + 4));
|
||||||
|
KT_EQ(t, memcmp(back, "shared bytes too", len + 4), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(sfsHardlinkRejectsDirectoriesAndBadArguments)
|
||||||
|
{
|
||||||
|
if (!KT_TRUE(t, sfsFormat()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
KT_TRUE(t, sfsCreateDir("adir") > 0);
|
||||||
|
KT_EQ(t, sfsLink("adir", "adiralias"), -1);
|
||||||
|
|
||||||
|
int32 f = sfsCreateFile("real.txt");
|
||||||
|
KT_TRUE(t, f > 0);
|
||||||
|
KT_EQ(t, sfsLink("does/not/exist.txt", "alias.txt"), -1);
|
||||||
|
|
||||||
|
KT_TRUE(t, sfsCreateFile("taken.txt") > 0);
|
||||||
|
KT_EQ(t, sfsLink("real.txt", "taken.txt"), -1);
|
||||||
|
|
||||||
|
/* None of the rejected attempts may have left a stray link count or
|
||||||
|
directory entry behind. */
|
||||||
|
KT_EQ(t, sfsLinkCountOf(f), 1);
|
||||||
|
KT_EQ(t, sfsLookup("alias.txt"), -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(sfsRemoveOneHardlinkKeepsOtherAlive)
|
||||||
|
{
|
||||||
|
if (!KT_TRUE(t, sfsFormat()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
int32 f = sfsCreateFile("first.txt");
|
||||||
|
KT_TRUE(t, f > 0);
|
||||||
|
KT_EQ(t, sfsWriteAt(f, 0, "durable", 7), 7);
|
||||||
|
KT_TRUE(t, sfsLink("first.txt", "second.txt") > 0);
|
||||||
|
|
||||||
|
KT_TRUE(t, sfsRemove("first.txt"));
|
||||||
|
KT_EQ(t, sfsLookup("first.txt"), -1);
|
||||||
|
|
||||||
Reference in new issue
Block a user