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");