2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 File: FileExtentMapping.c
25 Contains: xxx put contents here xxx
29 Written by: Dave Heller, Mark Day
31 Copyright: © 1996-1999 by Apple Computer, Inc., all rights reserved.
37 Other Contact: xxx put other contact here xxx
39 Technology: xxx put technology here xxx
47 Change History (most recent first):
48 <MacOSX> 9/9/99 djb Fix fcbModifiedMask flag testing logic.
49 <MacOSX> 8/25/98 djb Flush extents b-tree header if dirty (2371088).
50 <MacOSX> 6/30/98 djb Add functions NodesAreContiguous and ExtentsAreIntegral (for radar #2249539).
51 <MacOSX> 6/23/98 djb Changed DeallocFile to DeleteFile which now deletes the catalog record.
52 Fixed UpdateExtentRecord to pass correct fcb to Btree routines. Fixed
53 hfs+ bug in CreateExtentRecord (double dereference).
54 <MacOSX> 5/20/98 djb In ExtendFileC don't lie about the peof! (radar #2230094).
55 <MacOSX> 4/17/98 djb Add VCB locking.
56 <MacOSX> 4/2/98 djb Switch over to real BTree interface (no more BTreeWrapper.c).
57 <MacOSX> 3/31/98 djb Sync up with final HFSVolumes.h header file.
59 <CS24> 1/23/98 msd Bug 2208024: AllocContig is actually allocating one extent even
60 though there is not enough contiguous space.
61 <CS23> 12/2/97 DSH GetFCBExtentRecord no longer static so DFA can use it.
62 <CS22> 10/20/97 msd When allocating more space for a file, do the clump size
63 calculations in ExtendFileC, not BlockAllocate. Undo change from
65 <CS21> 10/17/97 msd Conditionalize DebugStrs.
66 <CS20> 10/16/97 msd Simplify the code path for MapFileBlockC (logical to physical
67 block mapping) in the typical case where the file isn't
68 fragmented so badly that it has extents in the extents B-tree.
69 Simplified some of the calculations for all cases.
70 <CS19> 10/13/97 DSH FindExtentRecord & DeleteExtentRecord are also being used by DFA
72 <CS18> 10/6/97 msd When extending a file, set the physical EOF to include any extra
73 space allocated due to a file's clump size.
74 <CS17> 9/19/97 msd Remove the MapLogicalToPhysical SPI. It was never used and is
75 not being tested anyway.
76 <CS16> 9/5/97 msd In CompareExtentKeys and CompareExtentKeysPlus, use the symbolic
77 constants for key length. Don't DebugStr unless DEBUG_BUILD is
79 <CS15> 7/24/97 djb Add instrumentation to MapFileBlockC
80 <CS14> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
82 <CS13> 7/15/97 DSH AdjEOF() mark the FCB as modified. (1664389)
83 <CS12> 7/8/97 DSH Loading PrecompiledHeaders from define passed in on C line
84 <CS11> 7/3/97 msd Bug #1663518. Remove DebugStr when setting the FCB extent record
85 for a volume control file.
86 <CS10> 6/27/97 msd Moved enum kFirstFileRefnum to FilesInternal.
87 <CS9> 6/24/97 djb Include "CatalogPrivate.h"
88 <CS8> 6/16/97 msd Finish implementation of CreateLargeFile SPI.
89 <CS7> 6/12/97 msd Add stub for CreateLargeFile SPI.
90 <CS6> 6/5/97 msd Add MapLogicalToPhysical.
91 <CS5> 6/2/97 msd In TruncateFileC, don't update the extent record unless it was
92 actually changed (prevents extra updates when truncating to the
93 end of the extent, and it is the last extent of the file.) Added
94 an AdjustEOF routine called by the assembly AdjEOF routine. It
95 copies the EOF, physical length, and extent information from one
96 FCB to all other FCBs for that fork.
97 <CS4> 5/20/97 DSH Removed const declaration in MapFileBlocC, const is benign when
98 passing by value, and SC requires it to match prototype.
99 <CS3> 5/15/97 msd Change enum kResourceForkType from -1 to 0xFF since it is now
100 unsigned. Change all forkType parameters to UInt8.
101 <CS2> 5/7/97 msd When checking for an unused extent descriptor, check the length,
102 not the starting block.
103 <CS1> 4/24/97 djb first checked in
104 <HFS25> 4/11/97 DSH use extended VCB fields catalogRefNum, and extentsRefNum.
105 <HFS24> 4/4/97 djb Get in sync with volume format changes.
106 <HFS23> 3/17/97 DSH Casting to compile with SC.
107 <HFS22> 2/26/97 msd Add instrumentation in ExtendFileC and TruncateFileC. In
108 CompareExtentKeys and CompareExtentKeysPlus, make sure the key
110 <HFS21> 2/5/97 msd The comparison with fsBTStartOfIterationErr didn't work because
111 the enum is an unsigned long; it is now casted to an OSErr
113 <HFS20> 1/31/97 msd In FindExtentRecord, turn an fsBTStartOfIterationErr error into
115 <HFS19> 1/28/97 msd Fixed bug in MapFileBlockC where it returned the wrong number of
116 bytes available at the given block number. This could
117 potentially cause programs to read or write over other files.
118 <HFS18> 1/16/97 djb Extent key compare procs now return SInt32. Fixed
119 UpdateExtentRecord - it was passing a pointer to an ExtentKey
121 <HFS17> 1/10/97 msd Change TruncateFileC to call DellocateFork when the new PEOF is
122 0. Fixes a fxRangeErr returned when no extents existed.
123 <HFS16> 1/6/97 msd Previous change prevents extent records from being removed if
124 the files new PEOF is in the local (FCB/catalog) extents.
125 <HFS15> 1/3/97 djb Temp fix in TruncateFileC to prevent unwanted calls to
127 <HFS14> 12/23/96 msd Previous change to SearchExtentFile didn't set up the outputs
128 for hint and key when the FCB extent record wasn't full.
129 <HFS13> 12/20/96 msd In SearchExtentFile, don't bother searching the extents file if
130 the FCB's extent record wasn't full, or if the FCB was for the
131 extents file itself. Modified SearchExtentRecord to return a
132 Boolean to indicate that the record was not full.
133 <HFS12> 12/19/96 DSH Changed refs from VCB to ExtendedVCB
134 <HFS11> 12/19/96 djb Updated for new B-tree Manager interface.
135 <HFS10> 12/12/96 djb Really use new SPI for GetCatalogNode.
136 <HFS9> 12/12/96 djb Use new Catalog SPI for GetCatalogNode. Added Mark's changes to
138 <HFS8> 12/11/96 msd TruncateFileC must always release extents, even if PEOF hasn't
139 changed (since allocation may have been rounded up due to clump
141 <HFS7> 12/10/96 msd Check PRAGMA_LOAD_SUPPORTED before loading precompiled headers.
142 <HFS6> 12/4/96 DSH Precompiled headers
143 <HFS5> 11/26/96 msd Add an exported routine to grow the parallel FCB table to
144 accomodate the HFS+ ExtentRecord.
145 <HFS4> 11/26/96 msd Convert internal routines to use ExtentKey and ExtentRecord
146 (instead of the raw HFS structures).
147 <HFS3> 11/21/96 msd Added CompareExtentKeysPlus().
148 <HFS2> 11/20/96 msd Finish porting FXM to C.
149 <HFS1> 11/6/96 DKH first checked in
154 #include "../../hfs.h"
155 #include "../../hfs_format.h"
156 #include "../../hfs_endian.h"
158 #include "../headers/FileMgrInternal.h"
159 #include "../headers/BTreesInternal.h"
160 #include "../headers/CatalogPrivate.h" // calling a private catalog routine (LocateCatalogNode)
162 #include <sys/malloc.h>
165 ============================================================
166 Public (Exported) Routines:
167 ============================================================
168 DeAllocFile Deallocate all disk space allocated to a specified file.
169 Both forks are deallocated.
171 ExtendFileC Allocate more space to a given file.
174 Compare two extents file keys (a search key and a trial
175 key). Used by the BTree manager when searching for,
176 adding, or deleting keys in the extents file of an HFS
179 CompareExtentKeysPlus
180 Compare two extents file keys (a search key and a trial
181 key). Used by the BTree manager when searching for,
182 adding, or deleting keys in the extents file of an HFS+
185 MapFileBlockC Convert (map) an offset within a given file into a
186 physical disk address.
188 TruncateFileC Truncates the disk space allocated to a file. The file
189 space is truncated to a specified new physical EOF, rounded
190 up to the next allocation block boundry. There is an option
191 to truncate to the end of the extent containing the new EOF.
194 Flush the extents file for a given volume.
197 Make sure the parallel FCB entries are big enough to support
198 the HFS+ ExtentRecord. If not, the array is grown and the
199 pre-existing data copied over.
202 Copy EOF, physical length, and extent records from one FCB
203 to all other FCBs for that fork. This is used when a file is
204 grown or shrunk as the result of a Write, SetEOF, or Allocate.
207 Map some position in a file to a volume block number. Also
208 returns the number of contiguous bytes that are mapped there.
209 This is a queued HFSDispatch call that does the equivalent of
210 MapFileBlockC, using a parameter block.
212 ============================================================
214 ============================================================
216 Search the extents BTree for a particular extent record.
218 Search the FCB and extents file for an extent record that
219 contains a given file position (in bytes).
221 Search a given extent record to see if it contains a given
222 file position (in bytes). Used by SearchExtentFile.
224 Deallocate all allocation blocks in all extents of an extent
227 Deallocate blocks and delete extent records for all allocation
228 blocks beyond a certain point in a file. The starting point
229 must be the first file allocation block for some extent record
232 Deallocate all allocation blocks belonging to a given fork.
234 If the extent record came from the extents file, write out
235 the updated record; otherwise, copy the updated record into
236 the FCB resident extent record. If the record has no extents,
237 and was in the extents file, then delete the record instead.
240 static const SInt64 kTwoGigabytes
= 0x80000000LL
;
245 kResourceForkType
= 0xFF,
250 void HFSToHFSPlusExtents(
251 const HFSExtentRecord oldExtents
,
252 HFSPlusExtentRecord newExtents
);
254 OSErr
HFSPlusToHFSExtents(
255 const HFSPlusExtentRecord oldExtents
,
256 HFSExtentRecord newExtents
);
258 OSErr
FindExtentRecord(
259 const ExtendedVCB
*vcb
,
263 Boolean allowPrevious
,
264 HFSPlusExtentKey
*foundKey
,
265 HFSPlusExtentRecord foundData
,
268 OSErr
DeleteExtentRecord(
269 const ExtendedVCB
*vcb
,
274 static OSErr
CreateExtentRecord(
275 const ExtendedVCB
*vcb
,
276 HFSPlusExtentKey
*key
,
277 HFSPlusExtentRecord extents
,
281 OSErr
GetFCBExtentRecord(
283 HFSPlusExtentRecord extents
);
285 static OSErr
SearchExtentFile(
286 const ExtendedVCB
*vcb
,
289 HFSPlusExtentKey
*foundExtentKey
,
290 HFSPlusExtentRecord foundExtentData
,
291 UInt32
*foundExtentDataIndex
,
292 UInt32
*extentBTreeHint
,
293 UInt32
*endingFABNPlusOne
);
295 static OSErr
SearchExtentRecord(
296 const ExtendedVCB
*vcb
,
298 const HFSPlusExtentRecord extentData
,
299 UInt32 extentDataStartFABN
,
300 UInt32
*foundExtentDataOffset
,
301 UInt32
*endingFABNPlusOne
,
302 Boolean
*noMoreExtents
);
304 static OSErr
ReleaseExtents(
306 const HFSPlusExtentRecord extentRecord
,
307 UInt32
*numReleasedAllocationBlocks
,
308 Boolean
*releasedLastExtent
);
310 static OSErr
DeallocateFork(
312 HFSCatalogNodeID fileID
,
314 HFSPlusExtentRecord catalogExtents
,
315 Boolean
* recordDeleted
);
317 static OSErr
TruncateExtents(
322 Boolean
* recordDeleted
);
324 static OSErr
UpdateExtentRecord (
325 const ExtendedVCB
*vcb
,
327 const HFSPlusExtentKey
*extentFileKey
,
328 const HFSPlusExtentRecord extentData
,
329 UInt32 extentBTreeHint
);
331 static Boolean
ExtentsAreIntegral(
332 const HFSPlusExtentRecord extentRecord
,
334 UInt32
*blocksChecked
,
335 Boolean
*checkedLastExtent
);
337 //_________________________________________________________________________________
339 // Routine: FindExtentRecord
341 // Purpose: Search the extents BTree for an extent record matching the given
342 // FileID, fork, and starting file allocation block number.
345 // vcb Volume to search
346 // forkType 0 = data fork, -1 = resource fork
347 // fileID File's FileID (CatalogNodeID)
348 // startBlock Starting file allocation block number
349 // allowPrevious If the desired record isn't found and this flag is set,
350 // then see if the previous record belongs to the same fork.
351 // If so, then return it.
354 // foundKey The key data for the record actually found
355 // foundData The extent record actually found (NOTE: on an HFS volume, the
356 // fourth entry will be zeroes.
357 // foundHint The BTree hint to find the node again
358 //_________________________________________________________________________________
359 OSErr
FindExtentRecord(
360 const ExtendedVCB
*vcb
,
364 Boolean allowPrevious
,
365 HFSPlusExtentKey
*foundKey
,
366 HFSPlusExtentRecord foundData
,
370 BTreeIterator
*btIterator
;
371 FSBufferDescriptor btRecord
;
377 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
379 MALLOC(btIterator
, BTreeIterator
*, sizeof(*btIterator
), M_TEMP
, M_WAITOK
);
380 bzero(btIterator
, sizeof(*btIterator
));
382 if (vcb
->vcbSigWord
== kHFSSigWord
) {
383 HFSExtentKey
* extentKeyPtr
;
384 HFSExtentRecord extentData
;
386 extentKeyPtr
= (HFSExtentKey
*) &btIterator
->key
;
387 extentKeyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
388 extentKeyPtr
->forkType
= forkType
;
389 extentKeyPtr
->fileID
= fileID
;
390 extentKeyPtr
->startBlock
= startBlock
;
392 btRecord
.bufferAddress
= &extentData
;
393 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
394 btRecord
.itemCount
= 1;
396 err
= BTSearchRecord(fcb
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
398 if (err
== btNotFound
&& allowPrevious
) {
399 err
= BTIterateRecord(fcb
, kBTreePrevRecord
, btIterator
, &btRecord
, &btRecordSize
);
401 // A previous record may not exist, so just return btNotFound (like we would if
402 // it was for the wrong file/fork).
403 if (err
== (OSErr
) fsBTStartOfIterationErr
) //¥¥ fsBTStartOfIterationErr is type unsigned long
407 // Found a previous record. Does it belong to the same fork of the same file?
408 if (extentKeyPtr
->fileID
!= fileID
|| extentKeyPtr
->forkType
!= forkType
)
416 // Copy the found key back for the caller
417 foundKey
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
418 foundKey
->forkType
= extentKeyPtr
->forkType
;
420 foundKey
->fileID
= extentKeyPtr
->fileID
;
421 foundKey
->startBlock
= extentKeyPtr
->startBlock
;
423 // Copy the found data back for the caller
424 foundData
[0].startBlock
= extentData
[0].startBlock
;
425 foundData
[0].blockCount
= extentData
[0].blockCount
;
426 foundData
[1].startBlock
= extentData
[1].startBlock
;
427 foundData
[1].blockCount
= extentData
[1].blockCount
;
428 foundData
[2].startBlock
= extentData
[2].startBlock
;
429 foundData
[2].blockCount
= extentData
[2].blockCount
;
431 for (i
= 3; i
< kHFSPlusExtentDensity
; ++i
)
433 foundData
[i
].startBlock
= 0;
434 foundData
[i
].blockCount
= 0;
438 else { // HFS Plus volume
439 HFSPlusExtentKey
* extentKeyPtr
;
440 HFSPlusExtentRecord extentData
;
442 extentKeyPtr
= (HFSPlusExtentKey
*) &btIterator
->key
;
443 extentKeyPtr
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
444 extentKeyPtr
->forkType
= forkType
;
445 extentKeyPtr
->pad
= 0;
446 extentKeyPtr
->fileID
= fileID
;
447 extentKeyPtr
->startBlock
= startBlock
;
449 btRecord
.bufferAddress
= &extentData
;
450 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
451 btRecord
.itemCount
= 1;
453 err
= BTSearchRecord(fcb
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
455 if (err
== btNotFound
&& allowPrevious
) {
456 err
= BTIterateRecord(fcb
, kBTreePrevRecord
, btIterator
, &btRecord
, &btRecordSize
);
458 // A previous record may not exist, so just return btNotFound (like we would if
459 // it was for the wrong file/fork).
460 if (err
== (OSErr
) fsBTStartOfIterationErr
) //¥¥ fsBTStartOfIterationErr is type unsigned long
464 // Found a previous record. Does it belong to the same fork of the same file?
465 if (extentKeyPtr
->fileID
!= fileID
|| extentKeyPtr
->forkType
!= forkType
)
471 // Copy the found key back for the caller
472 BlockMoveData(extentKeyPtr
, foundKey
, sizeof(HFSPlusExtentKey
));
473 // Copy the found data back for the caller
474 BlockMoveData(&extentData
, foundData
, sizeof(HFSPlusExtentRecord
));
478 *foundHint
= btIterator
->hint
.nodeNum
;
479 FREE(btIterator
, M_TEMP
);
485 static OSErr
CreateExtentRecord(
486 const ExtendedVCB
*vcb
,
487 HFSPlusExtentKey
*key
,
488 HFSPlusExtentRecord extents
,
491 BTreeIterator
* btIterator
;
492 FSBufferDescriptor btRecord
;
499 // XXXdbg - preflight that there's enough space
500 err
= BTCheckFreeSpace(GetFileControlBlock(vcb
->extentsRefNum
));
504 MALLOC(btIterator
, BTreeIterator
*, sizeof(*btIterator
), M_TEMP
, M_WAITOK
);
505 bzero(btIterator
, sizeof(*btIterator
));
507 if (vcb
->vcbSigWord
== kHFSSigWord
) {
508 HFSExtentKey
* keyPtr
;
509 HFSExtentRecord data
;
511 btRecordSize
= sizeof(HFSExtentRecord
);
512 btRecord
.bufferAddress
= &data
;
513 btRecord
.itemSize
= btRecordSize
;
514 btRecord
.itemCount
= 1;
516 keyPtr
= (HFSExtentKey
*) &btIterator
->key
;
517 keyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
518 keyPtr
->forkType
= key
->forkType
;
519 keyPtr
->fileID
= key
->fileID
;
520 keyPtr
->startBlock
= key
->startBlock
;
522 err
= HFSPlusToHFSExtents(extents
, data
);
524 else { // HFS Plus volume
525 btRecordSize
= sizeof(HFSPlusExtentRecord
);
526 btRecord
.bufferAddress
= extents
;
527 btRecord
.itemSize
= btRecordSize
;
528 btRecord
.itemCount
= 1;
530 BlockMoveData(key
, &btIterator
->key
, sizeof(HFSPlusExtentKey
));
534 err
= BTInsertRecord(GetFileControlBlock(vcb
->extentsRefNum
), btIterator
, &btRecord
, btRecordSize
);
537 *hint
= btIterator
->hint
.nodeNum
;
539 (void) BTFlushPath(GetFileControlBlock(vcb
->extentsRefNum
));
541 FREE(btIterator
, M_TEMP
);
546 OSErr
DeleteExtentRecord(
547 const ExtendedVCB
*vcb
,
552 BTreeIterator
* btIterator
;
557 // XXXdbg - preflight that there's enough space
558 err
= BTCheckFreeSpace(GetFileControlBlock(vcb
->extentsRefNum
));
562 MALLOC(btIterator
, BTreeIterator
*, sizeof(*btIterator
), M_TEMP
, M_WAITOK
);
563 bzero(btIterator
, sizeof(*btIterator
));
565 if (vcb
->vcbSigWord
== kHFSSigWord
) {
566 HFSExtentKey
* keyPtr
;
568 keyPtr
= (HFSExtentKey
*) &btIterator
->key
;
569 keyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
570 keyPtr
->forkType
= forkType
;
571 keyPtr
->fileID
= fileID
;
572 keyPtr
->startBlock
= startBlock
;
574 else { // HFS Plus volume
575 HFSPlusExtentKey
* keyPtr
;
577 keyPtr
= (HFSPlusExtentKey
*) &btIterator
->key
;
578 keyPtr
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
579 keyPtr
->forkType
= forkType
;
581 keyPtr
->fileID
= fileID
;
582 keyPtr
->startBlock
= startBlock
;
585 err
= BTDeleteRecord(GetFileControlBlock(vcb
->extentsRefNum
), btIterator
);
586 (void) BTFlushPath(GetFileControlBlock(vcb
->extentsRefNum
));
588 FREE(btIterator
, M_TEMP
);
594 //_________________________________________________________________________________
596 // Routine: MapFileBlock
598 // Function: Maps a file position into a physical disk address.
600 // Input: A2.L - VCB pointer
601 // (A1,D1.W) - FCB pointer
602 // D4.L - number of bytes desired
603 // D5.L - file position (byte address)
605 // Output: D3.L - physical start block
606 // D6.L - number of contiguous bytes available (up to D4 bytes)
607 // D0.L - result code <01Oct85>
609 // FXRangeErr = file position beyond mapped range <17Oct85>
610 // FXOvFlErr = extents file overflow <17Oct85>
611 // other = error <17Oct85>
613 // Called By: Log2Phys (read/write in place), Cache (map a file block).
614 //_________________________________________________________________________________
616 OSErr
MapFileBlockC (
617 ExtendedVCB
*vcb
, // volume that file resides on
618 FCB
*fcb
, // FCB of file
619 size_t numberOfBytes
, // number of contiguous bytes desired
620 off_t offset
, // starting offset within file (in bytes)
621 daddr_t
*startSector
, // first sector (NOT an allocation block)
622 size_t *availableBytes
) // number of contiguous bytes (up to numberOfBytes)
625 UInt32 allocBlockSize
; // Size of the volume's allocation block
627 HFSPlusExtentKey foundKey
;
628 HFSPlusExtentRecord foundData
;
631 UInt32 firstFABN
; // file allocation block of first block in found extent
632 UInt32 nextFABN
; // file allocation block of block after end of found extent
633 off_t dataEnd
; // (offset) end of range that is contiguous
634 UInt32 sectorsPerBlock
; // Number of sectors per allocation block
635 UInt32 startBlock
; // volume allocation block corresponding to firstFABN
639 allocBlockSize
= vcb
->blockSize
;
640 sectorSize
= VCBTOHFS(vcb
)->hfs_phys_block_size
;
642 err
= SearchExtentFile(vcb
, fcb
, offset
, &foundKey
, foundData
, &foundIndex
, &hint
, &nextFABN
);
644 startBlock
= foundData
[foundIndex
].startBlock
;
645 firstFABN
= nextFABN
- foundData
[foundIndex
].blockCount
;
654 // Determine the end of the available space. It will either be the end of the extent,
655 // or the file's PEOF, whichever is smaller.
657 dataEnd
= (off_t
)((off_t
)(nextFABN
) * (off_t
)(allocBlockSize
)); // Assume valid data through end of this extent
658 if (((off_t
)fcb
->ff_blocks
* (off_t
)allocBlockSize
) < dataEnd
) // Is PEOF shorter?
659 dataEnd
= (off_t
)fcb
->ff_blocks
* (off_t
)allocBlockSize
; // Yes, so only map up to PEOF
661 // Compute the number of sectors in an allocation block
662 sectorsPerBlock
= allocBlockSize
/ sectorSize
; // sectors per allocation block
665 // Compute the absolute sector number that contains the offset of the given file
666 // offset in sectors from start of the extent +
667 // offset in sectors from start of allocation block space
669 temp
= (daddr_t
)((offset
- (off_t
)((off_t
)(firstFABN
) * (off_t
)(allocBlockSize
)))/sectorSize
);
670 temp
+= startBlock
* sectorsPerBlock
;
672 /* Add in any volume offsets */
673 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
674 temp
+= vcb
->hfsPlusIOPosOffset
/ sectorSize
;
676 temp
+= vcb
->vcbAlBlSt
;
678 // Return the desired sector for file position "offset"
682 // Determine the number of contiguous bytes until the end of the extent
683 // (or the amount they asked for, whichever comes first).
685 tmpOff
= dataEnd
- offset
;
686 if (tmpOff
> (off_t
)(numberOfBytes
))
687 *availableBytes
= numberOfBytes
; // more there than they asked for, so pin the output
689 *availableBytes
= tmpOff
;
695 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
696 // Routine: ReleaseExtents
698 // Function: Release the extents of a single extent data record.
699 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
701 static OSErr
ReleaseExtents(
703 const HFSPlusExtentRecord extentRecord
,
704 UInt32
*numReleasedAllocationBlocks
,
705 Boolean
*releasedLastExtent
)
708 UInt32 numberOfExtents
;
711 *numReleasedAllocationBlocks
= 0;
712 *releasedLastExtent
= false;
714 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
715 numberOfExtents
= kHFSPlusExtentDensity
;
717 numberOfExtents
= kHFSExtentDensity
;
719 for( extentIndex
= 0; extentIndex
< numberOfExtents
; extentIndex
++)
721 UInt32 numAllocationBlocks
;
723 // Loop over the extent record and release the blocks associated with each extent.
725 numAllocationBlocks
= extentRecord
[extentIndex
].blockCount
;
726 if ( numAllocationBlocks
== 0 )
728 *releasedLastExtent
= true;
732 err
= BlockDeallocate( vcb
, extentRecord
[extentIndex
].startBlock
, numAllocationBlocks
);
736 *numReleasedAllocationBlocks
+= numAllocationBlocks
; // bump FABN to beg of next extent
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: TruncateExtents
747 // Purpose: Delete extent records whose starting file allocation block number
748 // is greater than or equal to a given starting block number. The
749 // allocation blocks represented by the extents are deallocated.
752 // vcb Volume to operate on
753 // fileID Which file to operate on
754 // startBlock Starting file allocation block number for first extent
756 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
758 static OSErr
TruncateExtents(
763 Boolean
* recordDeleted
)
766 UInt32 numberExtentsReleased
;
767 Boolean releasedLastExtent
;
769 HFSPlusExtentKey key
;
770 HFSPlusExtentRecord extents
;
773 err
= FindExtentRecord(vcb
, forkType
, fileID
, startBlock
, false, &key
, extents
, &hint
);
775 if (err
== btNotFound
)
780 err
= ReleaseExtents( vcb
, extents
, &numberExtentsReleased
, &releasedLastExtent
);
781 if (err
!= noErr
) break;
783 err
= DeleteExtentRecord(vcb
, forkType
, fileID
, startBlock
);
784 if (err
!= noErr
) break;
786 *recordDeleted
= true;
787 startBlock
+= numberExtentsReleased
;
795 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
796 // Routine: DeallocateFork
798 // Function: De-allocates all disk space allocated to a specified fork.
799 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
801 static OSErr
DeallocateFork(
803 HFSCatalogNodeID fileID
,
805 HFSPlusExtentRecord catalogExtents
,
806 Boolean
* recordDeleted
) /* true if a record was deleted */
809 UInt32 numReleasedAllocationBlocks
;
810 Boolean releasedLastExtent
;
812 // Release the catalog extents
813 err
= ReleaseExtents( vcb
, catalogExtents
, &numReleasedAllocationBlocks
, &releasedLastExtent
);
814 // Release the extra extents, if present
815 if (err
== noErr
&& !releasedLastExtent
)
816 err
= TruncateExtents(vcb
, forkType
, fileID
, numReleasedAllocationBlocks
, recordDeleted
);
821 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
822 // Routine: FlushExtentFile
824 // Function: Flushes the extent file for a specified volume
825 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
827 OSErr
FlushExtentFile( ExtendedVCB
*vcb
)
832 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
833 err
= BTFlushPath(fcb
);
836 // If the FCB for the extent "file" is dirty, mark the VCB as dirty.
838 if (FTOC(fcb
)->c_flag
& C_MODIFIED
)
841 // err = FlushVolumeControlBlock( vcb );
849 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
850 // Routine: CompareExtentKeys
852 // Function: Compares two extent file keys (a search key and a trial key) for
854 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
856 SInt32
CompareExtentKeys( const HFSExtentKey
*searchKey
, const HFSExtentKey
*trialKey
)
858 SInt32 result
; // ± 1
861 if (searchKey
->keyLength
!= kHFSExtentKeyMaximumLength
)
862 DebugStr("\pHFS: search Key is wrong length");
863 if (trialKey
->keyLength
!= kHFSExtentKeyMaximumLength
)
864 DebugStr("\pHFS: trial Key is wrong length");
867 result
= -1; // assume searchKey < trialKey
869 if (searchKey
->fileID
== trialKey
->fileID
) {
871 // FileNum's are equal; compare fork types
873 if (searchKey
->forkType
== trialKey
->forkType
) {
875 // Fork types are equal; compare allocation block number
877 if (searchKey
->startBlock
== trialKey
->startBlock
) {
879 // Everything is equal
885 // Allocation block numbers differ; determine sign
887 if (searchKey
->startBlock
> trialKey
->startBlock
)
893 // Fork types differ; determine sign
895 if (searchKey
->forkType
> trialKey
->forkType
)
901 // FileNums differ; determine sign
903 if (searchKey
->fileID
> trialKey
->fileID
)
912 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
913 // Routine: CompareExtentKeysPlus
915 // Function: Compares two extent file keys (a search key and a trial key) for
917 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
919 SInt32
CompareExtentKeysPlus( const HFSPlusExtentKey
*searchKey
, const HFSPlusExtentKey
*trialKey
)
921 SInt32 result
; // ± 1
924 if (searchKey
->keyLength
!= kHFSPlusExtentKeyMaximumLength
)
925 DebugStr("\pHFS: search Key is wrong length");
926 if (trialKey
->keyLength
!= kHFSPlusExtentKeyMaximumLength
)
927 DebugStr("\pHFS: trial Key is wrong length");
930 result
= -1; // assume searchKey < trialKey
932 if (searchKey
->fileID
== trialKey
->fileID
) {
934 // FileNum's are equal; compare fork types
936 if (searchKey
->forkType
== trialKey
->forkType
) {
938 // Fork types are equal; compare allocation block number
940 if (searchKey
->startBlock
== trialKey
->startBlock
) {
942 // Everything is equal
948 // Allocation block numbers differ; determine sign
950 if (searchKey
->startBlock
> trialKey
->startBlock
)
956 // Fork types differ; determine sign
958 if (searchKey
->forkType
> trialKey
->forkType
)
964 // FileNums differ; determine sign
966 if (searchKey
->fileID
> trialKey
->fileID
)
975 //_________________________________________________________________________________
977 // Routine: Extendfile
979 // Function: Extends the disk space allocated to a file.
981 // Input: A2.L - VCB pointer
982 // A1.L - pointer to FCB array
983 // D1.W - file refnum
984 // D3.B - option flags
985 // kEFContigMask - force contiguous allocation
986 // kEFAllMask - allocate all requested bytes or none
987 // NOTE: You may not set both options.
988 // D4.L - number of additional bytes to allocate
990 // Output: D0.W - result code
993 // D6.L - number of bytes allocated
995 // Called by: FileAloc,FileWrite,SetEof
997 // Note: ExtendFile updates the PEOF in the FCB.
998 //_________________________________________________________________________________
1001 ExtendedVCB
*vcb
, // volume that file resides on
1002 FCB
*fcb
, // FCB of file to truncate
1003 SInt64 bytesToAdd
, // number of bytes to allocate
1004 UInt32 blockHint
, // desired starting allocation block
1005 UInt32 flags
, // EFContig and/or EFAll
1006 SInt64
*actualBytesAdded
) // number of bytes actually allocated
1009 UInt32 volumeBlockSize
;
1011 SInt64 bytesThisExtent
;
1012 HFSPlusExtentKey foundKey
;
1013 HFSPlusExtentRecord foundData
;
1018 Boolean allOrNothing
;
1019 Boolean forceContig
;
1022 UInt32 actualStartBlock
;
1023 UInt32 actualNumBlocks
;
1024 UInt32 numExtentsPerRecord
;
1025 SInt64 maximumBytes
;
1031 *actualBytesAdded
= 0;
1032 volumeBlockSize
= vcb
->blockSize
;
1033 allOrNothing
= ((flags
& kEFAllMask
) != 0);
1034 forceContig
= ((flags
& kEFContigMask
) != 0);
1035 prevblocks
= fcb
->ff_blocks
;
1037 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1038 numExtentsPerRecord
= kHFSPlusExtentDensity
;
1040 numExtentsPerRecord
= kHFSExtentDensity
;
1043 // Make sure the request and new PEOF are less than 2GB if HFS.
1045 if (vcb
->vcbSigWord
== kHFSSigWord
) {
1046 if (bytesToAdd
>= kTwoGigabytes
)
1048 if ((((SInt64
)fcb
->ff_blocks
* (SInt64
)volumeBlockSize
) + bytesToAdd
) >= kTwoGigabytes
)
1052 // Determine how many blocks need to be allocated.
1053 // Round up the number of desired bytes to add.
1055 blocksToAdd
= FileBytesToBlocks(bytesToAdd
, volumeBlockSize
);
1056 bytesToAdd
= (SInt64
)((SInt64
)blocksToAdd
* (SInt64
)volumeBlockSize
);
1059 * For deferred allocations just reserve the blocks.
1061 if ((flags
& kEFDeferMask
)
1062 && (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1063 && (bytesToAdd
< (SInt64
)HFS_MAX_DEFERED_ALLOC
)
1064 && (blocksToAdd
< hfs_freeblks(VCBTOHFS(vcb
), 1))) {
1065 fcb
->ff_unallocblocks
+= blocksToAdd
;
1066 vcb
->loanedBlocks
+= blocksToAdd
;
1067 FTOC(fcb
)->c_blocks
+= blocksToAdd
;
1068 fcb
->ff_blocks
+= blocksToAdd
;
1070 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1071 *actualBytesAdded
= bytesToAdd
;
1075 * Give back any unallocated blocks before doing real allocations.
1077 if (fcb
->ff_unallocblocks
> 0) {
1078 blocksToAdd
+= fcb
->ff_unallocblocks
;
1079 bytesToAdd
= (SInt64
)blocksToAdd
* (SInt64
)volumeBlockSize
;
1081 vcb
->loanedBlocks
-= fcb
->ff_unallocblocks
;
1082 FTOC(fcb
)->c_blocks
-= fcb
->ff_unallocblocks
;
1083 fcb
->ff_blocks
-= fcb
->ff_unallocblocks
;
1084 fcb
->ff_unallocblocks
= 0;
1088 // If the file's clump size is larger than the allocation block size,
1089 // then set the maximum number of bytes to the requested number of bytes
1090 // rounded up to a multiple of the clump size.
1092 if ((fcb
->fcbClmpSize
> volumeBlockSize
)
1093 && (bytesToAdd
< (SInt64
)HFS_MAX_DEFERED_ALLOC
)
1094 && (flags
& kEFNoClumpMask
) == 0) {
1095 maximumBytes
= (SInt64
)FileBytesToBlocks(bytesToAdd
, fcb
->fcbClmpSize
);
1096 maximumBytes
*= fcb
->fcbClmpSize
;
1098 maximumBytes
= bytesToAdd
;
1102 // Compute new physical EOF, rounded up to a multiple of a block.
1104 if ((vcb
->vcbSigWord
== kHFSSigWord
) && ((((SInt64
)fcb
->ff_blocks
* (SInt64
)volumeBlockSize
) + bytesToAdd
) >= kTwoGigabytes
)) // Too big?
1105 if (allOrNothing
) // Yes, must they have it all?
1106 goto Overflow
; // Yes, can't have it
1108 --blocksToAdd
; // No, give give 'em one block less
1109 bytesToAdd
-= volumeBlockSize
;
1113 // If allocation is all-or-nothing, make sure there are
1114 // enough free blocks on the volume (quick test).
1117 (blocksToAdd
> hfs_freeblks(VCBTOHFS(vcb
), flags
& kEFReserveMask
))) {
1123 // See if there are already enough blocks allocated to the file.
1125 peof
= ((SInt64
)fcb
->ff_blocks
* (SInt64
)volumeBlockSize
) + bytesToAdd
; // potential new PEOF
1126 err
= SearchExtentFile(vcb
, fcb
, peof
-1, &foundKey
, foundData
, &foundIndex
, &hint
, &nextBlock
);
1128 // Enough blocks are already allocated. Just update the FCB to reflect the new length.
1129 fcb
->ff_blocks
= peof
/ volumeBlockSize
;
1130 FTOC(fcb
)->c_blocks
+= (bytesToAdd
/ volumeBlockSize
);
1131 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1134 if (err
!= fxRangeErr
) // Any real error?
1135 goto ErrorExit
; // Yes, so exit immediately
1138 // Adjust the PEOF to the end of the last extent.
1140 peof
= (SInt64
)((SInt64
)nextBlock
* (SInt64
)volumeBlockSize
); // currently allocated PEOF
1141 bytesThisExtent
= (SInt64
)(nextBlock
- fcb
->ff_blocks
) * (SInt64
)volumeBlockSize
;
1142 if (bytesThisExtent
!= 0) {
1143 fcb
->ff_blocks
= nextBlock
;
1144 FTOC(fcb
)->c_blocks
+= (bytesThisExtent
/ volumeBlockSize
);
1145 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1146 bytesToAdd
-= bytesThisExtent
;
1150 // Allocate some more space.
1152 // First try a contiguous allocation (of the whole amount).
1153 // If that fails, get whatever we can.
1154 // If forceContig, then take whatever we got
1155 // else, keep getting bits and pieces (non-contig)
1158 vcb
->vcbFreeExtCnt
= 0; /* For now, force rebuild of free extent list */
1161 startBlock
= blockHint
;
1163 startBlock
= foundData
[foundIndex
].startBlock
+ foundData
[foundIndex
].blockCount
;
1165 /* Force reserve checking if requested. */
1166 if (flags
& kEFReserveMask
) {
1169 actualNumBlocks
= 0;
1170 actualStartBlock
= 0;
1172 availbytes
= (SInt64
)hfs_freeblks(VCBTOHFS(vcb
), 1) *
1173 (SInt64
)volumeBlockSize
;
1174 if (availbytes
<= 0) {
1177 if (wantContig
&& (availbytes
< bytesToAdd
))
1180 err
= BlockAllocate(
1183 MIN(bytesToAdd
, availbytes
),
1184 MIN(maximumBytes
, availbytes
),
1191 err
= BlockAllocate(vcb
, startBlock
, bytesToAdd
, maximumBytes
,
1192 wantContig
, &actualStartBlock
, &actualNumBlocks
);
1194 if (err
== dskFulErr
) {
1196 break; // AllocContig failed because not enough contiguous space
1198 // Couldn't get one big chunk, so get whatever we can.
1203 if (actualNumBlocks
!= 0)
1207 // Add the new extent to the existing extent record, or create a new one.
1208 if ((actualStartBlock
== startBlock
) && (blockHint
== 0)) {
1209 // We grew the file's last extent, so just adjust the number of blocks.
1210 foundData
[foundIndex
].blockCount
+= actualNumBlocks
;
1211 err
= UpdateExtentRecord(vcb
, fcb
, &foundKey
, foundData
, hint
);
1212 if (err
!= noErr
) break;
1217 // Need to add a new extent. See if there is room in the current record.
1218 if (foundData
[foundIndex
].blockCount
!= 0) // Is current extent free to use?
1219 ++foundIndex
; // No, so use the next one.
1220 if (foundIndex
== numExtentsPerRecord
) {
1221 // This record is full. Need to create a new one.
1222 if (FTOC(fcb
)->c_fileid
== kHFSExtentsFileID
) {
1223 (void) BlockDeallocate(vcb
, actualStartBlock
, actualNumBlocks
);
1224 err
= dskFulErr
; // Oops. Can't extend extents file past first record.
1228 foundKey
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
1229 if (FORK_IS_RSRC(fcb
))
1230 foundKey
.forkType
= kResourceForkType
;
1232 foundKey
.forkType
= kDataForkType
;
1234 foundKey
.fileID
= FTOC(fcb
)->c_fileid
;
1235 foundKey
.startBlock
= nextBlock
;
1237 foundData
[0].startBlock
= actualStartBlock
;
1238 foundData
[0].blockCount
= actualNumBlocks
;
1240 // zero out remaining extents...
1241 for (i
= 1; i
< kHFSPlusExtentDensity
; ++i
)
1243 foundData
[i
].startBlock
= 0;
1244 foundData
[i
].blockCount
= 0;
1249 err
= CreateExtentRecord(vcb
, &foundKey
, foundData
, &hint
);
1250 if (err
== fxOvFlErr
) {
1251 // We couldn't create an extent record because extents B-tree
1252 // couldn't grow. Dellocate the extent just allocated and
1253 // return a disk full error.
1254 (void) BlockDeallocate(vcb
, actualStartBlock
, actualNumBlocks
);
1257 if (err
!= noErr
) break;
1259 needsFlush
= true; // We need to update the B-tree header
1262 // Add a new extent into this record and update.
1263 foundData
[foundIndex
].startBlock
= actualStartBlock
;
1264 foundData
[foundIndex
].blockCount
= actualNumBlocks
;
1265 err
= UpdateExtentRecord(vcb
, fcb
, &foundKey
, foundData
, hint
);
1266 if (err
!= noErr
) break;
1270 // Figure out how many bytes were actually allocated.
1271 // NOTE: BlockAllocate could have allocated more than we asked for.
1272 // Don't set the PEOF beyond what our client asked for.
1273 nextBlock
+= actualNumBlocks
;
1274 bytesThisExtent
= (SInt64
)((SInt64
)actualNumBlocks
* (SInt64
)volumeBlockSize
);
1275 if (bytesThisExtent
> bytesToAdd
) {
1279 bytesToAdd
-= bytesThisExtent
;
1280 maximumBytes
-= bytesThisExtent
;
1282 fcb
->ff_blocks
+= (bytesThisExtent
/ volumeBlockSize
);
1283 FTOC(fcb
)->c_blocks
+= (bytesThisExtent
/ volumeBlockSize
);
1284 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1286 // If contiguous allocation was requested, then we've already got one contiguous
1287 // chunk. If we didn't get all we wanted, then adjust the error to disk full.
1289 if (bytesToAdd
!= 0)
1291 break; // We've already got everything that's contiguous
1294 } while (err
== noErr
&& bytesToAdd
);
1298 *actualBytesAdded
= (SInt64
)(fcb
->ff_blocks
- prevblocks
) * (SInt64
)volumeBlockSize
;
1301 (void) FlushExtentFile(vcb
);
1306 err
= fileBoundsErr
;
1312 //_________________________________________________________________________________
1314 // Routine: TruncateFileC
1316 // Function: Truncates the disk space allocated to a file. The file space is
1317 // truncated to a specified new PEOF rounded up to the next allocation
1318 // block boundry. If the 'TFTrunExt' option is specified, the file is
1319 // truncated to the end of the extent containing the new PEOF.
1321 // Input: A2.L - VCB pointer
1322 // A1.L - pointer to FCB array
1323 // D1.W - file refnum
1324 // D2.B - option flags
1325 // TFTrunExt - truncate to the extent containing new PEOF
1328 // Output: D0.W - result code
1332 // Note: TruncateFile updates the PEOF in the FCB.
1333 //_________________________________________________________________________________
1335 OSErr
TruncateFileC (
1336 ExtendedVCB
*vcb
, // volume that file resides on
1337 FCB
*fcb
, // FCB of file to truncate
1338 SInt64 peof
, // new physical size for file
1339 Boolean truncateToExtent
) // if true, truncate to end of extent containing newPEOF
1342 UInt32 nextBlock
; // next file allocation block to consider
1343 UInt32 startBlock
; // Physical (volume) allocation block number of start of a range
1344 UInt32 physNumBlocks
; // Number of allocation blocks in file (according to PEOF)
1346 HFSPlusExtentKey key
; // key for current extent record; key->keyLength == 0 if FCB's extent record
1347 UInt32 hint
; // BTree hint corresponding to key
1348 HFSPlusExtentRecord extentRecord
;
1350 UInt32 extentNextBlock
;
1351 UInt32 numExtentsPerRecord
;
1354 Boolean extentChanged
; // true if we actually changed an extent
1355 Boolean recordDeleted
; // true if an extent record got deleted
1358 recordDeleted
= false;
1360 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1361 numExtentsPerRecord
= kHFSPlusExtentDensity
;
1363 numExtentsPerRecord
= kHFSExtentDensity
;
1365 if (FORK_IS_RSRC(fcb
))
1366 forkType
= kResourceForkType
;
1368 forkType
= kDataForkType
;
1370 temp64
= fcb
->ff_blocks
;
1371 physNumBlocks
= (UInt32
)temp64
;
1374 // Round newPEOF up to a multiple of the allocation block size. If new size is
1375 // two gigabytes or more, then round down by one allocation block (??? really?
1376 // shouldn't that be an error?).
1378 nextBlock
= FileBytesToBlocks(peof
, vcb
->blockSize
); // number of allocation blocks to remain in file
1379 peof
= (SInt64
)((SInt64
)nextBlock
* (SInt64
)vcb
->blockSize
); // number of bytes in those blocks
1380 if ((vcb
->vcbSigWord
== kHFSSigWord
) && (peof
>= kTwoGigabytes
)) {
1382 DebugStr("\pHFS: Trying to truncate a file to 2GB or more");
1384 err
= fileBoundsErr
;
1389 // Update FCB's length
1391 numBlocks
= peof
/ vcb
->blockSize
;
1392 FTOC(fcb
)->c_blocks
-= (fcb
->ff_blocks
- numBlocks
);
1393 fcb
->ff_blocks
= numBlocks
;
1394 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1397 // If the new PEOF is 0, then truncateToExtent has no meaning (we should always deallocate
1403 // Deallocate all the extents for this fork
1404 err
= DeallocateFork(vcb
, FTOC(fcb
)->c_fileid
, forkType
, fcb
->fcbExtents
, &recordDeleted
);
1405 if (err
!= noErr
) goto ErrorExit
; // got some error, so return it
1407 // Update the catalog extent record (making sure it's zeroed out)
1409 for (i
=0; i
< kHFSPlusExtentDensity
; i
++) {
1410 fcb
->fcbExtents
[i
].startBlock
= 0;
1411 fcb
->fcbExtents
[i
].blockCount
= 0;
1418 // Find the extent containing byte (peof-1). This is the last extent we'll keep.
1419 // (If truncateToExtent is true, we'll keep the whole extent; otherwise, we'll only
1420 // keep up through peof). The search will tell us how many allocation blocks exist
1421 // in the found extent plus all previous extents.
1423 err
= SearchExtentFile(vcb
, fcb
, peof
-1, &key
, extentRecord
, &extentIndex
, &hint
, &extentNextBlock
);
1424 if (err
!= noErr
) goto ErrorExit
;
1426 extentChanged
= false; // haven't changed the extent yet
1428 if (!truncateToExtent
) {
1430 // Shorten this extent. It may be the case that the entire extent gets
1433 numBlocks
= extentNextBlock
- nextBlock
; // How many blocks in this extent to free up
1434 if (numBlocks
!= 0) {
1435 // Compute first volume allocation block to free
1436 startBlock
= extentRecord
[extentIndex
].startBlock
+ extentRecord
[extentIndex
].blockCount
- numBlocks
;
1437 // Free the blocks in bitmap
1438 err
= BlockDeallocate(vcb
, startBlock
, numBlocks
);
1439 if (err
!= noErr
) goto ErrorExit
;
1440 // Adjust length of this extent
1441 extentRecord
[extentIndex
].blockCount
-= numBlocks
;
1442 // If extent is empty, set start block to 0
1443 if (extentRecord
[extentIndex
].blockCount
== 0)
1444 extentRecord
[extentIndex
].startBlock
= 0;
1445 // Remember that we changed the extent record
1446 extentChanged
= true;
1451 // Now move to the next extent in the record, and set up the file allocation block number
1453 nextBlock
= extentNextBlock
; // Next file allocation block to free
1454 ++extentIndex
; // Its index within the extent record
1457 // Release all following extents in this extent record. Update the record.
1459 while (extentIndex
< numExtentsPerRecord
&& extentRecord
[extentIndex
].blockCount
!= 0) {
1460 numBlocks
= extentRecord
[extentIndex
].blockCount
;
1461 // Deallocate this extent
1462 err
= BlockDeallocate(vcb
, extentRecord
[extentIndex
].startBlock
, numBlocks
);
1463 if (err
!= noErr
) goto ErrorExit
;
1464 // Update next file allocation block number
1465 nextBlock
+= numBlocks
;
1466 // Zero out start and length of this extent to delete it from record
1467 extentRecord
[extentIndex
].startBlock
= 0;
1468 extentRecord
[extentIndex
].blockCount
= 0;
1469 // Remember that we changed an extent
1470 extentChanged
= true;
1471 // Move to next extent in record
1476 // If any of the extents in the current record were changed, then update that
1477 // record (in the FCB, or extents file).
1479 if (extentChanged
) {
1480 err
= UpdateExtentRecord(vcb
, fcb
, &key
, extentRecord
, hint
);
1481 if (err
!= noErr
) goto ErrorExit
;
1485 // If there are any following allocation blocks, then we need
1486 // to seach for their extent records and delete those allocation
1489 if (nextBlock
< physNumBlocks
)
1490 err
= TruncateExtents(vcb
, forkType
, FTOC(fcb
)->c_fileid
, nextBlock
, &recordDeleted
);
1496 (void) FlushExtentFile(vcb
);
1503 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1504 // Routine: SearchExtentRecord (was XRSearch)
1506 // Function: Searches extent record for the extent mapping a given file
1507 // allocation block number (FABN).
1509 // Input: searchFABN - desired FABN
1510 // extentData - pointer to extent data record (xdr)
1511 // extentDataStartFABN - beginning FABN for extent record
1513 // Output: foundExtentDataOffset - offset to extent entry within xdr
1514 // result = noErr, offset to extent mapping desired FABN
1515 // result = FXRangeErr, offset to last extent in record
1516 // endingFABNPlusOne - ending FABN +1
1517 // noMoreExtents - True if the extent was not found, and the
1518 // extent record was not full (so don't bother
1519 // looking in subsequent records); false otherwise.
1521 // Result: noErr = ok
1522 // FXRangeErr = desired FABN > last mapped FABN in record
1523 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1525 static OSErr
SearchExtentRecord(
1526 const ExtendedVCB
*vcb
,
1528 const HFSPlusExtentRecord extentData
,
1529 UInt32 extentDataStartFABN
,
1530 UInt32
*foundExtentIndex
,
1531 UInt32
*endingFABNPlusOne
,
1532 Boolean
*noMoreExtents
)
1536 UInt32 numberOfExtents
;
1537 UInt32 numAllocationBlocks
;
1538 Boolean foundExtent
;
1540 *endingFABNPlusOne
= extentDataStartFABN
;
1541 *noMoreExtents
= false;
1542 foundExtent
= false;
1544 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1545 numberOfExtents
= kHFSPlusExtentDensity
;
1547 numberOfExtents
= kHFSExtentDensity
;
1549 for( extentIndex
= 0; extentIndex
< numberOfExtents
; ++extentIndex
)
1552 // Loop over the extent record and find the search FABN.
1554 numAllocationBlocks
= extentData
[extentIndex
].blockCount
;
1555 if ( numAllocationBlocks
== 0 )
1560 *endingFABNPlusOne
+= numAllocationBlocks
;
1562 if( searchFABN
< *endingFABNPlusOne
)
1564 // Found the extent.
1572 // Found the extent. Note the extent offset
1573 *foundExtentIndex
= extentIndex
;
1577 // Did not find the extent. Set foundExtentDataOffset accordingly
1578 if( extentIndex
> 0 )
1580 *foundExtentIndex
= extentIndex
- 1;
1584 *foundExtentIndex
= 0;
1587 // If we found an empty extent, then set noMoreExtents.
1588 if (extentIndex
< numberOfExtents
)
1589 *noMoreExtents
= true;
1591 // Finally, return an error to the caller
1598 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1599 // Routine: SearchExtentFile (was XFSearch)
1601 // Function: Searches extent file (including the FCB resident extent record)
1602 // for the extent mapping a given file position.
1604 // Input: vcb - VCB pointer
1605 // fcb - FCB pointer
1606 // filePosition - file position (byte address)
1608 // Output: foundExtentKey - extent key record (xkr)
1609 // If extent was found in the FCB's resident extent record,
1610 // then foundExtentKey->keyLength will be set to 0.
1611 // foundExtentData - extent data record(xdr)
1612 // foundExtentIndex - index to extent entry in xdr
1613 // result = 0, offset to extent mapping desired FABN
1614 // result = FXRangeErr, offset to last extent in record
1615 // (i.e., kNumExtentsPerRecord-1)
1616 // extentBTreeHint - BTree hint for extent record
1617 // kNoHint = Resident extent record
1618 // endingFABNPlusOne - ending FABN +1
1621 // noErr Found an extent that contains the given file position
1622 // FXRangeErr Given position is beyond the last allocated extent
1623 // (other) (some other internal I/O error)
1624 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1626 static OSErr
SearchExtentFile(
1627 const ExtendedVCB
*vcb
,
1629 SInt64 filePosition
,
1630 HFSPlusExtentKey
*foundExtentKey
,
1631 HFSPlusExtentRecord foundExtentData
,
1632 UInt32
*foundExtentIndex
,
1633 UInt32
*extentBTreeHint
,
1634 UInt32
*endingFABNPlusOne
)
1637 UInt32 filePositionBlock
;
1639 Boolean noMoreExtents
;
1641 temp64
= filePosition
/ (SInt64
)vcb
->blockSize
;
1642 filePositionBlock
= (UInt32
)temp64
;
1644 bcopy ( fcb
->fcbExtents
, foundExtentData
, sizeof(HFSPlusExtentRecord
));
1646 // Search the resident FCB first.
1647 err
= SearchExtentRecord( vcb
, filePositionBlock
, foundExtentData
, 0,
1648 foundExtentIndex
, endingFABNPlusOne
, &noMoreExtents
);
1650 if( err
== noErr
) {
1651 // Found the extent. Set results accordingly
1652 *extentBTreeHint
= kNoHint
; // no hint, because not in the BTree
1653 foundExtentKey
->keyLength
= 0; // 0 = the FCB itself
1658 // Didn't find extent in FCB. If FCB's extent record wasn't full, there's no point
1659 // in searching the extents file. Note that SearchExtentRecord left us pointing at
1660 // the last valid extent (or the first one, if none were valid). This means we need
1661 // to fill in the hint and key outputs, just like the "if" statement above.
1662 if ( noMoreExtents
) {
1663 *extentBTreeHint
= kNoHint
; // no hint, because not in the BTree
1664 foundExtentKey
->keyLength
= 0; // 0 = the FCB itself
1665 err
= fxRangeErr
; // There are no more extents, so must be beyond PEOF
1670 // Find the desired record, or the previous record if it is the same fork
1672 err
= FindExtentRecord(vcb
, FORK_IS_RSRC(fcb
) ? kResourceForkType
: kDataForkType
,
1673 FTOC(fcb
)->c_fileid
, filePositionBlock
, true, foundExtentKey
, foundExtentData
, extentBTreeHint
);
1675 if (err
== btNotFound
) {
1677 // If we get here, the desired position is beyond the extents in the FCB, and there are no extents
1678 // in the extents file. Return the FCB's extents and a range error.
1680 *extentBTreeHint
= kNoHint
;
1681 foundExtentKey
->keyLength
= 0;
1682 err
= GetFCBExtentRecord(fcb
, foundExtentData
);
1683 // Note: foundExtentIndex and endingFABNPlusOne have already been set as a result of the very
1684 // first SearchExtentRecord call in this function (when searching in the FCB's extents, and
1685 // we got a range error).
1691 // If we get here, there was either a BTree error, or we found an appropriate record.
1692 // If we found a record, then search it for the correct index into the extents.
1695 // Find appropriate index into extent record
1696 err
= SearchExtentRecord(vcb
, filePositionBlock
, foundExtentData
, foundExtentKey
->startBlock
,
1697 foundExtentIndex
, endingFABNPlusOne
, &noMoreExtents
);
1706 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1707 // Routine: UpdateExtentRecord
1709 // Function: Write new extent data to an existing extent record with a given key.
1710 // If all of the extents are empty, and the extent record is in the
1711 // extents file, then the record is deleted.
1713 // Input: vcb - the volume containing the extents
1714 // fcb - the file that owns the extents
1715 // extentFileKey - pointer to extent key record (xkr)
1716 // If the key length is 0, then the extents are actually part
1717 // of the catalog record, stored in the FCB.
1718 // extentData - pointer to extent data record (xdr)
1719 // extentBTreeHint - hint for given key, or kNoHint
1721 // Result: noErr = ok
1722 // (other) = error from BTree
1723 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1725 static OSErr
UpdateExtentRecord (
1726 const ExtendedVCB
*vcb
,
1728 const HFSPlusExtentKey
*extentFileKey
,
1729 const HFSPlusExtentRecord extentData
,
1730 UInt32 extentBTreeHint
)
1734 if (extentFileKey
->keyLength
== 0) { // keyLength == 0 means the FCB's extent record
1735 BlockMoveData(extentData
, fcb
->fcbExtents
, sizeof(HFSPlusExtentRecord
));
1736 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1739 BTreeIterator
* btIterator
;
1740 FSBufferDescriptor btRecord
;
1741 UInt16 btRecordSize
;
1745 // Need to find and change a record in Extents BTree
1747 btFCB
= GetFileControlBlock(vcb
->extentsRefNum
);
1749 // XXXdbg - preflight that there's enough space
1750 err
= BTCheckFreeSpace(btFCB
);
1754 MALLOC(btIterator
, BTreeIterator
*, sizeof(*btIterator
), M_TEMP
, M_WAITOK
);
1755 bzero(btIterator
, sizeof(*btIterator
));
1757 if (vcb
->vcbSigWord
== kHFSSigWord
) {
1758 HFSExtentKey
* key
; // Actual extent key used on disk in HFS
1759 HFSExtentRecord foundData
; // The extent data actually found
1761 key
= (HFSExtentKey
*) &btIterator
->key
;
1762 key
->keyLength
= kHFSExtentKeyMaximumLength
;
1763 key
->forkType
= extentFileKey
->forkType
;
1764 key
->fileID
= extentFileKey
->fileID
;
1765 key
->startBlock
= extentFileKey
->startBlock
;
1767 btIterator
->hint
.index
= 0;
1768 btIterator
->hint
.nodeNum
= extentBTreeHint
;
1770 btRecord
.bufferAddress
= &foundData
;
1771 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
1772 btRecord
.itemCount
= 1;
1774 err
= BTSearchRecord(btFCB
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
1777 err
= HFSPlusToHFSExtents(extentData
, (HFSExtentDescriptor
*)&foundData
);
1780 err
= BTReplaceRecord(btFCB
, btIterator
, &btRecord
, btRecordSize
);
1781 (void) BTFlushPath(btFCB
);
1783 else { // HFS Plus volume
1784 HFSPlusExtentRecord foundData
; // The extent data actually found
1786 BlockMoveData(extentFileKey
, &btIterator
->key
, sizeof(HFSPlusExtentKey
));
1788 btIterator
->hint
.index
= 0;
1789 btIterator
->hint
.nodeNum
= extentBTreeHint
;
1791 btRecord
.bufferAddress
= &foundData
;
1792 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
1793 btRecord
.itemCount
= 1;
1795 err
= BTSearchRecord(btFCB
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
1798 BlockMoveData(extentData
, &foundData
, sizeof(HFSPlusExtentRecord
));
1799 err
= BTReplaceRecord(btFCB
, btIterator
, &btRecord
, btRecordSize
);
1801 (void) BTFlushPath(btFCB
);
1803 FREE(btIterator
, M_TEMP
);
1811 void HFSToHFSPlusExtents(
1812 const HFSExtentRecord oldExtents
,
1813 HFSPlusExtentRecord newExtents
)
1817 // copy the first 3 extents
1818 newExtents
[0].startBlock
= oldExtents
[0].startBlock
;
1819 newExtents
[0].blockCount
= oldExtents
[0].blockCount
;
1820 newExtents
[1].startBlock
= oldExtents
[1].startBlock
;
1821 newExtents
[1].blockCount
= oldExtents
[1].blockCount
;
1822 newExtents
[2].startBlock
= oldExtents
[2].startBlock
;
1823 newExtents
[2].blockCount
= oldExtents
[2].blockCount
;
1825 // zero out the remaining ones
1826 for (i
= 3; i
< kHFSPlusExtentDensity
; ++i
)
1828 newExtents
[i
].startBlock
= 0;
1829 newExtents
[i
].blockCount
= 0;
1835 OSErr
HFSPlusToHFSExtents(
1836 const HFSPlusExtentRecord oldExtents
,
1837 HFSExtentRecord newExtents
)
1843 // copy the first 3 extents
1844 newExtents
[0].startBlock
= oldExtents
[0].startBlock
;
1845 newExtents
[0].blockCount
= oldExtents
[0].blockCount
;
1846 newExtents
[1].startBlock
= oldExtents
[1].startBlock
;
1847 newExtents
[1].blockCount
= oldExtents
[1].blockCount
;
1848 newExtents
[2].startBlock
= oldExtents
[2].startBlock
;
1849 newExtents
[2].blockCount
= oldExtents
[2].blockCount
;
1852 if (oldExtents
[3].startBlock
|| oldExtents
[3].blockCount
) {
1853 DebugStr("\pExtentRecord with > 3 extents is invalid for HFS");
1864 OSErr
GetFCBExtentRecord(
1866 HFSPlusExtentRecord extents
)
1869 BlockMoveData(fcb
->fcbExtents
, extents
, sizeof(HFSPlusExtentRecord
));
1875 //_________________________________________________________________________________
1877 // Routine: ExtentsAreIntegral
1879 // Purpose: Ensure that each extent can hold an integral number of nodes
1880 // Called by the NodesAreContiguous function
1881 //_________________________________________________________________________________
1883 static Boolean
ExtentsAreIntegral(
1884 const HFSPlusExtentRecord extentRecord
,
1886 UInt32
*blocksChecked
,
1887 Boolean
*checkedLastExtent
)
1893 *checkedLastExtent
= false;
1895 for(extentIndex
= 0; extentIndex
< kHFSPlusExtentDensity
; extentIndex
++)
1897 blocks
= extentRecord
[extentIndex
].blockCount
;
1901 *checkedLastExtent
= true;
1905 *blocksChecked
+= blocks
;
1915 //_________________________________________________________________________________
1917 // Routine: NodesAreContiguous
1919 // Purpose: Ensure that all b-tree nodes are contiguous on disk
1920 // Called by BTOpenPath during volume mount
1921 //_________________________________________________________________________________
1923 Boolean
NodesAreContiguous(
1930 UInt32 blocksChecked
;
1932 HFSPlusExtentKey key
;
1933 HFSPlusExtentRecord extents
;
1935 Boolean lastExtentReached
;
1938 if (vcb
->blockSize
>= nodeSize
)
1941 mask
= (nodeSize
/ vcb
->blockSize
) - 1;
1943 // check the local extents
1944 (void) GetFCBExtentRecord(fcb
, extents
);
1945 if ( !ExtentsAreIntegral(extents
, mask
, &blocksChecked
, &lastExtentReached
) )
1948 if (lastExtentReached
|| (SInt64
)((SInt64
)blocksChecked
* (SInt64
)vcb
->blockSize
) >= fcb
->ff_size
)
1951 startBlock
= blocksChecked
;
1953 // check the overflow extents (if any)
1954 while ( !lastExtentReached
)
1956 result
= FindExtentRecord(vcb
, kDataForkType
, fcb
->ff_cp
->c_fileid
, startBlock
, FALSE
, &key
, extents
, &hint
);
1959 if ( !ExtentsAreIntegral(extents
, mask
, &blocksChecked
, &lastExtentReached
) )
1962 startBlock
+= blocksChecked
;