]>
Commit | Line | Data |
---|---|---|
558d2836 A |
1 | /* |
2 | * Copyright (c) 2014-2015 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
5 | * | |
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. | |
14 | * | |
15 | * Please obtain a copy of the License at | |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
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. | |
25 | * | |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | */ | |
28 | ||
29 | #include "hfs_extents_test.h" | |
30 | #include "../core/hfs_extents.h" | |
31 | ||
32 | typedef struct extent_group { | |
33 | LIST_ENTRY(extent_group) chain; | |
34 | uint32_t start_block; | |
35 | HFSPlusExtentRecord extents; | |
36 | bool deleted; | |
37 | } extent_group_t; | |
38 | ||
39 | int32_t BTDeleteRecord(filefork_t *ff, | |
40 | BTreeIterator *iterator) | |
41 | { | |
42 | (void)ff; | |
43 | ||
44 | assert(!iterator->group->deleted); | |
45 | ||
46 | LIST_REMOVE(iterator->group, chain); | |
47 | ||
48 | iterator->group->deleted = true; | |
49 | ||
50 | return 0; | |
51 | } | |
52 | ||
53 | errno_t hfs_ext_iter_next_group(struct hfs_ext_iter *iter) | |
54 | { | |
55 | off_t offset = (iter->bt_iter.key.startBlock + iter->group_block_count) * 4096; | |
56 | ||
57 | errno_t ret = hfs_ext_find(iter->vp, offset, iter); | |
58 | ||
59 | if (ret) | |
60 | return ret; | |
61 | ||
62 | if (iter->bt_iter.key.startBlock != offset / 4096) | |
63 | return ESTALE; | |
64 | ||
65 | return 0; | |
66 | } | |
67 | ||
68 | errno_t hfs_ext_iter_update(hfs_ext_iter_t *iter, | |
69 | HFSPlusExtentDescriptor *extents, | |
70 | int count, | |
71 | HFSPlusExtentRecord cat_extents) | |
72 | { | |
73 | if (count % kHFSPlusExtentDensity) { | |
74 | // Zero out last group | |
75 | int to_zero = kHFSPlusExtentDensity - (count % 8); | |
76 | bzero(&extents[count], to_zero * sizeof(*extents)); | |
77 | count += to_zero; | |
78 | } | |
79 | ||
80 | extent_group_t *group = NULL; | |
81 | ||
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); | |
86 | ||
87 | iter->bt_iter.key.startBlock += hfs_total_blocks(extents, 8); | |
88 | ||
89 | count -= 8; | |
90 | extents += 8; | |
91 | } else { | |
92 | extent_group_t *next; | |
93 | LIST_FOREACH(next, &iter->vp->ffork->groups, chain) { | |
94 | if (iter->bt_iter.key.startBlock < next->start_block) | |
95 | break; | |
96 | group = next; | |
97 | } | |
98 | } | |
99 | ||
100 | iter->bt_iter.group = NULL; | |
101 | ||
102 | while (count > 0) { | |
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; | |
107 | ||
108 | if (group) { | |
109 | LIST_INSERT_AFTER(group, new_group, chain); | |
110 | } else | |
111 | LIST_INSERT_HEAD(&iter->vp->ffork->groups, new_group, chain); | |
112 | group = new_group; | |
113 | ||
114 | iter->bt_iter.key.startBlock += hfs_total_blocks(extents, 8); | |
115 | ||
116 | count -= 8; | |
117 | extents += 8; | |
118 | } | |
119 | ||
120 | return 0; | |
121 | } | |
122 | ||
123 | errno_t hfs_ext_find(vnode_t vp, off_t offset, hfs_ext_iter_t *iter) | |
124 | { | |
125 | (void)vp; | |
126 | const uint32_t block = (uint32_t)(offset / 4096); | |
127 | ||
128 | iter->vp = vp; | |
129 | extent_group_t *next, *group = NULL; | |
130 | LIST_FOREACH(next, &vp->ffork->groups, chain) { | |
131 | if (block < next->start_block) | |
132 | break; | |
133 | group = next; | |
134 | } | |
135 | ||
136 | if (!group) | |
137 | return ENOENT; | |
138 | ||
139 | iter->file_block = iter->bt_iter.key.startBlock = group->start_block; | |
140 | iter->bt_iter.group = group; | |
141 | ||
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); | |
147 | } | |
148 | iter->file_block += ext->blockCount; | |
149 | } | |
150 | ||
151 | return ENOENT; | |
152 | } | |
153 | ||
154 | void dump_extents(struct extent_groups *groups) | |
155 | { | |
156 | extent_group_t *group; | |
157 | int i = 0; | |
158 | ||
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)) | |
165 | goto keep_going; | |
166 | ||
167 | for (int k = j; k < 8; ++k) { | |
168 | if (group->extents[k].startBlock | |
169 | || group->extents[k].blockCount) { | |
170 | goto keep_going; | |
171 | } | |
172 | } | |
173 | ||
174 | break; | |
175 | } | |
176 | ||
177 | keep_going: | |
178 | ||
179 | printf("%s{ %u, %u }", (i == 0 ? "" : i % 4 == 0 ? ",\n" : ", "), | |
180 | ext->startBlock, ext->blockCount); | |
181 | ++i; | |
182 | } | |
183 | } | |
184 | printf("\n"); | |
185 | } | |
186 | ||
187 | void check_extents(unsigned line, vnode_t vnode, | |
188 | const HFSPlusExtentDescriptor *extents, int count) | |
189 | { | |
190 | uint32_t block = 0; | |
191 | extent_group_t *group; | |
192 | LIST_FOREACH(group, &vnode->ffork->groups, chain) { | |
193 | if (group->start_block != block) | |
194 | goto bad; | |
195 | int cnt = MIN(count, 8); | |
196 | if (memcmp(group->extents, extents, cnt * sizeof(*extents))) | |
197 | goto bad; | |
198 | while (cnt < 8) { | |
199 | if (group->extents[cnt].startBlock || group->extents[cnt].blockCount) | |
200 | goto bad; | |
201 | ++cnt; | |
202 | } | |
203 | if ((count -= 8) <= 0) | |
204 | break; | |
205 | block += hfs_total_blocks(extents, 8); | |
206 | extents += 8; | |
207 | } | |
208 | ||
209 | if (LIST_NEXT(group, chain)) | |
210 | goto bad; | |
211 | ||
212 | return; | |
213 | ||
214 | bad: | |
215 | ||
216 | printf("hfs_extents_test:%u: error: unexpected extents:\n", line); | |
217 | dump_extents(&vnode->ffork->groups); | |
218 | exit(1); | |
219 | } | |
220 | ||
221 | int main(void) | |
222 | { | |
223 | cnode_t cnode = {}; | |
224 | hfsmount_t mount = { | |
225 | .blockSize = 4096, | |
226 | .hfs_extents_cp = &cnode, | |
227 | }; | |
228 | filefork_t ffork = { | |
229 | .ff_blocks = 200, | |
230 | .ff_unallocblocks = 100, | |
231 | .groups = LIST_HEAD_INITIALIZER(ffork.groups), | |
232 | }; | |
233 | struct vnode vnode = { | |
234 | .ffork = &ffork, | |
235 | .mount = &mount, | |
236 | .is_rsrc = false, | |
237 | .cnode = &cnode, | |
238 | }; | |
239 | ||
240 | extent_group_t group = { | |
241 | .extents = { { 100, 100 } }, | |
242 | }; | |
243 | ||
244 | LIST_INSERT_HEAD(&ffork.groups, &group, chain); | |
245 | ||
246 | HFSPlusExtentRecord cat_extents; | |
247 | ||
248 | #define E(...) (HFSPlusExtentDescriptor[]) { __VA_ARGS__ } | |
249 | #define lengthof(x) (sizeof(x) / sizeof(*x)) | |
250 | ||
251 | #define CHECK_EXTENTS(...) \ | |
252 | do { \ | |
253 | HFSPlusExtentDescriptor extents_[] = __VA_ARGS__; \ | |
254 | check_extents(__LINE__, &vnode, extents_, \ | |
255 | lengthof(extents_)); \ | |
256 | } while (0) | |
257 | ||
258 | #define CHECK_REPLACE(file_block, repl, expected) \ | |
259 | do { \ | |
260 | HFSPlusExtentDescriptor repl_[] = repl; \ | |
261 | hfs_ext_replace(&mount, &vnode, file_block, repl_, \ | |
262 | lengthof(repl_), cat_extents); \ | |
263 | CHECK_EXTENTS(expected); \ | |
264 | } while (0) | |
265 | ||
266 | CHECK_REPLACE(10, E({ 200, 10 }), | |
267 | E({ 100, 10 }, { 200, 10 }, { 120, 80 })); | |
268 | ||
269 | CHECK_REPLACE(5, E({ 300, 10 }), | |
270 | E({ 100, 5 }, { 300, 10 }, { 205, 5 }, { 120, 80 })); | |
271 | ||
272 | CHECK_REPLACE(20, E({ 400, 1 }), | |
273 | E({ 100, 5 }, { 300, 10 }, { 205, 5 }, { 400, 1 }, | |
274 | { 121, 79 })); | |
275 | ||
276 | CHECK_REPLACE(21, E({ 402, 1 }), | |
277 | E({ 100, 5 }, { 300, 10 }, { 205, 5 }, { 400, 1 }, | |
278 | { 402, 1 }, { 122, 78 })); | |
279 | ||
280 | CHECK_REPLACE(22, E({ 404, 1 }), | |
281 | E({ 100, 5 }, { 300, 10 }, { 205, 5 }, { 400, 1 }, | |
282 | { 402, 1 }, { 404, 1 }, { 123, 77 })); | |
283 | ||
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 })); | |
287 | ||
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 }, | |
291 | { 125, 75 })); | |
292 | ||
293 | /* | |
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. | |
296 | */ | |
297 | ||
298 | for (int i = 25, j = 500; i < 25 + 24; ++i, j += 2) { | |
299 | hfs_ext_replace(&mount, &vnode, i, E({ j, 1 }), | |
300 | 1, cat_extents); | |
301 | } | |
302 | ||
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 }, | |
311 | { 149, 51 })); | |
312 | ||
313 | /* | |
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. | |
316 | */ | |
317 | ||
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 }, | |
329 | { 149, 51 })); | |
330 | ||
331 | /* | |
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. | |
335 | */ | |
336 | ||
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 })); | |
348 | ||
349 | // Now force 207-209 to be merged | |
350 | ||
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 })); | |
362 | ||
363 | // Now let's push the last extent into a new group | |
364 | ||
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 })); | |
376 | ||
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 })); | |
389 | ||
390 | /* | |
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. | |
393 | */ | |
394 | ||
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 })); | |
407 | ||
408 | // Some tests covering replacing the beginning and end | |
409 | ||
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 })); | |
413 | ||
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 })); | |
417 | ||
418 | /* | |
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. | |
422 | */ | |
423 | ||
424 | // First, set things up | |
425 | hfs_ext_iter_t iter; | |
426 | ||
427 | assert(!hfs_ext_find(&vnode, 0, &iter)); | |
428 | ||
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 } }; | |
432 | ||
433 | hfs_ext_iter_update(&iter, r, 8, cat_extents); | |
434 | } | |
435 | ||
436 | ffork.ff_blocks = 32768; | |
437 | ffork.ff_unallocblocks = 0; | |
438 | ||
439 | /* | |
440 | * Now we have 32768 extents that could be coalesced. Check the | |
441 | * following succeeds. | |
442 | */ | |
443 | assert(!hfs_ext_replace(&mount, &vnode, 0, | |
444 | E({ 1, 8 }), 1, cat_extents)); | |
445 | ||
446 | printf("[PASSED] hfs_extents_test\n"); | |
447 | ||
448 | return 0; | |
449 | } |