]>
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 | // Radar Component: HFS | X | |
30 | ||
31 | #include <sys/param.h> | |
32 | ||
33 | #include <sys/disk.h> | |
34 | #include <stdint.h> | |
35 | #include <stdbool.h> | |
36 | #include <errno.h> | |
37 | #include <stdlib.h> | |
38 | #include <libkern/OSByteOrder.h> | |
39 | #include <hfs/hfs_format.h> | |
40 | ||
41 | #include "test-utils.h" | |
42 | ||
43 | // For panic | |
44 | #include <mach/mach.h> | |
45 | ||
46 | #include <stdio.h> | |
47 | ||
48 | #define HFS_ALLOC_TEST 1 | |
49 | #define RANGELIST_TEST 1 | |
50 | #define KERNEL 1 | |
51 | #define HFS 1 | |
52 | #define DEBUG 1 | |
53 | ||
54 | #include "../core/rangelist.h" | |
55 | ||
56 | #define SYSCTL_DECL(a) | |
57 | #define SYSCTL_NODE(a, b, c, d, e, f) | |
58 | #define SYSCTL_INT(a, b, c, d, e, f, g) | |
59 | ||
60 | typedef int16_t OSErr; | |
61 | ||
62 | typedef struct cnode { | |
63 | uint32_t c_blocks; | |
64 | } cnode_t; | |
65 | ||
66 | typedef struct journal { | |
67 | ||
68 | ||
69 | } journal; | |
70 | ||
71 | #define HFS_READ_ONLY 0x00001 | |
72 | #define HFS_METADATA_ZONE 0x00080 | |
73 | #define HFS_HAS_SPARSE_DEVICE 0x00400 | |
74 | #define HFS_UNMAP 0x200000 | |
75 | #define HFS_SUMMARY_TABLE 0x800000 | |
76 | #define HFS_CS 0x1000000 | |
77 | ||
78 | #define SFL_BITMAP 0x0004 | |
79 | ||
80 | enum hfs_locktype { | |
81 | HFS_SHARED_LOCK = 1, | |
82 | HFS_EXCLUSIVE_LOCK = 2 | |
83 | }; | |
84 | ||
85 | typedef struct vnode { | |
86 | } *vnode_t; | |
87 | ||
88 | #define kMaxFreeExtents 10 | |
89 | ||
90 | typedef struct hfsmount { | |
91 | cnode_t *hfs_allocation_cp; | |
92 | uint32_t blockSize; | |
93 | struct journal *jnl; | |
94 | uint64_t hfs_logical_bytes; | |
95 | uint32_t hfsPlusIOPosOffset; | |
96 | uint8_t vcbVN[256]; | |
97 | uint32_t hfs_flags; | |
98 | int32_t hfs_raw_dev; /* device mounted */ | |
99 | uint32_t totalBlocks, allocLimit, freeBlocks, tentativeBlocks, lockedBlocks; | |
100 | uint32_t sparseAllocation, nextAllocation; | |
101 | uint32_t hfs_metazone_start, hfs_metazone_end; | |
102 | uint32_t vcbFreeExtCnt; | |
103 | u_int32_t hfs_freed_block_count; | |
104 | int16_t vcbFlags; | |
105 | uint32_t vcbVBMIOSize; | |
106 | vnode_t hfs_allocation_vp; | |
107 | uint16_t vcbSigWord; | |
108 | HFSPlusExtentDescriptor vcbFreeExt[kMaxFreeExtents]; | |
109 | uint8_t *hfs_summary_table; | |
110 | uint32_t hfs_summary_size; | |
111 | uint32_t hfs_summary_bytes; /* number of BYTES in summary table */ | |
112 | struct rl_head hfs_reserved_ranges[2]; | |
113 | } hfsmount_t; | |
114 | ||
115 | typedef hfsmount_t ExtendedVCB; | |
116 | ||
117 | typedef struct _dk_cs_map { | |
118 | dk_extent_t cm_extent; | |
119 | uint64_t cm_bytes_mapped; | |
120 | } _dk_cs_map_t; | |
121 | ||
122 | struct jnl_trim_list { | |
123 | uint32_t allocated_count; | |
124 | uint32_t extent_count; | |
125 | dk_extent_t *extents; | |
126 | }; | |
127 | ||
128 | typedef enum hfs_flush_mode { | |
129 | HFS_FLUSH_JOURNAL, // Flush journal | |
130 | HFS_FLUSH_JOURNAL_META, // Flush journal and metadata blocks | |
131 | HFS_FLUSH_FULL, // Flush journal and does a cache flush | |
132 | HFS_FLUSH_CACHE, // Flush track cache to media | |
133 | HFS_FLUSH_BARRIER, // Barrier-only flush to ensure write order | |
134 | } hfs_flush_mode_t; | |
135 | ||
136 | typedef bool Boolean; | |
137 | ||
138 | int hfs_isallocated(struct hfsmount *hfsmp, uint32_t startingBlock, | |
139 | uint32_t numBlocks); | |
140 | ||
141 | static int journal_trim_add_extent(__unused journal *jnl, | |
142 | __unused uint64_t offset, | |
143 | __unused uint64_t length) | |
144 | { | |
145 | return 0; | |
146 | } | |
147 | ||
148 | static int journal_trim_remove_extent(__unused journal *jnl, | |
149 | __unused uint64_t offset, | |
150 | __unused uint64_t length) | |
151 | { | |
152 | return 0; | |
153 | } | |
154 | ||
155 | static int journal_trim_extent_overlap(__unused journal *jnl, | |
156 | __unused uint64_t offset, | |
157 | __unused uint64_t length, | |
158 | __unused uint64_t *end) | |
159 | { | |
160 | return 0; | |
161 | } | |
162 | ||
163 | int | |
164 | hfs_systemfile_lock(__unused struct hfsmount *hfsmp, __unused int flags, __unused enum hfs_locktype locktype) | |
165 | { | |
166 | return 0; | |
167 | } | |
168 | ||
169 | void | |
170 | hfs_systemfile_unlock(__unused struct hfsmount *hfsmp, __unused int flags) | |
171 | { | |
172 | ||
173 | } | |
174 | ||
175 | typedef struct vfs_context * vfs_context_t; | |
176 | ||
177 | #define VNOP_IOCTL(a, b, c, d, e) ((void)c, 0) | |
178 | ||
179 | #define VCBTOHFS(x) (x) | |
180 | ||
181 | u_int32_t hfs_freeblks(struct hfsmount * hfsmp, __unused int wantreserve) | |
182 | { | |
183 | return hfsmp->freeBlocks - hfsmp->lockedBlocks; | |
184 | } | |
185 | ||
186 | enum { | |
187 | noErr = 0, | |
188 | dskFulErr = -34, /*disk full*/ | |
189 | bdNamErr = -37, /*there may be no bad names in the final system!*/ | |
190 | paramErr = -50, /*error in user parameter list*/ | |
191 | memFullErr = -108, /*Not enough room in heap zone*/ | |
192 | fileBoundsErr = -1309, /*file's EOF, offset, mark or size is too big*/ | |
193 | kTECUsedFallbacksStatus = -8783, | |
194 | }; | |
195 | ||
196 | static void hfs_lock_mount(__unused struct hfsmount *hfsmp) | |
197 | { | |
198 | } | |
199 | static void hfs_unlock_mount(__unused struct hfsmount *hfsmp) | |
200 | { | |
201 | } | |
202 | ||
203 | OSErr BlockDeallocate (ExtendedVCB *vcb, // Which volume to deallocate space on | |
204 | u_int32_t firstBlock, // First block in range to deallocate | |
205 | u_int32_t numBlocks, // Number of contiguous blocks to deallocate | |
206 | u_int32_t flags); | |
207 | ||
208 | #define lck_spin_lock(x) ((void)0) | |
209 | #define lck_spin_unlock(x) ((void)0) | |
210 | ||
211 | static void HFS_UPDATE_NEXT_ALLOCATION(hfsmount_t *hfsmp, | |
212 | uint32_t new_nextAllocation) | |
213 | { | |
214 | hfsmp->nextAllocation = new_nextAllocation; | |
215 | } | |
216 | ||
217 | static void MarkVCBDirty(ExtendedVCB *vcb) | |
218 | { | |
219 | vcb->vcbFlags |= 0xFF00; | |
220 | } | |
221 | ||
222 | #define hfs_generate_volume_notifications(x) ((void)0) | |
223 | #define REQUIRE_FILE_LOCK(a, b) ((void)0) | |
224 | #define journal_modify_block_start(a, b) (0) | |
225 | #define journal_modify_block_end(a, b, c, d) (0) | |
226 | ||
227 | #define SWAP_BE32(x) OSSwapBigToHostInt32(x) | |
228 | ||
229 | typedef enum { | |
230 | HFS_INCONSISTENCY_DETECTED, | |
231 | ||
232 | // Used when unable to rollback an operation that failed | |
233 | HFS_ROLLBACK_FAILED, | |
234 | ||
235 | // Used when the latter part of an operation failed, but we chose not to roll back | |
236 | HFS_OP_INCOMPLETE, | |
237 | ||
238 | // Used when someone told us to force an fsck on next mount | |
239 | HFS_FSCK_FORCED, | |
240 | } hfs_inconsistency_reason_t; | |
241 | ||
242 | static void hfs_mark_inconsistent(__unused struct hfsmount *hfsmp, | |
243 | __unused hfs_inconsistency_reason_t reason) | |
244 | { | |
245 | assert(false); | |
246 | } | |
247 | ||
248 | static int journal_request_immediate_flush(__unused journal *jnl) | |
249 | { | |
250 | return 0; | |
251 | } | |
252 | ||
253 | enum { | |
254 | // These are indices into the array below | |
255 | ||
256 | // Tentative ranges can be claimed back at any time | |
257 | HFS_TENTATIVE_BLOCKS = 0, | |
258 | ||
259 | // Locked ranges cannot be claimed back, but the allocation | |
260 | // won't have been written to disk yet | |
261 | HFS_LOCKED_BLOCKS = 1, | |
262 | }; | |
263 | ||
264 | static inline __attribute__((const)) | |
265 | off_t hfs_blk_to_bytes(uint32_t blk, uint32_t blk_size) | |
266 | { | |
267 | return (off_t)blk * blk_size; // Avoid the overflow | |
268 | } | |
269 | ||
270 | typedef unsigned char Str31[32]; | |
271 | #define EXTERN_API_C(x) extern x | |
272 | typedef const unsigned char * ConstUTF8Param; | |
273 | #define CALLBACK_API_C(_type, _name) _type ( * _name) | |
274 | typedef struct vnode* FileReference; | |
275 | typedef struct filefork FCB; | |
276 | typedef int64_t daddr64_t; | |
277 | ||
278 | #include "../core/FileMgrInternal.h" | |
279 | ||
280 | /* | |
281 | * Map HFS Common errors (negative) to BSD error codes (positive). | |
282 | * Positive errors (ie BSD errors) are passed through unchanged. | |
283 | */ | |
284 | short MacToVFSError(OSErr err) | |
285 | { | |
286 | if (err >= 0) | |
287 | return err; | |
288 | ||
289 | /* BSD/VFS internal errnos */ | |
290 | #if 0 | |
291 | switch (err) { | |
292 | case ERESERVEDNAME: /* -8 */ | |
293 | return err; | |
294 | } | |
295 | #endif | |
296 | ||
297 | switch (err) { | |
298 | case dskFulErr: /* -34 */ | |
299 | case btNoSpaceAvail: /* -32733 */ | |
300 | return ENOSPC; | |
301 | case fxOvFlErr: /* -32750 */ | |
302 | return EOVERFLOW; | |
303 | ||
304 | case btBadNode: /* -32731 */ | |
305 | return EIO; | |
306 | ||
307 | case memFullErr: /* -108 */ | |
308 | return ENOMEM; /* +12 */ | |
309 | ||
310 | case cmExists: /* -32718 */ | |
311 | case btExists: /* -32734 */ | |
312 | return EEXIST; /* +17 */ | |
313 | ||
314 | case cmNotFound: /* -32719 */ | |
315 | case btNotFound: /* -32735 */ | |
316 | return ENOENT; /* 28 */ | |
317 | ||
318 | case cmNotEmpty: /* -32717 */ | |
319 | return ENOTEMPTY; /* 66 */ | |
320 | ||
321 | case cmFThdDirErr: /* -32714 */ | |
322 | return EISDIR; /* 21 */ | |
323 | ||
324 | case fxRangeErr: /* -32751 */ | |
325 | return ERANGE; | |
326 | ||
327 | case bdNamErr: /* -37 */ | |
328 | return ENAMETOOLONG; /* 63 */ | |
329 | ||
330 | case paramErr: /* -50 */ | |
331 | case fileBoundsErr: /* -1309 */ | |
332 | return EINVAL; /* +22 */ | |
333 | ||
334 | #if 0 | |
335 | case fsBTBadNodeSize: | |
336 | return ENXIO; | |
337 | #endif | |
338 | ||
339 | default: | |
340 | return EIO; /* +5 */ | |
341 | } | |
342 | } | |
343 | ||
344 | #define min(a, b) \ | |
345 | ({ typeof(a) a_ = (a); typeof(b) b_ = (b); a_ < b_ ? a_ : b_; }) | |
346 | ||
347 | errno_t hfs_find_free_extents(struct hfsmount *hfsmp, | |
348 | void (*callback)(void *data, off_t), void *callback_arg); | |
349 | ||
350 | static void hfs_journal_lock(__unused hfsmount_t *hfsmp) | |
351 | { | |
352 | } | |
353 | ||
354 | static errno_t hfs_flush(__unused hfsmount_t *hfsmp, __unused hfs_flush_mode_t x) | |
355 | { | |
356 | return 0; | |
357 | } | |
358 | ||
359 | static void hfs_journal_unlock(__unused hfsmount_t *hfsmp) | |
360 | { | |
361 | } | |
362 | ||
363 | typedef struct BTreeIterator { } BTreeIterator; | |
364 | ||
365 | #define HFS_SYSCTL(...) | |
366 | ||
367 | static void *hfs_malloc(size_t size) | |
368 | { | |
369 | return malloc(size); | |
370 | } | |
371 | ||
372 | static void hfs_free(void *ptr, __unused size_t size) | |
373 | { | |
374 | return free(ptr); | |
375 | } | |
376 | ||
377 | static void *hfs_mallocz(size_t size) | |
378 | { | |
379 | return calloc(1, size); | |
380 | } | |
381 | ||
382 | bool panic_on_assert = true; | |
383 | ||
384 | void hfs_assert_fail(const char *file, unsigned line, const char *expr) | |
385 | { | |
386 | assert_fail_(file, line, "%s", expr); | |
387 | } | |
388 | ||
389 | #include "../core/VolumeAllocation.c" | |
390 | #include "../core/rangelist.c" | |
391 | ||
392 | static void *bitmap; | |
393 | ||
394 | typedef struct buf { | |
395 | void *ptr; | |
396 | int size; | |
397 | void *fsprivate; | |
398 | bool is_shadow; | |
399 | } *buf_t; | |
400 | ||
401 | errno_t buf_bdwrite(__unused buf_t bp) | |
402 | { | |
403 | return 0; | |
404 | } | |
405 | ||
406 | void buf_brelse(buf_t bp) | |
407 | { | |
408 | if (bp->is_shadow) | |
409 | free(bp->ptr); | |
410 | free(bp); | |
411 | } | |
412 | ||
413 | uint32_t buf_count(__unused buf_t bp) | |
414 | { | |
415 | return bp->size; | |
416 | } | |
417 | ||
418 | uintptr_t buf_dataptr(__unused buf_t bp) | |
419 | { | |
420 | return (uintptr_t)bp->ptr; | |
421 | } | |
422 | ||
423 | int32_t buf_flags(__unused buf_t bp) | |
424 | { | |
425 | return 0; | |
426 | } | |
427 | ||
428 | void buf_markinvalid(__unused buf_t bp) | |
429 | { | |
430 | } | |
431 | ||
432 | errno_t buf_meta_bread(__unused vnode_t vp, daddr64_t blkno, int size, | |
433 | __unused kauth_cred_t cred, buf_t *bpp) | |
434 | { | |
435 | buf_t bp = calloc(1, sizeof(struct buf)); | |
436 | ||
437 | bp->ptr = bitmap + blkno * 4096; | |
438 | bp->size = size; | |
439 | ||
440 | *bpp = bp; | |
441 | ||
442 | return 0; | |
443 | } | |
444 | ||
445 | buf_t | |
446 | buf_create_shadow(buf_t bp, boolean_t force_copy, | |
447 | uintptr_t external_storage, | |
448 | __unused void (*iodone)(buf_t, void *), __unused void *arg) | |
449 | { | |
450 | assert(force_copy && !external_storage); | |
451 | ||
452 | buf_t new_bp = calloc(1, sizeof(struct buf)); | |
453 | ||
454 | new_bp->ptr = malloc(bp->size); | |
455 | ||
456 | memcpy(new_bp->ptr, bp->ptr, bp->size); | |
457 | new_bp->size = bp->size; | |
458 | new_bp->is_shadow = true; | |
459 | ||
460 | return new_bp; | |
461 | } | |
462 | ||
463 | void *buf_fsprivate(buf_t bp) | |
464 | { | |
465 | return bp->fsprivate; | |
466 | } | |
467 | ||
468 | void buf_setflags(__unused buf_t bp, __unused int32_t flags) | |
469 | { | |
470 | } | |
471 | ||
472 | void buf_setfsprivate(buf_t bp, void *fsprivate) | |
473 | { | |
474 | bp->fsprivate = fsprivate; | |
475 | } | |
476 | ||
477 | unsigned int kdebug_enable; | |
478 | ||
479 | void kernel_debug(__unused uint32_t debugid, | |
480 | __unused uintptr_t arg1, | |
481 | __unused uintptr_t arg2, | |
482 | __unused uintptr_t arg3, | |
483 | __unused uintptr_t arg4, | |
484 | __unused uintptr_t arg5) | |
485 | { | |
486 | } | |
487 | ||
488 | int | |
489 | buf_invalidateblks(__unused vnode_t vp, | |
490 | __unused int flags, | |
491 | __unused int slpflag, | |
492 | __unused int slptimeo) | |
493 | { | |
494 | return 0; | |
495 | } | |
496 | ||
497 | #define BITMAP_CHUNK_SIZE 80 | |
498 | ||
499 | errno_t get_more_bits(bitmap_context_t *bitmap_ctx) | |
500 | { | |
501 | uint32_t start_bit; | |
502 | uint32_t iosize = 0; | |
503 | uint32_t last_bitmap_block; | |
504 | ||
505 | start_bit = bitmap_ctx->run_offset; | |
506 | ||
507 | if (start_bit >= bitmap_ctx->hfsmp->totalBlocks) { | |
508 | bitmap_ctx->chunk_end = 0; | |
509 | bitmap_ctx->bitmap = NULL; | |
510 | return 0; | |
511 | } | |
512 | ||
513 | iosize = BITMAP_CHUNK_SIZE; | |
514 | last_bitmap_block = start_bit + iosize; | |
515 | ||
516 | if (last_bitmap_block > bitmap_ctx->hfsmp->totalBlocks) | |
517 | last_bitmap_block = bitmap_ctx->hfsmp->totalBlocks; | |
518 | ||
519 | bitmap_ctx->chunk_current = 0; | |
520 | bitmap_ctx->chunk_end = last_bitmap_block - start_bit; | |
521 | if (bitmap_ctx->run_offset != 0) | |
522 | bitmap_ctx->bitmap += iosize / 8; | |
523 | ||
524 | return 0; | |
525 | } | |
526 | ||
527 | static errno_t update_summary_table(__unused bitmap_context_t *bitmap_ctx, | |
528 | __unused uint32_t start, | |
529 | __unused uint32_t count, | |
530 | __unused bool set) | |
531 | { | |
532 | return 0; | |
533 | } | |
534 | ||
535 | int | |
536 | hfs_find_free_extents_test(hfsmount_t *hfsmp) | |
537 | { | |
538 | uint8_t bitmap[] = { | |
539 | /* 0: */ 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, | |
540 | /* 64: */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
541 | /* 128: */ 0xff, 0xfe, 0xcf, 0xff, 0xff, 0xff, 0xff, 0x00, | |
542 | /* 192: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
543 | /* 256: */ 0xff, 0xfe, 0xcf, 0xff, 0xff, 0xff, 0xff, 0x00, | |
544 | }; | |
545 | ||
546 | assert(bit_count_set(bitmap, 0, 32) == 15); | |
547 | assert(bit_count_clr(bitmap, 15, 64) == 3); | |
548 | assert(bit_count_set(bitmap, 48, 160) == 16 + 64 + 15); | |
549 | assert(bit_count_set(bitmap, 48, 320) == 16 + 64 + 15); | |
550 | assert(bit_count_clr(bitmap, 190, 260) == 2 + 64); | |
551 | assert(bit_count_clr(bitmap, 190, 320) == 2 + 64); | |
552 | assert(bit_count_clr(bitmap, 316, 320) == 4); | |
553 | ||
554 | hfsmp->totalBlocks = sizeof(bitmap) * 8; | |
555 | ||
556 | struct bitmap_context ctx = { | |
557 | .hfsmp = hfsmp, | |
558 | .bitmap = bitmap, | |
559 | }; | |
560 | ||
561 | uint32_t count; | |
562 | ||
563 | assert(!hfs_bit_count_set(&ctx, &count) && count == 15); | |
564 | assert(!hfs_bit_count_clr(&ctx, &count) && count == 3); | |
565 | assert(!hfs_bit_count_set(&ctx, &count) && count == 125); | |
566 | assert(!hfs_bit_count_clr(&ctx, &count) && count == 1); | |
567 | assert(!hfs_bit_count_set(&ctx, &count) && count == 2); | |
568 | assert(!hfs_bit_count_clr(&ctx, &count) && count == 2); | |
569 | assert(!hfs_bit_count_set(&ctx, &count) && count == 36); | |
570 | assert(!hfs_bit_count_clr(&ctx, &count) && count == 72); | |
571 | assert(!hfs_bit_count_set(&ctx, &count) && count == 15); | |
572 | assert(!hfs_bit_count_clr(&ctx, &count) && count == 1); | |
573 | assert(!hfs_bit_count_set(&ctx, &count) && count == 2); | |
574 | assert(!hfs_bit_count_clr(&ctx, &count) && count == 2); | |
575 | assert(!hfs_bit_count_set(&ctx, &count) && count == 36); | |
576 | assert(!hfs_bit_count_clr(&ctx, &count) && count == 8); | |
577 | ||
578 | assert(hfs_bit_offset(&ctx) == 320); | |
579 | ||
580 | return 0; | |
581 | } | |
582 | ||
583 | int main(void) | |
584 | { | |
585 | const int blocks = 100000; | |
586 | ||
587 | size_t bitmap_size = howmany(blocks, 8); | |
588 | bitmap = calloc(1, bitmap_size); | |
589 | ||
590 | cnode_t alloc_cp = { | |
591 | .c_blocks = howmany(bitmap_size, 4096), | |
592 | }; | |
593 | ||
594 | struct journal jnl; | |
595 | ||
596 | struct hfsmount mnt = { | |
597 | .allocLimit = blocks, | |
598 | .totalBlocks = blocks, | |
599 | .freeBlocks = blocks, | |
600 | .blockSize = 4096, | |
601 | .vcbVBMIOSize = 4096, | |
602 | .hfs_allocation_cp = &alloc_cp, | |
603 | .vcbSigWord = kHFSPlusSigWord, | |
604 | .jnl = &jnl, | |
605 | .hfs_logical_bytes = blocks * 4096, | |
606 | }; | |
607 | ||
608 | assert(!hfs_init_summary (&mnt)); | |
609 | ||
610 | uint32_t start, count; | |
611 | assert(!BlockAllocate(&mnt, 0, 10, 10, 0, &start, &count) | |
612 | && start == 0 && count == 10); | |
613 | assert(!BlockAllocate(&mnt, 0, 10, 10, 0, &start, &count) | |
614 | && start == 10 && count == 10); | |
615 | assert(!BlockAllocate(&mnt, 0, 32768 - 20, 32768 - 20, 0, &start, &count) | |
616 | && start == 20 && count == 32768 - 20); | |
617 | ||
618 | assert(!ScanUnmapBlocks(&mnt)); | |
619 | ||
620 | assert(!hfs_find_summary_free(&mnt, 0, &start) && start == 32768); | |
621 | ||
622 | assert(!BlockAllocate(&mnt, 0, blocks - 32768, blocks - 32768, 0, | |
623 | &start, &count) | |
624 | && start == 32768 && count == blocks - 32768); | |
625 | ||
626 | assert(BlockAllocate(&mnt, 0, 1, 1, 0, &start, &count) == dskFulErr); | |
627 | ||
628 | assert(!BlockDeallocate(&mnt, 1, 1, 0)); | |
629 | assert(!BlockDeallocate(&mnt, 3, 1, 0)); | |
630 | ||
631 | assert(!hfs_find_summary_free(&mnt, 0, &start) && start == 0); | |
632 | assert(!hfs_find_summary_free(&mnt, 1, &start) && start == 1); | |
633 | assert(!hfs_find_summary_free(&mnt, 32767, &start) && start == 32767); | |
634 | ||
635 | assert(BlockAllocate(&mnt, 0, 2, 2, HFS_ALLOC_FORCECONTIG, | |
636 | &start, &count) == dskFulErr); | |
637 | ||
638 | // The last block never gets marked | |
639 | assert(!hfs_find_summary_free(&mnt, 32768, &start) && start == 98304); | |
640 | ||
641 | assert(!BlockDeallocate(&mnt, 33000, 1, 0)); | |
642 | ||
643 | assert(!hfs_find_summary_free(&mnt, 32768, &start) && start == 32768); | |
644 | assert(!hfs_find_summary_free(&mnt, 65535, &start) && start == 65535); | |
645 | assert(!hfs_find_summary_free(&mnt, 65536, &start) && start == 98304); | |
646 | ||
647 | assert(!BlockDeallocate(&mnt, 33001, 1, 0)); | |
648 | assert(!BlockAllocate(&mnt, 0, 2, 2, HFS_ALLOC_FORCECONTIG, | |
649 | &start, &count) && start == 33000 && count == 2); | |
650 | ||
651 | // Test tentative allocations | |
652 | ||
653 | HFSPlusExtentDescriptor ext = { | |
654 | .blockCount = 1, | |
655 | }; | |
656 | ||
657 | struct rl_entry *reservation = NULL; | |
658 | ||
659 | hfs_alloc_extra_args_t args = { | |
660 | .max_blocks = 2, | |
661 | .reservation_out = &reservation, | |
662 | }; | |
663 | ||
664 | assert(!hfs_block_alloc(&mnt, &ext, HFS_ALLOC_TENTATIVE, &args) | |
665 | && ext.startBlock == 1 && ext.blockCount == 1); | |
666 | ||
667 | assert(rl_len(reservation) == 1); | |
668 | ||
669 | // This shouldn't use the tentative block | |
670 | assert(!BlockAllocate(&mnt, 0, 1, 1, 0, &start, &count) | |
671 | && start == 3 && count == 1); | |
672 | ||
673 | // This should steal the tentative block | |
674 | assert(!BlockAllocate(&mnt, 0, 1, 1, 0, &start, &count) | |
675 | && start == 1 && count == 1); | |
676 | assert(reservation->rl_start == -1 && reservation->rl_end == -2); | |
677 | ||
678 | // Free 200 | |
679 | assert(!BlockDeallocate(&mnt, 32700, 200, 0)); | |
680 | ||
681 | // Make 100 tentative | |
682 | ext.blockCount = 100; | |
683 | ||
684 | args.max_blocks = 100; | |
685 | assert(!hfs_block_alloc(&mnt, &ext, HFS_ALLOC_TENTATIVE, &args) | |
686 | && ext.startBlock == 32700 && ext.blockCount == 100); | |
687 | ||
688 | // This should allocate the other 100 | |
689 | assert(!BlockAllocate(&mnt, 0, 100, 100, 0, &start, &count) | |
690 | && start == 32800 && count == 100); | |
691 | ||
692 | assert(mnt.tentativeBlocks == 100); | |
693 | ||
694 | // Allocate 25 in the middle of the tentative block | |
695 | assert(!BlockAllocate(&mnt, 32750, 25, 25, 0, &start, &count) | |
696 | && start == 32750 && count == 25); | |
697 | ||
698 | // That should have split the reservation | |
699 | assert(reservation->rl_start == 32700 && reservation->rl_end == 32749); | |
700 | ||
701 | assert(mnt.tentativeBlocks == 50); | |
702 | ||
703 | // The tail should have been freed | |
704 | assert(mnt.vcbFreeExtCnt == 1 | |
705 | && mnt.vcbFreeExt[0].startBlock == 32775 | |
706 | && mnt.vcbFreeExt[0].blockCount == 25); | |
707 | ||
708 | // Allocate the bit we just freed | |
709 | assert(!BlockAllocate(&mnt, 32705, 1, 25, HFS_ALLOC_FORCECONTIG, | |
710 | &start, &count) | |
711 | && start == 32775 && count == 25); | |
712 | ||
713 | // Split the tentative reservation again | |
714 | assert(!BlockAllocate(&mnt, 32705, 1, 3, 0, | |
715 | &start, &count) | |
716 | && start == 32705 && count == 3); | |
717 | ||
718 | // This time head should have been free | |
719 | assert(mnt.vcbFreeExtCnt == 1 | |
720 | && mnt.vcbFreeExt[0].startBlock == 32700 | |
721 | && mnt.vcbFreeExt[0].blockCount == 5); | |
722 | ||
723 | // Reservation will be tail | |
724 | assert(reservation->rl_start == 32708 && reservation->rl_end == 32749); | |
725 | ||
726 | assert(mnt.tentativeBlocks == 42); | |
727 | ||
728 | // Free up what we just allocated | |
729 | assert(!BlockDeallocate(&mnt, 32705, 3, 0)); | |
730 | ||
731 | // This should allocate something overlapping the start of the reservation | |
732 | assert(!BlockAllocate(&mnt, 0, 15, 15, HFS_ALLOC_FORCECONTIG, | |
733 | &start, &count) | |
734 | && start == 32700 && count == 15); | |
735 | ||
736 | // Should have eaten into start of reservation | |
737 | assert(reservation->rl_start == 32715 && reservation->rl_end == 32749); | |
738 | ||
739 | // Free up a bit at the end | |
740 | assert(!BlockDeallocate(&mnt, 32750, 5, 0)); | |
741 | ||
742 | // Allocate a bit overlapping end of reservation | |
743 | assert(!BlockAllocate(&mnt, 32740, 15, 15, HFS_ALLOC_FORCECONTIG, | |
744 | &start, &count) | |
745 | && start == 32740 && count == 15); | |
746 | ||
747 | // Should have eaten into end of reservation | |
748 | assert(reservation->rl_start == 32715 && reservation->rl_end == 32739); | |
749 | ||
750 | assert(!BlockDeallocate(&mnt, 32700, 15, 0)); | |
751 | ||
752 | ext.startBlock = 0; | |
753 | ext.blockCount = 40; | |
754 | ||
755 | struct rl_entry *locked_reservation; | |
756 | ||
757 | args.max_blocks = 40; | |
758 | args.reservation_in = &reservation; | |
759 | args.reservation_out = &locked_reservation; | |
760 | ||
761 | assert(!hfs_block_alloc(&mnt, &ext, | |
762 | HFS_ALLOC_TRY_HARD | HFS_ALLOC_USE_TENTATIVE | HFS_ALLOC_LOCKED, | |
763 | &args)); | |
764 | ||
765 | assert(ext.startBlock == 32700 && ext.blockCount == 40); | |
766 | assert(reservation == NULL); | |
767 | ||
768 | hfs_free_locked(&mnt, &locked_reservation); | |
769 | ||
770 | assert(mnt.freeBlocks == 40 && mnt.lockedBlocks == 0); | |
771 | ||
772 | args.reservation_out = &reservation; | |
773 | ||
774 | assert(!hfs_block_alloc(&mnt, &ext, HFS_ALLOC_TRY_HARD | HFS_ALLOC_TENTATIVE, | |
775 | &args)); | |
776 | ||
777 | assert(mnt.freeBlocks == 40 && mnt.tentativeBlocks == 40 | |
778 | && rl_len(reservation) == 40); | |
779 | ||
780 | // Hack lockedBlocks so that hfs_free_blocks returns 20 | |
781 | mnt.lockedBlocks = 20; | |
782 | ||
783 | ext.blockCount = 20; // Minimum | |
784 | ||
785 | args.reservation_in = &reservation; | |
786 | ||
787 | assert(!hfs_block_alloc(&mnt, &ext, | |
788 | HFS_ALLOC_TRY_HARD | HFS_ALLOC_USE_TENTATIVE, | |
789 | &args)); | |
790 | ||
791 | // Should have only returned 20 blocks | |
792 | assert(rl_len(reservation) == 20 && ext.blockCount == 20 | |
793 | && ext.startBlock == 32700); | |
794 | ||
795 | mnt.lockedBlocks = 10; | |
796 | ||
797 | // ENOSPC because minimum == 20 | |
798 | assert(hfs_block_alloc(&mnt, &ext, | |
799 | HFS_ALLOC_TRY_HARD | HFS_ALLOC_USE_TENTATIVE, | |
800 | &args) == ENOSPC); | |
801 | ||
802 | mnt.lockedBlocks = 0; | |
803 | assert(!hfs_block_alloc(&mnt, &ext, | |
804 | HFS_ALLOC_TRY_HARD | HFS_ALLOC_USE_TENTATIVE, | |
805 | &args)); | |
806 | ||
807 | // Should use up the remaining reservation | |
808 | assert(!reservation && ext.startBlock == 32720 && ext.blockCount == 20); | |
809 | ||
810 | assert(!BlockDeallocate(&mnt, 32700, 40, 0)); | |
811 | ||
812 | // Check alignment | |
813 | args.alignment = 16; | |
814 | args.max_blocks = 1000; | |
815 | ext.blockCount = 1; | |
816 | assert(!hfs_block_alloc(&mnt, &ext, HFS_ALLOC_TRY_HARD, &args)); | |
817 | ||
818 | assert(ext.startBlock == 32700 && ext.blockCount == 32); | |
819 | ||
820 | assert(!BlockDeallocate(&mnt, 32700, 32, 0)); | |
821 | ||
822 | args.alignment_offset = 3; | |
823 | ext.blockCount = 1; | |
824 | assert(!hfs_block_alloc(&mnt, &ext, HFS_ALLOC_TRY_HARD, &args)); | |
825 | ||
826 | assert(ext.startBlock == 32700 && ext.blockCount == 29); | |
827 | ||
828 | hfs_find_free_extents_test(&mnt); | |
829 | ||
830 | printf("[PASSED] hfs_alloc_test\n"); | |
831 | ||
832 | return 0; | |
833 | } |