2 * Copyright (c) 2000-2014 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>
38 #include <sys/vnode_internal.h>
41 ============================================================
42 Public (Exported) Routines:
43 ============================================================
45 ExtendFileC Allocate more space to a given file.
48 Compare two extents file keys (a search key and a trial
49 key). Used by the BTree manager when searching for,
50 adding, or deleting keys in the extents file of an HFS
54 Compare two extents file keys (a search key and a trial
55 key). Used by the BTree manager when searching for,
56 adding, or deleting keys in the extents file of an HFS+
59 MapFileBlockC Convert (map) an offset within a given file into a
60 physical disk address.
62 TruncateFileC Truncates the disk space allocated to a file. The file
63 space is truncated to a specified new physical EOF, rounded
64 up to the next allocation block boundry. There is an option
65 to truncate to the end of the extent containing the new EOF.
68 Flush the extents file for a given volume.
71 Search the FCB and extents file for an extent record that
72 contains a given file position (in bytes).
75 ============================================================
77 ============================================================
79 Search the extents BTree for a particular extent record.
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
SearchExtentRecord(
148 u_int32_t searchFABN
,
149 const HFSPlusExtentRecord extentData
,
150 u_int32_t extentDataStartFABN
,
151 u_int32_t
*foundExtentDataOffset
,
152 u_int32_t
*endingFABNPlusOne
,
153 Boolean
*noMoreExtents
);
155 static OSErr
ReleaseExtents(
157 const HFSPlusExtentRecord extentRecord
,
158 u_int32_t
*numReleasedAllocationBlocks
,
159 Boolean
*releasedLastExtent
);
161 static OSErr
DeallocateFork(
163 HFSCatalogNodeID fileID
,
165 HFSPlusExtentRecord catalogExtents
,
166 Boolean
* recordDeleted
);
168 static OSErr
TruncateExtents(
172 u_int32_t startBlock
,
173 Boolean
* recordDeleted
);
175 static OSErr
UpdateExtentRecord (
179 const HFSPlusExtentKey
*extentFileKey
,
180 const HFSPlusExtentRecord extentData
,
181 u_int32_t extentBTreeHint
);
183 static Boolean
ExtentsAreIntegral(
184 const HFSPlusExtentRecord extentRecord
,
186 u_int32_t
*blocksChecked
,
187 Boolean
*checkedLastExtent
);
189 //_________________________________________________________________________________
191 // Routine: FindExtentRecord
193 // Purpose: Search the extents BTree for an extent record matching the given
194 // FileID, fork, and starting file allocation block number.
197 // vcb Volume to search
198 // forkType 0 = data fork, -1 = resource fork
199 // fileID File's FileID (CatalogNodeID)
200 // startBlock Starting file allocation block number
201 // allowPrevious If the desired record isn't found and this flag is set,
202 // then see if the previous record belongs to the same fork.
203 // If so, then return it.
206 // foundKey The key data for the record actually found
207 // foundData The extent record actually found (NOTE: on an HFS volume, the
208 // fourth entry will be zeroes.
209 // foundHint The BTree hint to find the node again
210 //_________________________________________________________________________________
211 static OSErr
FindExtentRecord(
212 const ExtendedVCB
*vcb
,
215 u_int32_t startBlock
,
216 Boolean allowPrevious
,
217 HFSPlusExtentKey
*foundKey
,
218 HFSPlusExtentRecord foundData
,
219 u_int32_t
*foundHint
)
222 struct BTreeIterator
*btIterator
= NULL
;
223 FSBufferDescriptor btRecord
;
225 u_int16_t btRecordSize
;
230 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
232 MALLOC (btIterator
, struct BTreeIterator
*, sizeof(struct BTreeIterator
), M_TEMP
, M_WAITOK
);
233 if (btIterator
== NULL
) {
234 return memFullErr
; // translates to ENOMEM
236 bzero(btIterator
, sizeof(*btIterator
));
238 /* HFS Plus / HFSX */
239 if (vcb
->vcbSigWord
!= kHFSSigWord
) {
240 HFSPlusExtentKey
* extentKeyPtr
;
241 HFSPlusExtentRecord extentData
;
243 extentKeyPtr
= (HFSPlusExtentKey
*) &btIterator
->key
;
244 extentKeyPtr
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
245 extentKeyPtr
->forkType
= forkType
;
246 extentKeyPtr
->pad
= 0;
247 extentKeyPtr
->fileID
= fileID
;
248 extentKeyPtr
->startBlock
= startBlock
;
250 btRecord
.bufferAddress
= &extentData
;
251 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
252 btRecord
.itemCount
= 1;
254 err
= BTSearchRecord(fcb
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
256 if (err
== btNotFound
&& allowPrevious
) {
257 err
= BTIterateRecord(fcb
, kBTreePrevRecord
, btIterator
, &btRecord
, &btRecordSize
);
259 // A previous record may not exist, so just return btNotFound (like we would if
260 // it was for the wrong file/fork).
261 if (err
== (OSErr
) fsBTStartOfIterationErr
) //¥¥ fsBTStartOfIterationErr is type unsigned long
265 // Found a previous record. Does it belong to the same fork of the same file?
266 if (extentKeyPtr
->fileID
!= fileID
|| extentKeyPtr
->forkType
!= forkType
)
272 // Copy the found key back for the caller
274 BlockMoveData(extentKeyPtr
, foundKey
, sizeof(HFSPlusExtentKey
));
275 // Copy the found data back for the caller
276 BlockMoveData(&extentData
, foundData
, sizeof(HFSPlusExtentRecord
));
281 HFSExtentKey
* extentKeyPtr
;
282 HFSExtentRecord extentData
;
284 extentKeyPtr
= (HFSExtentKey
*) &btIterator
->key
;
285 extentKeyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
286 extentKeyPtr
->forkType
= forkType
;
287 extentKeyPtr
->fileID
= fileID
;
288 extentKeyPtr
->startBlock
= startBlock
;
290 btRecord
.bufferAddress
= &extentData
;
291 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
292 btRecord
.itemCount
= 1;
294 err
= BTSearchRecord(fcb
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
296 if (err
== btNotFound
&& allowPrevious
) {
297 err
= BTIterateRecord(fcb
, kBTreePrevRecord
, btIterator
, &btRecord
, &btRecordSize
);
299 // A previous record may not exist, so just return btNotFound (like we would if
300 // it was for the wrong file/fork).
301 if (err
== (OSErr
) fsBTStartOfIterationErr
) //¥¥ fsBTStartOfIterationErr is type unsigned long
305 // Found a previous record. Does it belong to the same fork of the same file?
306 if (extentKeyPtr
->fileID
!= fileID
|| extentKeyPtr
->forkType
!= forkType
)
314 // Copy the found key back for the caller
316 foundKey
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
317 foundKey
->forkType
= extentKeyPtr
->forkType
;
319 foundKey
->fileID
= extentKeyPtr
->fileID
;
320 foundKey
->startBlock
= extentKeyPtr
->startBlock
;
322 // Copy the found data back for the caller
323 foundData
[0].startBlock
= extentData
[0].startBlock
;
324 foundData
[0].blockCount
= extentData
[0].blockCount
;
325 foundData
[1].startBlock
= extentData
[1].startBlock
;
326 foundData
[1].blockCount
= extentData
[1].blockCount
;
327 foundData
[2].startBlock
= extentData
[2].startBlock
;
328 foundData
[2].blockCount
= extentData
[2].blockCount
;
330 for (i
= 3; i
< kHFSPlusExtentDensity
; ++i
)
332 foundData
[i
].startBlock
= 0;
333 foundData
[i
].blockCount
= 0;
340 *foundHint
= btIterator
->hint
.nodeNum
;
342 FREE(btIterator
, M_TEMP
);
348 static OSErr
CreateExtentRecord(
350 HFSPlusExtentKey
*key
,
351 HFSPlusExtentRecord extents
,
354 struct BTreeIterator
*btIterator
= NULL
;
355 FSBufferDescriptor btRecord
;
356 u_int16_t btRecordSize
;
363 MALLOC (btIterator
, struct BTreeIterator
*, sizeof(struct BTreeIterator
), M_TEMP
, M_WAITOK
);
364 if (btIterator
== NULL
) {
365 return memFullErr
; // translates to ENOMEM
367 bzero(btIterator
, sizeof(*btIterator
));
370 * The lock taken by callers of ExtendFileC is speculative and
371 * only occurs when the file already has overflow extents. So
372 * We need to make sure we have the lock here. The extents
373 * btree lock can be nested (its recursive) so we always take
376 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
379 if (vcb
->vcbSigWord
!= kHFSSigWord
) {
380 btRecordSize
= sizeof(HFSPlusExtentRecord
);
381 btRecord
.bufferAddress
= extents
;
382 btRecord
.itemSize
= btRecordSize
;
383 btRecord
.itemCount
= 1;
385 BlockMoveData(key
, &btIterator
->key
, sizeof(HFSPlusExtentKey
));
390 HFSExtentKey
* keyPtr
;
391 HFSExtentRecord data
;
393 btRecordSize
= sizeof(HFSExtentRecord
);
394 btRecord
.bufferAddress
= &data
;
395 btRecord
.itemSize
= btRecordSize
;
396 btRecord
.itemCount
= 1;
398 keyPtr
= (HFSExtentKey
*) &btIterator
->key
;
399 keyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
400 keyPtr
->forkType
= key
->forkType
;
401 keyPtr
->fileID
= key
->fileID
;
402 keyPtr
->startBlock
= key
->startBlock
;
404 err
= HFSPlusToHFSExtents(extents
, data
);
409 err
= BTInsertRecord(GetFileControlBlock(vcb
->extentsRefNum
), btIterator
, &btRecord
, btRecordSize
);
412 *hint
= btIterator
->hint
.nodeNum
;
414 (void) BTFlushPath(GetFileControlBlock(vcb
->extentsRefNum
));
416 hfs_systemfile_unlock(vcb
, lockflags
);
418 FREE (btIterator
, M_TEMP
);
423 static OSErr
DeleteExtentRecord(
424 const ExtendedVCB
*vcb
,
427 u_int32_t startBlock
)
429 struct BTreeIterator
*btIterator
= NULL
;
434 MALLOC (btIterator
, struct BTreeIterator
*, sizeof(struct BTreeIterator
), M_TEMP
, M_WAITOK
);
435 if (btIterator
== NULL
) {
436 return memFullErr
; // translates to ENOMEM
438 bzero(btIterator
, sizeof(*btIterator
));
441 if (vcb
->vcbSigWord
!= kHFSSigWord
) { // HFS Plus volume
442 HFSPlusExtentKey
* keyPtr
;
444 keyPtr
= (HFSPlusExtentKey
*) &btIterator
->key
;
445 keyPtr
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
446 keyPtr
->forkType
= forkType
;
448 keyPtr
->fileID
= fileID
;
449 keyPtr
->startBlock
= startBlock
;
454 HFSExtentKey
* keyPtr
;
456 keyPtr
= (HFSExtentKey
*) &btIterator
->key
;
457 keyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
458 keyPtr
->forkType
= forkType
;
459 keyPtr
->fileID
= fileID
;
460 keyPtr
->startBlock
= startBlock
;
464 err
= BTDeleteRecord(GetFileControlBlock(vcb
->extentsRefNum
), btIterator
);
465 (void) BTFlushPath(GetFileControlBlock(vcb
->extentsRefNum
));
468 FREE(btIterator
, M_TEMP
);
474 //_________________________________________________________________________________
476 // Routine: MapFileBlock
478 // Function: Maps a file position into a physical disk address.
480 //_________________________________________________________________________________
482 OSErr
MapFileBlockC (
483 ExtendedVCB
*vcb
, // volume that file resides on
484 FCB
*fcb
, // FCB of file
485 size_t numberOfBytes
, // number of contiguous bytes desired
486 off_t offset
, // starting offset within file (in bytes)
487 daddr64_t
*startSector
, // first sector (NOT an allocation block)
488 size_t *availableBytes
) // number of contiguous bytes (up to numberOfBytes)
491 u_int32_t allocBlockSize
; // Size of the volume's allocation block
492 u_int32_t sectorSize
;
493 HFSPlusExtentKey foundKey
;
494 HFSPlusExtentRecord foundData
;
495 u_int32_t foundIndex
;
497 u_int32_t firstFABN
; // file allocation block of first block in found extent
498 u_int32_t nextFABN
; // file allocation block of block after end of found extent
499 off_t dataEnd
; // (offset) end of range that is contiguous
500 u_int32_t sectorsPerBlock
; // Number of sectors per allocation block
501 u_int32_t startBlock
; // volume allocation block corresponding to firstFABN
505 allocBlockSize
= vcb
->blockSize
;
506 sectorSize
= VCBTOHFS(vcb
)->hfs_logical_block_size
;
508 err
= SearchExtentFile(vcb
, fcb
, offset
, &foundKey
, foundData
, &foundIndex
, &hint
, &nextFABN
);
510 startBlock
= foundData
[foundIndex
].startBlock
;
511 firstFABN
= nextFABN
- foundData
[foundIndex
].blockCount
;
520 // Determine the end of the available space. It will either be the end of the extent,
521 // or the file's PEOF, whichever is smaller.
523 dataEnd
= (off_t
)((off_t
)(nextFABN
) * (off_t
)(allocBlockSize
)); // Assume valid data through end of this extent
524 if (((off_t
)fcb
->ff_blocks
* (off_t
)allocBlockSize
) < dataEnd
) // Is PEOF shorter?
525 dataEnd
= (off_t
)fcb
->ff_blocks
* (off_t
)allocBlockSize
; // Yes, so only map up to PEOF
527 // Compute the number of sectors in an allocation block
528 sectorsPerBlock
= allocBlockSize
/ sectorSize
; // sectors per allocation block
531 // Compute the absolute sector number that contains the offset of the given file
532 // offset in sectors from start of the extent +
533 // offset in sectors from start of allocation block space
535 temp
= (daddr64_t
)((offset
- (off_t
)((off_t
)(firstFABN
) * (off_t
)(allocBlockSize
)))/sectorSize
);
536 temp
+= (daddr64_t
)startBlock
* (daddr64_t
)sectorsPerBlock
;
538 /* Add in any volume offsets */
539 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
540 temp
+= vcb
->hfsPlusIOPosOffset
/ sectorSize
;
542 temp
+= vcb
->vcbAlBlSt
;
544 // Return the desired sector for file position "offset"
548 // Determine the number of contiguous bytes until the end of the extent
549 // (or the amount they asked for, whichever comes first).
553 tmpOff
= dataEnd
- offset
;
555 * Disallow negative runs.
561 if (tmpOff
> (off_t
)(numberOfBytes
)) {
562 *availableBytes
= numberOfBytes
; // more there than they asked for, so pin the output
565 *availableBytes
= tmpOff
;
573 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
574 // Routine: ReleaseExtents
576 // Function: Release the extents of a single extent data record.
577 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
579 static OSErr
ReleaseExtents(
581 const HFSPlusExtentRecord extentRecord
,
582 u_int32_t
*numReleasedAllocationBlocks
,
583 Boolean
*releasedLastExtent
)
585 u_int32_t extentIndex
;
586 u_int32_t numberOfExtents
;
589 *numReleasedAllocationBlocks
= 0;
590 *releasedLastExtent
= false;
592 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
593 numberOfExtents
= kHFSPlusExtentDensity
;
595 numberOfExtents
= kHFSExtentDensity
;
597 for( extentIndex
= 0; extentIndex
< numberOfExtents
; extentIndex
++)
599 u_int32_t numAllocationBlocks
;
601 // Loop over the extent record and release the blocks associated with each extent.
603 numAllocationBlocks
= extentRecord
[extentIndex
].blockCount
;
604 if ( numAllocationBlocks
== 0 )
606 *releasedLastExtent
= true;
610 err
= BlockDeallocate( vcb
, extentRecord
[extentIndex
].startBlock
, numAllocationBlocks
, 0);
614 *numReleasedAllocationBlocks
+= numAllocationBlocks
; // bump FABN to beg of next extent
622 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
623 // Routine: TruncateExtents
625 // Purpose: Delete extent records whose starting file allocation block number
626 // is greater than or equal to a given starting block number. The
627 // allocation blocks represented by the extents are deallocated.
630 // vcb Volume to operate on
631 // fileID Which file to operate on
632 // startBlock Starting file allocation block number for first extent
634 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
636 static OSErr
TruncateExtents(
640 u_int32_t startBlock
,
641 Boolean
* recordDeleted
)
644 u_int32_t numberExtentsReleased
;
645 Boolean releasedLastExtent
;
647 HFSPlusExtentKey key
;
648 HFSPlusExtentRecord extents
;
652 * The lock taken by callers of TruncateFileC is speculative and
653 * only occurs when the file already has overflow extents. So
654 * We need to make sure we have the lock here. The extents
655 * btree lock can be nested (its recursive) so we always take
658 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
661 err
= FindExtentRecord(vcb
, forkType
, fileID
, startBlock
, false, &key
, extents
, &hint
);
663 if (err
== btNotFound
)
668 err
= ReleaseExtents( vcb
, extents
, &numberExtentsReleased
, &releasedLastExtent
);
669 if (err
!= noErr
) break;
671 err
= DeleteExtentRecord(vcb
, forkType
, fileID
, startBlock
);
672 if (err
!= noErr
) break;
674 *recordDeleted
= true;
675 startBlock
+= numberExtentsReleased
;
677 hfs_systemfile_unlock(vcb
, lockflags
);
684 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
685 // Routine: DeallocateFork
687 // Function: De-allocates all disk space allocated to a specified fork.
688 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
690 static OSErr
DeallocateFork(
692 HFSCatalogNodeID fileID
,
694 HFSPlusExtentRecord catalogExtents
,
695 Boolean
* recordDeleted
) /* true if a record was deleted */
698 u_int32_t numReleasedAllocationBlocks
;
699 Boolean releasedLastExtent
;
701 // Release the catalog extents
702 err
= ReleaseExtents( vcb
, catalogExtents
, &numReleasedAllocationBlocks
, &releasedLastExtent
);
703 // Release the extra extents, if present
704 if (err
== noErr
&& !releasedLastExtent
)
705 err
= TruncateExtents(vcb
, forkType
, fileID
, numReleasedAllocationBlocks
, recordDeleted
);
710 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
711 // Routine: FlushExtentFile
713 // Function: Flushes the extent file for a specified volume
714 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
716 OSErr
FlushExtentFile( ExtendedVCB
*vcb
)
722 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
724 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
725 err
= BTFlushPath(fcb
);
726 hfs_systemfile_unlock(vcb
, lockflags
);
730 // If the FCB for the extent "file" is dirty, mark the VCB as dirty.
732 if (FTOC(fcb
)->c_flag
& C_MODIFIED
)
735 // err = FlushVolumeControlBlock( vcb );
744 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
745 // Routine: CompareExtentKeys
747 // Function: Compares two extent file keys (a search key and a trial key) for
749 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
752 int32_t CompareExtentKeys( const HFSExtentKey
*searchKey
, const HFSExtentKey
*trialKey
)
754 int32_t result
; // ± 1
757 if (searchKey
->keyLength
!= kHFSExtentKeyMaximumLength
)
758 DebugStr("HFS: search Key is wrong length");
759 if (trialKey
->keyLength
!= kHFSExtentKeyMaximumLength
)
760 DebugStr("HFS: trial Key is wrong length");
763 result
= -1; // assume searchKey < trialKey
765 if (searchKey
->fileID
== trialKey
->fileID
) {
767 // FileNum's are equal; compare fork types
769 if (searchKey
->forkType
== trialKey
->forkType
) {
771 // Fork types are equal; compare allocation block number
773 if (searchKey
->startBlock
== trialKey
->startBlock
) {
775 // Everything is equal
781 // Allocation block numbers differ; determine sign
783 if (searchKey
->startBlock
> trialKey
->startBlock
)
789 // Fork types differ; determine sign
791 if (searchKey
->forkType
> trialKey
->forkType
)
797 // FileNums differ; determine sign
799 if (searchKey
->fileID
> trialKey
->fileID
)
808 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
809 // Routine: CompareExtentKeysPlus
811 // Function: Compares two extent file keys (a search key and a trial key) for
813 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
816 int32_t CompareExtentKeysPlus( const HFSPlusExtentKey
*searchKey
, const HFSPlusExtentKey
*trialKey
)
818 int32_t result
; // ± 1
821 if (searchKey
->keyLength
!= kHFSPlusExtentKeyMaximumLength
)
822 DebugStr("HFS: search Key is wrong length");
823 if (trialKey
->keyLength
!= kHFSPlusExtentKeyMaximumLength
)
824 DebugStr("HFS: trial Key is wrong length");
827 result
= -1; // assume searchKey < trialKey
829 if (searchKey
->fileID
== trialKey
->fileID
) {
831 // FileNum's are equal; compare fork types
833 if (searchKey
->forkType
== trialKey
->forkType
) {
835 // Fork types are equal; compare allocation block number
837 if (searchKey
->startBlock
== trialKey
->startBlock
) {
839 // Everything is equal
845 // Allocation block numbers differ; determine sign
847 if (searchKey
->startBlock
> trialKey
->startBlock
)
853 // Fork types differ; determine sign
855 if (searchKey
->forkType
> trialKey
->forkType
)
861 // FileNums differ; determine sign
863 if (searchKey
->fileID
> trialKey
->fileID
)
871 should_pin_blocks(hfsmount_t
*hfsmp
, FCB
*fcb
)
873 if (!ISSET(hfsmp
->hfs_flags
, HFS_CS_HOTFILE_PIN
)
874 || fcb
->ff_cp
== NULL
|| fcb
->ff_cp
->c_vp
== NULL
) {
881 // File system metadata should get pinned
883 if (vnode_issystem(fcb
->ff_cp
->c_vp
)) {
888 // If a file is AutoCandidate, we should not pin its blocks because
889 // it was an automatically added file and this function is intended
890 // to pin new blocks being added to user-generated content.
892 if (fcb
->ff_cp
->c_attr
.ca_recflags
& kHFSAutoCandidateMask
) {
897 // If a file is marked FastDevPinned it is an existing pinned file
898 // or a new file that should be pinned.
900 // If a file is marked FastDevCandidate it is a new file that is
901 // being written to for the first time so we don't want to pin it
902 // just yet as it may not meet the criteria (i.e. too large).
904 if ((fcb
->ff_cp
->c_attr
.ca_recflags
& (kHFSFastDevPinnedMask
)) != 0) {
916 pin_blocks_if_needed(ExtendedVCB
*vcb
, FCB
*fcb
, u_int32_t startBlock
, u_int32_t blockCount
)
918 if (!should_pin_blocks(vcb
, fcb
)) {
922 // ask CoreStorage to pin the new blocks being added to this file
923 if (hfs_pin_block_range((struct hfsmount
*)vcb
, HFS_PIN_IT
, startBlock
, blockCount
, vfs_context_kernel()) == 0) {
924 struct vnode
*vp
= fcb
->ff_cp
->c_vp
;
926 // and make sure to keep our accounting in order
927 hfs_hotfile_adjust_blocks(vp
, -blockCount
);
934 * Add a file extent to a file.
936 * Used by hfs_extendfs to extend the volume allocation bitmap file.
940 AddFileExtent(ExtendedVCB
*vcb
, FCB
*fcb
, u_int32_t startBlock
, u_int32_t blockCount
)
942 HFSPlusExtentKey foundKey
;
943 HFSPlusExtentRecord foundData
;
944 u_int32_t foundIndex
;
951 peof
= (int64_t)(fcb
->ff_blocks
+ blockCount
) * (int64_t)vcb
->blockSize
;
953 error
= SearchExtentFile(vcb
, fcb
, peof
-1, &foundKey
, foundData
, &foundIndex
, &hint
, &nextBlock
);
954 if (error
!= fxRangeErr
)
958 * Add new extent. See if there is room in the current record.
960 if (foundData
[foundIndex
].blockCount
!= 0)
962 if (foundIndex
== kHFSPlusExtentDensity
) {
964 * Existing record is full so create a new one.
966 foundKey
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
967 foundKey
.forkType
= kDataForkType
;
969 foundKey
.fileID
= FTOC(fcb
)->c_fileid
;
970 foundKey
.startBlock
= nextBlock
;
972 foundData
[0].startBlock
= startBlock
;
973 foundData
[0].blockCount
= blockCount
;
975 /* zero out remaining extents. */
976 for (i
= 1; i
< kHFSPlusExtentDensity
; ++i
) {
977 foundData
[i
].startBlock
= 0;
978 foundData
[i
].blockCount
= 0;
983 error
= CreateExtentRecord(vcb
, &foundKey
, foundData
, &hint
);
984 if (error
== fxOvFlErr
) {
986 } else if (error
== 0) {
987 pin_blocks_if_needed(vcb
, fcb
, startBlock
, blockCount
);
992 * Add a new extent into existing record.
994 foundData
[foundIndex
].startBlock
= startBlock
;
995 foundData
[foundIndex
].blockCount
= blockCount
;
996 error
= UpdateExtentRecord(vcb
, fcb
, 0, &foundKey
, foundData
, hint
);
998 pin_blocks_if_needed(vcb
, fcb
, startBlock
, blockCount
);
1001 (void) FlushExtentFile(vcb
);
1007 //_________________________________________________________________________________
1009 // Routine: Extendfile
1011 // Function: Extends the disk space allocated to a file.
1013 //_________________________________________________________________________________
1016 ExtendedVCB
*vcb
, // volume that file resides on
1017 FCB
*fcb
, // FCB of file to truncate
1018 int64_t bytesToAdd
, // number of bytes to allocate
1019 u_int32_t blockHint
, // desired starting allocation block
1020 u_int32_t flags
, // EFContig and/or EFAll
1021 int64_t *actualBytesAdded
) // number of bytes actually allocated
1024 u_int32_t volumeBlockSize
;
1025 int64_t blocksToAdd
;
1026 int64_t bytesThisExtent
;
1027 HFSPlusExtentKey foundKey
;
1028 HFSPlusExtentRecord foundData
;
1029 u_int32_t foundIndex
;
1031 u_int32_t nextBlock
;
1032 u_int32_t startBlock
;
1033 Boolean allOrNothing
;
1034 Boolean forceContig
;
1036 Boolean useMetaZone
;
1039 u_int32_t actualStartBlock
;
1040 u_int32_t actualNumBlocks
;
1041 u_int32_t numExtentsPerRecord
;
1042 int64_t maximumBytes
;
1045 u_int32_t prevblocks
;
1046 uint32_t fastdev
= 0;
1048 struct hfsmount
*hfsmp
= (struct hfsmount
*)vcb
;
1051 *actualBytesAdded
= 0;
1052 volumeBlockSize
= vcb
->blockSize
;
1053 allOrNothing
= ((flags
& kEFAllMask
) != 0);
1054 forceContig
= ((flags
& kEFContigMask
) != 0);
1055 prevblocks
= fcb
->ff_blocks
;
1057 if (vcb
->vcbSigWord
!= kHFSSigWord
) {
1058 numExtentsPerRecord
= kHFSPlusExtentDensity
;
1063 numExtentsPerRecord
= kHFSExtentDensity
;
1065 /* Make sure the request and new PEOF are less than 2GB if HFS std*/
1066 if (bytesToAdd
>= kTwoGigabytes
)
1067 goto HFS_Std_Overflow
;
1068 if ((((int64_t)fcb
->ff_blocks
* (int64_t)volumeBlockSize
) + bytesToAdd
) >= kTwoGigabytes
)
1069 goto HFS_Std_Overflow
;
1074 // Determine how many blocks need to be allocated.
1075 // Round up the number of desired bytes to add.
1077 blocksToAdd
= howmany(bytesToAdd
, volumeBlockSize
);
1078 bytesToAdd
= (int64_t)((int64_t)blocksToAdd
* (int64_t)volumeBlockSize
);
1081 * For deferred allocations just reserve the blocks.
1083 if ((flags
& kEFDeferMask
)
1084 && (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1085 && (bytesToAdd
< (int64_t)HFS_MAX_DEFERED_ALLOC
)
1086 && (blocksToAdd
< hfs_freeblks(VCBTOHFS(vcb
), 1))) {
1087 hfs_lock_mount (hfsmp
);
1088 vcb
->loanedBlocks
+= blocksToAdd
;
1089 hfs_unlock_mount(hfsmp
);
1091 fcb
->ff_unallocblocks
+= blocksToAdd
;
1092 FTOC(fcb
)->c_blocks
+= blocksToAdd
;
1093 fcb
->ff_blocks
+= blocksToAdd
;
1096 * We haven't touched the disk here; no blocks have been
1097 * allocated and the volume will not be inconsistent if we
1098 * don't update the catalog record immediately.
1100 FTOC(fcb
)->c_flag
|= C_MINOR_MOD
;
1101 *actualBytesAdded
= bytesToAdd
;
1105 * Give back any unallocated blocks before doing real allocations.
1107 if (fcb
->ff_unallocblocks
> 0) {
1108 u_int32_t loanedBlocks
;
1110 loanedBlocks
= fcb
->ff_unallocblocks
;
1111 blocksToAdd
+= loanedBlocks
;
1112 bytesToAdd
= (int64_t)blocksToAdd
* (int64_t)volumeBlockSize
;
1113 FTOC(fcb
)->c_blocks
-= loanedBlocks
;
1114 fcb
->ff_blocks
-= loanedBlocks
;
1115 fcb
->ff_unallocblocks
= 0;
1117 hfs_lock_mount(hfsmp
);
1118 vcb
->loanedBlocks
-= loanedBlocks
;
1119 hfs_unlock_mount(hfsmp
);
1123 // If the file's clump size is larger than the allocation block size,
1124 // then set the maximum number of bytes to the requested number of bytes
1125 // rounded up to a multiple of the clump size.
1127 if ((vcb
->vcbClpSiz
> (int32_t)volumeBlockSize
)
1128 && (bytesToAdd
< (int64_t)HFS_MAX_DEFERED_ALLOC
)
1129 && (flags
& kEFNoClumpMask
) == 0) {
1130 maximumBytes
= (int64_t)howmany(bytesToAdd
, vcb
->vcbClpSiz
);
1131 maximumBytes
*= vcb
->vcbClpSiz
;
1133 maximumBytes
= bytesToAdd
;
1138 // Compute new physical EOF, rounded up to a multiple of a block.
1140 if ( (vcb
->vcbSigWord
== kHFSSigWord
) && // Too big?
1141 ((((int64_t)fcb
->ff_blocks
* (int64_t)volumeBlockSize
) + bytesToAdd
) >= kTwoGigabytes
) ) {
1142 if (allOrNothing
) // Yes, must they have it all?
1143 goto HFS_Std_Overflow
; // Yes, can't have it
1145 --blocksToAdd
; // No, give give 'em one block less
1146 bytesToAdd
-= volumeBlockSize
;
1152 // If allocation is all-or-nothing, make sure there are
1153 // enough free blocks on the volume (quick test).
1156 (blocksToAdd
> hfs_freeblks(VCBTOHFS(vcb
), flags
& kEFReserveMask
))) {
1162 // See if there are already enough blocks allocated to the file.
1164 peof
= ((int64_t)fcb
->ff_blocks
* (int64_t)volumeBlockSize
) + bytesToAdd
; // potential new PEOF
1165 err
= SearchExtentFile(vcb
, fcb
, peof
-1, &foundKey
, foundData
, &foundIndex
, &hint
, &nextBlock
);
1167 // Enough blocks are already allocated. Just update the FCB to reflect the new length.
1168 fcb
->ff_blocks
= peof
/ volumeBlockSize
;
1169 FTOC(fcb
)->c_blocks
+= (bytesToAdd
/ volumeBlockSize
);
1170 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1173 if (err
!= fxRangeErr
) // Any real error?
1174 goto ErrorExit
; // Yes, so exit immediately
1177 // Adjust the PEOF to the end of the last extent.
1179 peof
= (int64_t)((int64_t)nextBlock
* (int64_t)volumeBlockSize
); // currently allocated PEOF
1180 bytesThisExtent
= (int64_t)(nextBlock
- fcb
->ff_blocks
) * (int64_t)volumeBlockSize
;
1181 if (bytesThisExtent
!= 0) {
1182 fcb
->ff_blocks
= nextBlock
;
1183 FTOC(fcb
)->c_blocks
+= (bytesThisExtent
/ volumeBlockSize
);
1184 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1185 bytesToAdd
-= bytesThisExtent
;
1189 // Allocate some more space.
1191 // First try a contiguous allocation (of the whole amount).
1192 // If that fails, get whatever we can.
1193 // If forceContig, then take whatever we got
1194 // else, keep getting bits and pieces (non-contig)
1197 * Note that for sparse devices (like sparse bundle dmgs), we
1198 * should only be aggressive with re-using once-allocated pieces
1199 * if we're not dealing with system files. If we're trying to operate
1200 * on behalf of a system file, we need the maximum contiguous amount
1201 * possible. For non-system files we favor locality and fragmentation over
1202 * contiguity as it can result in fewer blocks being needed from the underlying
1203 * filesystem that the sparse image resides upon.
1206 if ( (vcb
->hfs_flags
& HFS_HAS_SPARSE_DEVICE
)
1207 && (fcb
->ff_cp
->c_fileid
>= kHFSFirstUserCatalogNodeID
)
1208 && (flags
& kEFMetadataMask
) == 0) {
1210 * We want locality over contiguity so by default we set wantContig to
1211 * false unless we hit one of the circumstances below.
1214 if (hfs_isrbtree_active(VCBTOHFS(vcb
))) {
1216 * If the red-black tree is acive, we can always find a suitable contiguous
1217 * chunk. So if the user specifically requests contiguous files, we should
1218 * honor that no matter what kind of device it is.
1226 * If the red-black tree is not active, then only set wantContig to true
1227 * if we have never done a contig scan on the device, which would populate
1228 * the free extent cache. Note that the caller may explicitly unset the
1229 * DID_CONTIG_SCAN bit in order to force us to vend a contiguous extent here
1230 * if the caller wants to get a contiguous chunk.
1232 if ((vcb
->hfs_flags
& HFS_DID_CONTIG_SCAN
) == 0) {
1233 vcb
->hfs_flags
|= HFS_DID_CONTIG_SCAN
;
1242 if (should_pin_blocks(hfsmp
, fcb
))
1243 fastdev
= HFS_ALLOC_FAST_DEV
;
1245 useMetaZone
= flags
& kEFMetadataMask
;
1248 startBlock
= blockHint
;
1250 startBlock
= foundData
[foundIndex
].startBlock
+ foundData
[foundIndex
].blockCount
;
1252 actualNumBlocks
= 0;
1253 actualStartBlock
= 0;
1255 /* Find number of free blocks based on reserved block flag option */
1256 availbytes
= (int64_t)hfs_freeblks(VCBTOHFS(vcb
), flags
& kEFReserveMask
) *
1257 (int64_t)volumeBlockSize
;
1258 if (availbytes
<= 0) {
1261 if (wantContig
&& (availbytes
< bytesToAdd
)) {
1265 uint32_t ba_flags
= fastdev
;
1268 ba_flags
|= HFS_ALLOC_FORCECONTIG
;
1271 ba_flags
|= HFS_ALLOC_METAZONE
;
1273 if (allowFlushTxns
) {
1274 ba_flags
|= HFS_ALLOC_FLUSHTXN
;
1277 err
= BlockAllocate(
1280 howmany(MIN(bytesToAdd
, availbytes
), volumeBlockSize
),
1281 howmany(MIN(maximumBytes
, availbytes
), volumeBlockSize
),
1287 if (err
== dskFulErr
) {
1289 if (allowFlushTxns
== 0) {
1290 /* If we're forcing contiguity, re-try but allow plucking from recently freed regions */
1297 break; // AllocContig failed because not enough contiguous space
1301 // Couldn't get one big chunk, so get whatever we can.
1306 if (actualNumBlocks
!= 0)
1309 if (useMetaZone
== 0) {
1310 /* Couldn't get anything so dip into metadat zone */
1316 /* If we couldn't find what we needed without flushing the journal, then go ahead and do it now */
1317 if (allowFlushTxns
== 0) {
1325 // Add the new extent to the existing extent record, or create a new one.
1326 if ((actualStartBlock
== startBlock
) && (blockHint
== 0)) {
1327 // We grew the file's last extent, so just adjust the number of blocks.
1328 foundData
[foundIndex
].blockCount
+= actualNumBlocks
;
1329 err
= UpdateExtentRecord(vcb
, fcb
, 0, &foundKey
, foundData
, hint
);
1330 if (err
!= noErr
) break;
1335 // Need to add a new extent. See if there is room in the current record.
1336 if (foundData
[foundIndex
].blockCount
!= 0) // Is current extent free to use?
1337 ++foundIndex
; // No, so use the next one.
1338 if (foundIndex
== numExtentsPerRecord
) {
1339 // This record is full. Need to create a new one.
1340 if (FTOC(fcb
)->c_fileid
== kHFSExtentsFileID
) {
1341 (void) BlockDeallocate(vcb
, actualStartBlock
, actualNumBlocks
, 0);
1342 err
= dskFulErr
; // Oops. Can't extend extents file past first record.
1346 foundKey
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
1347 if (FORK_IS_RSRC(fcb
))
1348 foundKey
.forkType
= kResourceForkType
;
1350 foundKey
.forkType
= kDataForkType
;
1352 foundKey
.fileID
= FTOC(fcb
)->c_fileid
;
1353 foundKey
.startBlock
= nextBlock
;
1355 foundData
[0].startBlock
= actualStartBlock
;
1356 foundData
[0].blockCount
= actualNumBlocks
;
1358 // zero out remaining extents...
1359 for (i
= 1; i
< kHFSPlusExtentDensity
; ++i
)
1361 foundData
[i
].startBlock
= 0;
1362 foundData
[i
].blockCount
= 0;
1367 err
= CreateExtentRecord(vcb
, &foundKey
, foundData
, &hint
);
1368 if (err
== fxOvFlErr
) {
1369 // We couldn't create an extent record because extents B-tree
1370 // couldn't grow. Dellocate the extent just allocated and
1371 // return a disk full error.
1372 (void) BlockDeallocate(vcb
, actualStartBlock
, actualNumBlocks
, 0);
1375 if (err
!= noErr
) break;
1377 needsFlush
= true; // We need to update the B-tree header
1380 // Add a new extent into this record and update.
1381 foundData
[foundIndex
].startBlock
= actualStartBlock
;
1382 foundData
[foundIndex
].blockCount
= actualNumBlocks
;
1383 err
= UpdateExtentRecord(vcb
, fcb
, 0, &foundKey
, foundData
, hint
);
1384 if (err
!= noErr
) break;
1388 // Figure out how many bytes were actually allocated.
1389 // NOTE: BlockAllocate could have allocated more than we asked for.
1390 // Don't set the PEOF beyond what our client asked for.
1391 nextBlock
+= actualNumBlocks
;
1392 bytesThisExtent
= (int64_t)((int64_t)actualNumBlocks
* (int64_t)volumeBlockSize
);
1393 if (bytesThisExtent
> bytesToAdd
) {
1397 bytesToAdd
-= bytesThisExtent
;
1398 maximumBytes
-= bytesThisExtent
;
1400 fcb
->ff_blocks
+= (bytesThisExtent
/ volumeBlockSize
);
1401 FTOC(fcb
)->c_blocks
+= (bytesThisExtent
/ volumeBlockSize
);
1402 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1404 // If contiguous allocation was requested, then we've already got one contiguous
1405 // chunk. If we didn't get all we wanted, then adjust the error to disk full.
1407 if (bytesToAdd
!= 0)
1409 break; // We've already got everything that's contiguous
1412 } while (err
== noErr
&& bytesToAdd
);
1416 if (VCBTOHFS(vcb
)->hfs_flags
& HFS_METADATA_ZONE
) {
1417 /* Keep the roving allocator out of the metadata zone. */
1418 if (vcb
->nextAllocation
>= VCBTOHFS(vcb
)->hfs_metazone_start
&&
1419 vcb
->nextAllocation
<= VCBTOHFS(vcb
)->hfs_metazone_end
) {
1420 hfs_lock_mount (hfsmp
);
1421 HFS_UPDATE_NEXT_ALLOCATION(vcb
, VCBTOHFS(vcb
)->hfs_metazone_end
+ 1);
1423 hfs_unlock_mount(hfsmp
);
1426 if (prevblocks
< fcb
->ff_blocks
) {
1427 *actualBytesAdded
= (int64_t)(fcb
->ff_blocks
- prevblocks
) * (int64_t)volumeBlockSize
;
1429 *actualBytesAdded
= 0;
1433 hfs_hotfile_adjust_blocks(fcb
->ff_cp
->c_vp
,
1434 (int64_t)prevblocks
- fcb
->ff_blocks
);
1438 (void) FlushExtentFile(vcb
);
1444 err
= fileBoundsErr
;
1451 //_________________________________________________________________________________
1453 // Routine: TruncateFileC
1455 // Function: Truncates the disk space allocated to a file. The file space is
1456 // truncated to a specified new PEOF rounded up to the next allocation
1457 // block boundry. If the 'TFTrunExt' option is specified, the file is
1458 // truncated to the end of the extent containing the new PEOF.
1460 //_________________________________________________________________________________
1462 OSErr
TruncateFileC (
1463 ExtendedVCB
*vcb
, // volume that file resides on
1464 FCB
*fcb
, // FCB of file to truncate
1465 int64_t peof
, // new physical size for file
1466 int deleted
, // if nonzero, the file's catalog record has already been deleted.
1467 int rsrc
, // does this represent a resource fork or not?
1468 uint32_t fileid
, // the fileid of the file we're manipulating.
1469 Boolean truncateToExtent
) // if true, truncate to end of extent containing newPEOF
1473 u_int32_t nextBlock
; // next file allocation block to consider
1474 u_int32_t startBlock
; // Physical (volume) allocation block number of start of a range
1475 u_int32_t physNumBlocks
; // Number of allocation blocks in file (according to PEOF)
1476 u_int32_t numBlocks
;
1477 HFSPlusExtentKey key
; // key for current extent record; key->keyLength == 0 if FCB's extent record
1478 u_int32_t hint
; // BTree hint corresponding to key
1479 HFSPlusExtentRecord extentRecord
;
1480 u_int32_t extentIndex
;
1481 u_int32_t extentNextBlock
;
1482 u_int32_t numExtentsPerRecord
;
1485 Boolean extentChanged
; // true if we actually changed an extent
1486 Boolean recordDeleted
; // true if an extent record got deleted
1488 recordDeleted
= false;
1490 if (vcb
->vcbSigWord
== kHFSPlusSigWord
) {
1491 numExtentsPerRecord
= kHFSPlusExtentDensity
;
1494 numExtentsPerRecord
= kHFSExtentDensity
;
1498 forkType
= kResourceForkType
;
1501 forkType
= kDataForkType
;
1504 temp64
= fcb
->ff_blocks
;
1505 physNumBlocks
= (u_int32_t
)temp64
;
1508 // Round newPEOF up to a multiple of the allocation block size. If new size is
1509 // two gigabytes or more, then round down by one allocation block (??? really?
1510 // shouldn't that be an error?).
1512 nextBlock
= howmany(peof
, vcb
->blockSize
); // number of allocation blocks to remain in file
1513 peof
= (int64_t)((int64_t)nextBlock
* (int64_t)vcb
->blockSize
); // number of bytes in those blocks
1516 if ((vcb
->vcbSigWord
== kHFSSigWord
) && (peof
>= kTwoGigabytes
)) {
1518 DebugStr("HFS: Trying to truncate a file to 2GB or more");
1520 err
= fileBoundsErr
;
1526 // Update FCB's length
1529 * XXX Any errors could cause ff_blocks and c_blocks to get out of sync...
1531 numBlocks
= peof
/ vcb
->blockSize
;
1533 FTOC(fcb
)->c_blocks
-= (fcb
->ff_blocks
- numBlocks
);
1535 fcb
->ff_blocks
= numBlocks
;
1537 // this catalog entry is modified and *must* get forced
1538 // to disk when hfs_update() is called
1541 * If the file is already C_NOEXISTS, then the catalog record
1542 * has been removed from disk already. We wouldn't need to force
1545 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1548 // If the new PEOF is 0, then truncateToExtent has no meaning (we should always deallocate
1554 // Deallocate all the extents for this fork
1555 err
= DeallocateFork(vcb
, fileid
, forkType
, fcb
->fcbExtents
, &recordDeleted
);
1556 if (err
!= noErr
) goto ErrorExit
; // got some error, so return it
1558 // Update the catalog extent record (making sure it's zeroed out)
1560 for (i
=0; i
< kHFSPlusExtentDensity
; i
++) {
1561 fcb
->fcbExtents
[i
].startBlock
= 0;
1562 fcb
->fcbExtents
[i
].blockCount
= 0;
1569 // Find the extent containing byte (peof-1). This is the last extent we'll keep.
1570 // (If truncateToExtent is true, we'll keep the whole extent; otherwise, we'll only
1571 // keep up through peof). The search will tell us how many allocation blocks exist
1572 // in the found extent plus all previous extents.
1574 err
= SearchExtentFile(vcb
, fcb
, peof
-1, &key
, extentRecord
, &extentIndex
, &hint
, &extentNextBlock
);
1575 if (err
!= noErr
) goto ErrorExit
;
1577 extentChanged
= false; // haven't changed the extent yet
1579 if (!truncateToExtent
) {
1581 // Shorten this extent. It may be the case that the entire extent gets
1584 numBlocks
= extentNextBlock
- nextBlock
; // How many blocks in this extent to free up
1585 if (numBlocks
!= 0) {
1586 // Compute first volume allocation block to free
1587 startBlock
= extentRecord
[extentIndex
].startBlock
+ extentRecord
[extentIndex
].blockCount
- numBlocks
;
1588 // Free the blocks in bitmap
1589 err
= BlockDeallocate(vcb
, startBlock
, numBlocks
, 0);
1590 if (err
!= noErr
) goto ErrorExit
;
1591 // Adjust length of this extent
1592 extentRecord
[extentIndex
].blockCount
-= numBlocks
;
1593 // If extent is empty, set start block to 0
1594 if (extentRecord
[extentIndex
].blockCount
== 0)
1595 extentRecord
[extentIndex
].startBlock
= 0;
1596 // Remember that we changed the extent record
1597 extentChanged
= true;
1602 // Now move to the next extent in the record, and set up the file allocation block number
1604 nextBlock
= extentNextBlock
; // Next file allocation block to free
1605 ++extentIndex
; // Its index within the extent record
1608 // Release all following extents in this extent record. Update the record.
1610 while (extentIndex
< numExtentsPerRecord
&& extentRecord
[extentIndex
].blockCount
!= 0) {
1611 numBlocks
= extentRecord
[extentIndex
].blockCount
;
1612 // Deallocate this extent
1613 err
= BlockDeallocate(vcb
, extentRecord
[extentIndex
].startBlock
, numBlocks
, 0);
1614 if (err
!= noErr
) goto ErrorExit
;
1615 // Update next file allocation block number
1616 nextBlock
+= numBlocks
;
1617 // Zero out start and length of this extent to delete it from record
1618 extentRecord
[extentIndex
].startBlock
= 0;
1619 extentRecord
[extentIndex
].blockCount
= 0;
1620 // Remember that we changed an extent
1621 extentChanged
= true;
1622 // Move to next extent in record
1627 // If any of the extents in the current record were changed, then update that
1628 // record (in the FCB, or extents file).
1630 if (extentChanged
) {
1631 err
= UpdateExtentRecord(vcb
, fcb
, deleted
, &key
, extentRecord
, hint
);
1632 if (err
!= noErr
) goto ErrorExit
;
1636 // If there are any following allocation blocks, then we need
1637 // to seach for their extent records and delete those allocation
1640 if (nextBlock
< physNumBlocks
)
1641 err
= TruncateExtents(vcb
, forkType
, fileid
, nextBlock
, &recordDeleted
);
1646 (void) FlushExtentFile(vcb
);
1656 OSErr
HeadTruncateFile (
1661 HFSPlusExtentRecord extents
;
1662 HFSPlusExtentRecord tailExtents
;
1663 HFSCatalogNodeID fileID
;
1667 u_int32_t blksfreed
;
1673 if (vcb
->vcbSigWord
!= kHFSPlusSigWord
)
1676 forkType
= FORK_IS_RSRC(fcb
) ? kResourceForkType
: kDataForkType
;
1677 fileID
= FTOC(fcb
)->c_fileid
;
1678 bzero(tailExtents
, sizeof(tailExtents
));
1684 * Process catalog resident extents
1686 for (i
= 0, j
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
1687 blkcnt
= fcb
->fcbExtents
[i
].blockCount
;
1689 break; /* end of extents */
1691 if (blksfreed
< headblks
) {
1692 error
= BlockDeallocate(vcb
, fcb
->fcbExtents
[i
].startBlock
, blkcnt
, 0);
1694 * Any errors after the first BlockDeallocate
1695 * must be ignored so we can put the file in
1700 goto ErrorExit
; /* uh oh */
1703 printf("hfs: HeadTruncateFile: problems deallocating %s (%d)\n",
1704 FTOC(fcb
)->c_desc
.cd_nameptr
? (const char *)FTOC(fcb
)->c_desc
.cd_nameptr
: "", error
);
1708 blksfreed
+= blkcnt
;
1709 fcb
->fcbExtents
[i
].startBlock
= 0;
1710 fcb
->fcbExtents
[i
].blockCount
= 0;
1712 tailExtents
[j
].startBlock
= fcb
->fcbExtents
[i
].startBlock
;
1713 tailExtents
[j
].blockCount
= blkcnt
;
1722 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
1725 * Process overflow extents
1730 error
= FindExtentRecord(vcb
, forkType
, fileID
, startblk
, false, NULL
, extents
, NULL
);
1733 * Any errors after the first BlockDeallocate
1734 * must be ignored so we can put the file in
1737 if (error
!= btNotFound
)
1738 printf("hfs: HeadTruncateFile: problems finding extents %s (%d)\n",
1739 FTOC(fcb
)->c_desc
.cd_nameptr
? (const char *)FTOC(fcb
)->c_desc
.cd_nameptr
: "", error
);
1744 for(i
= 0, extblks
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
1745 blkcnt
= extents
[i
].blockCount
;
1747 break; /* end of extents */
1749 if (blksfreed
< headblks
) {
1750 error
= BlockDeallocate(vcb
, extents
[i
].startBlock
, blkcnt
, 0);
1752 printf("hfs: HeadTruncateFile: problems deallocating %s (%d)\n",
1753 FTOC(fcb
)->c_desc
.cd_nameptr
? (const char *)FTOC(fcb
)->c_desc
.cd_nameptr
: "", error
);
1756 blksfreed
+= blkcnt
;
1758 tailExtents
[j
].startBlock
= extents
[i
].startBlock
;
1759 tailExtents
[j
].blockCount
= blkcnt
;
1765 error
= DeleteExtentRecord(vcb
, forkType
, fileID
, startblk
);
1767 printf("hfs: HeadTruncateFile: problems deallocating %s (%d)\n",
1768 FTOC(fcb
)->c_desc
.cd_nameptr
? (const char *)FTOC(fcb
)->c_desc
.cd_nameptr
: "", error
);
1773 break; /* all done */
1775 startblk
+= extblks
;
1777 hfs_systemfile_unlock(vcb
, lockflags
);
1781 bcopy(tailExtents
, fcb
->fcbExtents
, sizeof(tailExtents
));
1782 blkcnt
= fcb
->ff_blocks
- headblks
;
1783 FTOC(fcb
)->c_blocks
-= headblks
;
1784 fcb
->ff_blocks
= blkcnt
;
1786 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1787 FTOC(fcb
)->c_touch_chgtime
= TRUE
;
1789 (void) FlushExtentFile(vcb
);
1793 return MacToVFSError(error
);
1798 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1799 // Routine: SearchExtentRecord (was XRSearch)
1801 // Function: Searches extent record for the extent mapping a given file
1802 // allocation block number (FABN).
1804 // Input: searchFABN - desired FABN
1805 // extentData - pointer to extent data record (xdr)
1806 // extentDataStartFABN - beginning FABN for extent record
1808 // Output: foundExtentDataOffset - offset to extent entry within xdr
1809 // result = noErr, offset to extent mapping desired FABN
1810 // result = FXRangeErr, offset to last extent in record
1811 // endingFABNPlusOne - ending FABN +1
1812 // noMoreExtents - True if the extent was not found, and the
1813 // extent record was not full (so don't bother
1814 // looking in subsequent records); false otherwise.
1816 // Result: noErr = ok
1817 // FXRangeErr = desired FABN > last mapped FABN in record
1818 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1820 static OSErr
SearchExtentRecord(
1822 u_int32_t searchFABN
,
1823 const HFSPlusExtentRecord extentData
,
1824 u_int32_t extentDataStartFABN
,
1825 u_int32_t
*foundExtentIndex
,
1826 u_int32_t
*endingFABNPlusOne
,
1827 Boolean
*noMoreExtents
)
1830 u_int32_t extentIndex
;
1831 /* Set it to the HFS std value */
1832 u_int32_t numberOfExtents
= kHFSExtentDensity
;
1833 u_int32_t numAllocationBlocks
;
1834 Boolean foundExtent
;
1836 *endingFABNPlusOne
= extentDataStartFABN
;
1837 *noMoreExtents
= false;
1838 foundExtent
= false;
1840 /* Override numberOfExtents for HFS+/HFSX */
1841 if (vcb
->vcbSigWord
!= kHFSSigWord
) {
1842 numberOfExtents
= kHFSPlusExtentDensity
;
1845 for( extentIndex
= 0; extentIndex
< numberOfExtents
; ++extentIndex
)
1848 // Loop over the extent record and find the search FABN.
1850 numAllocationBlocks
= extentData
[extentIndex
].blockCount
;
1851 if ( numAllocationBlocks
== 0 )
1856 *endingFABNPlusOne
+= numAllocationBlocks
;
1858 if( searchFABN
< *endingFABNPlusOne
)
1860 // Found the extent.
1868 // Found the extent. Note the extent offset
1869 *foundExtentIndex
= extentIndex
;
1873 // Did not find the extent. Set foundExtentDataOffset accordingly
1874 if( extentIndex
> 0 )
1876 *foundExtentIndex
= extentIndex
- 1;
1880 *foundExtentIndex
= 0;
1883 // If we found an empty extent, then set noMoreExtents.
1884 if (extentIndex
< numberOfExtents
)
1885 *noMoreExtents
= true;
1887 // Finally, return an error to the caller
1894 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1895 // Routine: SearchExtentFile (was XFSearch)
1897 // Function: Searches extent file (including the FCB resident extent record)
1898 // for the extent mapping a given file position.
1900 // Input: vcb - VCB pointer
1901 // fcb - FCB pointer
1902 // filePosition - file position (byte address)
1904 // Output: foundExtentKey - extent key record (xkr)
1905 // If extent was found in the FCB's resident extent record,
1906 // then foundExtentKey->keyLength will be set to 0.
1907 // foundExtentData - extent data record(xdr)
1908 // foundExtentIndex - index to extent entry in xdr
1909 // result = 0, offset to extent mapping desired FABN
1910 // result = FXRangeErr, offset to last extent in record
1911 // (i.e., kNumExtentsPerRecord-1)
1912 // extentBTreeHint - BTree hint for extent record
1913 // kNoHint = Resident extent record
1914 // endingFABNPlusOne - ending FABN +1
1917 // noErr Found an extent that contains the given file position
1918 // FXRangeErr Given position is beyond the last allocated extent
1919 // (other) (some other internal I/O error)
1920 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1922 OSErr
SearchExtentFile(
1925 int64_t filePosition
,
1926 HFSPlusExtentKey
*foundExtentKey
,
1927 HFSPlusExtentRecord foundExtentData
,
1928 u_int32_t
*foundExtentIndex
,
1929 u_int32_t
*extentBTreeHint
,
1930 u_int32_t
*endingFABNPlusOne
)
1933 u_int32_t filePositionBlock
;
1935 Boolean noMoreExtents
;
1938 temp64
= filePosition
/ (int64_t)vcb
->blockSize
;
1939 filePositionBlock
= (u_int32_t
)temp64
;
1941 bcopy ( fcb
->fcbExtents
, foundExtentData
, sizeof(HFSPlusExtentRecord
));
1943 // Search the resident FCB first.
1944 err
= SearchExtentRecord( vcb
, filePositionBlock
, foundExtentData
, 0,
1945 foundExtentIndex
, endingFABNPlusOne
, &noMoreExtents
);
1947 if( err
== noErr
) {
1948 // Found the extent. Set results accordingly
1949 *extentBTreeHint
= kNoHint
; // no hint, because not in the BTree
1950 foundExtentKey
->keyLength
= 0; // 0 = the FCB itself
1955 // Didn't find extent in FCB. If FCB's extent record wasn't full, there's no point
1956 // in searching the extents file. Note that SearchExtentRecord left us pointing at
1957 // the last valid extent (or the first one, if none were valid). This means we need
1958 // to fill in the hint and key outputs, just like the "if" statement above.
1959 if ( noMoreExtents
) {
1960 *extentBTreeHint
= kNoHint
; // no hint, because not in the BTree
1961 foundExtentKey
->keyLength
= 0; // 0 = the FCB itself
1962 err
= fxRangeErr
; // There are no more extents, so must be beyond PEOF
1967 // Find the desired record, or the previous record if it is the same fork
1969 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
1971 err
= FindExtentRecord(vcb
, FORK_IS_RSRC(fcb
) ? kResourceForkType
: kDataForkType
,
1972 FTOC(fcb
)->c_fileid
, filePositionBlock
, true, foundExtentKey
, foundExtentData
, extentBTreeHint
);
1973 hfs_systemfile_unlock(vcb
, lockflags
);
1975 if (err
== btNotFound
) {
1977 // If we get here, the desired position is beyond the extents in the FCB, and there are no extents
1978 // in the extents file. Return the FCB's extents and a range error.
1980 *extentBTreeHint
= kNoHint
;
1981 foundExtentKey
->keyLength
= 0;
1982 err
= GetFCBExtentRecord(fcb
, foundExtentData
);
1983 // Note: foundExtentIndex and endingFABNPlusOne have already been set as a result of the very
1984 // first SearchExtentRecord call in this function (when searching in the FCB's extents, and
1985 // we got a range error).
1991 // If we get here, there was either a BTree error, or we found an appropriate record.
1992 // If we found a record, then search it for the correct index into the extents.
1995 // Find appropriate index into extent record
1996 err
= SearchExtentRecord(vcb
, filePositionBlock
, foundExtentData
, foundExtentKey
->startBlock
,
1997 foundExtentIndex
, endingFABNPlusOne
, &noMoreExtents
);
2006 //============================================================================
2007 // Routine: UpdateExtentRecord
2009 // Function: Write new extent data to an existing extent record with a given key.
2010 // If all of the extents are empty, and the extent record is in the
2011 // extents file, then the record is deleted.
2013 // Input: vcb - the volume containing the extents
2014 // fcb - the file that owns the extents
2015 // deleted - whether or not the file is already deleted
2016 // extentFileKey - pointer to extent key record (xkr)
2017 // If the key length is 0, then the extents are actually part
2018 // of the catalog record, stored in the FCB.
2019 // extentData - pointer to extent data record (xdr)
2020 // extentBTreeHint - hint for given key, or kNoHint
2022 // Result: noErr = ok
2023 // (other) = error from BTree
2024 //============================================================================
2026 static OSErr
UpdateExtentRecord (ExtendedVCB
*vcb
, FCB
*fcb
, int deleted
,
2027 const HFSPlusExtentKey
*extentFileKey
,
2028 const HFSPlusExtentRecord extentData
,
2029 u_int32_t extentBTreeHint
)
2033 if (extentFileKey
->keyLength
== 0) { // keyLength == 0 means the FCB's extent record
2034 BlockMoveData(extentData
, fcb
->fcbExtents
, sizeof(HFSPlusExtentRecord
));
2036 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
2040 struct BTreeIterator
*btIterator
= NULL
;
2041 FSBufferDescriptor btRecord
;
2042 u_int16_t btRecordSize
;
2047 // Need to find and change a record in Extents BTree
2049 btFCB
= GetFileControlBlock(vcb
->extentsRefNum
);
2051 MALLOC (btIterator
, struct BTreeIterator
*, sizeof(struct BTreeIterator
), M_TEMP
, M_WAITOK
);
2052 if (btIterator
== NULL
) {
2053 return memFullErr
; // translates to ENOMEM
2055 bzero(btIterator
, sizeof(*btIterator
));
2058 * The lock taken by callers of ExtendFileC/TruncateFileC is
2059 * speculative and only occurs when the file already has
2060 * overflow extents. So we need to make sure we have the lock
2061 * here. The extents btree lock can be nested (its recursive)
2062 * so we always take it here.
2064 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
2067 if (vcb
->vcbSigWord
!= kHFSSigWord
) { // HFS Plus volume
2068 HFSPlusExtentRecord foundData
; // The extent data actually found
2070 BlockMoveData(extentFileKey
, &btIterator
->key
, sizeof(HFSPlusExtentKey
));
2072 btIterator
->hint
.index
= 0;
2073 btIterator
->hint
.nodeNum
= extentBTreeHint
;
2075 btRecord
.bufferAddress
= &foundData
;
2076 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
2077 btRecord
.itemCount
= 1;
2079 err
= BTSearchRecord(btFCB
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
2082 BlockMoveData(extentData
, &foundData
, sizeof(HFSPlusExtentRecord
));
2083 err
= BTReplaceRecord(btFCB
, btIterator
, &btRecord
, btRecordSize
);
2085 (void) BTFlushPath(btFCB
);
2090 HFSExtentKey
* key
; // Actual extent key used on disk in HFS
2091 HFSExtentRecord foundData
; // The extent data actually found
2093 key
= (HFSExtentKey
*) &btIterator
->key
;
2094 key
->keyLength
= kHFSExtentKeyMaximumLength
;
2095 key
->forkType
= extentFileKey
->forkType
;
2096 key
->fileID
= extentFileKey
->fileID
;
2097 key
->startBlock
= extentFileKey
->startBlock
;
2099 btIterator
->hint
.index
= 0;
2100 btIterator
->hint
.nodeNum
= extentBTreeHint
;
2102 btRecord
.bufferAddress
= &foundData
;
2103 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
2104 btRecord
.itemCount
= 1;
2106 err
= BTSearchRecord(btFCB
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
2109 err
= HFSPlusToHFSExtents(extentData
, (HFSExtentDescriptor
*)&foundData
);
2112 err
= BTReplaceRecord(btFCB
, btIterator
, &btRecord
, btRecordSize
);
2113 (void) BTFlushPath(btFCB
);
2118 hfs_systemfile_unlock(vcb
, lockflags
);
2120 FREE(btIterator
, M_TEMP
);
2129 static OSErr
HFSPlusToHFSExtents(
2130 const HFSPlusExtentRecord oldExtents
,
2131 HFSExtentRecord newExtents
)
2137 // copy the first 3 extents
2138 newExtents
[0].startBlock
= oldExtents
[0].startBlock
;
2139 newExtents
[0].blockCount
= oldExtents
[0].blockCount
;
2140 newExtents
[1].startBlock
= oldExtents
[1].startBlock
;
2141 newExtents
[1].blockCount
= oldExtents
[1].blockCount
;
2142 newExtents
[2].startBlock
= oldExtents
[2].startBlock
;
2143 newExtents
[2].blockCount
= oldExtents
[2].blockCount
;
2146 if (oldExtents
[3].startBlock
|| oldExtents
[3].blockCount
) {
2147 DebugStr("ExtentRecord with > 3 extents is invalid for HFS");
2158 static OSErr
GetFCBExtentRecord(
2160 HFSPlusExtentRecord extents
)
2163 BlockMoveData(fcb
->fcbExtents
, extents
, sizeof(HFSPlusExtentRecord
));
2169 //_________________________________________________________________________________
2171 // Routine: ExtentsAreIntegral
2173 // Purpose: Ensure that each extent can hold an integral number of nodes
2174 // Called by the NodesAreContiguous function
2175 //_________________________________________________________________________________
2177 static Boolean
ExtentsAreIntegral(
2178 const HFSPlusExtentRecord extentRecord
,
2180 u_int32_t
*blocksChecked
,
2181 Boolean
*checkedLastExtent
)
2184 u_int32_t extentIndex
;
2187 *checkedLastExtent
= false;
2189 for(extentIndex
= 0; extentIndex
< kHFSPlusExtentDensity
; extentIndex
++)
2191 blocks
= extentRecord
[extentIndex
].blockCount
;
2195 *checkedLastExtent
= true;
2199 *blocksChecked
+= blocks
;
2209 //_________________________________________________________________________________
2211 // Routine: NodesAreContiguous
2213 // Purpose: Ensure that all b-tree nodes are contiguous on disk
2214 // Called by BTOpenPath during volume mount
2215 //_________________________________________________________________________________
2217 Boolean
NodesAreContiguous(
2223 u_int32_t startBlock
;
2224 u_int32_t blocksChecked
;
2226 HFSPlusExtentKey key
;
2227 HFSPlusExtentRecord extents
;
2229 Boolean lastExtentReached
;
2233 if (vcb
->blockSize
>= nodeSize
)
2236 mask
= (nodeSize
/ vcb
->blockSize
) - 1;
2238 // check the local extents
2239 (void) GetFCBExtentRecord(fcb
, extents
);
2240 if ( !ExtentsAreIntegral(extents
, mask
, &blocksChecked
, &lastExtentReached
) )
2243 if ( lastExtentReached
||
2244 (int64_t)((int64_t)blocksChecked
* (int64_t)vcb
->blockSize
) >= (int64_t)fcb
->ff_size
)
2247 startBlock
= blocksChecked
;
2249 lockflags
= hfs_systemfile_lock(vcb
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
2251 // check the overflow extents (if any)
2252 while ( !lastExtentReached
)
2254 result
= FindExtentRecord(vcb
, kDataForkType
, fcb
->ff_cp
->c_fileid
, startBlock
, FALSE
, &key
, extents
, &hint
);
2257 if ( !ExtentsAreIntegral(extents
, mask
, &blocksChecked
, &lastExtentReached
) ) {
2258 hfs_systemfile_unlock(vcb
, lockflags
);
2261 startBlock
+= blocksChecked
;
2263 hfs_systemfile_unlock(vcb
, lockflags
);