2 * Copyright (c) 2014-2015 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include "hfs_extents_test.h"
30 #include "../core/hfs_extents.h"
32 typedef struct extent_group
{
33 LIST_ENTRY(extent_group
) chain
;
35 HFSPlusExtentRecord extents
;
39 int32_t BTDeleteRecord(filefork_t
*ff
,
40 BTreeIterator
*iterator
)
44 assert(!iterator
->group
->deleted
);
46 LIST_REMOVE(iterator
->group
, chain
);
48 iterator
->group
->deleted
= true;
53 errno_t
hfs_ext_iter_next_group(struct hfs_ext_iter
*iter
)
55 off_t offset
= (iter
->bt_iter
.key
.startBlock
+ iter
->group_block_count
) * 4096;
57 errno_t ret
= hfs_ext_find(iter
->vp
, offset
, iter
);
62 if (iter
->bt_iter
.key
.startBlock
!= offset
/ 4096)
68 errno_t
hfs_ext_iter_update(hfs_ext_iter_t
*iter
,
69 HFSPlusExtentDescriptor
*extents
,
71 HFSPlusExtentRecord cat_extents
)
73 if (count
% kHFSPlusExtentDensity
) {
74 // Zero out last group
75 int to_zero
= kHFSPlusExtentDensity
- (count
% 8);
76 bzero(&extents
[count
], to_zero
* sizeof(*extents
));
80 extent_group_t
*group
= NULL
;
82 if (!iter
->bt_iter
.key
.startBlock
) {
83 memcpy(cat_extents
, extents
, sizeof(HFSPlusExtentRecord
));
84 group
= LIST_FIRST(&iter
->vp
->ffork
->groups
);
85 hfs_ext_copy_rec(extents
, group
->extents
);
87 iter
->bt_iter
.key
.startBlock
+= hfs_total_blocks(extents
, 8);
93 LIST_FOREACH(next
, &iter
->vp
->ffork
->groups
, chain
) {
94 if (iter
->bt_iter
.key
.startBlock
< next
->start_block
)
100 iter
->bt_iter
.group
= NULL
;
103 extent_group_t
*new_group
= malloc(sizeof(*new_group
));
104 hfs_ext_copy_rec(extents
, new_group
->extents
);
105 new_group
->start_block
= iter
->bt_iter
.key
.startBlock
;
106 new_group
->deleted
= false;
109 LIST_INSERT_AFTER(group
, new_group
, chain
);
111 LIST_INSERT_HEAD(&iter
->vp
->ffork
->groups
, new_group
, chain
);
114 iter
->bt_iter
.key
.startBlock
+= hfs_total_blocks(extents
, 8);
123 errno_t
hfs_ext_find(vnode_t vp
, off_t offset
, hfs_ext_iter_t
*iter
)
126 const uint32_t block
= (uint32_t)(offset
/ 4096);
129 extent_group_t
*next
, *group
= NULL
;
130 LIST_FOREACH(next
, &vp
->ffork
->groups
, chain
) {
131 if (block
< next
->start_block
)
139 iter
->file_block
= iter
->bt_iter
.key
.startBlock
= group
->start_block
;
140 iter
->bt_iter
.group
= group
;
142 for (iter
->ndx
= 0; iter
->ndx
< 8; ++iter
->ndx
) {
143 HFSPlusExtentDescriptor
*ext
= &group
->extents
[iter
->ndx
];
144 if (iter
->file_block
+ ext
->blockCount
> block
) {
145 hfs_ext_copy_rec(group
->extents
, iter
->group
);
146 return hfs_ext_iter_check_group(iter
);
148 iter
->file_block
+= ext
->blockCount
;
154 void dump_extents(struct extent_groups
*groups
)
156 extent_group_t
*group
;
159 LIST_FOREACH(group
, groups
, chain
) {
160 for (int j
= 0; j
< 8; ++j
) {
161 HFSPlusExtentDescriptor
*ext
= &group
->extents
[j
];
162 if (!ext
->blockCount
) {
163 // Make sure all the reset are NULL
164 if (LIST_NEXT(group
, chain
))
167 for (int k
= j
; k
< 8; ++k
) {
168 if (group
->extents
[k
].startBlock
169 || group
->extents
[k
].blockCount
) {
179 printf("%s{ %u, %u }", (i
== 0 ? "" : i
% 4 == 0 ? ",\n" : ", "),
180 ext
->startBlock
, ext
->blockCount
);
187 void check_extents(unsigned line
, vnode_t vnode
,
188 const HFSPlusExtentDescriptor
*extents
, int count
)
191 extent_group_t
*group
;
192 LIST_FOREACH(group
, &vnode
->ffork
->groups
, chain
) {
193 if (group
->start_block
!= block
)
195 int cnt
= MIN(count
, 8);
196 if (memcmp(group
->extents
, extents
, cnt
* sizeof(*extents
)))
199 if (group
->extents
[cnt
].startBlock
|| group
->extents
[cnt
].blockCount
)
203 if ((count
-= 8) <= 0)
205 block
+= hfs_total_blocks(extents
, 8);
209 if (LIST_NEXT(group
, chain
))
216 printf("hfs_extents_test:%u: error: unexpected extents:\n", line
);
217 dump_extents(&vnode
->ffork
->groups
);
226 .hfs_extents_cp
= &cnode
,
230 .ff_unallocblocks
= 100,
231 .groups
= LIST_HEAD_INITIALIZER(ffork
.groups
),
233 struct vnode vnode
= {
240 extent_group_t group
= {
241 .extents
= { { 100, 100 } },
244 LIST_INSERT_HEAD(&ffork
.groups
, &group
, chain
);
246 HFSPlusExtentRecord cat_extents
;
248 #define E(...) (HFSPlusExtentDescriptor[]) { __VA_ARGS__ }
249 #define lengthof(x) (sizeof(x) / sizeof(*x))
251 #define CHECK_EXTENTS(...) \
253 HFSPlusExtentDescriptor extents_[] = __VA_ARGS__; \
254 check_extents(__LINE__, &vnode, extents_, \
255 lengthof(extents_)); \
258 #define CHECK_REPLACE(file_block, repl, expected) \
260 HFSPlusExtentDescriptor repl_[] = repl; \
261 hfs_ext_replace(&mount, &vnode, file_block, repl_, \
262 lengthof(repl_), cat_extents); \
263 CHECK_EXTENTS(expected); \
266 CHECK_REPLACE(10, E({ 200, 10 }),
267 E({ 100, 10 }, { 200, 10 }, { 120, 80 }));
269 CHECK_REPLACE(5, E({ 300, 10 }),
270 E({ 100, 5 }, { 300, 10 }, { 205, 5 }, { 120, 80 }));
272 CHECK_REPLACE(20, E({ 400, 1 }),
273 E({ 100, 5 }, { 300, 10 }, { 205, 5 }, { 400, 1 },
276 CHECK_REPLACE(21, E({ 402, 1 }),
277 E({ 100, 5 }, { 300, 10 }, { 205, 5 }, { 400, 1 },
278 { 402, 1 }, { 122, 78 }));
280 CHECK_REPLACE(22, E({ 404, 1 }),
281 E({ 100, 5 }, { 300, 10 }, { 205, 5 }, { 400, 1 },
282 { 402, 1 }, { 404, 1 }, { 123, 77 }));
284 CHECK_REPLACE(23, E({ 406, 1 }),
285 E({ 100, 5 }, { 300, 10 }, { 205, 5 }, { 400, 1 },
286 { 402, 1 }, { 404, 1 }, { 406, 1 }, { 124, 76 }));
288 CHECK_REPLACE(24, E({ 408, 1 }),
289 E({ 100, 5 }, { 300, 10 }, { 205, 5 }, { 400, 1 },
290 { 402, 1 }, { 404, 1 }, { 406, 1 }, { 408, 1 },
294 * OK, now we won't see any padding until we get the number of extents
295 * over 32. So let's get to that point now.
298 for (int i
= 25, j
= 500; i
< 25 + 24; ++i
, j
+= 2) {
299 hfs_ext_replace(&mount
, &vnode
, i
, E({ j
, 1 }),
303 CHECK_EXTENTS(E({ 100, 5 }, { 300, 10 }, { 205, 5 }, { 400, 1 },
304 { 402, 1 }, { 404, 1 }, { 406, 1 }, { 408, 1 },
305 { 500, 1 }, { 502, 1 }, { 504, 1 }, { 506, 1 },
306 { 508, 1 }, { 510, 1 }, { 512, 1 }, { 514, 1 },
307 { 516, 1 }, { 518, 1 }, { 520, 1 }, { 522, 1 },
308 { 524, 1 }, { 526, 1 }, { 528, 1 }, { 530, 1 },
309 { 532, 1 }, { 534, 1 }, { 536, 1 }, { 538, 1 },
310 { 540, 1 }, { 542, 1 }, { 544, 1 }, { 546, 1 },
314 * So if we replace an extent in the first group, we should see it pad using
315 * the 205 extent and some of the 300 extent.
318 CHECK_REPLACE(2, E({ 600, 1 }),
319 E({ 100, 2 }, { 600, 1 }, { 103, 2 }, { 300, 8 },
320 { 308, 1 }, { 309, 1 }, { 205, 1 }, { 206, 1 },
321 { 207, 1 }, { 208, 1 }, { 209, 1 }, { 400, 1 },
322 { 402, 1 }, { 404, 1 }, { 406, 1 }, { 408, 1 },
323 { 500, 1 }, { 502, 1 }, { 504, 1 }, { 506, 1 },
324 { 508, 1 }, { 510, 1 }, { 512, 1 }, { 514, 1 },
325 { 516, 1 }, { 518, 1 }, { 520, 1 }, { 522, 1 },
326 { 524, 1 }, { 526, 1 }, { 528, 1 }, { 530, 1 },
327 { 532, 1 }, { 534, 1 }, { 536, 1 }, { 538, 1 },
328 { 540, 1 }, { 542, 1 }, { 544, 1 }, { 546, 1 },
332 * Now try and test the case where it fails to pad and is forced to move
333 * to the next group. First, merge the 544 and 546 extents to reduce
334 * the number of extents by 1.
337 CHECK_REPLACE(47, E({ 700, 2 }),
338 E({ 100, 2 }, { 600, 1 }, { 103, 2 }, { 300, 8 },
339 { 308, 1 }, { 309, 1 }, { 205, 1 }, { 206, 1 },
340 { 207, 1 }, { 208, 1 }, { 209, 1 }, { 400, 1 },
341 { 402, 1 }, { 404, 1 }, { 406, 1 }, { 408, 1 },
342 { 500, 1 }, { 502, 1 }, { 504, 1 }, { 506, 1 },
343 { 508, 1 }, { 510, 1 }, { 512, 1 }, { 514, 1 },
344 { 516, 1 }, { 518, 1 }, { 520, 1 }, { 522, 1 },
345 { 524, 1 }, { 526, 1 }, { 528, 1 }, { 530, 1 },
346 { 532, 1 }, { 534, 1 }, { 536, 1 }, { 538, 1 },
347 { 540, 1 }, { 542, 1 }, { 700, 2 }, { 149, 51 }));
349 // Now force 207-209 to be merged
351 CHECK_REPLACE(21, E({ 800, 1 }),
352 E({ 100, 2 }, { 600, 1 }, { 103, 2 }, { 300, 8 },
353 { 308, 1 }, { 309, 1 }, { 205, 1 }, { 206, 1 },
354 { 207, 3 }, { 400, 1 }, { 800, 1 }, { 404, 1 },
355 { 406, 1 }, { 408, 1 }, { 500, 1 }, { 502, 1 },
356 { 504, 1 }, { 506, 1 }, { 508, 1 }, { 510, 1 },
357 { 512, 1 }, { 514, 1 }, { 516, 1 }, { 518, 1 },
358 { 520, 1 }, { 522, 1 }, { 524, 1 }, { 526, 1 },
359 { 528, 1 }, { 530, 1 }, { 532, 1 }, { 534, 1 },
360 { 536, 1 }, { 538, 1 }, { 540, 1 }, { 542, 1 },
361 { 700, 2 }, { 149, 51 }));
363 // Now let's push the last extent into a new group
365 CHECK_REPLACE(50, E({ 800, 1 }),
366 E({ 100, 2 }, { 600, 1 }, { 103, 2 }, { 300, 8 },
367 { 308, 1 }, { 309, 1 }, { 205, 1 }, { 206, 1 },
368 { 207, 3 }, { 400, 1 }, { 800, 1 }, { 404, 1 },
369 { 406, 1 }, { 408, 1 }, { 500, 1 }, { 502, 1 },
370 { 504, 1 }, { 506, 1 }, { 508, 1 }, { 510, 1 },
371 { 512, 1 }, { 514, 1 }, { 516, 1 }, { 518, 1 },
372 { 520, 1 }, { 522, 1 }, { 524, 1 }, { 526, 1 },
373 { 528, 1 }, { 530, 1 }, { 532, 1 }, { 534, 1 },
374 { 536, 1 }, { 538, 1 }, { 540, 1 }, { 542, 1 },
375 { 700, 2 }, { 149, 1 }, { 800, 1 }, { 151, 49 }));
377 CHECK_REPLACE(52, E({ 802, 1 }),
378 E({ 100, 2 }, { 600, 1 }, { 103, 2 }, { 300, 8 },
379 { 308, 1 }, { 309, 1 }, { 205, 1 }, { 206, 1 },
380 { 207, 3 }, { 400, 1 }, { 800, 1 }, { 404, 1 },
381 { 406, 1 }, { 408, 1 }, { 500, 1 }, { 502, 1 },
382 { 504, 1 }, { 506, 1 }, { 508, 1 }, { 510, 1 },
383 { 512, 1 }, { 514, 1 }, { 516, 1 }, { 518, 1 },
384 { 520, 1 }, { 522, 1 }, { 524, 1 }, { 526, 1 },
385 { 528, 1 }, { 530, 1 }, { 532, 1 }, { 534, 1 },
386 { 536, 1 }, { 538, 1 }, { 540, 1 }, { 542, 1 },
387 { 700, 2 }, { 149, 1 }, { 800, 1 }, { 151, 1 },
388 { 802, 1 }, { 153, 47 }));
391 * And now if we split the 207 extent, it will fail to pad within the
392 * 32 extents and so it will be forced to pull in more extents.
395 CHECK_REPLACE(18, E({ 900, 1 }),
396 E({ 100, 2 }, { 600, 1 }, { 103, 2 }, { 300, 8 },
397 { 308, 1 }, { 309, 1 }, { 205, 1 }, { 206, 1 },
398 { 207, 1 }, { 900, 1 }, { 209, 1 }, { 400, 1 },
399 { 800, 1 }, { 404, 1 }, { 406, 1 }, { 408, 1 },
400 { 500, 1 }, { 502, 1 }, { 504, 1 }, { 506, 1 },
401 { 508, 1 }, { 510, 1 }, { 512, 1 }, { 514, 1 },
402 { 516, 1 }, { 518, 1 }, { 520, 1 }, { 522, 1 },
403 { 524, 1 }, { 526, 1 }, { 528, 1 }, { 530, 1 },
404 { 532, 1 }, { 534, 1 }, { 536, 1 }, { 538, 1 },
405 { 540, 1 }, { 542, 1 }, { 700, 2 }, { 149, 1 },
406 { 800, 1 }, { 151, 1 }, { 802, 1 }, { 153, 47 }));
408 // Some tests covering replacing the beginning and end
410 CHECK_REPLACE(0, E({ 100, 100 }), E({ 100, 100 }));
411 CHECK_REPLACE(10, E({ 200, 90 }), E({ 100, 10 }, { 200, 90 }));
412 CHECK_REPLACE(0, E({ 300, 10 }), E({ 300, 10 }, { 200, 90 }));
414 // Test replacing with multiple extents
415 CHECK_REPLACE(5, E({ 400, 1 }, { 400, 2 }),
416 E({ 300, 5 }, { 400, 1 }, { 400, 2 }, { 308, 2 }, { 200, 90 }));
419 * Test an unlikely case where we have lots of extents that could
420 * be coalesced. When replacing extents here, we can't coalesce
421 * all of them because we will not be able to roll back.
424 // First, set things up
427 assert(!hfs_ext_find(&vnode
, 0, &iter
));
429 for (int i
= 0; i
< 32768; i
+= 8) {
430 HFSPlusExtentRecord r
= { { i
, 1 }, { i
+ 1, 1 }, { i
+ 2, 1 }, { i
+ 3, 1 },
431 { i
+ 4, 1 }, { i
+ 5, 1 }, { i
+ 6, 1 }, { i
+ 7, 1 } };
433 hfs_ext_iter_update(&iter
, r
, 8, cat_extents
);
436 ffork
.ff_blocks
= 32768;
437 ffork
.ff_unallocblocks
= 0;
440 * Now we have 32768 extents that could be coalesced. Check the
441 * following succeeds.
443 assert(!hfs_ext_replace(&mount
, &vnode
, 0,
444 E({ 1, 8 }), 1, cat_extents
));
446 printf("[PASSED] hfs_extents_test\n");