2 * Copyright (c) 2000-2013 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@
30 #include "../../hfs.h"
31 #include "../../hfs_format.h"
32 #include "../../hfs_endian.h"
34 #include "../headers/FileMgrInternal.h"
35 #include "../headers/BTreesInternal.h"
37 #include <sys/malloc.h>
40 ============================================================
41 Public (Exported) Routines:
42 ============================================================
44 ExtendFileC Allocate more space to a given file.
47 Compare two extents file keys (a search key and a trial
48 key). Used by the BTree manager when searching for,
49 adding, or deleting keys in the extents file of an HFS
53 Compare two extents file keys (a search key and a trial
54 key). Used by the BTree manager when searching for,
55 adding, or deleting keys in the extents file of an HFS+
58 MapFileBlockC Convert (map) an offset within a given file into a
59 physical disk address.
61 TruncateFileC Truncates the disk space allocated to a file. The file
62 space is truncated to a specified new physical EOF, rounded
63 up to the next allocation block boundry. There is an option
64 to truncate to the end of the extent containing the new EOF.
67 Flush the extents file for a given volume.
72 ============================================================
74 ============================================================
76 Search the extents BTree for a particular extent record.
78 Search the FCB and extents file for an extent record that
79 contains a given file position (in bytes).
81 Search a given extent record to see if it contains a given
82 file position (in bytes). Used by SearchExtentFile.
84 Deallocate all allocation blocks in all extents of an extent
87 Deallocate blocks and delete extent records for all allocation
88 blocks beyond a certain point in a file. The starting point
89 must be the first file allocation block for some extent record
92 Deallocate all allocation blocks belonging to a given fork.
94 If the extent record came from the extents file, write out
95 the updated record; otherwise, copy the updated record into
96 the FCB resident extent record. If the record has no extents,
97 and was in the extents file, then delete the record instead.
101 static const int64_t kTwoGigabytes
= 0x80000000LL
;
107 kResourceForkType
= 0xFF,
114 static OSErr
HFSPlusToHFSExtents(
115 const HFSPlusExtentRecord oldExtents
,
116 HFSExtentRecord newExtents
);
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
);
129 static OSErr
DeleteExtentRecord(
130 const ExtendedVCB
*vcb
,
133 u_int32_t startBlock
);
135 static OSErr
CreateExtentRecord(
137 HFSPlusExtentKey
*key
,
138 HFSPlusExtentRecord extents
,
142 static OSErr
GetFCBExtentRecord(
144 HFSPlusExtentRecord extents
);
146 static OSErr
SearchExtentFile(
149 int64_t filePosition
,
150 HFSPlusExtentKey
*foundExtentKey
,
151 HFSPlusExtentRecord foundExtentData
,
152 u_int32_t
*foundExtentDataIndex
,
153 u_int32_t
*extentBTreeHint
,
154 u_int32_t
*endingFABNPlusOne
);
156 static OSErr
SearchExtentRecord(
158 u_int32_t searchFABN
,
159 const HFSPlusExtentRecord extentData
,
160 u_int32_t extentDataStartFABN
,
161 u_int32_t
*foundExtentDataOffset
,
162 u_int32_t
*endingFABNPlusOne
,
163 Boolean
*noMoreExtents
);
165 static OSErr
ReleaseExtents(
167 const HFSPlusExtentRecord extentRecord
,
168 u_int32_t
*numReleasedAllocationBlocks
,
169 Boolean
*releasedLastExtent
);
171 static OSErr
DeallocateFork(
173 HFSCatalogNodeID fileID
,
175 HFSPlusExtentRecord catalogExtents
,
176 Boolean
* recordDeleted
);
178 static OSErr
TruncateExtents(
182 u_int32_t startBlock
,
183 Boolean
* recordDeleted
);
185 static OSErr
UpdateExtentRecord (
189 const HFSPlusExtentKey
*extentFileKey
,
190 const HFSPlusExtentRecord extentData
,
191 u_int32_t extentBTreeHint
);
193 static Boolean
ExtentsAreIntegral(
194 const HFSPlusExtentRecord extentRecord
,
196 u_int32_t
*blocksChecked
,
197 Boolean
*checkedLastExtent
);
199 //_________________________________________________________________________________
201 // Routine: FindExtentRecord
203 // Purpose: Search the extents BTree for an extent record matching the given
204 // FileID, fork, and starting file allocation block number.
207 // vcb Volume to search
208 // forkType 0 = data fork, -1 = resource fork
209 // fileID File's FileID (CatalogNodeID)
210 // startBlock Starting file allocation block number
211 // allowPrevious If the desired record isn't found and this flag is set,
212 // then see if the previous record belongs to the same fork.
213 // If so, then return it.
216 // foundKey The key data for the record actually found
217 // foundData The extent record actually found (NOTE: on an HFS volume, the
218 // fourth entry will be zeroes.
219 // foundHint The BTree hint to find the node again
220 //_________________________________________________________________________________
221 static OSErr
FindExtentRecord(
222 const ExtendedVCB
*vcb
,
225 u_int32_t startBlock
,
226 Boolean allowPrevious
,
227 HFSPlusExtentKey
*foundKey
,
228 HFSPlusExtentRecord foundData
,
229 u_int32_t
*foundHint
)
232 struct BTreeIterator
*btIterator
= NULL
;
233 FSBufferDescriptor btRecord
;
235 u_int16_t btRecordSize
;
240 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
242 MALLOC (btIterator
, struct BTreeIterator
*, sizeof(struct BTreeIterator
), M_TEMP
, M_WAITOK
);
243 if (btIterator
== NULL
) {
244 return memFullErr
; // translates to ENOMEM
246 bzero(btIterator
, sizeof(*btIterator
));
248 /* HFS Plus / HFSX */
249 if (vcb
->vcbSigWord
!= kHFSSigWord
) {
250 HFSPlusExtentKey
* extentKeyPtr
;
251 HFSPlusExtentRecord extentData
;
253 extentKeyPtr
= (HFSPlusExtentKey
*) &btIterator
->key
;
254 extentKeyPtr
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
255 extentKeyPtr
->forkType
= forkType
;
256 extentKeyPtr
->pad
= 0;
257 extentKeyPtr
->fileID
= fileID
;
258 extentKeyPtr
->startBlock
= startBlock
;
260 btRecord
.bufferAddress
= &extentData
;
261 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
262 btRecord
.itemCount
= 1;
264 err
= BTSearchRecord(fcb
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
266 if (err
== btNotFound
&& allowPrevious
) {
267 err
= BTIterateRecord(fcb
, kBTreePrevRecord
, btIterator
, &btRecord
, &btRecordSize
);
269 // A previous record may not exist, so just return btNotFound (like we would if
270 // it was for the wrong file/fork).
271 if (err
== (OSErr
) fsBTStartOfIterationErr
) //¥¥ fsBTStartOfIterationErr is type unsigned long
275 // Found a previous record. Does it belong to the same fork of the same file?
276 if (extentKeyPtr
->fileID
!= fileID
|| extentKeyPtr
->forkType
!= forkType
)
282 // Copy the found key back for the caller
284 BlockMoveData(extentKeyPtr
, foundKey
, sizeof(HFSPlusExtentKey
));
285 // Copy the found data back for the caller
286 BlockMoveData(&extentData
, foundData
, sizeof(HFSPlusExtentRecord
));
291 HFSExtentKey
* extentKeyPtr
;
292 HFSExtentRecord extentData
;
294 extentKeyPtr
= (HFSExtentKey
*) &btIterator
->key
;
295 extentKeyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
296 extentKeyPtr
->forkType
= forkType
;
297 extentKeyPtr
->fileID
= fileID
;
298 extentKeyPtr
->startBlock
= startBlock
;
300 btRecord
.bufferAddress
= &extentData
;
301 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
302 btRecord
.itemCount
= 1;
304 err
= BTSearchRecord(fcb
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
306 if (err
== btNotFound
&& allowPrevious
) {
307 err
= BTIterateRecord(fcb
, kBTreePrevRecord
, btIterator
, &btRecord
, &btRecordSize
);
309 // A previous record may not exist, so just return btNotFound (like we would if
310 // it was for the wrong file/fork).
311 if (err
== (OSErr
) fsBTStartOfIterationErr
) //¥¥ fsBTStartOfIterationErr is type unsigned long
315 // Found a previous record. Does it belong to the same fork of the same file?
316 if (extentKeyPtr
->fileID
!= fileID
|| extentKeyPtr
->forkType
!= forkType
)
324 // Copy the found key back for the caller
326 foundKey
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
327 foundKey
->forkType
= extentKeyPtr
->forkType
;
329 foundKey
->fileID
= extentKeyPtr
->fileID
;
330 foundKey
->startBlock
= extentKeyPtr
->startBlock
;
332 // Copy the found data back for the caller
333 foundData
[0].startBlock
= extentData
[0].startBlock
;
334 foundData
[0].blockCount
= extentData
[0].blockCount
;
335 foundData
[1].startBlock
= extentData
[1].startBlock
;
336 foundData
[1].blockCount
= extentData
[1].blockCount
;
337 foundData
[2].startBlock
= extentData
[2].startBlock
;
338 foundData
[2].blockCount
= extentData
[2].blockCount
;
340 for (i
= 3; i
< kHFSPlusExtentDensity
; ++i
)
342 foundData
[i
].startBlock
= 0;
343 foundData
[i
].blockCount
= 0;
350 *foundHint
= btIterator
->hint
.nodeNum
;
352 FREE(btIterator
, M_TEMP
);
358 static OSErr
CreateExtentRecord(
360 HFSPlusExtentKey
*key
,
361 HFSPlusExtentRecord extents
,
364 struct BTreeIterator
*btIterator
= NULL
;
365 FSBufferDescriptor btRecord
;
366 u_int16_t btRecordSize
;
373 MALLOC (btIterator
, struct BTreeIterator
*, sizeof(struct BTreeIterator
), M_TEMP
, M_WAITOK
);
374 if (btIterator
== NULL
) {
375 return memFullErr
; // translates to ENOMEM
377 bzero(btIterator
, sizeof(*btIterator
));
380 * The lock taken by callers of ExtendFileC is speculative and
381 * only occurs when the file already has overflow extents. So
382 * We need to make sure we have the lock here. The extents
383 * btree lock can be nested (its recursive) so we always take
386 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
389 if (vcb
->vcbSigWord
!= kHFSSigWord
) {
390 btRecordSize
= sizeof(HFSPlusExtentRecord
);
391 btRecord
.bufferAddress
= extents
;
392 btRecord
.itemSize
= btRecordSize
;
393 btRecord
.itemCount
= 1;
395 BlockMoveData(key
, &btIterator
->key
, sizeof(HFSPlusExtentKey
));
400 HFSExtentKey
* keyPtr
;
401 HFSExtentRecord data
;
403 btRecordSize
= sizeof(HFSExtentRecord
);
404 btRecord
.bufferAddress
= &data
;
405 btRecord
.itemSize
= btRecordSize
;
406 btRecord
.itemCount
= 1;
408 keyPtr
= (HFSExtentKey
*) &btIterator
->key
;
409 keyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
410 keyPtr
->forkType
= key
->forkType
;
411 keyPtr
->fileID
= key
->fileID
;
412 keyPtr
->startBlock
= key
->startBlock
;
414 err
= HFSPlusToHFSExtents(extents
, data
);
419 err
= BTInsertRecord(GetFileControlBlock(vcb
->extentsRefNum
), btIterator
, &btRecord
, btRecordSize
);
422 *hint
= btIterator
->hint
.nodeNum
;
424 (void) BTFlushPath(GetFileControlBlock(vcb
->extentsRefNum
));
426 hfs_systemfile_unlock(vcb
, lockflags
);
428 FREE (btIterator
, M_TEMP
);
433 static OSErr
DeleteExtentRecord(
434 const ExtendedVCB
*vcb
,
437 u_int32_t startBlock
)
439 struct BTreeIterator
*btIterator
= NULL
;
444 MALLOC (btIterator
, struct BTreeIterator
*, sizeof(struct BTreeIterator
), M_TEMP
, M_WAITOK
);
445 if (btIterator
== NULL
) {
446 return memFullErr
; // translates to ENOMEM
448 bzero(btIterator
, sizeof(*btIterator
));
451 if (vcb
->vcbSigWord
!= kHFSSigWord
) { // HFS Plus volume
452 HFSPlusExtentKey
* keyPtr
;
454 keyPtr
= (HFSPlusExtentKey
*) &btIterator
->key
;
455 keyPtr
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
456 keyPtr
->forkType
= forkType
;
458 keyPtr
->fileID
= fileID
;
459 keyPtr
->startBlock
= startBlock
;
464 HFSExtentKey
* keyPtr
;
466 keyPtr
= (HFSExtentKey
*) &btIterator
->key
;
467 keyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
468 keyPtr
->forkType
= forkType
;
469 keyPtr
->fileID
= fileID
;
470 keyPtr
->startBlock
= startBlock
;
474 err
= BTDeleteRecord(GetFileControlBlock(vcb
->extentsRefNum
), btIterator
);
475 (void) BTFlushPath(GetFileControlBlock(vcb
->extentsRefNum
));
478 FREE(btIterator
, M_TEMP
);
484 //_________________________________________________________________________________
486 // Routine: MapFileBlock
488 // Function: Maps a file position into a physical disk address.
490 //_________________________________________________________________________________
492 OSErr
MapFileBlockC (
493 ExtendedVCB
*vcb
, // volume that file resides on
494 FCB
*fcb
, // FCB of file
495 size_t numberOfBytes
, // number of contiguous bytes desired
496 off_t offset
, // starting offset within file (in bytes)
497 daddr64_t
*startSector
, // first sector (NOT an allocation block)
498 size_t *availableBytes
) // number of contiguous bytes (up to numberOfBytes)
501 u_int32_t allocBlockSize
; // Size of the volume's allocation block
502 u_int32_t sectorSize
;
503 HFSPlusExtentKey foundKey
;
504 HFSPlusExtentRecord foundData
;
505 u_int32_t foundIndex
;
507 u_int32_t firstFABN
; // file allocation block of first block in found extent
508 u_int32_t nextFABN
; // file allocation block of block after end of found extent
509 off_t dataEnd
; // (offset) end of range that is contiguous
510 u_int32_t sectorsPerBlock
; // Number of sectors per allocation block
511 u_int32_t startBlock
; // volume allocation block corresponding to firstFABN
515 allocBlockSize
= vcb
->blockSize
;
516 sectorSize
= VCBTOHFS(vcb
)->hfs_logical_block_size
;
518 err
= SearchExtentFile(vcb
, fcb
, offset
, &foundKey
, foundData
, &foundIndex
, &hint
, &nextFABN
);
520 startBlock
= foundData
[foundIndex
].startBlock
;
521 firstFABN
= nextFABN
- foundData
[foundIndex
].blockCount
;
530 // Determine the end of the available space. It will either be the end of the extent,
531 // or the file's PEOF, whichever is smaller.
533 dataEnd
= (off_t
)((off_t
)(nextFABN
) * (off_t
)(allocBlockSize
)); // Assume valid data through end of this extent
534 if (((off_t
)fcb
->ff_blocks
* (off_t
)allocBlockSize
) < dataEnd
) // Is PEOF shorter?
535 dataEnd
= (off_t
)fcb
->ff_blocks
* (off_t
)allocBlockSize
; // Yes, so only map up to PEOF
537 // Compute the number of sectors in an allocation block
538 sectorsPerBlock
= allocBlockSize
/ sectorSize
; // sectors per allocation block
541 // Compute the absolute sector number that contains the offset of the given file
542 // offset in sectors from start of the extent +
543 // offset in sectors from start of allocation block space
545 temp
= (daddr64_t
)((offset
- (off_t
)((off_t
)(firstFABN
) * (off_t
)(allocBlockSize
)))/sectorSize
);
546 temp
+= (daddr64_t
)startBlock
* (daddr64_t
)sectorsPerBlock
;
548 /* Add in any volume offsets */
549 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
550 temp
+= vcb
->hfsPlusIOPosOffset
/ sectorSize
;
552 temp
+= vcb
->vcbAlBlSt
;
554 // Return the desired sector for file position "offset"
558 // Determine the number of contiguous bytes until the end of the extent
559 // (or the amount they asked for, whichever comes first).
563 tmpOff
= dataEnd
- offset
;
565 * Disallow negative runs.
571 if (tmpOff
> (off_t
)(numberOfBytes
)) {
572 *availableBytes
= numberOfBytes
; // more there than they asked for, so pin the output
575 *availableBytes
= tmpOff
;
583 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
584 // Routine: ReleaseExtents
586 // Function: Release the extents of a single extent data record.
587 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
589 static OSErr
ReleaseExtents(
591 const HFSPlusExtentRecord extentRecord
,
592 u_int32_t
*numReleasedAllocationBlocks
,
593 Boolean
*releasedLastExtent
)
595 u_int32_t extentIndex
;
596 u_int32_t numberOfExtents
;
599 *numReleasedAllocationBlocks
= 0;
600 *releasedLastExtent
= false;
602 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
603 numberOfExtents
= kHFSPlusExtentDensity
;
605 numberOfExtents
= kHFSExtentDensity
;
607 for( extentIndex
= 0; extentIndex
< numberOfExtents
; extentIndex
++)
609 u_int32_t numAllocationBlocks
;
611 // Loop over the extent record and release the blocks associated with each extent.
613 numAllocationBlocks
= extentRecord
[extentIndex
].blockCount
;
614 if ( numAllocationBlocks
== 0 )
616 *releasedLastExtent
= true;
620 err
= BlockDeallocate( vcb
, extentRecord
[extentIndex
].startBlock
, numAllocationBlocks
, 0);
624 *numReleasedAllocationBlocks
+= numAllocationBlocks
; // bump FABN to beg of next extent
632 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
633 // Routine: TruncateExtents
635 // Purpose: Delete extent records whose starting file allocation block number
636 // is greater than or equal to a given starting block number. The
637 // allocation blocks represented by the extents are deallocated.
640 // vcb Volume to operate on
641 // fileID Which file to operate on
642 // startBlock Starting file allocation block number for first extent
644 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
646 static OSErr
TruncateExtents(
650 u_int32_t startBlock
,
651 Boolean
* recordDeleted
)
654 u_int32_t numberExtentsReleased
;
655 Boolean releasedLastExtent
;
657 HFSPlusExtentKey key
;
658 HFSPlusExtentRecord extents
;
662 * The lock taken by callers of TruncateFileC is speculative and
663 * only occurs when the file already has overflow extents. So
664 * We need to make sure we have the lock here. The extents
665 * btree lock can be nested (its recursive) so we always take
668 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
671 err
= FindExtentRecord(vcb
, forkType
, fileID
, startBlock
, false, &key
, extents
, &hint
);
673 if (err
== btNotFound
)
678 err
= ReleaseExtents( vcb
, extents
, &numberExtentsReleased
, &releasedLastExtent
);
679 if (err
!= noErr
) break;
681 err
= DeleteExtentRecord(vcb
, forkType
, fileID
, startBlock
);
682 if (err
!= noErr
) break;
684 *recordDeleted
= true;
685 startBlock
+= numberExtentsReleased
;
687 hfs_systemfile_unlock(vcb
, lockflags
);
694 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
695 // Routine: DeallocateFork
697 // Function: De-allocates all disk space allocated to a specified fork.
698 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
700 static OSErr
DeallocateFork(
702 HFSCatalogNodeID fileID
,
704 HFSPlusExtentRecord catalogExtents
,
705 Boolean
* recordDeleted
) /* true if a record was deleted */
708 u_int32_t numReleasedAllocationBlocks
;
709 Boolean releasedLastExtent
;
711 // Release the catalog extents
712 err
= ReleaseExtents( vcb
, catalogExtents
, &numReleasedAllocationBlocks
, &releasedLastExtent
);
713 // Release the extra extents, if present
714 if (err
== noErr
&& !releasedLastExtent
)
715 err
= TruncateExtents(vcb
, forkType
, fileID
, numReleasedAllocationBlocks
, recordDeleted
);
720 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
721 // Routine: FlushExtentFile
723 // Function: Flushes the extent file for a specified volume
724 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
726 OSErr
FlushExtentFile( ExtendedVCB
*vcb
)
732 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
734 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
735 err
= BTFlushPath(fcb
);
736 hfs_systemfile_unlock(vcb
, lockflags
);
740 // If the FCB for the extent "file" is dirty, mark the VCB as dirty.
742 if (FTOC(fcb
)->c_flag
& C_MODIFIED
)
745 // err = FlushVolumeControlBlock( vcb );
754 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
755 // Routine: CompareExtentKeys
757 // Function: Compares two extent file keys (a search key and a trial key) for
759 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
762 int32_t CompareExtentKeys( const HFSExtentKey
*searchKey
, const HFSExtentKey
*trialKey
)
764 int32_t result
; // ± 1
767 if (searchKey
->keyLength
!= kHFSExtentKeyMaximumLength
)
768 DebugStr("HFS: search Key is wrong length");
769 if (trialKey
->keyLength
!= kHFSExtentKeyMaximumLength
)
770 DebugStr("HFS: trial Key is wrong length");
773 result
= -1; // assume searchKey < trialKey
775 if (searchKey
->fileID
== trialKey
->fileID
) {
777 // FileNum's are equal; compare fork types
779 if (searchKey
->forkType
== trialKey
->forkType
) {
781 // Fork types are equal; compare allocation block number
783 if (searchKey
->startBlock
== trialKey
->startBlock
) {
785 // Everything is equal
791 // Allocation block numbers differ; determine sign
793 if (searchKey
->startBlock
> trialKey
->startBlock
)
799 // Fork types differ; determine sign
801 if (searchKey
->forkType
> trialKey
->forkType
)
807 // FileNums differ; determine sign
809 if (searchKey
->fileID
> trialKey
->fileID
)
818 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
819 // Routine: CompareExtentKeysPlus
821 // Function: Compares two extent file keys (a search key and a trial key) for
823 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
826 int32_t CompareExtentKeysPlus( const HFSPlusExtentKey
*searchKey
, const HFSPlusExtentKey
*trialKey
)
828 int32_t result
; // ± 1
831 if (searchKey
->keyLength
!= kHFSPlusExtentKeyMaximumLength
)
832 DebugStr("HFS: search Key is wrong length");
833 if (trialKey
->keyLength
!= kHFSPlusExtentKeyMaximumLength
)
834 DebugStr("HFS: trial Key is wrong length");
837 result
= -1; // assume searchKey < trialKey
839 if (searchKey
->fileID
== trialKey
->fileID
) {
841 // FileNum's are equal; compare fork types
843 if (searchKey
->forkType
== trialKey
->forkType
) {
845 // Fork types are equal; compare allocation block number
847 if (searchKey
->startBlock
== trialKey
->startBlock
) {
849 // Everything is equal
855 // Allocation block numbers differ; determine sign
857 if (searchKey
->startBlock
> trialKey
->startBlock
)
863 // Fork types differ; determine sign
865 if (searchKey
->forkType
> trialKey
->forkType
)
871 // FileNums differ; determine sign
873 if (searchKey
->fileID
> trialKey
->fileID
)
881 * Add a file extent to a file.
883 * Used by hfs_extendfs to extend the volume allocation bitmap file.
887 AddFileExtent(ExtendedVCB
*vcb
, FCB
*fcb
, u_int32_t startBlock
, u_int32_t blockCount
)
889 HFSPlusExtentKey foundKey
;
890 HFSPlusExtentRecord foundData
;
891 u_int32_t foundIndex
;
898 peof
= (int64_t)(fcb
->ff_blocks
+ blockCount
) * (int64_t)vcb
->blockSize
;
900 error
= SearchExtentFile(vcb
, fcb
, peof
-1, &foundKey
, foundData
, &foundIndex
, &hint
, &nextBlock
);
901 if (error
!= fxRangeErr
)
905 * Add new extent. See if there is room in the current record.
907 if (foundData
[foundIndex
].blockCount
!= 0)
909 if (foundIndex
== kHFSPlusExtentDensity
) {
911 * Existing record is full so create a new one.
913 foundKey
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
914 foundKey
.forkType
= kDataForkType
;
916 foundKey
.fileID
= FTOC(fcb
)->c_fileid
;
917 foundKey
.startBlock
= nextBlock
;
919 foundData
[0].startBlock
= startBlock
;
920 foundData
[0].blockCount
= blockCount
;
922 /* zero out remaining extents. */
923 for (i
= 1; i
< kHFSPlusExtentDensity
; ++i
) {
924 foundData
[i
].startBlock
= 0;
925 foundData
[i
].blockCount
= 0;
930 error
= CreateExtentRecord(vcb
, &foundKey
, foundData
, &hint
);
931 if (error
== fxOvFlErr
)
935 * Add a new extent into existing record.
937 foundData
[foundIndex
].startBlock
= startBlock
;
938 foundData
[foundIndex
].blockCount
= blockCount
;
939 error
= UpdateExtentRecord(vcb
, fcb
, 0, &foundKey
, foundData
, hint
);
941 (void) FlushExtentFile(vcb
);
947 //_________________________________________________________________________________
949 // Routine: Extendfile
951 // Function: Extends the disk space allocated to a file.
953 //_________________________________________________________________________________
956 ExtendedVCB
*vcb
, // volume that file resides on
957 FCB
*fcb
, // FCB of file to truncate
958 int64_t bytesToAdd
, // number of bytes to allocate
959 u_int32_t blockHint
, // desired starting allocation block
960 u_int32_t flags
, // EFContig and/or EFAll
961 int64_t *actualBytesAdded
) // number of bytes actually allocated
964 u_int32_t volumeBlockSize
;
966 int64_t bytesThisExtent
;
967 HFSPlusExtentKey foundKey
;
968 HFSPlusExtentRecord foundData
;
969 u_int32_t foundIndex
;
972 u_int32_t startBlock
;
973 Boolean allOrNothing
;
979 u_int32_t actualStartBlock
;
980 u_int32_t actualNumBlocks
;
981 u_int32_t numExtentsPerRecord
;
982 int64_t maximumBytes
;
985 u_int32_t prevblocks
;
986 struct hfsmount
*hfsmp
= (struct hfsmount
*)vcb
;
989 *actualBytesAdded
= 0;
990 volumeBlockSize
= vcb
->blockSize
;
991 allOrNothing
= ((flags
& kEFAllMask
) != 0);
992 forceContig
= ((flags
& kEFContigMask
) != 0);
993 prevblocks
= fcb
->ff_blocks
;
995 if (vcb
->vcbSigWord
!= kHFSSigWord
) {
996 numExtentsPerRecord
= kHFSPlusExtentDensity
;
1001 numExtentsPerRecord
= kHFSExtentDensity
;
1003 /* Make sure the request and new PEOF are less than 2GB if HFS std*/
1004 if (bytesToAdd
>= kTwoGigabytes
)
1005 goto HFS_Std_Overflow
;
1006 if ((((int64_t)fcb
->ff_blocks
* (int64_t)volumeBlockSize
) + bytesToAdd
) >= kTwoGigabytes
)
1007 goto HFS_Std_Overflow
;
1012 // Determine how many blocks need to be allocated.
1013 // Round up the number of desired bytes to add.
1015 blocksToAdd
= howmany(bytesToAdd
, volumeBlockSize
);
1016 bytesToAdd
= (int64_t)((int64_t)blocksToAdd
* (int64_t)volumeBlockSize
);
1019 * For deferred allocations just reserve the blocks.
1021 if ((flags
& kEFDeferMask
)
1022 && (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1023 && (bytesToAdd
< (int64_t)HFS_MAX_DEFERED_ALLOC
)
1024 && (blocksToAdd
< hfs_freeblks(VCBTOHFS(vcb
), 1))) {
1025 hfs_lock_mount (hfsmp
);
1026 vcb
->loanedBlocks
+= blocksToAdd
;
1027 hfs_unlock_mount(hfsmp
);
1029 fcb
->ff_unallocblocks
+= blocksToAdd
;
1030 FTOC(fcb
)->c_blocks
+= blocksToAdd
;
1031 fcb
->ff_blocks
+= blocksToAdd
;
1033 FTOC(fcb
)->c_flag
|= C_MODIFIED
| C_FORCEUPDATE
;
1034 *actualBytesAdded
= bytesToAdd
;
1038 * Give back any unallocated blocks before doing real allocations.
1040 if (fcb
->ff_unallocblocks
> 0) {
1041 u_int32_t loanedBlocks
;
1043 loanedBlocks
= fcb
->ff_unallocblocks
;
1044 blocksToAdd
+= loanedBlocks
;
1045 bytesToAdd
= (int64_t)blocksToAdd
* (int64_t)volumeBlockSize
;
1046 FTOC(fcb
)->c_blocks
-= loanedBlocks
;
1047 fcb
->ff_blocks
-= loanedBlocks
;
1048 fcb
->ff_unallocblocks
= 0;
1050 hfs_lock_mount(hfsmp
);
1051 vcb
->loanedBlocks
-= loanedBlocks
;
1052 hfs_unlock_mount(hfsmp
);
1056 // If the file's clump size is larger than the allocation block size,
1057 // then set the maximum number of bytes to the requested number of bytes
1058 // rounded up to a multiple of the clump size.
1060 if ((vcb
->vcbClpSiz
> (int32_t)volumeBlockSize
)
1061 && (bytesToAdd
< (int64_t)HFS_MAX_DEFERED_ALLOC
)
1062 && (flags
& kEFNoClumpMask
) == 0) {
1063 maximumBytes
= (int64_t)howmany(bytesToAdd
, vcb
->vcbClpSiz
);
1064 maximumBytes
*= vcb
->vcbClpSiz
;
1066 maximumBytes
= bytesToAdd
;
1071 // Compute new physical EOF, rounded up to a multiple of a block.
1073 if ( (vcb
->vcbSigWord
== kHFSSigWord
) && // Too big?
1074 ((((int64_t)fcb
->ff_blocks
* (int64_t)volumeBlockSize
) + bytesToAdd
) >= kTwoGigabytes
) ) {
1075 if (allOrNothing
) // Yes, must they have it all?
1076 goto HFS_Std_Overflow
; // Yes, can't have it
1078 --blocksToAdd
; // No, give give 'em one block less
1079 bytesToAdd
-= volumeBlockSize
;
1085 // If allocation is all-or-nothing, make sure there are
1086 // enough free blocks on the volume (quick test).
1089 (blocksToAdd
> hfs_freeblks(VCBTOHFS(vcb
), flags
& kEFReserveMask
))) {
1095 // See if there are already enough blocks allocated to the file.
1097 peof
= ((int64_t)fcb
->ff_blocks
* (int64_t)volumeBlockSize
) + bytesToAdd
; // potential new PEOF
1098 err
= SearchExtentFile(vcb
, fcb
, peof
-1, &foundKey
, foundData
, &foundIndex
, &hint
, &nextBlock
);
1100 // Enough blocks are already allocated. Just update the FCB to reflect the new length.
1101 fcb
->ff_blocks
= peof
/ volumeBlockSize
;
1102 FTOC(fcb
)->c_blocks
+= (bytesToAdd
/ volumeBlockSize
);
1103 FTOC(fcb
)->c_flag
|= C_MODIFIED
| C_FORCEUPDATE
;
1106 if (err
!= fxRangeErr
) // Any real error?
1107 goto ErrorExit
; // Yes, so exit immediately
1110 // Adjust the PEOF to the end of the last extent.
1112 peof
= (int64_t)((int64_t)nextBlock
* (int64_t)volumeBlockSize
); // currently allocated PEOF
1113 bytesThisExtent
= (int64_t)(nextBlock
- fcb
->ff_blocks
) * (int64_t)volumeBlockSize
;
1114 if (bytesThisExtent
!= 0) {
1115 fcb
->ff_blocks
= nextBlock
;
1116 FTOC(fcb
)->c_blocks
+= (bytesThisExtent
/ volumeBlockSize
);
1117 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1118 bytesToAdd
-= bytesThisExtent
;
1122 // Allocate some more space.
1124 // First try a contiguous allocation (of the whole amount).
1125 // If that fails, get whatever we can.
1126 // If forceContig, then take whatever we got
1127 // else, keep getting bits and pieces (non-contig)
1130 * Note that for sparse devices (like sparse bundle dmgs), we
1131 * should only be aggressive with re-using once-allocated pieces
1132 * if we're not dealing with system files. If we're trying to operate
1133 * on behalf of a system file, we need the maximum contiguous amount
1134 * possible. For non-system files we favor locality and fragmentation over
1135 * contiguity as it can result in fewer blocks being needed from the underlying
1136 * filesystem that the sparse image resides upon.
1139 if ( (vcb
->hfs_flags
& HFS_HAS_SPARSE_DEVICE
)
1140 && (fcb
->ff_cp
->c_fileid
>= kHFSFirstUserCatalogNodeID
)
1141 && (flags
& kEFMetadataMask
) == 0) {
1143 * We want locality over contiguity so by default we set wantContig to
1144 * false unless we hit one of the circumstances below.
1147 if (hfs_isrbtree_active(VCBTOHFS(vcb
))) {
1149 * If the red-black tree is acive, we can always find a suitable contiguous
1150 * chunk. So if the user specifically requests contiguous files, we should
1151 * honor that no matter what kind of device it is.
1159 * If the red-black tree is not active, then only set wantContig to true
1160 * if we have never done a contig scan on the device, which would populate
1161 * the free extent cache. Note that the caller may explicitly unset the
1162 * DID_CONTIG_SCAN bit in order to force us to vend a contiguous extent here
1163 * if the caller wants to get a contiguous chunk.
1165 if ((vcb
->hfs_flags
& HFS_DID_CONTIG_SCAN
) == 0) {
1166 vcb
->hfs_flags
|= HFS_DID_CONTIG_SCAN
;
1176 useMetaZone
= flags
& kEFMetadataMask
;
1179 startBlock
= blockHint
;
1181 startBlock
= foundData
[foundIndex
].startBlock
+ foundData
[foundIndex
].blockCount
;
1183 actualNumBlocks
= 0;
1184 actualStartBlock
= 0;
1186 /* Find number of free blocks based on reserved block flag option */
1187 availbytes
= (int64_t)hfs_freeblks(VCBTOHFS(vcb
), flags
& kEFReserveMask
) *
1188 (int64_t)volumeBlockSize
;
1189 if (availbytes
<= 0) {
1192 if (wantContig
&& (availbytes
< bytesToAdd
)) {
1196 uint32_t ba_flags
= 0;
1199 ba_flags
|= HFS_ALLOC_FORCECONTIG
;
1202 ba_flags
|= HFS_ALLOC_METAZONE
;
1204 if (allowFlushTxns
) {
1205 ba_flags
|= HFS_ALLOC_FLUSHTXN
;
1208 err
= BlockAllocate(
1211 howmany(MIN(bytesToAdd
, availbytes
), volumeBlockSize
),
1212 howmany(MIN(maximumBytes
, availbytes
), volumeBlockSize
),
1218 if (err
== dskFulErr
) {
1220 if (allowFlushTxns
== 0) {
1221 /* If we're forcing contiguity, re-try but allow plucking from recently freed regions */
1228 break; // AllocContig failed because not enough contiguous space
1232 // Couldn't get one big chunk, so get whatever we can.
1237 if (actualNumBlocks
!= 0)
1240 if (useMetaZone
== 0) {
1241 /* Couldn't get anything so dip into metadat zone */
1247 /* If we couldn't find what we needed without flushing the journal, then go ahead and do it now */
1248 if (allowFlushTxns
== 0) {
1256 if (actualNumBlocks
!= 0) {
1257 // this catalog entry *must* get forced to disk when
1258 // hfs_update() is called
1259 FTOC(fcb
)->c_flag
|= C_FORCEUPDATE
;
1262 // Add the new extent to the existing extent record, or create a new one.
1263 if ((actualStartBlock
== startBlock
) && (blockHint
== 0)) {
1264 // We grew the file's last extent, so just adjust the number of blocks.
1265 foundData
[foundIndex
].blockCount
+= actualNumBlocks
;
1266 err
= UpdateExtentRecord(vcb
, fcb
, 0, &foundKey
, foundData
, hint
);
1267 if (err
!= noErr
) break;
1272 // Need to add a new extent. See if there is room in the current record.
1273 if (foundData
[foundIndex
].blockCount
!= 0) // Is current extent free to use?
1274 ++foundIndex
; // No, so use the next one.
1275 if (foundIndex
== numExtentsPerRecord
) {
1276 // This record is full. Need to create a new one.
1277 if (FTOC(fcb
)->c_fileid
== kHFSExtentsFileID
) {
1278 (void) BlockDeallocate(vcb
, actualStartBlock
, actualNumBlocks
, 0);
1279 err
= dskFulErr
; // Oops. Can't extend extents file past first record.
1283 foundKey
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
1284 if (FORK_IS_RSRC(fcb
))
1285 foundKey
.forkType
= kResourceForkType
;
1287 foundKey
.forkType
= kDataForkType
;
1289 foundKey
.fileID
= FTOC(fcb
)->c_fileid
;
1290 foundKey
.startBlock
= nextBlock
;
1292 foundData
[0].startBlock
= actualStartBlock
;
1293 foundData
[0].blockCount
= actualNumBlocks
;
1295 // zero out remaining extents...
1296 for (i
= 1; i
< kHFSPlusExtentDensity
; ++i
)
1298 foundData
[i
].startBlock
= 0;
1299 foundData
[i
].blockCount
= 0;
1304 err
= CreateExtentRecord(vcb
, &foundKey
, foundData
, &hint
);
1305 if (err
== fxOvFlErr
) {
1306 // We couldn't create an extent record because extents B-tree
1307 // couldn't grow. Dellocate the extent just allocated and
1308 // return a disk full error.
1309 (void) BlockDeallocate(vcb
, actualStartBlock
, actualNumBlocks
, 0);
1312 if (err
!= noErr
) break;
1314 needsFlush
= true; // We need to update the B-tree header
1317 // Add a new extent into this record and update.
1318 foundData
[foundIndex
].startBlock
= actualStartBlock
;
1319 foundData
[foundIndex
].blockCount
= actualNumBlocks
;
1320 err
= UpdateExtentRecord(vcb
, fcb
, 0, &foundKey
, foundData
, hint
);
1321 if (err
!= noErr
) break;
1325 // Figure out how many bytes were actually allocated.
1326 // NOTE: BlockAllocate could have allocated more than we asked for.
1327 // Don't set the PEOF beyond what our client asked for.
1328 nextBlock
+= actualNumBlocks
;
1329 bytesThisExtent
= (int64_t)((int64_t)actualNumBlocks
* (int64_t)volumeBlockSize
);
1330 if (bytesThisExtent
> bytesToAdd
) {
1334 bytesToAdd
-= bytesThisExtent
;
1335 maximumBytes
-= bytesThisExtent
;
1337 fcb
->ff_blocks
+= (bytesThisExtent
/ volumeBlockSize
);
1338 FTOC(fcb
)->c_blocks
+= (bytesThisExtent
/ volumeBlockSize
);
1339 FTOC(fcb
)->c_flag
|= C_MODIFIED
| C_FORCEUPDATE
;
1341 // If contiguous allocation was requested, then we've already got one contiguous
1342 // chunk. If we didn't get all we wanted, then adjust the error to disk full.
1344 if (bytesToAdd
!= 0)
1346 break; // We've already got everything that's contiguous
1349 } while (err
== noErr
&& bytesToAdd
);
1353 if (VCBTOHFS(vcb
)->hfs_flags
& HFS_METADATA_ZONE
) {
1354 /* Keep the roving allocator out of the metadata zone. */
1355 if (vcb
->nextAllocation
>= VCBTOHFS(vcb
)->hfs_metazone_start
&&
1356 vcb
->nextAllocation
<= VCBTOHFS(vcb
)->hfs_metazone_end
) {
1357 hfs_lock_mount (hfsmp
);
1358 HFS_UPDATE_NEXT_ALLOCATION(vcb
, VCBTOHFS(vcb
)->hfs_metazone_end
+ 1);
1360 hfs_unlock_mount(hfsmp
);
1363 if (prevblocks
< fcb
->ff_blocks
) {
1364 *actualBytesAdded
= (int64_t)(fcb
->ff_blocks
- prevblocks
) * (int64_t)volumeBlockSize
;
1366 *actualBytesAdded
= 0;
1370 (void) FlushExtentFile(vcb
);
1376 err
= fileBoundsErr
;
1383 //_________________________________________________________________________________
1385 // Routine: TruncateFileC
1387 // Function: Truncates the disk space allocated to a file. The file space is
1388 // truncated to a specified new PEOF rounded up to the next allocation
1389 // block boundry. If the 'TFTrunExt' option is specified, the file is
1390 // truncated to the end of the extent containing the new PEOF.
1392 //_________________________________________________________________________________
1394 OSErr
TruncateFileC (
1395 ExtendedVCB
*vcb
, // volume that file resides on
1396 FCB
*fcb
, // FCB of file to truncate
1397 int64_t peof
, // new physical size for file
1398 int deleted
, // if nonzero, the file's catalog record has already been deleted.
1399 int rsrc
, // does this represent a resource fork or not?
1400 uint32_t fileid
, // the fileid of the file we're manipulating.
1401 Boolean truncateToExtent
) // if true, truncate to end of extent containing newPEOF
1405 u_int32_t nextBlock
; // next file allocation block to consider
1406 u_int32_t startBlock
; // Physical (volume) allocation block number of start of a range
1407 u_int32_t physNumBlocks
; // Number of allocation blocks in file (according to PEOF)
1408 u_int32_t numBlocks
;
1409 HFSPlusExtentKey key
; // key for current extent record; key->keyLength == 0 if FCB's extent record
1410 u_int32_t hint
; // BTree hint corresponding to key
1411 HFSPlusExtentRecord extentRecord
;
1412 u_int32_t extentIndex
;
1413 u_int32_t extentNextBlock
;
1414 u_int32_t numExtentsPerRecord
;
1417 Boolean extentChanged
; // true if we actually changed an extent
1418 Boolean recordDeleted
; // true if an extent record got deleted
1420 recordDeleted
= false;
1422 if (vcb
->vcbSigWord
== kHFSPlusSigWord
) {
1423 numExtentsPerRecord
= kHFSPlusExtentDensity
;
1426 numExtentsPerRecord
= kHFSExtentDensity
;
1430 forkType
= kResourceForkType
;
1433 forkType
= kDataForkType
;
1436 temp64
= fcb
->ff_blocks
;
1437 physNumBlocks
= (u_int32_t
)temp64
;
1440 // Round newPEOF up to a multiple of the allocation block size. If new size is
1441 // two gigabytes or more, then round down by one allocation block (??? really?
1442 // shouldn't that be an error?).
1444 nextBlock
= howmany(peof
, vcb
->blockSize
); // number of allocation blocks to remain in file
1445 peof
= (int64_t)((int64_t)nextBlock
* (int64_t)vcb
->blockSize
); // number of bytes in those blocks
1448 if ((vcb
->vcbSigWord
== kHFSSigWord
) && (peof
>= kTwoGigabytes
)) {
1450 DebugStr("HFS: Trying to truncate a file to 2GB or more");
1452 err
= fileBoundsErr
;
1458 // Update FCB's length
1461 * XXX Any errors could cause ff_blocks and c_blocks to get out of sync...
1463 numBlocks
= peof
/ vcb
->blockSize
;
1465 FTOC(fcb
)->c_blocks
-= (fcb
->ff_blocks
- numBlocks
);
1467 fcb
->ff_blocks
= numBlocks
;
1469 // this catalog entry is modified and *must* get forced
1470 // to disk when hfs_update() is called
1473 * If the file is already C_NOEXISTS, then the catalog record
1474 * has been removed from disk already. We wouldn't need to force
1477 FTOC(fcb
)->c_flag
|= (C_MODIFIED
| C_FORCEUPDATE
);
1480 // If the new PEOF is 0, then truncateToExtent has no meaning (we should always deallocate
1486 // Deallocate all the extents for this fork
1487 err
= DeallocateFork(vcb
, fileid
, forkType
, fcb
->fcbExtents
, &recordDeleted
);
1488 if (err
!= noErr
) goto ErrorExit
; // got some error, so return it
1490 // Update the catalog extent record (making sure it's zeroed out)
1492 for (i
=0; i
< kHFSPlusExtentDensity
; i
++) {
1493 fcb
->fcbExtents
[i
].startBlock
= 0;
1494 fcb
->fcbExtents
[i
].blockCount
= 0;
1501 // Find the extent containing byte (peof-1). This is the last extent we'll keep.
1502 // (If truncateToExtent is true, we'll keep the whole extent; otherwise, we'll only
1503 // keep up through peof). The search will tell us how many allocation blocks exist
1504 // in the found extent plus all previous extents.
1506 err
= SearchExtentFile(vcb
, fcb
, peof
-1, &key
, extentRecord
, &extentIndex
, &hint
, &extentNextBlock
);
1507 if (err
!= noErr
) goto ErrorExit
;
1509 extentChanged
= false; // haven't changed the extent yet
1511 if (!truncateToExtent
) {
1513 // Shorten this extent. It may be the case that the entire extent gets
1516 numBlocks
= extentNextBlock
- nextBlock
; // How many blocks in this extent to free up
1517 if (numBlocks
!= 0) {
1518 // Compute first volume allocation block to free
1519 startBlock
= extentRecord
[extentIndex
].startBlock
+ extentRecord
[extentIndex
].blockCount
- numBlocks
;
1520 // Free the blocks in bitmap
1521 err
= BlockDeallocate(vcb
, startBlock
, numBlocks
, 0);
1522 if (err
!= noErr
) goto ErrorExit
;
1523 // Adjust length of this extent
1524 extentRecord
[extentIndex
].blockCount
-= numBlocks
;
1525 // If extent is empty, set start block to 0
1526 if (extentRecord
[extentIndex
].blockCount
== 0)
1527 extentRecord
[extentIndex
].startBlock
= 0;
1528 // Remember that we changed the extent record
1529 extentChanged
= true;
1534 // Now move to the next extent in the record, and set up the file allocation block number
1536 nextBlock
= extentNextBlock
; // Next file allocation block to free
1537 ++extentIndex
; // Its index within the extent record
1540 // Release all following extents in this extent record. Update the record.
1542 while (extentIndex
< numExtentsPerRecord
&& extentRecord
[extentIndex
].blockCount
!= 0) {
1543 numBlocks
= extentRecord
[extentIndex
].blockCount
;
1544 // Deallocate this extent
1545 err
= BlockDeallocate(vcb
, extentRecord
[extentIndex
].startBlock
, numBlocks
, 0);
1546 if (err
!= noErr
) goto ErrorExit
;
1547 // Update next file allocation block number
1548 nextBlock
+= numBlocks
;
1549 // Zero out start and length of this extent to delete it from record
1550 extentRecord
[extentIndex
].startBlock
= 0;
1551 extentRecord
[extentIndex
].blockCount
= 0;
1552 // Remember that we changed an extent
1553 extentChanged
= true;
1554 // Move to next extent in record
1559 // If any of the extents in the current record were changed, then update that
1560 // record (in the FCB, or extents file).
1562 if (extentChanged
) {
1563 err
= UpdateExtentRecord(vcb
, fcb
, deleted
, &key
, extentRecord
, hint
);
1564 if (err
!= noErr
) goto ErrorExit
;
1568 // If there are any following allocation blocks, then we need
1569 // to seach for their extent records and delete those allocation
1572 if (nextBlock
< physNumBlocks
)
1573 err
= TruncateExtents(vcb
, forkType
, fileid
, nextBlock
, &recordDeleted
);
1578 (void) FlushExtentFile(vcb
);
1588 OSErr
HeadTruncateFile (
1593 HFSPlusExtentRecord extents
;
1594 HFSPlusExtentRecord tailExtents
;
1595 HFSCatalogNodeID fileID
;
1599 u_int32_t blksfreed
;
1605 if (vcb
->vcbSigWord
!= kHFSPlusSigWord
)
1608 forkType
= FORK_IS_RSRC(fcb
) ? kResourceForkType
: kDataForkType
;
1609 fileID
= FTOC(fcb
)->c_fileid
;
1610 bzero(tailExtents
, sizeof(tailExtents
));
1616 * Process catalog resident extents
1618 for (i
= 0, j
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
1619 blkcnt
= fcb
->fcbExtents
[i
].blockCount
;
1621 break; /* end of extents */
1623 if (blksfreed
< headblks
) {
1624 error
= BlockDeallocate(vcb
, fcb
->fcbExtents
[i
].startBlock
, blkcnt
, 0);
1626 * Any errors after the first BlockDeallocate
1627 * must be ignored so we can put the file in
1632 goto ErrorExit
; /* uh oh */
1635 printf("hfs: HeadTruncateFile: problems deallocating %s (%d)\n",
1636 FTOC(fcb
)->c_desc
.cd_nameptr
? (const char *)FTOC(fcb
)->c_desc
.cd_nameptr
: "", error
);
1640 blksfreed
+= blkcnt
;
1641 fcb
->fcbExtents
[i
].startBlock
= 0;
1642 fcb
->fcbExtents
[i
].blockCount
= 0;
1644 tailExtents
[j
].startBlock
= fcb
->fcbExtents
[i
].startBlock
;
1645 tailExtents
[j
].blockCount
= blkcnt
;
1654 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
1657 * Process overflow extents
1662 error
= FindExtentRecord(vcb
, forkType
, fileID
, startblk
, false, NULL
, extents
, NULL
);
1665 * Any errors after the first BlockDeallocate
1666 * must be ignored so we can put the file in
1669 if (error
!= btNotFound
)
1670 printf("hfs: HeadTruncateFile: problems finding extents %s (%d)\n",
1671 FTOC(fcb
)->c_desc
.cd_nameptr
? (const char *)FTOC(fcb
)->c_desc
.cd_nameptr
: "", error
);
1676 for(i
= 0, extblks
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
1677 blkcnt
= extents
[i
].blockCount
;
1679 break; /* end of extents */
1681 if (blksfreed
< headblks
) {
1682 error
= BlockDeallocate(vcb
, extents
[i
].startBlock
, blkcnt
, 0);
1684 printf("hfs: HeadTruncateFile: problems deallocating %s (%d)\n",
1685 FTOC(fcb
)->c_desc
.cd_nameptr
? (const char *)FTOC(fcb
)->c_desc
.cd_nameptr
: "", error
);
1688 blksfreed
+= blkcnt
;
1690 tailExtents
[j
].startBlock
= extents
[i
].startBlock
;
1691 tailExtents
[j
].blockCount
= blkcnt
;
1697 error
= DeleteExtentRecord(vcb
, forkType
, fileID
, startblk
);
1699 printf("hfs: HeadTruncateFile: problems deallocating %s (%d)\n",
1700 FTOC(fcb
)->c_desc
.cd_nameptr
? (const char *)FTOC(fcb
)->c_desc
.cd_nameptr
: "", error
);
1705 break; /* all done */
1707 startblk
+= extblks
;
1709 hfs_systemfile_unlock(vcb
, lockflags
);
1713 bcopy(tailExtents
, fcb
->fcbExtents
, sizeof(tailExtents
));
1714 blkcnt
= fcb
->ff_blocks
- headblks
;
1715 FTOC(fcb
)->c_blocks
-= headblks
;
1716 fcb
->ff_blocks
= blkcnt
;
1718 FTOC(fcb
)->c_flag
|= C_FORCEUPDATE
;
1719 FTOC(fcb
)->c_touch_chgtime
= TRUE
;
1721 (void) FlushExtentFile(vcb
);
1725 return MacToVFSError(error
);
1730 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1731 // Routine: SearchExtentRecord (was XRSearch)
1733 // Function: Searches extent record for the extent mapping a given file
1734 // allocation block number (FABN).
1736 // Input: searchFABN - desired FABN
1737 // extentData - pointer to extent data record (xdr)
1738 // extentDataStartFABN - beginning FABN for extent record
1740 // Output: foundExtentDataOffset - offset to extent entry within xdr
1741 // result = noErr, offset to extent mapping desired FABN
1742 // result = FXRangeErr, offset to last extent in record
1743 // endingFABNPlusOne - ending FABN +1
1744 // noMoreExtents - True if the extent was not found, and the
1745 // extent record was not full (so don't bother
1746 // looking in subsequent records); false otherwise.
1748 // Result: noErr = ok
1749 // FXRangeErr = desired FABN > last mapped FABN in record
1750 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1752 static OSErr
SearchExtentRecord(
1754 u_int32_t searchFABN
,
1755 const HFSPlusExtentRecord extentData
,
1756 u_int32_t extentDataStartFABN
,
1757 u_int32_t
*foundExtentIndex
,
1758 u_int32_t
*endingFABNPlusOne
,
1759 Boolean
*noMoreExtents
)
1762 u_int32_t extentIndex
;
1763 /* Set it to the HFS std value */
1764 u_int32_t numberOfExtents
= kHFSExtentDensity
;
1765 u_int32_t numAllocationBlocks
;
1766 Boolean foundExtent
;
1768 *endingFABNPlusOne
= extentDataStartFABN
;
1769 *noMoreExtents
= false;
1770 foundExtent
= false;
1772 /* Override numberOfExtents for HFS+/HFSX */
1773 if (vcb
->vcbSigWord
!= kHFSSigWord
) {
1774 numberOfExtents
= kHFSPlusExtentDensity
;
1777 for( extentIndex
= 0; extentIndex
< numberOfExtents
; ++extentIndex
)
1780 // Loop over the extent record and find the search FABN.
1782 numAllocationBlocks
= extentData
[extentIndex
].blockCount
;
1783 if ( numAllocationBlocks
== 0 )
1788 *endingFABNPlusOne
+= numAllocationBlocks
;
1790 if( searchFABN
< *endingFABNPlusOne
)
1792 // Found the extent.
1800 // Found the extent. Note the extent offset
1801 *foundExtentIndex
= extentIndex
;
1805 // Did not find the extent. Set foundExtentDataOffset accordingly
1806 if( extentIndex
> 0 )
1808 *foundExtentIndex
= extentIndex
- 1;
1812 *foundExtentIndex
= 0;
1815 // If we found an empty extent, then set noMoreExtents.
1816 if (extentIndex
< numberOfExtents
)
1817 *noMoreExtents
= true;
1819 // Finally, return an error to the caller
1826 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1827 // Routine: SearchExtentFile (was XFSearch)
1829 // Function: Searches extent file (including the FCB resident extent record)
1830 // for the extent mapping a given file position.
1832 // Input: vcb - VCB pointer
1833 // fcb - FCB pointer
1834 // filePosition - file position (byte address)
1836 // Output: foundExtentKey - extent key record (xkr)
1837 // If extent was found in the FCB's resident extent record,
1838 // then foundExtentKey->keyLength will be set to 0.
1839 // foundExtentData - extent data record(xdr)
1840 // foundExtentIndex - index to extent entry in xdr
1841 // result = 0, offset to extent mapping desired FABN
1842 // result = FXRangeErr, offset to last extent in record
1843 // (i.e., kNumExtentsPerRecord-1)
1844 // extentBTreeHint - BTree hint for extent record
1845 // kNoHint = Resident extent record
1846 // endingFABNPlusOne - ending FABN +1
1849 // noErr Found an extent that contains the given file position
1850 // FXRangeErr Given position is beyond the last allocated extent
1851 // (other) (some other internal I/O error)
1852 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1854 static OSErr
SearchExtentFile(
1857 int64_t filePosition
,
1858 HFSPlusExtentKey
*foundExtentKey
,
1859 HFSPlusExtentRecord foundExtentData
,
1860 u_int32_t
*foundExtentIndex
,
1861 u_int32_t
*extentBTreeHint
,
1862 u_int32_t
*endingFABNPlusOne
)
1865 u_int32_t filePositionBlock
;
1867 Boolean noMoreExtents
;
1870 temp64
= filePosition
/ (int64_t)vcb
->blockSize
;
1871 filePositionBlock
= (u_int32_t
)temp64
;
1873 bcopy ( fcb
->fcbExtents
, foundExtentData
, sizeof(HFSPlusExtentRecord
));
1875 // Search the resident FCB first.
1876 err
= SearchExtentRecord( vcb
, filePositionBlock
, foundExtentData
, 0,
1877 foundExtentIndex
, endingFABNPlusOne
, &noMoreExtents
);
1879 if( err
== noErr
) {
1880 // Found the extent. Set results accordingly
1881 *extentBTreeHint
= kNoHint
; // no hint, because not in the BTree
1882 foundExtentKey
->keyLength
= 0; // 0 = the FCB itself
1887 // Didn't find extent in FCB. If FCB's extent record wasn't full, there's no point
1888 // in searching the extents file. Note that SearchExtentRecord left us pointing at
1889 // the last valid extent (or the first one, if none were valid). This means we need
1890 // to fill in the hint and key outputs, just like the "if" statement above.
1891 if ( noMoreExtents
) {
1892 *extentBTreeHint
= kNoHint
; // no hint, because not in the BTree
1893 foundExtentKey
->keyLength
= 0; // 0 = the FCB itself
1894 err
= fxRangeErr
; // There are no more extents, so must be beyond PEOF
1899 // Find the desired record, or the previous record if it is the same fork
1901 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
1903 err
= FindExtentRecord(vcb
, FORK_IS_RSRC(fcb
) ? kResourceForkType
: kDataForkType
,
1904 FTOC(fcb
)->c_fileid
, filePositionBlock
, true, foundExtentKey
, foundExtentData
, extentBTreeHint
);
1905 hfs_systemfile_unlock(vcb
, lockflags
);
1907 if (err
== btNotFound
) {
1909 // If we get here, the desired position is beyond the extents in the FCB, and there are no extents
1910 // in the extents file. Return the FCB's extents and a range error.
1912 *extentBTreeHint
= kNoHint
;
1913 foundExtentKey
->keyLength
= 0;
1914 err
= GetFCBExtentRecord(fcb
, foundExtentData
);
1915 // Note: foundExtentIndex and endingFABNPlusOne have already been set as a result of the very
1916 // first SearchExtentRecord call in this function (when searching in the FCB's extents, and
1917 // we got a range error).
1923 // If we get here, there was either a BTree error, or we found an appropriate record.
1924 // If we found a record, then search it for the correct index into the extents.
1927 // Find appropriate index into extent record
1928 err
= SearchExtentRecord(vcb
, filePositionBlock
, foundExtentData
, foundExtentKey
->startBlock
,
1929 foundExtentIndex
, endingFABNPlusOne
, &noMoreExtents
);
1938 //============================================================================
1939 // Routine: UpdateExtentRecord
1941 // Function: Write new extent data to an existing extent record with a given key.
1942 // If all of the extents are empty, and the extent record is in the
1943 // extents file, then the record is deleted.
1945 // Input: vcb - the volume containing the extents
1946 // fcb - the file that owns the extents
1947 // deleted - whether or not the file is already deleted
1948 // extentFileKey - pointer to extent key record (xkr)
1949 // If the key length is 0, then the extents are actually part
1950 // of the catalog record, stored in the FCB.
1951 // extentData - pointer to extent data record (xdr)
1952 // extentBTreeHint - hint for given key, or kNoHint
1954 // Result: noErr = ok
1955 // (other) = error from BTree
1956 //============================================================================
1958 static OSErr
UpdateExtentRecord (ExtendedVCB
*vcb
, FCB
*fcb
, int deleted
,
1959 const HFSPlusExtentKey
*extentFileKey
,
1960 const HFSPlusExtentRecord extentData
,
1961 u_int32_t extentBTreeHint
)
1965 if (extentFileKey
->keyLength
== 0) { // keyLength == 0 means the FCB's extent record
1966 BlockMoveData(extentData
, fcb
->fcbExtents
, sizeof(HFSPlusExtentRecord
));
1968 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1972 struct BTreeIterator
*btIterator
= NULL
;
1973 FSBufferDescriptor btRecord
;
1974 u_int16_t btRecordSize
;
1979 // Need to find and change a record in Extents BTree
1981 btFCB
= GetFileControlBlock(vcb
->extentsRefNum
);
1983 MALLOC (btIterator
, struct BTreeIterator
*, sizeof(struct BTreeIterator
), M_TEMP
, M_WAITOK
);
1984 if (btIterator
== NULL
) {
1985 return memFullErr
; // translates to ENOMEM
1987 bzero(btIterator
, sizeof(*btIterator
));
1990 * The lock taken by callers of ExtendFileC/TruncateFileC is
1991 * speculative and only occurs when the file already has
1992 * overflow extents. So we need to make sure we have the lock
1993 * here. The extents btree lock can be nested (its recursive)
1994 * so we always take it here.
1996 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
1999 if (vcb
->vcbSigWord
!= kHFSSigWord
) { // HFS Plus volume
2000 HFSPlusExtentRecord foundData
; // The extent data actually found
2002 BlockMoveData(extentFileKey
, &btIterator
->key
, sizeof(HFSPlusExtentKey
));
2004 btIterator
->hint
.index
= 0;
2005 btIterator
->hint
.nodeNum
= extentBTreeHint
;
2007 btRecord
.bufferAddress
= &foundData
;
2008 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
2009 btRecord
.itemCount
= 1;
2011 err
= BTSearchRecord(btFCB
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
2014 BlockMoveData(extentData
, &foundData
, sizeof(HFSPlusExtentRecord
));
2015 err
= BTReplaceRecord(btFCB
, btIterator
, &btRecord
, btRecordSize
);
2017 (void) BTFlushPath(btFCB
);
2022 HFSExtentKey
* key
; // Actual extent key used on disk in HFS
2023 HFSExtentRecord foundData
; // The extent data actually found
2025 key
= (HFSExtentKey
*) &btIterator
->key
;
2026 key
->keyLength
= kHFSExtentKeyMaximumLength
;
2027 key
->forkType
= extentFileKey
->forkType
;
2028 key
->fileID
= extentFileKey
->fileID
;
2029 key
->startBlock
= extentFileKey
->startBlock
;
2031 btIterator
->hint
.index
= 0;
2032 btIterator
->hint
.nodeNum
= extentBTreeHint
;
2034 btRecord
.bufferAddress
= &foundData
;
2035 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
2036 btRecord
.itemCount
= 1;
2038 err
= BTSearchRecord(btFCB
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
2041 err
= HFSPlusToHFSExtents(extentData
, (HFSExtentDescriptor
*)&foundData
);
2044 err
= BTReplaceRecord(btFCB
, btIterator
, &btRecord
, btRecordSize
);
2045 (void) BTFlushPath(btFCB
);
2050 hfs_systemfile_unlock(vcb
, lockflags
);
2052 FREE(btIterator
, M_TEMP
);
2061 static OSErr
HFSPlusToHFSExtents(
2062 const HFSPlusExtentRecord oldExtents
,
2063 HFSExtentRecord newExtents
)
2069 // copy the first 3 extents
2070 newExtents
[0].startBlock
= oldExtents
[0].startBlock
;
2071 newExtents
[0].blockCount
= oldExtents
[0].blockCount
;
2072 newExtents
[1].startBlock
= oldExtents
[1].startBlock
;
2073 newExtents
[1].blockCount
= oldExtents
[1].blockCount
;
2074 newExtents
[2].startBlock
= oldExtents
[2].startBlock
;
2075 newExtents
[2].blockCount
= oldExtents
[2].blockCount
;
2078 if (oldExtents
[3].startBlock
|| oldExtents
[3].blockCount
) {
2079 DebugStr("ExtentRecord with > 3 extents is invalid for HFS");
2090 static OSErr
GetFCBExtentRecord(
2092 HFSPlusExtentRecord extents
)
2095 BlockMoveData(fcb
->fcbExtents
, extents
, sizeof(HFSPlusExtentRecord
));
2101 //_________________________________________________________________________________
2103 // Routine: ExtentsAreIntegral
2105 // Purpose: Ensure that each extent can hold an integral number of nodes
2106 // Called by the NodesAreContiguous function
2107 //_________________________________________________________________________________
2109 static Boolean
ExtentsAreIntegral(
2110 const HFSPlusExtentRecord extentRecord
,
2112 u_int32_t
*blocksChecked
,
2113 Boolean
*checkedLastExtent
)
2116 u_int32_t extentIndex
;
2119 *checkedLastExtent
= false;
2121 for(extentIndex
= 0; extentIndex
< kHFSPlusExtentDensity
; extentIndex
++)
2123 blocks
= extentRecord
[extentIndex
].blockCount
;
2127 *checkedLastExtent
= true;
2131 *blocksChecked
+= blocks
;
2141 //_________________________________________________________________________________
2143 // Routine: NodesAreContiguous
2145 // Purpose: Ensure that all b-tree nodes are contiguous on disk
2146 // Called by BTOpenPath during volume mount
2147 //_________________________________________________________________________________
2149 Boolean
NodesAreContiguous(
2155 u_int32_t startBlock
;
2156 u_int32_t blocksChecked
;
2158 HFSPlusExtentKey key
;
2159 HFSPlusExtentRecord extents
;
2161 Boolean lastExtentReached
;
2165 if (vcb
->blockSize
>= nodeSize
)
2168 mask
= (nodeSize
/ vcb
->blockSize
) - 1;
2170 // check the local extents
2171 (void) GetFCBExtentRecord(fcb
, extents
);
2172 if ( !ExtentsAreIntegral(extents
, mask
, &blocksChecked
, &lastExtentReached
) )
2175 if ( lastExtentReached
||
2176 (int64_t)((int64_t)blocksChecked
* (int64_t)vcb
->blockSize
) >= (int64_t)fcb
->ff_size
)
2179 startBlock
= blocksChecked
;
2181 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
2183 // check the overflow extents (if any)
2184 while ( !lastExtentReached
)
2186 result
= FindExtentRecord(vcb
, kDataForkType
, fcb
->ff_cp
->c_fileid
, startBlock
, FALSE
, &key
, extents
, &hint
);
2189 if ( !ExtentsAreIntegral(extents
, mask
, &blocksChecked
, &lastExtentReached
) ) {
2190 hfs_systemfile_unlock(vcb
, lockflags
);
2193 startBlock
+= blocksChecked
;
2195 hfs_systemfile_unlock(vcb
, lockflags
);