2 // lf_hfs_file_extent_mapping.c
5 // Created by Yakov Ben Zaken on 22/03/2018.
9 #include "lf_hfs_format.h"
10 #include "lf_hfs_file_extent_mapping.h"
11 #include "lf_hfs_endian.h"
12 #include "lf_hfs_file_mgr_internal.h"
13 #include "lf_hfs_btrees_internal.h"
14 #include "lf_hfs_vfsutils.h"
15 #include "lf_hfs_logger.h"
16 #include "lf_hfs_utils.h"
22 kResourceForkType
= 0xFF,
27 static OSErr
FindExtentRecord(
28 const ExtendedVCB
*vcb
,
32 Boolean allowPrevious
,
33 HFSPlusExtentKey
*foundKey
,
34 HFSPlusExtentRecord foundData
,
35 u_int32_t
*foundHint
);
37 static OSErr
DeleteExtentRecord(
38 const ExtendedVCB
*vcb
,
41 u_int32_t startBlock
);
43 static OSErr
CreateExtentRecord(
45 HFSPlusExtentKey
*key
,
46 HFSPlusExtentRecord extents
,
50 static OSErr
GetFCBExtentRecord(
52 HFSPlusExtentRecord extents
);
54 static OSErr
SearchExtentRecord(
57 const HFSPlusExtentRecord extentData
,
58 u_int32_t extentDataStartFABN
,
59 u_int32_t
*foundExtentDataOffset
,
60 u_int32_t
*endingFABNPlusOne
,
61 Boolean
*noMoreExtents
);
63 static OSErr
ReleaseExtents(
65 const HFSPlusExtentRecord extentRecord
,
66 u_int32_t
*numReleasedAllocationBlocks
,
67 Boolean
*releasedLastExtent
);
69 static OSErr
DeallocateFork(
71 HFSCatalogNodeID fileID
,
73 HFSPlusExtentRecord catalogExtents
,
74 Boolean
* recordDeleted
);
76 static OSErr
TruncateExtents(
81 Boolean
* recordDeleted
);
83 static OSErr
UpdateExtentRecord (
87 const HFSPlusExtentKey
*extentFileKey
,
88 const HFSPlusExtentRecord extentData
,
89 u_int32_t extentBTreeHint
);
91 static Boolean
ExtentsAreIntegral(
92 const HFSPlusExtentRecord extentRecord
,
94 u_int32_t
*blocksChecked
,
95 Boolean
*checkedLastExtent
);
97 //_________________________________________________________________________________
99 // Routine: FindExtentRecord
101 // Purpose: Search the extents BTree for an extent record matching the given
102 // FileID, fork, and starting file allocation block number.
105 // vcb Volume to search
106 // forkType 0 = data fork, -1 = resource fork
107 // fileID File's FileID (CatalogNodeID)
108 // startBlock Starting file allocation block number
109 // allowPrevious If the desired record isn't found and this flag is set,
110 // then see if the previous record belongs to the same fork.
111 // If so, then return it.
114 // foundKey The key data for the record actually found
115 // foundData The extent record actually found (NOTE: on an HFS volume, the
116 // fourth entry will be zeroes.
117 // foundHint The BTree hint to find the node again
118 //_________________________________________________________________________________
119 static OSErr
FindExtentRecord(
120 const ExtendedVCB
*vcb
,
123 u_int32_t startBlock
,
124 Boolean allowPrevious
,
125 HFSPlusExtentKey
*foundKey
,
126 HFSPlusExtentRecord foundData
,
127 u_int32_t
*foundHint
)
130 BTreeIterator
*btIterator
= NULL
;
131 FSBufferDescriptor btRecord
;
133 u_int16_t btRecordSize
;
138 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
140 btIterator
= hfs_mallocz(sizeof(BTreeIterator
));
142 /* HFS Plus / HFSX */
143 if (vcb
->vcbSigWord
!= kHFSSigWord
) {
144 HFSPlusExtentKey
* extentKeyPtr
;
145 HFSPlusExtentRecord extentData
;
147 extentKeyPtr
= (HFSPlusExtentKey
*) &btIterator
->key
;
148 extentKeyPtr
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
149 extentKeyPtr
->forkType
= forkType
;
150 extentKeyPtr
->pad
= 0;
151 extentKeyPtr
->fileID
= fileID
;
152 extentKeyPtr
->startBlock
= startBlock
;
154 btRecord
.bufferAddress
= &extentData
;
155 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
156 btRecord
.itemCount
= 1;
158 err
= BTSearchRecord(fcb
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
160 if (err
== btNotFound
&& allowPrevious
) {
161 err
= BTIterateRecord(fcb
, kBTreePrevRecord
, btIterator
, &btRecord
, &btRecordSize
);
163 // A previous record may not exist, so just return btNotFound (like we would if
164 // it was for the wrong file/fork).
165 if (err
== (OSErr
) fsBTStartOfIterationErr
) //•• fsBTStartOfIterationErr is type unsigned long
169 // Found a previous record. Does it belong to the same fork of the same file?
170 if (extentKeyPtr
->fileID
!= fileID
|| extentKeyPtr
->forkType
!= forkType
)
176 // Copy the found key back for the caller
178 BlockMoveData(extentKeyPtr
, foundKey
, sizeof(HFSPlusExtentKey
));
179 // Copy the found data back for the caller
180 BlockMoveData(&extentData
, foundData
, sizeof(HFSPlusExtentRecord
));
185 *foundHint
= btIterator
->hint
.nodeNum
;
187 hfs_free(btIterator
);
193 static OSErr
CreateExtentRecord(
195 HFSPlusExtentKey
*key
,
196 HFSPlusExtentRecord extents
,
199 BTreeIterator
*btIterator
= NULL
;
200 FSBufferDescriptor btRecord
;
201 u_int16_t btRecordSize
= 0;
208 btIterator
= hfs_mallocz(sizeof(BTreeIterator
));
211 * The lock taken by callers of ExtendFileC is speculative and
212 * only occurs when the file already has overflow extents. So
213 * We need to make sure we have the lock here. The extents
214 * btree lock can be nested (its recursive) so we always take
217 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
220 btRecordSize
= sizeof(HFSPlusExtentRecord
);
221 btRecord
.bufferAddress
= extents
;
222 btRecord
.itemSize
= btRecordSize
;
223 btRecord
.itemCount
= 1;
225 BlockMoveData(key
, &btIterator
->key
, sizeof(HFSPlusExtentKey
));
228 err
= BTInsertRecord(GetFileControlBlock(vcb
->extentsRefNum
), btIterator
, &btRecord
, btRecordSize
);
231 *hint
= btIterator
->hint
.nodeNum
;
233 (void) BTFlushPath(GetFileControlBlock(vcb
->extentsRefNum
));
235 hfs_systemfile_unlock(vcb
, lockflags
);
237 hfs_free(btIterator
);
242 static OSErr
DeleteExtentRecord(
243 const ExtendedVCB
*vcb
,
246 u_int32_t startBlock
)
248 BTreeIterator
*btIterator
= NULL
;
251 btIterator
= hfs_mallocz(sizeof(BTreeIterator
));
252 if (btIterator
== NULL
) return ENOMEM
;
255 HFSPlusExtentKey
* keyPtr
;
257 keyPtr
= (HFSPlusExtentKey
*) &btIterator
->key
;
258 keyPtr
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
259 keyPtr
->forkType
= forkType
;
261 keyPtr
->fileID
= fileID
;
262 keyPtr
->startBlock
= startBlock
;
264 err
= BTDeleteRecord(GetFileControlBlock(vcb
->extentsRefNum
), btIterator
);
265 (void) BTFlushPath(GetFileControlBlock(vcb
->extentsRefNum
));
268 hfs_free(btIterator
);
274 //_________________________________________________________________________________
276 // Routine: MapFileBlock
278 // Function: Maps a file position into a physical disk address.
280 //_________________________________________________________________________________
282 OSErr
MapFileBlockC (
283 ExtendedVCB
*vcb
, // volume that file resides on
284 FCB
*fcb
, // FCB of file
285 size_t numberOfBytes
, // number of contiguous bytes desired
286 off_t offset
, // starting offset within file (in bytes)
287 daddr64_t
*startSector
, // first sector (NOT an allocation block)
288 size_t *availableBytes
) // number of contiguous bytes (up to numberOfBytes)
291 u_int32_t allocBlockSize
; // Size of the volume's allocation block
292 u_int32_t sectorSize
;
293 HFSPlusExtentKey foundKey
;
294 HFSPlusExtentRecord foundData
;
295 u_int32_t foundIndex
;
297 u_int32_t firstFABN
= 0; // file allocation block of first block in found extent
298 u_int32_t nextFABN
; // file allocation block of block after end of found extent
299 off_t dataEnd
; // (offset) end of range that is contiguous
300 u_int32_t sectorsPerBlock
; // Number of sectors per allocation block
301 u_int32_t startBlock
= 0; // volume allocation block corresponding to firstFABN
305 allocBlockSize
= vcb
->blockSize
;
306 sectorSize
= VCBTOHFS(vcb
)->hfs_logical_block_size
;
308 err
= SearchExtentFile(vcb
, fcb
, offset
, &foundKey
, foundData
, &foundIndex
, &hint
, &nextFABN
);
310 startBlock
= foundData
[foundIndex
].startBlock
;
311 firstFABN
= nextFABN
- foundData
[foundIndex
].blockCount
;
320 // Determine the end of the available space. It will either be the end of the extent,
321 // or the file's PEOF, whichever is smaller.
323 dataEnd
= (off_t
)((off_t
)(nextFABN
) * (off_t
)(allocBlockSize
)); // Assume valid data through end of this extent
324 if (((off_t
)fcb
->ff_blocks
* (off_t
)allocBlockSize
) < dataEnd
) // Is PEOF shorter?
325 dataEnd
= (off_t
)fcb
->ff_blocks
* (off_t
)allocBlockSize
; // Yes, so only map up to PEOF
327 // Compute the number of sectors in an allocation block
328 sectorsPerBlock
= allocBlockSize
/ sectorSize
; // sectors per allocation block
331 // Compute the absolute sector number that contains the offset of the given file
332 // offset in sectors from start of the extent +
333 // offset in sectors from start of allocation block space
335 temp
= (daddr64_t
)((offset
- (off_t
)((off_t
)(firstFABN
) * (off_t
)(allocBlockSize
)))/sectorSize
);
336 temp
+= (daddr64_t
)startBlock
* (daddr64_t
)sectorsPerBlock
;
338 /* Add in any volume offsets */
339 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
340 temp
+= vcb
->hfsPlusIOPosOffset
/ sectorSize
;
342 temp
+= vcb
->vcbAlBlSt
;
344 // Return the desired sector for file position "offset"
348 // Determine the number of contiguous bytes until the end of the extent
349 // (or the amount they asked for, whichever comes first).
353 tmpOff
= dataEnd
- offset
;
355 * Disallow negative runs.
358 /* This shouldn't happen unless something is corrupt */
359 LFHFS_LOG( LEVEL_ERROR
, "MapFileBlockC: tmpOff <= 0 (%lld)\n", tmpOff
);
363 if (tmpOff
> (off_t
)(numberOfBytes
)) {
364 *availableBytes
= numberOfBytes
; // more there than they asked for, so pin the output
367 *availableBytes
= tmpOff
;
375 //ããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããã
376 // Routine: ReleaseExtents
378 // Function: Release the extents of a single extent data record.
379 //ããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããã
381 static OSErr
ReleaseExtents(
383 const HFSPlusExtentRecord extentRecord
,
384 u_int32_t
*numReleasedAllocationBlocks
,
385 Boolean
*releasedLastExtent
)
387 u_int32_t extentIndex
;
388 u_int32_t numberOfExtents
;
391 *numReleasedAllocationBlocks
= 0;
392 *releasedLastExtent
= false;
394 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
395 numberOfExtents
= kHFSPlusExtentDensity
;
397 numberOfExtents
= kHFSExtentDensity
;
399 for( extentIndex
= 0; extentIndex
< numberOfExtents
; extentIndex
++)
401 u_int32_t numAllocationBlocks
;
403 // Loop over the extent record and release the blocks associated with each extent.
404 numAllocationBlocks
= extentRecord
[extentIndex
].blockCount
;
405 if ( numAllocationBlocks
== 0 )
407 *releasedLastExtent
= true;
411 err
= BlockDeallocate( vcb
, extentRecord
[extentIndex
].startBlock
, numAllocationBlocks
, 0);
415 *numReleasedAllocationBlocks
+= numAllocationBlocks
; // bump FABN to beg of next extent
423 //ããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããã
424 // Routine: TruncateExtents
426 // Purpose: Delete extent records whose starting file allocation block number
427 // is greater than or equal to a given starting block number. The
428 // allocation blocks represented by the extents are deallocated.
431 // vcb Volume to operate on
432 // fileID Which file to operate on
433 // startBlock Starting file allocation block number for first extent
435 //ããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããã
437 static OSErr
TruncateExtents(
441 u_int32_t startBlock
,
442 Boolean
* recordDeleted
)
445 u_int32_t numberExtentsReleased
;
446 Boolean releasedLastExtent
;
448 HFSPlusExtentKey key
;
449 HFSPlusExtentRecord extents
= {{0}};
453 * The lock taken by callers of TruncateFileC is speculative and
454 * only occurs when the file already has overflow extents. So
455 * We need to make sure we have the lock here. The extents
456 * btree lock can be nested (its recursive) so we always take
459 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
462 err
= FindExtentRecord(vcb
, forkType
, fileID
, startBlock
, false, &key
, extents
, &hint
);
464 if (err
== btNotFound
)
469 err
= ReleaseExtents( vcb
, extents
, &numberExtentsReleased
, &releasedLastExtent
);
470 if (err
!= noErr
) break;
472 err
= DeleteExtentRecord(vcb
, forkType
, fileID
, startBlock
);
473 if (err
!= noErr
) break;
475 *recordDeleted
= true;
476 startBlock
+= numberExtentsReleased
;
478 hfs_systemfile_unlock(vcb
, lockflags
);
485 //ããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããã
486 // Routine: DeallocateFork
488 // Function: De-allocates all disk space allocated to a specified fork.
489 //ããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããã
491 static OSErr
DeallocateFork(
493 HFSCatalogNodeID fileID
,
495 HFSPlusExtentRecord catalogExtents
,
496 Boolean
* recordDeleted
) /* true if a record was deleted */
499 u_int32_t numReleasedAllocationBlocks
;
500 Boolean releasedLastExtent
;
502 // Release the catalog extents
503 err
= ReleaseExtents( vcb
, catalogExtents
, &numReleasedAllocationBlocks
, &releasedLastExtent
);
504 // Release the extra extents, if present
505 if (err
== noErr
&& !releasedLastExtent
)
506 err
= TruncateExtents(vcb
, forkType
, fileID
, numReleasedAllocationBlocks
, recordDeleted
);
511 //ããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããã
512 // Routine: FlushExtentFile
514 // Function: Flushes the extent file for a specified volume
515 //ããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããã
517 OSErr
FlushExtentFile( ExtendedVCB
*vcb
)
523 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
525 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
526 err
= BTFlushPath(fcb
);
527 hfs_systemfile_unlock(vcb
, lockflags
);
531 // If the FCB for the extent "file" is dirty, mark the VCB as dirty.
532 if (FTOC(fcb
)->c_flag
& C_MODIFIED
)
542 //ããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããã
543 // Routine: CompareExtentKeysPlus
545 // Function: Compares two extent file keys (a search key and a trial key) for
547 //ããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããã
549 __attribute__((weak
)) int32_t CompareExtentKeysPlus( const HFSPlusExtentKey
*searchKey
, const HFSPlusExtentKey
*trialKey
)
551 int32_t result
; // ± 1
554 if (searchKey
->keyLength
!= kHFSPlusExtentKeyMaximumLength
)
555 LFHFS_LOG( LEVEL_ERROR
, "HFS: search Key is wrong length" );
556 if (trialKey
->keyLength
!= kHFSPlusExtentKeyMaximumLength
)
557 LFHFS_LOG( LEVEL_ERROR
, "HFS: search Key is wrong length" );
560 result
= -1; // assume searchKey < trialKey
562 if (searchKey
->fileID
== trialKey
->fileID
) {
564 // FileNum's are equal; compare fork types
566 if (searchKey
->forkType
== trialKey
->forkType
) {
568 // Fork types are equal; compare allocation block number
570 if (searchKey
->startBlock
== trialKey
->startBlock
) {
572 // Everything is equal
578 // Allocation block numbers differ; determine sign
580 if (searchKey
->startBlock
> trialKey
->startBlock
)
586 // Fork types differ; determine sign
588 if (searchKey
->forkType
> trialKey
->forkType
)
594 // FileNums differ; determine sign
596 if (searchKey
->fileID
> trialKey
->fileID
)
604 * Add a file extent to a file.
606 * Used by hfs_extendfs to extend the volume allocation bitmap file.
610 AddFileExtent(ExtendedVCB
*vcb
, FCB
*fcb
, u_int32_t startBlock
, u_int32_t blockCount
)
612 HFSPlusExtentKey foundKey
;
613 HFSPlusExtentRecord foundData
;
614 u_int32_t foundIndex
;
621 peof
= (int64_t)(fcb
->ff_blocks
+ blockCount
) * (int64_t)vcb
->blockSize
;
623 error
= SearchExtentFile(vcb
, fcb
, peof
-1, &foundKey
, foundData
, &foundIndex
, &hint
, &nextBlock
);
624 if (error
!= fxRangeErr
)
628 * Add new extent. See if there is room in the current record.
630 if (foundData
[foundIndex
].blockCount
!= 0)
632 if (foundIndex
== kHFSPlusExtentDensity
) {
634 * Existing record is full so create a new one.
636 foundKey
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
637 foundKey
.forkType
= kDataForkType
;
639 foundKey
.fileID
= FTOC(fcb
)->c_fileid
;
640 foundKey
.startBlock
= nextBlock
;
642 foundData
[0].startBlock
= startBlock
;
643 foundData
[0].blockCount
= blockCount
;
645 /* zero out remaining extents. */
646 for (i
= 1; i
< kHFSPlusExtentDensity
; ++i
) {
647 foundData
[i
].startBlock
= 0;
648 foundData
[i
].blockCount
= 0;
653 error
= CreateExtentRecord(vcb
, &foundKey
, foundData
, &hint
);
654 if (error
== fxOvFlErr
) {
660 * Add a new extent into existing record.
662 foundData
[foundIndex
].startBlock
= startBlock
;
663 foundData
[foundIndex
].blockCount
= blockCount
;
664 error
= UpdateExtentRecord(vcb
, fcb
, 0, &foundKey
, foundData
, hint
);
666 (void) FlushExtentFile(vcb
);
672 //_________________________________________________________________________________
674 // Routine: Extendfile
676 // Function: Extends the disk space allocated to a file.
678 //_________________________________________________________________________________
681 ExtendedVCB
*vcb
, // volume that file resides on
682 FCB
*fcb
, // FCB of file to truncate
683 int64_t bytesToAdd
, // number of bytes to allocate
684 u_int32_t blockHint
, // desired starting allocation block
685 u_int32_t flags
, // EFContig and/or EFAll
686 int64_t *actualBytesAdded
) // number of bytes actually allocated
689 u_int32_t volumeBlockSize
;
691 int64_t bytesThisExtent
;
692 HFSPlusExtentKey foundKey
;
693 HFSPlusExtentRecord foundData
;
694 u_int32_t foundIndex
;
697 u_int32_t startBlock
;
698 Boolean allOrNothing
;
704 u_int32_t actualStartBlock
;
705 u_int32_t actualNumBlocks
;
706 u_int32_t numExtentsPerRecord
= 0;
707 int64_t maximumBytes
;
710 u_int32_t prevblocks
;
711 uint32_t fastdev
= 0;
713 struct hfsmount
*hfsmp
= (struct hfsmount
*)vcb
;
716 *actualBytesAdded
= 0;
717 volumeBlockSize
= vcb
->blockSize
;
718 allOrNothing
= ((flags
& kEFAllMask
) != 0);
719 forceContig
= ((flags
& kEFContigMask
) != 0);
720 prevblocks
= fcb
->ff_blocks
;
722 numExtentsPerRecord
= kHFSPlusExtentDensity
;
725 // Determine how many blocks need to be allocated.
726 // Round up the number of desired bytes to add.
728 blocksToAdd
= howmany(bytesToAdd
, volumeBlockSize
);
729 bytesToAdd
= (int64_t)((int64_t)blocksToAdd
* (int64_t)volumeBlockSize
);
732 * For deferred allocations just reserve the blocks.
734 if ((flags
& kEFDeferMask
)
735 && (vcb
->vcbSigWord
== kHFSPlusSigWord
)
736 && (bytesToAdd
< (int64_t)HFS_MAX_DEFERED_ALLOC
)
737 && (blocksToAdd
< hfs_freeblks(VCBTOHFS(vcb
), 1))) {
738 hfs_lock_mount (hfsmp
);
739 vcb
->loanedBlocks
+= blocksToAdd
;
740 hfs_unlock_mount(hfsmp
);
742 fcb
->ff_unallocblocks
+= blocksToAdd
;
743 FTOC(fcb
)->c_blocks
+= blocksToAdd
;
744 fcb
->ff_blocks
+= blocksToAdd
;
747 * We haven't touched the disk here; no blocks have been
748 * allocated and the volume will not be inconsistent if we
749 * don't update the catalog record immediately.
751 FTOC(fcb
)->c_flag
|= C_MINOR_MOD
;
752 *actualBytesAdded
= bytesToAdd
;
756 * Give back any unallocated blocks before doing real allocations.
758 if (fcb
->ff_unallocblocks
> 0) {
759 u_int32_t loanedBlocks
;
761 loanedBlocks
= fcb
->ff_unallocblocks
;
762 blocksToAdd
+= loanedBlocks
;
763 bytesToAdd
= (int64_t)blocksToAdd
* (int64_t)volumeBlockSize
;
764 FTOC(fcb
)->c_blocks
-= loanedBlocks
;
765 fcb
->ff_blocks
-= loanedBlocks
;
766 fcb
->ff_unallocblocks
= 0;
768 hfs_lock_mount(hfsmp
);
769 vcb
->loanedBlocks
-= loanedBlocks
;
770 hfs_unlock_mount(hfsmp
);
774 // If the file's clump size is larger than the allocation block size,
775 // then set the maximum number of bytes to the requested number of bytes
776 // rounded up to a multiple of the clump size.
778 if ((vcb
->vcbClpSiz
> (int32_t)volumeBlockSize
)
779 && (bytesToAdd
< (int64_t)HFS_MAX_DEFERED_ALLOC
)
780 && (flags
& kEFNoClumpMask
) == 0) {
781 maximumBytes
= (int64_t)howmany(bytesToAdd
, vcb
->vcbClpSiz
);
782 maximumBytes
*= vcb
->vcbClpSiz
;
784 maximumBytes
= bytesToAdd
;
788 // If allocation is all-or-nothing, make sure there are
789 // enough free blocks on the volume (quick test).
792 (blocksToAdd
> hfs_freeblks(VCBTOHFS(vcb
), flags
& kEFReserveMask
))) {
798 // See if there are already enough blocks allocated to the file.
800 peof
= ((int64_t)fcb
->ff_blocks
* (int64_t)volumeBlockSize
) + bytesToAdd
; // potential new PEOF
801 err
= SearchExtentFile(vcb
, fcb
, peof
-1, &foundKey
, foundData
, &foundIndex
, &hint
, &nextBlock
);
803 // Enough blocks are already allocated. Just update the FCB to reflect the new length.
804 fcb
->ff_blocks
= (uint32_t)( peof
/ (int64_t)volumeBlockSize
);
805 FTOC(fcb
)->c_blocks
+= (bytesToAdd
/ volumeBlockSize
);
806 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
809 if (err
!= fxRangeErr
) // Any real error?
810 goto ErrorExit
; // Yes, so exit immediately
813 // Adjust the PEOF to the end of the last extent.
815 bytesThisExtent
= (int64_t)(nextBlock
- fcb
->ff_blocks
) * (int64_t)volumeBlockSize
;
816 if (bytesThisExtent
!= 0) {
817 fcb
->ff_blocks
= nextBlock
;
818 FTOC(fcb
)->c_blocks
+= (bytesThisExtent
/ volumeBlockSize
);
819 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
820 bytesToAdd
-= bytesThisExtent
;
824 // Allocate some more space.
826 // First try a contiguous allocation (of the whole amount).
827 // If that fails, get whatever we can.
828 // If forceContig, then take whatever we got
829 // else, keep getting bits and pieces (non-contig)
832 * Note that for sparse devices (like sparse bundle dmgs), we
833 * should only be aggressive with re-using once-allocated pieces
834 * if we're not dealing with system files. If we're trying to operate
835 * on behalf of a system file, we need the maximum contiguous amount
836 * possible. For non-system files we favor locality and fragmentation over
837 * contiguity as it can result in fewer blocks being needed from the underlying
838 * filesystem that the sparse image resides upon.
841 useMetaZone
= flags
& kEFMetadataMask
;
844 startBlock
= blockHint
;
846 startBlock
= foundData
[foundIndex
].startBlock
+ foundData
[foundIndex
].blockCount
;
849 actualStartBlock
= 0;
851 /* Find number of free blocks based on reserved block flag option */
852 availbytes
= (int64_t)hfs_freeblks(VCBTOHFS(vcb
), flags
& kEFReserveMask
) *
853 (int64_t)volumeBlockSize
;
854 if (availbytes
<= 0) {
857 if (wantContig
&& (availbytes
< bytesToAdd
)) {
861 uint32_t ba_flags
= fastdev
;
864 ba_flags
|= HFS_ALLOC_FORCECONTIG
;
867 ba_flags
|= HFS_ALLOC_METAZONE
;
869 if (allowFlushTxns
) {
870 ba_flags
|= HFS_ALLOC_FLUSHTXN
;
876 (uint32_t)howmany(MIN(bytesToAdd
, availbytes
), (int64_t)volumeBlockSize
),
877 (uint32_t)howmany(MIN(maximumBytes
, availbytes
), (int64_t)volumeBlockSize
),
883 if (err
== dskFulErr
) {
885 if (allowFlushTxns
== 0) {
886 /* If we're forcing contiguity, re-try but allow plucking from recently freed regions */
893 break; // AllocContig failed because not enough contiguous space
897 // Couldn't get one big chunk, so get whatever we can.
902 if (actualNumBlocks
!= 0)
905 if (useMetaZone
== 0) {
906 /* Couldn't get anything so dip into metadat zone */
912 /* If we couldn't find what we needed without flushing the journal, then go ahead and do it now */
913 if (allowFlushTxns
== 0) {
921 // Add the new extent to the existing extent record, or create a new one.
922 if ((actualStartBlock
== startBlock
) && (blockHint
== 0)) {
923 // We grew the file's last extent, so just adjust the number of blocks.
924 foundData
[foundIndex
].blockCount
+= actualNumBlocks
;
925 err
= UpdateExtentRecord(vcb
, fcb
, 0, &foundKey
, foundData
, hint
);
926 if (err
!= noErr
) break;
931 // Need to add a new extent. See if there is room in the current record.
932 if (foundData
[foundIndex
].blockCount
!= 0) // Is current extent free to use?
933 ++foundIndex
; // No, so use the next one.
934 if (foundIndex
== numExtentsPerRecord
) {
935 // This record is full. Need to create a new one.
936 if (FTOC(fcb
)->c_fileid
== kHFSExtentsFileID
) {
937 (void) BlockDeallocate(vcb
, actualStartBlock
, actualNumBlocks
, 0);
938 err
= dskFulErr
; // Oops. Can't extend extents file past first record.
942 foundKey
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
943 if (FORK_IS_RSRC(fcb
))
944 foundKey
.forkType
= kResourceForkType
;
946 foundKey
.forkType
= kDataForkType
;
948 foundKey
.fileID
= FTOC(fcb
)->c_fileid
;
949 foundKey
.startBlock
= nextBlock
;
951 foundData
[0].startBlock
= actualStartBlock
;
952 foundData
[0].blockCount
= actualNumBlocks
;
954 // zero out remaining extents...
955 for (i
= 1; i
< kHFSPlusExtentDensity
; ++i
)
957 foundData
[i
].startBlock
= 0;
958 foundData
[i
].blockCount
= 0;
963 err
= CreateExtentRecord(vcb
, &foundKey
, foundData
, &hint
);
964 if (err
== fxOvFlErr
) {
965 // We couldn't create an extent record because extents B-tree
966 // couldn't grow. Dellocate the extent just allocated and
967 // return a disk full error.
968 (void) BlockDeallocate(vcb
, actualStartBlock
, actualNumBlocks
, 0);
971 if (err
!= noErr
) break;
973 needsFlush
= true; // We need to update the B-tree header
976 // Add a new extent into this record and update.
977 foundData
[foundIndex
].startBlock
= actualStartBlock
;
978 foundData
[foundIndex
].blockCount
= actualNumBlocks
;
979 err
= UpdateExtentRecord(vcb
, fcb
, 0, &foundKey
, foundData
, hint
);
980 if (err
!= noErr
) break;
984 // Figure out how many bytes were actually allocated.
985 // NOTE: BlockAllocate could have allocated more than we asked for.
986 // Don't set the PEOF beyond what our client asked for.
987 nextBlock
+= actualNumBlocks
;
988 bytesThisExtent
= (int64_t)((int64_t)actualNumBlocks
* (int64_t)volumeBlockSize
);
989 if (bytesThisExtent
> bytesToAdd
) {
993 bytesToAdd
-= bytesThisExtent
;
994 maximumBytes
-= bytesThisExtent
;
996 fcb
->ff_blocks
+= (bytesThisExtent
/ volumeBlockSize
);
997 FTOC(fcb
)->c_blocks
+= (bytesThisExtent
/ volumeBlockSize
);
998 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1000 // If contiguous allocation was requested, then we've already got one contiguous
1001 // chunk. If we didn't get all we wanted, then adjust the error to disk full.
1003 if (bytesToAdd
!= 0)
1005 break; // We've already got everything that's contiguous
1008 } while (err
== noErr
&& bytesToAdd
);
1012 if (VCBTOHFS(vcb
)->hfs_flags
& HFS_METADATA_ZONE
) {
1013 /* Keep the roving allocator out of the metadata zone. */
1014 if (vcb
->nextAllocation
>= VCBTOHFS(vcb
)->hfs_metazone_start
&&
1015 vcb
->nextAllocation
<= VCBTOHFS(vcb
)->hfs_metazone_end
) {
1016 hfs_lock_mount (hfsmp
);
1017 HFS_UPDATE_NEXT_ALLOCATION(vcb
, VCBTOHFS(vcb
)->hfs_metazone_end
+ 1);
1019 hfs_unlock_mount(hfsmp
);
1022 if (prevblocks
< fcb
->ff_blocks
) {
1023 *actualBytesAdded
= (int64_t)(fcb
->ff_blocks
- prevblocks
) * (int64_t)volumeBlockSize
;
1025 *actualBytesAdded
= 0;
1029 (void) FlushExtentFile(vcb
);
1036 //_________________________________________________________________________________
1038 // Routine: TruncateFileC
1040 // Function: Truncates the disk space allocated to a file. The file space is
1041 // truncated to a specified new PEOF rounded up to the next allocation
1042 // block boundry. If the 'TFTrunExt' option is specified, the file is
1043 // truncated to the end of the extent containing the new PEOF.
1045 //_________________________________________________________________________________
1047 OSErr
TruncateFileC (
1048 ExtendedVCB
*vcb
, // volume that file resides on
1049 FCB
*fcb
, // FCB of file to truncate
1050 int64_t peof
, // new physical size for file
1051 int deleted
, // if nonzero, the file's catalog record has already been deleted.
1052 int rsrc
, // does this represent a resource fork or not?
1053 uint32_t fileid
, // the fileid of the file we're manipulating.
1054 Boolean truncateToExtent
) // if true, truncate to end of extent containing newPEOF
1058 u_int32_t nextBlock
; // next file allocation block to consider
1059 u_int32_t startBlock
; // Physical (volume) allocation block number of start of a range
1060 u_int32_t physNumBlocks
; // Number of allocation blocks in file (according to PEOF)
1061 u_int32_t numBlocks
;
1062 HFSPlusExtentKey key
; // key for current extent record; key->keyLength == 0 if FCB's extent record
1063 u_int32_t hint
; // BTree hint corresponding to key
1064 HFSPlusExtentRecord extentRecord
;
1065 u_int32_t extentIndex
;
1066 u_int32_t extentNextBlock
;
1067 u_int32_t numExtentsPerRecord
;
1070 Boolean extentChanged
; // true if we actually changed an extent
1071 Boolean recordDeleted
; // true if an extent record got deleted
1073 recordDeleted
= false;
1075 if (vcb
->vcbSigWord
== kHFSPlusSigWord
) {
1076 numExtentsPerRecord
= kHFSPlusExtentDensity
;
1079 numExtentsPerRecord
= kHFSExtentDensity
;
1083 forkType
= kResourceForkType
;
1086 forkType
= kDataForkType
;
1089 temp64
= fcb
->ff_blocks
;
1090 physNumBlocks
= (u_int32_t
)temp64
;
1093 // Round newPEOF up to a multiple of the allocation block size. If new size is
1094 // two gigabytes or more, then round down by one allocation block (??? really?
1095 // shouldn't that be an error?).
1097 nextBlock
= (uint32_t)howmany(peof
, (int64_t)vcb
->blockSize
); // number of allocation blocks to remain in file
1098 peof
= (int64_t)((int64_t)nextBlock
* (int64_t)vcb
->blockSize
); // number of bytes in those blocks
1101 // Update FCB's length
1104 * XXX Any errors could cause ff_blocks and c_blocks to get out of sync...
1106 numBlocks
= (uint32_t)( peof
/ (int64_t)vcb
->blockSize
);
1108 FTOC(fcb
)->c_blocks
-= (fcb
->ff_blocks
- numBlocks
);
1110 fcb
->ff_blocks
= numBlocks
;
1112 // this catalog entry is modified and *must* get forced
1113 // to disk when hfs_update() is called
1116 * If the file is already C_NOEXISTS, then the catalog record
1117 * has been removed from disk already. We wouldn't need to force
1120 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1123 // If the new PEOF is 0, then truncateToExtent has no meaning (we should always deallocate
1129 // Deallocate all the extents for this fork
1130 err
= DeallocateFork(vcb
, fileid
, forkType
, fcb
->fcbExtents
, &recordDeleted
);
1131 if (err
!= noErr
) goto ErrorExit
; // got some error, so return it
1133 // Update the catalog extent record (making sure it's zeroed out)
1135 for (i
=0; i
< kHFSPlusExtentDensity
; i
++) {
1136 fcb
->fcbExtents
[i
].startBlock
= 0;
1137 fcb
->fcbExtents
[i
].blockCount
= 0;
1144 // Find the extent containing byte (peof-1). This is the last extent we'll keep.
1145 // (If truncateToExtent is true, we'll keep the whole extent; otherwise, we'll only
1146 // keep up through peof). The search will tell us how many allocation blocks exist
1147 // in the found extent plus all previous extents.
1149 err
= SearchExtentFile(vcb
, fcb
, peof
-1, &key
, extentRecord
, &extentIndex
, &hint
, &extentNextBlock
);
1150 if (err
!= noErr
) goto ErrorExit
;
1152 extentChanged
= false; // haven't changed the extent yet
1154 if (!truncateToExtent
) {
1156 // Shorten this extent. It may be the case that the entire extent gets
1159 numBlocks
= extentNextBlock
- nextBlock
; // How many blocks in this extent to free up
1160 if (numBlocks
!= 0) {
1161 // Compute first volume allocation block to free
1162 startBlock
= extentRecord
[extentIndex
].startBlock
+ extentRecord
[extentIndex
].blockCount
- numBlocks
;
1163 // Free the blocks in bitmap
1164 err
= BlockDeallocate(vcb
, startBlock
, numBlocks
, 0);
1165 if (err
!= noErr
) goto ErrorExit
;
1166 // Adjust length of this extent
1167 extentRecord
[extentIndex
].blockCount
-= numBlocks
;
1168 // If extent is empty, set start block to 0
1169 if (extentRecord
[extentIndex
].blockCount
== 0)
1170 extentRecord
[extentIndex
].startBlock
= 0;
1171 // Remember that we changed the extent record
1172 extentChanged
= true;
1177 // Now move to the next extent in the record, and set up the file allocation block number
1179 nextBlock
= extentNextBlock
; // Next file allocation block to free
1180 ++extentIndex
; // Its index within the extent record
1183 // Release all following extents in this extent record. Update the record.
1185 while (extentIndex
< numExtentsPerRecord
&& extentRecord
[extentIndex
].blockCount
!= 0) {
1186 numBlocks
= extentRecord
[extentIndex
].blockCount
;
1187 // Deallocate this extent
1188 err
= BlockDeallocate(vcb
, extentRecord
[extentIndex
].startBlock
, numBlocks
, 0);
1189 if (err
!= noErr
) goto ErrorExit
;
1190 // Update next file allocation block number
1191 nextBlock
+= numBlocks
;
1192 // Zero out start and length of this extent to delete it from record
1193 extentRecord
[extentIndex
].startBlock
= 0;
1194 extentRecord
[extentIndex
].blockCount
= 0;
1195 // Remember that we changed an extent
1196 extentChanged
= true;
1197 // Move to next extent in record
1202 // If any of the extents in the current record were changed, then update that
1203 // record (in the FCB, or extents file).
1205 if (extentChanged
) {
1206 err
= UpdateExtentRecord(vcb
, fcb
, deleted
, &key
, extentRecord
, hint
);
1207 if (err
!= noErr
) goto ErrorExit
;
1211 // If there are any following allocation blocks, then we need
1212 // to seach for their extent records and delete those allocation
1215 if (nextBlock
< physNumBlocks
)
1216 err
= TruncateExtents(vcb
, forkType
, fileid
, nextBlock
, &recordDeleted
);
1221 (void) FlushExtentFile(vcb
);
1231 OSErr
HeadTruncateFile (
1236 HFSPlusExtentRecord extents
;
1237 HFSPlusExtentRecord tailExtents
;
1238 HFSCatalogNodeID fileID
;
1240 u_int32_t blkcnt
= 0;
1242 u_int32_t blksfreed
;
1248 if (vcb
->vcbSigWord
!= kHFSPlusSigWord
)
1251 forkType
= FORK_IS_RSRC(fcb
) ? kResourceForkType
: kDataForkType
;
1252 fileID
= FTOC(fcb
)->c_fileid
;
1253 bzero(tailExtents
, sizeof(tailExtents
));
1259 * Process catalog resident extents
1261 for (i
= 0, j
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
1262 blkcnt
= fcb
->fcbExtents
[i
].blockCount
;
1264 break; /* end of extents */
1266 if (blksfreed
< headblks
) {
1267 error
= BlockDeallocate(vcb
, fcb
->fcbExtents
[i
].startBlock
, blkcnt
, 0);
1269 * Any errors after the first BlockDeallocate
1270 * must be ignored so we can put the file in
1275 goto ErrorExit
; /* uh oh */
1278 LFHFS_LOG(LEVEL_ERROR
, "HeadTruncateFile: problems deallocating %s (%d)\n",FTOC(fcb
)->c_desc
.cd_nameptr
? (const char *)FTOC(fcb
)->c_desc
.cd_nameptr
: "", error
);
1282 blksfreed
+= blkcnt
;
1283 fcb
->fcbExtents
[i
].startBlock
= 0;
1284 fcb
->fcbExtents
[i
].blockCount
= 0;
1286 tailExtents
[j
].startBlock
= fcb
->fcbExtents
[i
].startBlock
;
1287 tailExtents
[j
].blockCount
= blkcnt
;
1296 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
1299 * Process overflow extents
1304 error
= FindExtentRecord(vcb
, forkType
, fileID
, startblk
, false, NULL
, extents
, NULL
);
1307 * Any errors after the first BlockDeallocate
1308 * must be ignored so we can put the file in
1311 if (error
!= btNotFound
)
1312 LFHFS_LOG(LEVEL_ERROR
, "HeadTruncateFile: problems finding extents %s (%d)\n",
1313 FTOC(fcb
)->c_desc
.cd_nameptr
? (const char *)FTOC(fcb
)->c_desc
.cd_nameptr
: "", error
);
1318 for(i
= 0, extblks
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
1319 blkcnt
= extents
[i
].blockCount
;
1321 break; /* end of extents */
1323 if (blksfreed
< headblks
) {
1324 error
= BlockDeallocate(vcb
, extents
[i
].startBlock
, blkcnt
, 0);
1326 LFHFS_LOG(LEVEL_ERROR
, "HeadTruncateFile: problems deallocating %s (%d)\n",
1327 FTOC(fcb
)->c_desc
.cd_nameptr
? (const char *)FTOC(fcb
)->c_desc
.cd_nameptr
: "", error
);
1329 blksfreed
+= blkcnt
;
1331 tailExtents
[j
].startBlock
= extents
[i
].startBlock
;
1332 tailExtents
[j
].blockCount
= blkcnt
;
1338 error
= DeleteExtentRecord(vcb
, forkType
, fileID
, startblk
);
1340 LFHFS_LOG(LEVEL_ERROR
, "HeadTruncateFile: problems deallocating %s (%d)\n",
1341 FTOC(fcb
)->c_desc
.cd_nameptr
? (const char *)FTOC(fcb
)->c_desc
.cd_nameptr
: "", error
);
1346 break; /* all done */
1348 startblk
+= extblks
;
1350 hfs_systemfile_unlock(vcb
, lockflags
);
1354 bcopy(tailExtents
, fcb
->fcbExtents
, sizeof(tailExtents
));
1355 blkcnt
= fcb
->ff_blocks
- headblks
;
1356 FTOC(fcb
)->c_blocks
-= headblks
;
1357 fcb
->ff_blocks
= blkcnt
;
1359 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1360 FTOC(fcb
)->c_touch_chgtime
= TRUE
;
1362 (void) FlushExtentFile(vcb
);
1366 return MacToVFSError(error
);
1369 //ããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããã
1370 // Routine: SearchExtentRecord (was XRSearch)
1372 // Function: Searches extent record for the extent mapping a given file
1373 // allocation block number (FABN).
1375 // Input: searchFABN - desired FABN
1376 // extentData - pointer to extent data record (xdr)
1377 // extentDataStartFABN - beginning FABN for extent record
1379 // Output: foundExtentDataOffset - offset to extent entry within xdr
1380 // result = noErr, offset to extent mapping desired FABN
1381 // result = FXRangeErr, offset to last extent in record
1382 // endingFABNPlusOne - ending FABN +1
1383 // noMoreExtents - True if the extent was not found, and the
1384 // extent record was not full (so don't bother
1385 // looking in subsequent records); false otherwise.
1387 // Result: noErr = ok
1388 // FXRangeErr = desired FABN > last mapped FABN in record
1389 //ããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããã
1391 static OSErr
SearchExtentRecord(
1393 u_int32_t searchFABN
,
1394 const HFSPlusExtentRecord extentData
,
1395 u_int32_t extentDataStartFABN
,
1396 u_int32_t
*foundExtentIndex
,
1397 u_int32_t
*endingFABNPlusOne
,
1398 Boolean
*noMoreExtents
)
1400 #pragma unused (vcb)
1402 u_int32_t extentIndex
;
1403 /* Set it to the HFS std value */
1404 u_int32_t numberOfExtents
= kHFSExtentDensity
;
1405 u_int32_t numAllocationBlocks
;
1406 Boolean foundExtent
;
1408 *endingFABNPlusOne
= extentDataStartFABN
;
1409 *noMoreExtents
= false;
1410 foundExtent
= false;
1412 /* Override numberOfExtents for HFS+/HFSX */
1413 numberOfExtents
= kHFSPlusExtentDensity
;
1415 for( extentIndex
= 0; extentIndex
< numberOfExtents
; ++extentIndex
)
1418 // Loop over the extent record and find the search FABN.
1420 numAllocationBlocks
= extentData
[extentIndex
].blockCount
;
1421 if ( numAllocationBlocks
== 0 )
1426 *endingFABNPlusOne
+= numAllocationBlocks
;
1428 if( searchFABN
< *endingFABNPlusOne
)
1430 // Found the extent.
1438 // Found the extent. Note the extent offset
1439 *foundExtentIndex
= extentIndex
;
1443 // Did not find the extent. Set foundExtentDataOffset accordingly
1444 if( extentIndex
> 0 )
1446 *foundExtentIndex
= extentIndex
- 1;
1450 *foundExtentIndex
= 0;
1453 // If we found an empty extent, then set noMoreExtents.
1454 if (extentIndex
< numberOfExtents
)
1455 *noMoreExtents
= true;
1457 // Finally, return an error to the caller
1464 //ããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããã
1465 // Routine: SearchExtentFile (was XFSearch)
1467 // Function: Searches extent file (including the FCB resident extent record)
1468 // for the extent mapping a given file position.
1470 // Input: vcb - VCB pointer
1471 // fcb - FCB pointer
1472 // filePosition - file position (byte address)
1474 // Output: foundExtentKey - extent key record (xkr)
1475 // If extent was found in the FCB's resident extent record,
1476 // then foundExtentKey->keyLength will be set to 0.
1477 // foundExtentData - extent data record(xdr)
1478 // foundExtentIndex - index to extent entry in xdr
1479 // result = 0, offset to extent mapping desired FABN
1480 // result = FXRangeErr, offset to last extent in record
1481 // (i.e., kNumExtentsPerRecord-1)
1482 // extentBTreeHint - BTree hint for extent record
1483 // kNoHint = Resident extent record
1484 // endingFABNPlusOne - ending FABN +1
1487 // noErr Found an extent that contains the given file position
1488 // FXRangeErr Given position is beyond the last allocated extent
1489 // (other) (some other internal I/O error)
1490 //ããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããããã
1491 OSErr
SearchExtentFile(
1494 int64_t filePosition
,
1495 HFSPlusExtentKey
*foundExtentKey
,
1496 HFSPlusExtentRecord foundExtentData
,
1497 u_int32_t
*foundExtentIndex
,
1498 u_int32_t
*extentBTreeHint
,
1499 u_int32_t
*endingFABNPlusOne
)
1502 u_int32_t filePositionBlock
;
1504 Boolean noMoreExtents
;
1507 temp64
= filePosition
/ (int64_t)vcb
->blockSize
;
1508 filePositionBlock
= (u_int32_t
)temp64
;
1510 bcopy ( fcb
->fcbExtents
, foundExtentData
, sizeof(HFSPlusExtentRecord
));
1512 // Search the resident FCB first.
1513 err
= SearchExtentRecord( vcb
, filePositionBlock
, foundExtentData
, 0,
1514 foundExtentIndex
, endingFABNPlusOne
, &noMoreExtents
);
1516 if( err
== noErr
) {
1517 // Found the extent. Set results accordingly
1518 *extentBTreeHint
= kNoHint
; // no hint, because not in the BTree
1519 foundExtentKey
->keyLength
= 0; // 0 = the FCB itself
1524 // Didn't find extent in FCB. If FCB's extent record wasn't full, there's no point
1525 // in searching the extents file. Note that SearchExtentRecord left us pointing at
1526 // the last valid extent (or the first one, if none were valid). This means we need
1527 // to fill in the hint and key outputs, just like the "if" statement above.
1528 if ( noMoreExtents
) {
1529 *extentBTreeHint
= kNoHint
; // no hint, because not in the BTree
1530 foundExtentKey
->keyLength
= 0; // 0 = the FCB itself
1531 err
= fxRangeErr
; // There are no more extents, so must be beyond PEOF
1536 // Find the desired record, or the previous record if it is the same fork
1538 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
1540 err
= FindExtentRecord(vcb
, FORK_IS_RSRC(fcb
) ? kResourceForkType
: kDataForkType
,
1541 FTOC(fcb
)->c_fileid
, filePositionBlock
, true, foundExtentKey
, foundExtentData
, extentBTreeHint
);
1542 hfs_systemfile_unlock(vcb
, lockflags
);
1544 if (err
== btNotFound
) {
1546 // If we get here, the desired position is beyond the extents in the FCB, and there are no extents
1547 // in the extents file. Return the FCB's extents and a range error.
1549 *extentBTreeHint
= kNoHint
;
1550 foundExtentKey
->keyLength
= 0;
1551 (void)GetFCBExtentRecord(fcb
, foundExtentData
);
1552 // Note: foundExtentIndex and endingFABNPlusOne have already been set as a result of the very
1553 // first SearchExtentRecord call in this function (when searching in the FCB's extents, and
1554 // we got a range error).
1560 // If we get here, there was either a BTree error, or we found an appropriate record.
1561 // If we found a record, then search it for the correct index into the extents.
1564 // Find appropriate index into extent record
1565 err
= SearchExtentRecord(vcb
, filePositionBlock
, foundExtentData
, foundExtentKey
->startBlock
,
1566 foundExtentIndex
, endingFABNPlusOne
, &noMoreExtents
);
1574 //============================================================================
1575 // Routine: UpdateExtentRecord
1577 // Function: Write new extent data to an existing extent record with a given key.
1578 // If all of the extents are empty, and the extent record is in the
1579 // extents file, then the record is deleted.
1581 // Input: vcb - the volume containing the extents
1582 // fcb - the file that owns the extents
1583 // deleted - whether or not the file is already deleted
1584 // extentFileKey - pointer to extent key record (xkr)
1585 // If the key length is 0, then the extents are actually part
1586 // of the catalog record, stored in the FCB.
1587 // extentData - pointer to extent data record (xdr)
1588 // extentBTreeHint - hint for given key, or kNoHint
1590 // Result: noErr = ok
1591 // (other) = error from BTree
1592 //============================================================================
1594 static OSErr
UpdateExtentRecord (ExtendedVCB
*vcb
, FCB
*fcb
, int deleted
,
1595 const HFSPlusExtentKey
*extentFileKey
,
1596 const HFSPlusExtentRecord extentData
,
1597 u_int32_t extentBTreeHint
)
1601 if (extentFileKey
->keyLength
== 0) { // keyLength == 0 means the FCB's extent record
1602 BlockMoveData(extentData
, fcb
->fcbExtents
, sizeof(HFSPlusExtentRecord
));
1604 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1608 BTreeIterator
*btIterator
= NULL
;
1609 FSBufferDescriptor btRecord
;
1610 u_int16_t btRecordSize
;
1615 // Need to find and change a record in Extents BTree
1617 btFCB
= GetFileControlBlock(vcb
->extentsRefNum
);
1619 btIterator
= hfs_mallocz(sizeof(BTreeIterator
));
1622 * The lock taken by callers of ExtendFileC/TruncateFileC is
1623 * speculative and only occurs when the file already has
1624 * overflow extents. So we need to make sure we have the lock
1625 * here. The extents btree lock can be nested (its recursive)
1626 * so we always take it here.
1628 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
1631 HFSPlusExtentRecord foundData
; // The extent data actually found
1633 BlockMoveData(extentFileKey
, &btIterator
->key
, sizeof(HFSPlusExtentKey
));
1635 btIterator
->hint
.index
= 0;
1636 btIterator
->hint
.nodeNum
= extentBTreeHint
;
1638 btRecord
.bufferAddress
= &foundData
;
1639 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
1640 btRecord
.itemCount
= 1;
1642 err
= BTSearchRecord(btFCB
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
1645 BlockMoveData(extentData
, &foundData
, sizeof(HFSPlusExtentRecord
));
1646 err
= BTReplaceRecord(btFCB
, btIterator
, &btRecord
, btRecordSize
);
1648 (void) BTFlushPath(btFCB
);
1650 hfs_systemfile_unlock(vcb
, lockflags
);
1652 hfs_free(btIterator
);
1658 static OSErr
GetFCBExtentRecord(
1660 HFSPlusExtentRecord extents
)
1663 BlockMoveData(fcb
->fcbExtents
, extents
, sizeof(HFSPlusExtentRecord
));
1669 //_________________________________________________________________________________
1671 // Routine: ExtentsAreIntegral
1673 // Purpose: Ensure that each extent can hold an integral number of nodes
1674 // Called by the NodesAreContiguous function
1675 //_________________________________________________________________________________
1677 static Boolean
ExtentsAreIntegral(
1678 const HFSPlusExtentRecord extentRecord
,
1680 u_int32_t
*blocksChecked
,
1681 Boolean
*checkedLastExtent
)
1684 u_int32_t extentIndex
;
1687 *checkedLastExtent
= false;
1689 for(extentIndex
= 0; extentIndex
< kHFSPlusExtentDensity
; extentIndex
++)
1691 blocks
= extentRecord
[extentIndex
].blockCount
;
1695 *checkedLastExtent
= true;
1699 *blocksChecked
+= blocks
;
1709 //_________________________________________________________________________________
1711 // Routine: NodesAreContiguous
1713 // Purpose: Ensure that all b-tree nodes are contiguous on disk
1714 // Called by BTOpenPath during volume mount
1715 //_________________________________________________________________________________
1717 Boolean
NodesAreContiguous(
1723 u_int32_t startBlock
;
1724 u_int32_t blocksChecked
;
1726 HFSPlusExtentKey key
;
1727 HFSPlusExtentRecord extents
;
1729 Boolean lastExtentReached
;
1733 if (vcb
->blockSize
>= nodeSize
)
1736 mask
= (nodeSize
/ vcb
->blockSize
) - 1;
1738 // check the local extents
1739 (void) GetFCBExtentRecord(fcb
, extents
);
1740 if ( !ExtentsAreIntegral(extents
, mask
, &blocksChecked
, &lastExtentReached
) )
1743 if ( lastExtentReached
||
1744 (int64_t)((int64_t)blocksChecked
* (int64_t)vcb
->blockSize
) >= (int64_t)fcb
->ff_size
)
1747 startBlock
= blocksChecked
;
1749 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
1751 // check the overflow extents (if any)
1752 while ( !lastExtentReached
)
1754 result
= FindExtentRecord(vcb
, kDataForkType
, fcb
->ff_cp
->c_fileid
, startBlock
, FALSE
, &key
, extents
, &hint
);
1757 if ( !ExtentsAreIntegral(extents
, mask
, &blocksChecked
, &lastExtentReached
) ) {
1758 hfs_systemfile_unlock(vcb
, lockflags
);
1761 startBlock
+= blocksChecked
;
1763 hfs_systemfile_unlock(vcb
, lockflags
);