hfs_extents_test.c [plain text]
#include "hfs_extents_test.h"
#include "../core/hfs_extents.h"
typedef struct extent_group {
LIST_ENTRY(extent_group) chain;
uint32_t start_block;
HFSPlusExtentRecord extents;
bool deleted;
} extent_group_t;
int32_t BTDeleteRecord(filefork_t *ff,
BTreeIterator *iterator)
{
(void)ff;
assert(!iterator->group->deleted);
LIST_REMOVE(iterator->group, chain);
iterator->group->deleted = true;
return 0;
}
errno_t hfs_ext_iter_next_group(struct hfs_ext_iter *iter)
{
off_t offset = (iter->bt_iter.key.startBlock + iter->group_block_count) * 4096;
errno_t ret = hfs_ext_find(iter->vp, offset, iter);
if (ret)
return ret;
if (iter->bt_iter.key.startBlock != offset / 4096)
return ESTALE;
return 0;
}
errno_t hfs_ext_iter_update(hfs_ext_iter_t *iter,
HFSPlusExtentDescriptor *extents,
int count,
HFSPlusExtentRecord cat_extents)
{
if (count % kHFSPlusExtentDensity) {
int to_zero = kHFSPlusExtentDensity - (count % 8);
bzero(&extents[count], to_zero * sizeof(*extents));
count += to_zero;
}
extent_group_t *group = NULL;
if (!iter->bt_iter.key.startBlock) {
memcpy(cat_extents, extents, sizeof(HFSPlusExtentRecord));
group = LIST_FIRST(&iter->vp->ffork->groups);
hfs_ext_copy_rec(extents, group->extents);
iter->bt_iter.key.startBlock += hfs_total_blocks(extents, 8);
count -= 8;
extents += 8;
} else {
extent_group_t *next;
LIST_FOREACH(next, &iter->vp->ffork->groups, chain) {
if (iter->bt_iter.key.startBlock < next->start_block)
break;
group = next;
}
}
iter->bt_iter.group = NULL;
while (count > 0) {
extent_group_t *new_group = malloc(sizeof(*new_group));
hfs_ext_copy_rec(extents, new_group->extents);
new_group->start_block = iter->bt_iter.key.startBlock;
new_group->deleted = false;
if (group) {
LIST_INSERT_AFTER(group, new_group, chain);
} else
LIST_INSERT_HEAD(&iter->vp->ffork->groups, new_group, chain);
group = new_group;
iter->bt_iter.key.startBlock += hfs_total_blocks(extents, 8);
count -= 8;
extents += 8;
}
return 0;
}
errno_t hfs_ext_find(vnode_t vp, off_t offset, hfs_ext_iter_t *iter)
{
(void)vp;
const uint32_t block = (uint32_t)(offset / 4096);
iter->vp = vp;
extent_group_t *next, *group = NULL;
LIST_FOREACH(next, &vp->ffork->groups, chain) {
if (block < next->start_block)
break;
group = next;
}
if (!group)
return ENOENT;
iter->file_block = iter->bt_iter.key.startBlock = group->start_block;
iter->bt_iter.group = group;
for (iter->ndx = 0; iter->ndx < 8; ++iter->ndx) {
HFSPlusExtentDescriptor *ext = &group->extents[iter->ndx];
if (iter->file_block + ext->blockCount > block) {
hfs_ext_copy_rec(group->extents, iter->group);
return hfs_ext_iter_check_group(iter);
}
iter->file_block += ext->blockCount;
}
return ENOENT;
}
void dump_extents(struct extent_groups *groups)
{
extent_group_t *group;
int i = 0;
LIST_FOREACH(group, groups, chain) {
for (int j = 0; j < 8; ++j) {
HFSPlusExtentDescriptor *ext = &group->extents[j];
if (!ext->blockCount) {
if (LIST_NEXT(group, chain))
goto keep_going;
for (int k = j; k < 8; ++k) {
if (group->extents[k].startBlock
|| group->extents[k].blockCount) {
goto keep_going;
}
}
break;
}
keep_going:
printf("%s{ %u, %u }", (i == 0 ? "" : i % 4 == 0 ? ",\n" : ", "),
ext->startBlock, ext->blockCount);
++i;
}
}
printf("\n");
}
void check_extents(unsigned line, vnode_t vnode,
const HFSPlusExtentDescriptor *extents, int count)
{
uint32_t block = 0;
extent_group_t *group;
LIST_FOREACH(group, &vnode->ffork->groups, chain) {
if (group->start_block != block)
goto bad;
int cnt = MIN(count, 8);
if (memcmp(group->extents, extents, cnt * sizeof(*extents)))
goto bad;
while (cnt < 8) {
if (group->extents[cnt].startBlock || group->extents[cnt].blockCount)
goto bad;
++cnt;
}
if ((count -= 8) <= 0)
break;
block += hfs_total_blocks(extents, 8);
extents += 8;
}
if (LIST_NEXT(group, chain))
goto bad;
return;
bad:
printf("hfs_extents_test:%u: error: unexpected extents:\n", line);
dump_extents(&vnode->ffork->groups);
exit(1);
}
int main(void)
{
cnode_t cnode = {};
hfsmount_t mount = {
.blockSize = 4096,
.hfs_extents_cp = &cnode,
};
filefork_t ffork = {
.ff_blocks = 200,
.ff_unallocblocks = 100,
.groups = LIST_HEAD_INITIALIZER(ffork.groups),
};
struct vnode vnode = {
.ffork = &ffork,
.mount = &mount,
.is_rsrc = false,
.cnode = &cnode,
};
extent_group_t group = {
.extents = { { 100, 100 } },
};
LIST_INSERT_HEAD(&ffork.groups, &group, chain);
HFSPlusExtentRecord cat_extents;
#define E(...) (HFSPlusExtentDescriptor[]) { __VA_ARGS__ }
#define lengthof(x) (sizeof(x) / sizeof(*x))
#define CHECK_EXTENTS(...) \
do { \
HFSPlusExtentDescriptor extents_[] = __VA_ARGS__; \
check_extents(__LINE__, &vnode, extents_, \
lengthof(extents_)); \
} while (0)
#define CHECK_REPLACE(file_block, repl, expected) \
do { \
HFSPlusExtentDescriptor repl_[] = repl; \
hfs_ext_replace(&mount, &vnode, file_block, repl_, \
lengthof(repl_), cat_extents); \
CHECK_EXTENTS(expected); \
} while (0)
CHECK_REPLACE(10, E({ 200, 10 }),
E({ 100, 10 }, { 200, 10 }, { 120, 80 }));
CHECK_REPLACE(5, E({ 300, 10 }),
E({ 100, 5 }, { 300, 10 }, { 205, 5 }, { 120, 80 }));
CHECK_REPLACE(20, E({ 400, 1 }),
E({ 100, 5 }, { 300, 10 }, { 205, 5 }, { 400, 1 },
{ 121, 79 }));
CHECK_REPLACE(21, E({ 402, 1 }),
E({ 100, 5 }, { 300, 10 }, { 205, 5 }, { 400, 1 },
{ 402, 1 }, { 122, 78 }));
CHECK_REPLACE(22, E({ 404, 1 }),
E({ 100, 5 }, { 300, 10 }, { 205, 5 }, { 400, 1 },
{ 402, 1 }, { 404, 1 }, { 123, 77 }));
CHECK_REPLACE(23, E({ 406, 1 }),
E({ 100, 5 }, { 300, 10 }, { 205, 5 }, { 400, 1 },
{ 402, 1 }, { 404, 1 }, { 406, 1 }, { 124, 76 }));
CHECK_REPLACE(24, E({ 408, 1 }),
E({ 100, 5 }, { 300, 10 }, { 205, 5 }, { 400, 1 },
{ 402, 1 }, { 404, 1 }, { 406, 1 }, { 408, 1 },
{ 125, 75 }));
for (int i = 25, j = 500; i < 25 + 24; ++i, j += 2) {
hfs_ext_replace(&mount, &vnode, i, E({ j, 1 }),
1, cat_extents);
}
CHECK_EXTENTS(E({ 100, 5 }, { 300, 10 }, { 205, 5 }, { 400, 1 },
{ 402, 1 }, { 404, 1 }, { 406, 1 }, { 408, 1 },
{ 500, 1 }, { 502, 1 }, { 504, 1 }, { 506, 1 },
{ 508, 1 }, { 510, 1 }, { 512, 1 }, { 514, 1 },
{ 516, 1 }, { 518, 1 }, { 520, 1 }, { 522, 1 },
{ 524, 1 }, { 526, 1 }, { 528, 1 }, { 530, 1 },
{ 532, 1 }, { 534, 1 }, { 536, 1 }, { 538, 1 },
{ 540, 1 }, { 542, 1 }, { 544, 1 }, { 546, 1 },
{ 149, 51 }));
CHECK_REPLACE(2, E({ 600, 1 }),
E({ 100, 2 }, { 600, 1 }, { 103, 2 }, { 300, 8 },
{ 308, 1 }, { 309, 1 }, { 205, 1 }, { 206, 1 },
{ 207, 1 }, { 208, 1 }, { 209, 1 }, { 400, 1 },
{ 402, 1 }, { 404, 1 }, { 406, 1 }, { 408, 1 },
{ 500, 1 }, { 502, 1 }, { 504, 1 }, { 506, 1 },
{ 508, 1 }, { 510, 1 }, { 512, 1 }, { 514, 1 },
{ 516, 1 }, { 518, 1 }, { 520, 1 }, { 522, 1 },
{ 524, 1 }, { 526, 1 }, { 528, 1 }, { 530, 1 },
{ 532, 1 }, { 534, 1 }, { 536, 1 }, { 538, 1 },
{ 540, 1 }, { 542, 1 }, { 544, 1 }, { 546, 1 },
{ 149, 51 }));
CHECK_REPLACE(47, E({ 700, 2 }),
E({ 100, 2 }, { 600, 1 }, { 103, 2 }, { 300, 8 },
{ 308, 1 }, { 309, 1 }, { 205, 1 }, { 206, 1 },
{ 207, 1 }, { 208, 1 }, { 209, 1 }, { 400, 1 },
{ 402, 1 }, { 404, 1 }, { 406, 1 }, { 408, 1 },
{ 500, 1 }, { 502, 1 }, { 504, 1 }, { 506, 1 },
{ 508, 1 }, { 510, 1 }, { 512, 1 }, { 514, 1 },
{ 516, 1 }, { 518, 1 }, { 520, 1 }, { 522, 1 },
{ 524, 1 }, { 526, 1 }, { 528, 1 }, { 530, 1 },
{ 532, 1 }, { 534, 1 }, { 536, 1 }, { 538, 1 },
{ 540, 1 }, { 542, 1 }, { 700, 2 }, { 149, 51 }));
CHECK_REPLACE(21, E({ 800, 1 }),
E({ 100, 2 }, { 600, 1 }, { 103, 2 }, { 300, 8 },
{ 308, 1 }, { 309, 1 }, { 205, 1 }, { 206, 1 },
{ 207, 3 }, { 400, 1 }, { 800, 1 }, { 404, 1 },
{ 406, 1 }, { 408, 1 }, { 500, 1 }, { 502, 1 },
{ 504, 1 }, { 506, 1 }, { 508, 1 }, { 510, 1 },
{ 512, 1 }, { 514, 1 }, { 516, 1 }, { 518, 1 },
{ 520, 1 }, { 522, 1 }, { 524, 1 }, { 526, 1 },
{ 528, 1 }, { 530, 1 }, { 532, 1 }, { 534, 1 },
{ 536, 1 }, { 538, 1 }, { 540, 1 }, { 542, 1 },
{ 700, 2 }, { 149, 51 }));
CHECK_REPLACE(50, E({ 800, 1 }),
E({ 100, 2 }, { 600, 1 }, { 103, 2 }, { 300, 8 },
{ 308, 1 }, { 309, 1 }, { 205, 1 }, { 206, 1 },
{ 207, 3 }, { 400, 1 }, { 800, 1 }, { 404, 1 },
{ 406, 1 }, { 408, 1 }, { 500, 1 }, { 502, 1 },
{ 504, 1 }, { 506, 1 }, { 508, 1 }, { 510, 1 },
{ 512, 1 }, { 514, 1 }, { 516, 1 }, { 518, 1 },
{ 520, 1 }, { 522, 1 }, { 524, 1 }, { 526, 1 },
{ 528, 1 }, { 530, 1 }, { 532, 1 }, { 534, 1 },
{ 536, 1 }, { 538, 1 }, { 540, 1 }, { 542, 1 },
{ 700, 2 }, { 149, 1 }, { 800, 1 }, { 151, 49 }));
CHECK_REPLACE(52, E({ 802, 1 }),
E({ 100, 2 }, { 600, 1 }, { 103, 2 }, { 300, 8 },
{ 308, 1 }, { 309, 1 }, { 205, 1 }, { 206, 1 },
{ 207, 3 }, { 400, 1 }, { 800, 1 }, { 404, 1 },
{ 406, 1 }, { 408, 1 }, { 500, 1 }, { 502, 1 },
{ 504, 1 }, { 506, 1 }, { 508, 1 }, { 510, 1 },
{ 512, 1 }, { 514, 1 }, { 516, 1 }, { 518, 1 },
{ 520, 1 }, { 522, 1 }, { 524, 1 }, { 526, 1 },
{ 528, 1 }, { 530, 1 }, { 532, 1 }, { 534, 1 },
{ 536, 1 }, { 538, 1 }, { 540, 1 }, { 542, 1 },
{ 700, 2 }, { 149, 1 }, { 800, 1 }, { 151, 1 },
{ 802, 1 }, { 153, 47 }));
CHECK_REPLACE(18, E({ 900, 1 }),
E({ 100, 2 }, { 600, 1 }, { 103, 2 }, { 300, 8 },
{ 308, 1 }, { 309, 1 }, { 205, 1 }, { 206, 1 },
{ 207, 1 }, { 900, 1 }, { 209, 1 }, { 400, 1 },
{ 800, 1 }, { 404, 1 }, { 406, 1 }, { 408, 1 },
{ 500, 1 }, { 502, 1 }, { 504, 1 }, { 506, 1 },
{ 508, 1 }, { 510, 1 }, { 512, 1 }, { 514, 1 },
{ 516, 1 }, { 518, 1 }, { 520, 1 }, { 522, 1 },
{ 524, 1 }, { 526, 1 }, { 528, 1 }, { 530, 1 },
{ 532, 1 }, { 534, 1 }, { 536, 1 }, { 538, 1 },
{ 540, 1 }, { 542, 1 }, { 700, 2 }, { 149, 1 },
{ 800, 1 }, { 151, 1 }, { 802, 1 }, { 153, 47 }));
CHECK_REPLACE(0, E({ 100, 100 }), E({ 100, 100 }));
CHECK_REPLACE(10, E({ 200, 90 }), E({ 100, 10 }, { 200, 90 }));
CHECK_REPLACE(0, E({ 300, 10 }), E({ 300, 10 }, { 200, 90 }));
CHECK_REPLACE(5, E({ 400, 1 }, { 400, 2 }),
E({ 300, 5 }, { 400, 1 }, { 400, 2 }, { 308, 2 }, { 200, 90 }));
hfs_ext_iter_t iter;
assert(!hfs_ext_find(&vnode, 0, &iter));
for (int i = 0; i < 32768; i += 8) {
HFSPlusExtentRecord r = { { i, 1 }, { i + 1, 1 }, { i + 2, 1 }, { i + 3, 1 },
{ i + 4, 1 }, { i + 5, 1 }, { i + 6, 1 }, { i + 7, 1 } };
hfs_ext_iter_update(&iter, r, 8, cat_extents);
}
ffork.ff_blocks = 32768;
ffork.ff_unallocblocks = 0;
assert(!hfs_ext_replace(&mount, &vnode, 0,
E({ 1, 8 }), 1, cat_extents));
printf("[PASSED] hfs_extents_test\n");
return 0;
}