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.
242 kTwoGigabytes
= (UInt32
) 0x80000000,
245 kResourceForkType
= 0xFF,
247 kPreviousRecord
= -1,
249 kSectorSize
= 512 // Size of a physical sector
252 void HFSToHFSPlusExtents(
253 const HFSExtentRecord oldExtents
,
254 HFSPlusExtentRecord newExtents
);
256 OSErr
HFSPlusToHFSExtents(
257 const HFSPlusExtentRecord oldExtents
,
258 HFSExtentRecord newExtents
);
260 OSErr
FindExtentRecord(
261 const ExtendedVCB
*vcb
,
265 Boolean allowPrevious
,
266 HFSPlusExtentKey
*foundKey
,
267 HFSPlusExtentRecord foundData
,
270 OSErr
DeleteExtentRecord(
271 const ExtendedVCB
*vcb
,
276 static OSErr
CreateExtentRecord(
277 const ExtendedVCB
*vcb
,
278 HFSPlusExtentKey
*key
,
279 HFSPlusExtentRecord extents
,
283 OSErr
GetFCBExtentRecord(
285 HFSPlusExtentRecord extents
);
287 static OSErr
SearchExtentFile(
288 const ExtendedVCB
*vcb
,
291 HFSPlusExtentKey
*foundExtentKey
,
292 HFSPlusExtentRecord foundExtentData
,
293 UInt32
*foundExtentDataIndex
,
294 UInt32
*extentBTreeHint
,
295 UInt32
*endingFABNPlusOne
);
297 static OSErr
SearchExtentRecord(
298 const ExtendedVCB
*vcb
,
300 const HFSPlusExtentRecord extentData
,
301 UInt32 extentDataStartFABN
,
302 UInt32
*foundExtentDataOffset
,
303 UInt32
*endingFABNPlusOne
,
304 Boolean
*noMoreExtents
);
306 static OSErr
ReleaseExtents(
308 const HFSPlusExtentRecord extentRecord
,
309 UInt32
*numReleasedAllocationBlocks
,
310 Boolean
*releasedLastExtent
);
312 static OSErr
DeallocateFork(
314 HFSCatalogNodeID fileID
,
316 HFSPlusExtentRecord catalogExtents
,
317 Boolean
* recordDeleted
);
319 static OSErr
TruncateExtents(
324 Boolean
* recordDeleted
);
326 static OSErr
UpdateExtentRecord (
327 const ExtendedVCB
*vcb
,
329 const HFSPlusExtentKey
*extentFileKey
,
330 const HFSPlusExtentRecord extentData
,
331 UInt32 extentBTreeHint
);
333 static Boolean
ExtentsAreIntegral(
334 const HFSPlusExtentRecord extentRecord
,
336 UInt32
*blocksChecked
,
337 Boolean
*checkedLastExtent
);
339 //_________________________________________________________________________________
341 // Routine: FindExtentRecord
343 // Purpose: Search the extents BTree for an extent record matching the given
344 // FileID, fork, and starting file allocation block number.
347 // vcb Volume to search
348 // forkType 0 = data fork, -1 = resource fork
349 // fileID File's FileID (CatalogNodeID)
350 // startBlock Starting file allocation block number
351 // allowPrevious If the desired record isn't found and this flag is set,
352 // then see if the previous record belongs to the same fork.
353 // If so, then return it.
356 // foundKey The key data for the record actually found
357 // foundData The extent record actually found (NOTE: on an HFS volume, the
358 // fourth entry will be zeroes.
359 // foundHint The BTree hint to find the node again
360 //_________________________________________________________________________________
361 OSErr
FindExtentRecord(
362 const ExtendedVCB
*vcb
,
366 Boolean allowPrevious
,
367 HFSPlusExtentKey
*foundKey
,
368 HFSPlusExtentRecord foundData
,
372 BTreeIterator btIterator
;
373 FSBufferDescriptor btRecord
;
379 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
381 (void) BTInvalidateHint(&btIterator
);
383 if (vcb
->vcbSigWord
== kHFSSigWord
) {
384 HFSExtentKey
* extentKeyPtr
;
385 HFSExtentRecord extentData
;
387 extentKeyPtr
= (HFSExtentKey
*) &btIterator
.key
;
388 extentKeyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
389 extentKeyPtr
->forkType
= forkType
;
390 extentKeyPtr
->fileID
= fileID
;
391 extentKeyPtr
->startBlock
= startBlock
;
393 btRecord
.bufferAddress
= &extentData
;
394 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
395 btRecord
.itemCount
= 1;
397 err
= BTSearchRecord(fcb
, &btIterator
, kInvalidMRUCacheKey
, &btRecord
, &btRecordSize
, &btIterator
);
399 if (err
== btNotFound
&& allowPrevious
) {
400 err
= BTIterateRecord(fcb
, kBTreePrevRecord
, &btIterator
, &btRecord
, &btRecordSize
);
402 // A previous record may not exist, so just return btNotFound (like we would if
403 // it was for the wrong file/fork).
404 if (err
== (OSErr
) fsBTStartOfIterationErr
) //¥¥ fsBTStartOfIterationErr is type unsigned long
408 // Found a previous record. Does it belong to the same fork of the same file?
409 if (extentKeyPtr
->fileID
!= fileID
|| extentKeyPtr
->forkType
!= forkType
)
417 // Copy the found key back for the caller
418 foundKey
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
419 foundKey
->forkType
= extentKeyPtr
->forkType
;
421 foundKey
->fileID
= extentKeyPtr
->fileID
;
422 foundKey
->startBlock
= extentKeyPtr
->startBlock
;
424 // Copy the found data back for the caller
425 foundData
[0].startBlock
= extentData
[0].startBlock
;
426 foundData
[0].blockCount
= extentData
[0].blockCount
;
427 foundData
[1].startBlock
= extentData
[1].startBlock
;
428 foundData
[1].blockCount
= extentData
[1].blockCount
;
429 foundData
[2].startBlock
= extentData
[2].startBlock
;
430 foundData
[2].blockCount
= extentData
[2].blockCount
;
432 for (i
= 3; i
< kHFSPlusExtentDensity
; ++i
)
434 foundData
[i
].startBlock
= 0;
435 foundData
[i
].blockCount
= 0;
439 else { // HFS Plus volume
440 HFSPlusExtentKey
* extentKeyPtr
;
441 HFSPlusExtentRecord extentData
;
443 extentKeyPtr
= (HFSPlusExtentKey
*) &btIterator
.key
;
444 extentKeyPtr
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
445 extentKeyPtr
->forkType
= forkType
;
446 extentKeyPtr
->pad
= 0;
447 extentKeyPtr
->fileID
= fileID
;
448 extentKeyPtr
->startBlock
= startBlock
;
450 btRecord
.bufferAddress
= &extentData
;
451 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
452 btRecord
.itemCount
= 1;
454 err
= BTSearchRecord(fcb
, &btIterator
, kInvalidMRUCacheKey
, &btRecord
, &btRecordSize
, &btIterator
);
456 if (err
== btNotFound
&& allowPrevious
) {
457 err
= BTIterateRecord(fcb
, kBTreePrevRecord
, &btIterator
, &btRecord
, &btRecordSize
);
459 // A previous record may not exist, so just return btNotFound (like we would if
460 // it was for the wrong file/fork).
461 if (err
== (OSErr
) fsBTStartOfIterationErr
) //¥¥ fsBTStartOfIterationErr is type unsigned long
465 // Found a previous record. Does it belong to the same fork of the same file?
466 if (extentKeyPtr
->fileID
!= fileID
|| extentKeyPtr
->forkType
!= forkType
)
472 // Copy the found key back for the caller
473 BlockMoveData(extentKeyPtr
, foundKey
, sizeof(HFSPlusExtentKey
));
474 // Copy the found data back for the caller
475 BlockMoveData(&extentData
, foundData
, sizeof(HFSPlusExtentRecord
));
479 *foundHint
= btIterator
.hint
.nodeNum
;
485 static OSErr
CreateExtentRecord(
486 const ExtendedVCB
*vcb
,
487 HFSPlusExtentKey
*key
,
488 HFSPlusExtentRecord extents
,
491 BTreeIterator btIterator
;
492 FSBufferDescriptor btRecord
;
498 (void) BTInvalidateHint(&btIterator
);
500 if (vcb
->vcbSigWord
== kHFSSigWord
) {
501 HFSExtentKey
* keyPtr
;
502 HFSExtentRecord data
;
504 btRecordSize
= sizeof(HFSExtentRecord
);
505 btRecord
.bufferAddress
= &data
;
506 btRecord
.itemSize
= btRecordSize
;
507 btRecord
.itemCount
= 1;
509 keyPtr
= (HFSExtentKey
*) &btIterator
.key
;
510 keyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
511 keyPtr
->forkType
= key
->forkType
;
512 keyPtr
->fileID
= key
->fileID
;
513 keyPtr
->startBlock
= key
->startBlock
;
515 err
= HFSPlusToHFSExtents(extents
, data
);
517 else { // HFS Plus volume
518 btRecordSize
= sizeof(HFSPlusExtentRecord
);
519 btRecord
.bufferAddress
= extents
;
520 btRecord
.itemSize
= btRecordSize
;
521 btRecord
.itemCount
= 1;
523 BlockMoveData(key
, &btIterator
.key
, sizeof(HFSPlusExtentKey
));
527 err
= BTInsertRecord(GetFileControlBlock(vcb
->extentsRefNum
), &btIterator
, &btRecord
, btRecordSize
);
530 *hint
= btIterator
.hint
.nodeNum
;
536 OSErr
DeleteExtentRecord(
537 const ExtendedVCB
*vcb
,
542 BTreeIterator btIterator
;
546 (void) BTInvalidateHint(&btIterator
);
548 if (vcb
->vcbSigWord
== kHFSSigWord
) {
549 HFSExtentKey
* keyPtr
;
551 keyPtr
= (HFSExtentKey
*) &btIterator
.key
;
552 keyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
553 keyPtr
->forkType
= forkType
;
554 keyPtr
->fileID
= fileID
;
555 keyPtr
->startBlock
= startBlock
;
557 else { // HFS Plus volume
558 HFSPlusExtentKey
* keyPtr
;
560 keyPtr
= (HFSPlusExtentKey
*) &btIterator
.key
;
561 keyPtr
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
562 keyPtr
->forkType
= forkType
;
564 keyPtr
->fileID
= fileID
;
565 keyPtr
->startBlock
= startBlock
;
568 err
= BTDeleteRecord(GetFileControlBlock(vcb
->extentsRefNum
), &btIterator
);
575 //_________________________________________________________________________________
577 // Routine: MapFileBlock
579 // Function: Maps a file position into a physical disk address.
581 // Input: A2.L - VCB pointer
582 // (A1,D1.W) - FCB pointer
583 // D4.L - number of bytes desired
584 // D5.L - file position (byte address)
586 // Output: D3.L - physical start block
587 // D6.L - number of contiguous bytes available (up to D4 bytes)
588 // D0.L - result code <01Oct85>
590 // FXRangeErr = file position beyond mapped range <17Oct85>
591 // FXOvFlErr = extents file overflow <17Oct85>
592 // other = error <17Oct85>
594 // Called By: Log2Phys (read/write in place), Cache (map a file block).
595 //_________________________________________________________________________________
597 OSErr
MapFileBlockC (
598 ExtendedVCB
*vcb
, // volume that file resides on
599 FCB
*fcb
, // FCB of file
600 size_t numberOfBytes
, // number of contiguous bytes desired
601 off_t offset
, // starting offset within file (in bytes)
602 daddr_t
*startSector
, // first 512-byte sector (NOT an allocation block)
603 size_t *availableBytes
) // number of contiguous bytes (up to numberOfBytes)
606 UInt32 allocBlockSize
; // Size of the volume's allocation block
607 HFSPlusExtentKey foundKey
;
608 HFSPlusExtentRecord foundData
;
611 UInt32 firstFABN
; // file allocation block of first block in found extent
612 UInt32 nextFABN
; // file allocation block of block after end of found extent
613 off_t dataEnd
; // (offset) end of range that is contiguous
614 UInt32 sectorsPerBlock
; // Number of sectors per allocation block
615 UInt32 startBlock
; // volume allocation block corresponding to firstFABN
619 allocBlockSize
= vcb
->blockSize
;
621 err
= SearchExtentFile(vcb
, fcb
, offset
, &foundKey
, foundData
, &foundIndex
, &hint
, &nextFABN
);
623 startBlock
= foundData
[foundIndex
].startBlock
;
624 firstFABN
= nextFABN
- foundData
[foundIndex
].blockCount
;
633 // Determine the end of the available space. It will either be the end of the extent,
634 // or the file's PEOF, whichever is smaller.
636 dataEnd
= (off_t
)((off_t
)(nextFABN
) * (off_t
)(allocBlockSize
)); // Assume valid data through end of this extent
637 if (fcb
->fcbPLen
< dataEnd
) // Is PEOF shorter?
638 dataEnd
= fcb
->fcbPLen
; // Yes, so only map up to PEOF
640 // Compute the number of sectors in an allocation block
641 sectorsPerBlock
= allocBlockSize
/ kSectorSize
; // sectors per allocation block
644 // Compute the absolute sector number that contains the offset of the given file
646 temp
= (daddr_t
)((offset
- (off_t
)((off_t
)(firstFABN
) * (off_t
)(allocBlockSize
)))/kSectorSize
); // offset in sectors from start of the extent
647 temp
+= startBlock
* sectorsPerBlock
; // offset in sectors from start of allocation block space
648 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
649 temp
+= vcb
->hfsPlusIOPosOffset
/512; /* offset inside wrapper */
651 temp
+= vcb
->vcbAlBlSt
; /* offset in sectors from start of volume */
653 // Return the desired sector for file position "offset"
657 // Determine the number of contiguous bytes until the end of the extent
658 // (or the amount they asked for, whichever comes first).
660 tmpOff
= dataEnd
- offset
;
661 if (tmpOff
> (off_t
)(numberOfBytes
))
662 *availableBytes
= numberOfBytes
; // more there than they asked for, so pin the output
664 *availableBytes
= tmpOff
;
670 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
671 // Routine: ReleaseExtents
673 // Function: Release the extents of a single extent data record.
674 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
676 static OSErr
ReleaseExtents(
678 const HFSPlusExtentRecord extentRecord
,
679 UInt32
*numReleasedAllocationBlocks
,
680 Boolean
*releasedLastExtent
)
683 UInt32 numberOfExtents
;
686 *numReleasedAllocationBlocks
= 0;
687 *releasedLastExtent
= false;
689 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
690 numberOfExtents
= kHFSPlusExtentDensity
;
692 numberOfExtents
= kHFSExtentDensity
;
694 for( extentIndex
= 0; extentIndex
< numberOfExtents
; extentIndex
++)
696 UInt32 numAllocationBlocks
;
698 // Loop over the extent record and release the blocks associated with each extent.
700 numAllocationBlocks
= extentRecord
[extentIndex
].blockCount
;
701 if ( numAllocationBlocks
== 0 )
703 *releasedLastExtent
= true;
707 err
= BlockDeallocate( vcb
, extentRecord
[extentIndex
].startBlock
, numAllocationBlocks
);
711 *numReleasedAllocationBlocks
+= numAllocationBlocks
; // bump FABN to beg of next extent
719 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
720 // Routine: TruncateExtents
722 // Purpose: Delete extent records whose starting file allocation block number
723 // is greater than or equal to a given starting block number. The
724 // allocation blocks represented by the extents are deallocated.
727 // vcb Volume to operate on
728 // fileID Which file to operate on
729 // startBlock Starting file allocation block number for first extent
731 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
733 static OSErr
TruncateExtents(
738 Boolean
* recordDeleted
)
741 UInt32 numberExtentsReleased
;
742 Boolean releasedLastExtent
;
744 HFSPlusExtentKey key
;
745 HFSPlusExtentRecord extents
;
748 err
= FindExtentRecord(vcb
, forkType
, fileID
, startBlock
, false, &key
, extents
, &hint
);
750 if (err
== btNotFound
)
755 err
= ReleaseExtents( vcb
, extents
, &numberExtentsReleased
, &releasedLastExtent
);
756 if (err
!= noErr
) break;
758 err
= DeleteExtentRecord(vcb
, forkType
, fileID
, startBlock
);
759 if (err
!= noErr
) break;
761 *recordDeleted
= true;
762 startBlock
+= numberExtentsReleased
;
770 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
771 // Routine: DeallocateFork
773 // Function: De-allocates all disk space allocated to a specified fork.
774 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
776 static OSErr
DeallocateFork(
778 HFSCatalogNodeID fileID
,
780 HFSPlusExtentRecord catalogExtents
,
781 Boolean
* recordDeleted
) /* true if a record was deleted */
784 UInt32 numReleasedAllocationBlocks
;
785 Boolean releasedLastExtent
;
787 // Release the catalog extents
788 err
= ReleaseExtents( vcb
, catalogExtents
, &numReleasedAllocationBlocks
, &releasedLastExtent
);
789 // Release the extra extents, if present
790 if (err
== noErr
&& !releasedLastExtent
)
791 err
= TruncateExtents(vcb
, forkType
, fileID
, numReleasedAllocationBlocks
, recordDeleted
);
796 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
797 // Routine: FlushExtentFile
799 // Function: Flushes the extent file for a specified volume
800 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
802 OSErr
FlushExtentFile( ExtendedVCB
*vcb
)
807 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
808 err
= BTFlushPath(fcb
);
811 // If the FCB for the extent "file" is dirty, mark the VCB as dirty.
813 if ((fcb
->fcbFlags
& fcbModifiedMask
) != 0)
816 err
= FlushVolumeControlBlock( vcb
);
823 //-------------------------------------------------------------------------------
824 // Routine: DeleteFile
826 // Function: De-allocates all disk space allocated to a specified file
827 // including the space used by the catalog (ie the catalog record).
828 // The space occupied by both forks is also deallocated.
830 //-------------------------------------------------------------------------------
832 OSErr
DeleteFile( ExtendedVCB
*vcb
, HFSCatalogNodeID parDirID
, ConstUTF8Param catalogName
, UInt32 catalogHint
)
836 CatalogNodeData catalogData
;
837 Boolean recordDeleted
;
839 recordDeleted
= false;
841 INIT_CATALOGDATA(&catalogData
, kCatNameNoCopyName
);
843 // Find catalog data in catalog
844 err
= GetCatalogNode( vcb
, parDirID
, catalogName
, kUndefinedStrLen
, catalogHint
, &catalogData
, &catalogHint
);
849 // Check to make sure record is for a file
850 if ( catalogData
.cnd_type
!= kCatalogFileNode
)
857 // Always delete the Catalog record first (to minimize disk corruption)
859 err
= DeleteCatalogNode(vcb
, parDirID
, catalogName
, catalogHint
);
864 // Note: we don't report errors from DeallocateFork since the
865 // file no longer exists (since DeleteCatalogNode succeeded).
866 // Any errors mean that there are possibly some orphaned disk
867 // blocks but from the clients perspective the file was deleted.
870 // Deallocate data fork extents
871 errDF
= DeallocateFork( vcb
, catalogData
.cnd_nodeID
, kDataForkType
,
872 catalogData
.cnd_datafork
.extents
, &recordDeleted
);
874 // Deallocate resource fork extents
875 errRF
= DeallocateFork( vcb
, catalogData
.cnd_nodeID
, kResourceForkType
,
876 catalogData
.cnd_rsrcfork
.extents
, &recordDeleted
);
879 err
= FlushExtentFile( vcb
);
881 CLEAN_CATALOGDATA(&catalogData
);
882 return (errDF
? errDF
: (errRF
? errRF
: err
));
885 CLEAN_CATALOGDATA(&catalogData
);
889 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
890 // Routine: CompareExtentKeys
892 // Function: Compares two extent file keys (a search key and a trial key) for
894 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
896 SInt32
CompareExtentKeys( const HFSExtentKey
*searchKey
, const HFSExtentKey
*trialKey
)
898 SInt32 result
; // ± 1
901 if (searchKey
->keyLength
!= kHFSExtentKeyMaximumLength
)
902 DebugStr("\pHFS: search Key is wrong length");
903 if (trialKey
->keyLength
!= kHFSExtentKeyMaximumLength
)
904 DebugStr("\pHFS: trial Key is wrong length");
907 result
= -1; // assume searchKey < trialKey
909 if (searchKey
->fileID
== trialKey
->fileID
) {
911 // FileNum's are equal; compare fork types
913 if (searchKey
->forkType
== trialKey
->forkType
) {
915 // Fork types are equal; compare allocation block number
917 if (searchKey
->startBlock
== trialKey
->startBlock
) {
919 // Everything is equal
925 // Allocation block numbers differ; determine sign
927 if (searchKey
->startBlock
> trialKey
->startBlock
)
933 // Fork types differ; determine sign
935 if (searchKey
->forkType
> trialKey
->forkType
)
941 // FileNums differ; determine sign
943 if (searchKey
->fileID
> trialKey
->fileID
)
952 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
953 // Routine: CompareExtentKeysPlus
955 // Function: Compares two extent file keys (a search key and a trial key) for
957 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
959 SInt32
CompareExtentKeysPlus( const HFSPlusExtentKey
*searchKey
, const HFSPlusExtentKey
*trialKey
)
961 SInt32 result
; // ± 1
964 if (searchKey
->keyLength
!= kHFSPlusExtentKeyMaximumLength
)
965 DebugStr("\pHFS: search Key is wrong length");
966 if (trialKey
->keyLength
!= kHFSPlusExtentKeyMaximumLength
)
967 DebugStr("\pHFS: trial Key is wrong length");
970 result
= -1; // assume searchKey < trialKey
972 if (searchKey
->fileID
== trialKey
->fileID
) {
974 // FileNum's are equal; compare fork types
976 if (searchKey
->forkType
== trialKey
->forkType
) {
978 // Fork types are equal; compare allocation block number
980 if (searchKey
->startBlock
== trialKey
->startBlock
) {
982 // Everything is equal
988 // Allocation block numbers differ; determine sign
990 if (searchKey
->startBlock
> trialKey
->startBlock
)
996 // Fork types differ; determine sign
998 if (searchKey
->forkType
> trialKey
->forkType
)
1004 // FileNums differ; determine sign
1006 if (searchKey
->fileID
> trialKey
->fileID
)
1015 //_________________________________________________________________________________
1017 // Routine: Extendfile
1019 // Function: Extends the disk space allocated to a file.
1021 // Input: A2.L - VCB pointer
1022 // A1.L - pointer to FCB array
1023 // D1.W - file refnum
1024 // D3.B - option flags
1025 // kEFContigMask - force contiguous allocation
1026 // kEFAllMask - allocate all requested bytes or none
1027 // NOTE: You may not set both options.
1028 // D4.L - number of additional bytes to allocate
1030 // Output: D0.W - result code
1033 // D6.L - number of bytes allocated
1035 // Called by: FileAloc,FileWrite,SetEof
1037 // Note: ExtendFile updates the PEOF in the FCB.
1038 //_________________________________________________________________________________
1041 ExtendedVCB
*vcb
, // volume that file resides on
1042 FCB
*fcb
, // FCB of file to truncate
1043 SInt64 bytesToAdd
, // number of bytes to allocate
1044 UInt32 blockHint
, // desired starting allocation block
1045 UInt32 flags
, // EFContig and/or EFAll
1046 SInt64
*actualBytesAdded
) // number of bytes actually allocated
1049 UInt32 volumeBlockSize
;
1051 SInt64 bytesThisExtent
;
1052 HFSPlusExtentKey foundKey
;
1053 HFSPlusExtentRecord foundData
;
1058 Boolean allOrNothing
;
1059 Boolean forceContig
;
1062 UInt32 actualStartBlock
;
1063 UInt32 actualNumBlocks
;
1064 UInt32 numExtentsPerRecord
;
1065 SInt64 maximumBytes
;
1067 SInt64 previousPEOF
;
1071 *actualBytesAdded
= 0;
1072 volumeBlockSize
= vcb
->blockSize
;
1073 allOrNothing
= ((flags
& kEFAllMask
) != 0);
1074 forceContig
= ((flags
& kEFContigMask
) != 0);
1075 previousPEOF
= fcb
->fcbPLen
;
1077 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1078 numExtentsPerRecord
= kHFSPlusExtentDensity
;
1080 numExtentsPerRecord
= kHFSExtentDensity
;
1083 // Make sure the request and new PEOF are less than 2GB if HFS.
1085 if (vcb
->vcbSigWord
== kHFSSigWord
) {
1086 if (bytesToAdd
>= kTwoGigabytes
)
1088 if ((fcb
->fcbPLen
+ bytesToAdd
) >= kTwoGigabytes
)
1092 // Determine how many blocks need to be allocated.
1093 // Round up the number of desired bytes to add.
1095 blocksToAdd
= FileBytesToBlocks(bytesToAdd
, volumeBlockSize
);
1096 bytesToAdd
= (SInt64
)((SInt64
)blocksToAdd
* (SInt64
)volumeBlockSize
);
1099 // If the file's clump size is larger than the allocation block size,
1100 // then set the maximum number of bytes to the requested number of bytes
1101 // rounded up to a multiple of the clump size.
1103 if (fcb
->fcbClmpSize
> volumeBlockSize
) {
1104 maximumBytes
= (SInt64
)FileBytesToBlocks(bytesToAdd
, fcb
->fcbClmpSize
);
1105 maximumBytes
*= fcb
->fcbClmpSize
;
1108 maximumBytes
= bytesToAdd
;
1112 // Compute new physical EOF, rounded up to a multiple of a block.
1114 if ((vcb
->vcbSigWord
== kHFSSigWord
) && ((fcb
->fcbPLen
+ bytesToAdd
) >= (SInt64
) kTwoGigabytes
)) // Too big?
1115 if (allOrNothing
) // Yes, must they have it all?
1116 goto Overflow
; // Yes, can't have it
1118 --blocksToAdd
; // No, give give 'em one block less
1119 bytesToAdd
-= volumeBlockSize
;
1123 // If allocation is all-or-nothing, make sure there are
1124 // enough free blocks on the volume (quick test).
1126 if (allOrNothing
&& (blocksToAdd
> (SInt64
)vcb
->freeBlocks
)) {
1132 // See if there are already enough blocks allocated to the file.
1134 peof
= fcb
->fcbPLen
+ bytesToAdd
; // potential new PEOF
1135 err
= SearchExtentFile(vcb
, fcb
, peof
-1, &foundKey
, foundData
, &foundIndex
, &hint
, &nextBlock
);
1137 // Enough blocks are already allocated. Just update the FCB to reflect the new length.
1138 fcb
->fcbPLen
= peof
;
1139 H_EXTENDSIZE(fcb
, bytesToAdd
);
1140 fcb
->fcbFlags
|= fcbModifiedMask
;
1143 if (err
!= fxRangeErr
) // Any real error?
1144 goto ErrorExit
; // Yes, so exit immediately
1147 // Adjust the PEOF to the end of the last extent.
1149 peof
= (SInt64
)((SInt64
)nextBlock
* (SInt64
)volumeBlockSize
); // currently allocated PEOF
1150 bytesThisExtent
= peof
- fcb
->fcbPLen
;
1151 if (bytesThisExtent
!= 0) {
1152 fcb
->fcbPLen
= peof
;
1153 H_EXTENDSIZE(fcb
, bytesThisExtent
);
1154 fcb
->fcbFlags
|= fcbModifiedMask
;
1155 bytesToAdd
-= bytesThisExtent
;
1159 // Allocate some more space.
1161 // First try a contiguous allocation (of the whole amount).
1162 // If that fails, get whatever we can.
1163 // If forceContig, then take whatever we got
1164 // else, keep getting bits and pieces (non-contig)
1167 vcb
->vcbFreeExtCnt
= 0; /* For now, force rebuild of free extent list */
1170 startBlock
= blockHint
;
1172 startBlock
= foundData
[foundIndex
].startBlock
+ foundData
[foundIndex
].blockCount
;
1173 err
= BlockAllocate(vcb
, startBlock
, bytesToAdd
, maximumBytes
, wantContig
, &actualStartBlock
, &actualNumBlocks
);
1174 if (err
== dskFulErr
) {
1176 break; // AllocContig failed because not enough contiguous space
1178 // Couldn't get one big chunk, so get whatever we can.
1183 if (actualNumBlocks
!= 0)
1187 // Add the new extent to the existing extent record, or create a new one.
1188 if ((actualStartBlock
== startBlock
) && (blockHint
== 0)) {
1189 // We grew the file's last extent, so just adjust the number of blocks.
1190 foundData
[foundIndex
].blockCount
+= actualNumBlocks
;
1191 err
= UpdateExtentRecord(vcb
, fcb
, &foundKey
, foundData
, hint
);
1192 if (err
!= noErr
) break;
1197 // Need to add a new extent. See if there is room in the current record.
1198 if (foundData
[foundIndex
].blockCount
!= 0) // Is current extent free to use?
1199 ++foundIndex
; // No, so use the next one.
1200 if (foundIndex
== numExtentsPerRecord
) {
1201 // This record is full. Need to create a new one.
1202 if (H_FILEID(fcb
) == kHFSExtentsFileID
) {
1203 (void) BlockDeallocate(vcb
, actualStartBlock
, actualNumBlocks
);
1204 err
= dskFulErr
; // Oops. Can't extend extents file past first record.
1208 foundKey
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
1209 if (fcb
->fcbFlags
& fcbResourceMask
)
1210 foundKey
.forkType
= kResourceForkType
;
1212 foundKey
.forkType
= kDataForkType
;
1214 foundKey
.fileID
= H_FILEID(fcb
);
1215 foundKey
.startBlock
= nextBlock
;
1217 foundData
[0].startBlock
= actualStartBlock
;
1218 foundData
[0].blockCount
= actualNumBlocks
;
1220 // zero out remaining extents...
1221 for (i
= 1; i
< kHFSPlusExtentDensity
; ++i
)
1223 foundData
[i
].startBlock
= 0;
1224 foundData
[i
].blockCount
= 0;
1229 err
= CreateExtentRecord(vcb
, &foundKey
, foundData
, &hint
);
1230 if (err
== fxOvFlErr
) {
1231 // We couldn't create an extent record because extents B-tree
1232 // couldn't grow. Dellocate the extent just allocated and
1233 // return a disk full error.
1234 (void) BlockDeallocate(vcb
, actualStartBlock
, actualNumBlocks
);
1237 if (err
!= noErr
) break;
1239 needsFlush
= true; // We need to update the B-tree header
1242 // Add a new extent into this record and update.
1243 foundData
[foundIndex
].startBlock
= actualStartBlock
;
1244 foundData
[foundIndex
].blockCount
= actualNumBlocks
;
1245 err
= UpdateExtentRecord(vcb
, fcb
, &foundKey
, foundData
, hint
);
1246 if (err
!= noErr
) break;
1250 // Figure out how many bytes were actually allocated.
1251 // NOTE: BlockAllocate could have allocated more than we asked for.
1252 // Don't set the PEOF beyond what our client asked for.
1253 nextBlock
+= actualNumBlocks
;
1254 bytesThisExtent
= (SInt64
)((SInt64
)actualNumBlocks
* (SInt64
)volumeBlockSize
);
1255 if (bytesThisExtent
> bytesToAdd
) {
1259 bytesToAdd
-= bytesThisExtent
;
1260 maximumBytes
-= bytesThisExtent
;
1262 fcb
->fcbPLen
+= bytesThisExtent
;
1263 H_EXTENDSIZE(fcb
, bytesThisExtent
);
1264 fcb
->fcbFlags
|= fcbModifiedMask
;
1266 // If contiguous allocation was requested, then we've already got one contiguous
1267 // chunk. If we didn't get all we wanted, then adjust the error to disk full.
1269 if (bytesToAdd
!= 0)
1271 break; // We've already got everything that's contiguous
1274 } while (err
== noErr
&& bytesToAdd
);
1278 *actualBytesAdded
= fcb
->fcbPLen
- previousPEOF
;
1281 (void) FlushExtentFile(vcb
);
1286 err
= fileBoundsErr
;
1292 //_________________________________________________________________________________
1294 // Routine: TruncateFileC
1296 // Function: Truncates the disk space allocated to a file. The file space is
1297 // truncated to a specified new PEOF rounded up to the next allocation
1298 // block boundry. If the 'TFTrunExt' option is specified, the file is
1299 // truncated to the end of the extent containing the new PEOF.
1301 // Input: A2.L - VCB pointer
1302 // A1.L - pointer to FCB array
1303 // D1.W - file refnum
1304 // D2.B - option flags
1305 // TFTrunExt - truncate to the extent containing new PEOF
1308 // Output: D0.W - result code
1312 // Note: TruncateFile updates the PEOF in the FCB.
1313 //_________________________________________________________________________________
1315 OSErr
TruncateFileC (
1316 ExtendedVCB
*vcb
, // volume that file resides on
1317 FCB
*fcb
, // FCB of file to truncate
1318 SInt64 peof
, // new physical size for file
1319 Boolean truncateToExtent
) // if true, truncate to end of extent containing newPEOF
1322 UInt32 nextBlock
; // next file allocation block to consider
1323 UInt32 startBlock
; // Physical (volume) allocation block number of start of a range
1324 UInt32 physNumBlocks
; // Number of allocation blocks in file (according to PEOF)
1326 HFSPlusExtentKey key
; // key for current extent record; key->keyLength == 0 if FCB's extent record
1327 UInt32 hint
; // BTree hint corresponding to key
1328 HFSPlusExtentRecord extentRecord
;
1330 UInt32 extentNextBlock
;
1331 UInt32 numExtentsPerRecord
;
1334 Boolean extentChanged
; // true if we actually changed an extent
1335 Boolean recordDeleted
; // true if an extent record got deleted
1338 recordDeleted
= false;
1340 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1341 numExtentsPerRecord
= kHFSPlusExtentDensity
;
1343 numExtentsPerRecord
= kHFSExtentDensity
;
1345 if (fcb
->fcbFlags
& fcbResourceMask
)
1346 forkType
= kResourceForkType
;
1348 forkType
= kDataForkType
;
1350 temp64
= fcb
->fcbPLen
/ (SInt64
)vcb
->blockSize
; // number of allocation blocks currently in file
1351 physNumBlocks
= (UInt32
)temp64
;
1354 // Round newPEOF up to a multiple of the allocation block size. If new size is
1355 // two gigabytes or more, then round down by one allocation block (??? really?
1356 // shouldn't that be an error?).
1358 nextBlock
= FileBytesToBlocks(peof
, vcb
->blockSize
); // number of allocation blocks to remain in file
1359 peof
= (SInt64
)((SInt64
)nextBlock
* (SInt64
)vcb
->blockSize
); // number of bytes in those blocks
1360 if ((vcb
->vcbSigWord
== kHFSSigWord
) && (peof
>= (UInt32
) kTwoGigabytes
)) {
1362 DebugStr("\pHFS: Trying to truncate a file to 2GB or more");
1364 err
= fileBoundsErr
;
1369 // Update FCB's length
1371 H_TRUNCSIZE(fcb
, fcb
->fcbPLen
- peof
);
1372 fcb
->fcbPLen
= peof
;
1373 fcb
->fcbFlags
|= fcbModifiedMask
;
1376 // If the new PEOF is 0, then truncateToExtent has no meaning (we should always deallocate
1382 // Deallocate all the extents for this fork
1383 err
= DeallocateFork(vcb
, H_FILEID(fcb
), forkType
, fcb
->fcbExtents
, &recordDeleted
);
1384 if (err
!= noErr
) goto ErrorExit
; // got some error, so return it
1386 // Update the catalog extent record (making sure it's zeroed out)
1388 for (i
=0; i
< kHFSPlusExtentDensity
; i
++) {
1389 fcb
->fcbExtents
[i
].startBlock
= 0;
1390 fcb
->fcbExtents
[i
].blockCount
= 0;
1397 // Find the extent containing byte (peof-1). This is the last extent we'll keep.
1398 // (If truncateToExtent is true, we'll keep the whole extent; otherwise, we'll only
1399 // keep up through peof). The search will tell us how many allocation blocks exist
1400 // in the found extent plus all previous extents.
1402 err
= SearchExtentFile(vcb
, fcb
, peof
-1, &key
, extentRecord
, &extentIndex
, &hint
, &extentNextBlock
);
1403 if (err
!= noErr
) goto ErrorExit
;
1405 extentChanged
= false; // haven't changed the extent yet
1407 if (!truncateToExtent
) {
1409 // Shorten this extent. It may be the case that the entire extent gets
1412 numBlocks
= extentNextBlock
- nextBlock
; // How many blocks in this extent to free up
1413 if (numBlocks
!= 0) {
1414 // Compute first volume allocation block to free
1415 startBlock
= extentRecord
[extentIndex
].startBlock
+ extentRecord
[extentIndex
].blockCount
- numBlocks
;
1416 // Free the blocks in bitmap
1417 err
= BlockDeallocate(vcb
, startBlock
, numBlocks
);
1418 if (err
!= noErr
) goto ErrorExit
;
1419 // Adjust length of this extent
1420 extentRecord
[extentIndex
].blockCount
-= numBlocks
;
1421 // If extent is empty, set start block to 0
1422 if (extentRecord
[extentIndex
].blockCount
== 0)
1423 extentRecord
[extentIndex
].startBlock
= 0;
1424 // Remember that we changed the extent record
1425 extentChanged
= true;
1430 // Now move to the next extent in the record, and set up the file allocation block number
1432 nextBlock
= extentNextBlock
; // Next file allocation block to free
1433 ++extentIndex
; // Its index within the extent record
1436 // Release all following extents in this extent record. Update the record.
1438 while (extentIndex
< numExtentsPerRecord
&& extentRecord
[extentIndex
].blockCount
!= 0) {
1439 numBlocks
= extentRecord
[extentIndex
].blockCount
;
1440 // Deallocate this extent
1441 err
= BlockDeallocate(vcb
, extentRecord
[extentIndex
].startBlock
, numBlocks
);
1442 if (err
!= noErr
) goto ErrorExit
;
1443 // Update next file allocation block number
1444 nextBlock
+= numBlocks
;
1445 // Zero out start and length of this extent to delete it from record
1446 extentRecord
[extentIndex
].startBlock
= 0;
1447 extentRecord
[extentIndex
].blockCount
= 0;
1448 // Remember that we changed an extent
1449 extentChanged
= true;
1450 // Move to next extent in record
1455 // If any of the extents in the current record were changed, then update that
1456 // record (in the FCB, or extents file).
1458 if (extentChanged
) {
1459 err
= UpdateExtentRecord(vcb
, fcb
, &key
, extentRecord
, hint
);
1460 if (err
!= noErr
) goto ErrorExit
;
1464 // If there are any following allocation blocks, then we need
1465 // to seach for their extent records and delete those allocation
1468 if (nextBlock
< physNumBlocks
)
1469 err
= TruncateExtents(vcb
, forkType
, H_FILEID(fcb
), nextBlock
, &recordDeleted
);
1475 (void) FlushExtentFile(vcb
);
1482 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1483 // Routine: SearchExtentRecord (was XRSearch)
1485 // Function: Searches extent record for the extent mapping a given file
1486 // allocation block number (FABN).
1488 // Input: searchFABN - desired FABN
1489 // extentData - pointer to extent data record (xdr)
1490 // extentDataStartFABN - beginning FABN for extent record
1492 // Output: foundExtentDataOffset - offset to extent entry within xdr
1493 // result = noErr, offset to extent mapping desired FABN
1494 // result = FXRangeErr, offset to last extent in record
1495 // endingFABNPlusOne - ending FABN +1
1496 // noMoreExtents - True if the extent was not found, and the
1497 // extent record was not full (so don't bother
1498 // looking in subsequent records); false otherwise.
1500 // Result: noErr = ok
1501 // FXRangeErr = desired FABN > last mapped FABN in record
1502 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\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 static OSErr
SearchExtentRecord(
1505 const ExtendedVCB
*vcb
,
1507 const HFSPlusExtentRecord extentData
,
1508 UInt32 extentDataStartFABN
,
1509 UInt32
*foundExtentIndex
,
1510 UInt32
*endingFABNPlusOne
,
1511 Boolean
*noMoreExtents
)
1515 UInt32 numberOfExtents
;
1516 UInt32 numAllocationBlocks
;
1517 Boolean foundExtent
;
1519 *endingFABNPlusOne
= extentDataStartFABN
;
1520 *noMoreExtents
= false;
1521 foundExtent
= false;
1523 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1524 numberOfExtents
= kHFSPlusExtentDensity
;
1526 numberOfExtents
= kHFSExtentDensity
;
1528 for( extentIndex
= 0; extentIndex
< numberOfExtents
; ++extentIndex
)
1531 // Loop over the extent record and find the search FABN.
1533 numAllocationBlocks
= extentData
[extentIndex
].blockCount
;
1534 if ( numAllocationBlocks
== 0 )
1539 *endingFABNPlusOne
+= numAllocationBlocks
;
1541 if( searchFABN
< *endingFABNPlusOne
)
1543 // Found the extent.
1551 // Found the extent. Note the extent offset
1552 *foundExtentIndex
= extentIndex
;
1556 // Did not find the extent. Set foundExtentDataOffset accordingly
1557 if( extentIndex
> 0 )
1559 *foundExtentIndex
= extentIndex
- 1;
1563 *foundExtentIndex
= 0;
1566 // If we found an empty extent, then set noMoreExtents.
1567 if (extentIndex
< numberOfExtents
)
1568 *noMoreExtents
= true;
1570 // Finally, return an error to the caller
1577 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1578 // Routine: SearchExtentFile (was XFSearch)
1580 // Function: Searches extent file (including the FCB resident extent record)
1581 // for the extent mapping a given file position.
1583 // Input: vcb - VCB pointer
1584 // fcb - FCB pointer
1585 // filePosition - file position (byte address)
1587 // Output: foundExtentKey - extent key record (xkr)
1588 // If extent was found in the FCB's resident extent record,
1589 // then foundExtentKey->keyLength will be set to 0.
1590 // foundExtentData - extent data record(xdr)
1591 // foundExtentIndex - index to extent entry in xdr
1592 // result = 0, offset to extent mapping desired FABN
1593 // result = FXRangeErr, offset to last extent in record
1594 // (i.e., kNumExtentsPerRecord-1)
1595 // extentBTreeHint - BTree hint for extent record
1596 // kNoHint = Resident extent record
1597 // endingFABNPlusOne - ending FABN +1
1600 // noErr Found an extent that contains the given file position
1601 // FXRangeErr Given position is beyond the last allocated extent
1602 // (other) (some other internal I/O error)
1603 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1605 static OSErr
SearchExtentFile(
1606 const ExtendedVCB
*vcb
,
1608 SInt64 filePosition
,
1609 HFSPlusExtentKey
*foundExtentKey
,
1610 HFSPlusExtentRecord foundExtentData
,
1611 UInt32
*foundExtentIndex
,
1612 UInt32
*extentBTreeHint
,
1613 UInt32
*endingFABNPlusOne
)
1616 UInt32 filePositionBlock
;
1618 Boolean noMoreExtents
;
1620 temp64
= filePosition
/ (SInt64
)vcb
->blockSize
;
1621 filePositionBlock
= (UInt32
)temp64
;
1623 bcopy ( fcb
->fcbExtents
, foundExtentData
, sizeof(HFSPlusExtentRecord
));
1625 // Search the resident FCB first.
1626 err
= SearchExtentRecord( vcb
, filePositionBlock
, foundExtentData
, 0,
1627 foundExtentIndex
, endingFABNPlusOne
, &noMoreExtents
);
1629 if( err
== noErr
) {
1630 // Found the extent. Set results accordingly
1631 *extentBTreeHint
= kNoHint
; // no hint, because not in the BTree
1632 foundExtentKey
->keyLength
= 0; // 0 = the FCB itself
1637 // Didn't find extent in FCB. If FCB's extent record wasn't full, there's no point
1638 // in searching the extents file. Note that SearchExtentRecord left us pointing at
1639 // the last valid extent (or the first one, if none were valid). This means we need
1640 // to fill in the hint and key outputs, just like the "if" statement above.
1641 if ( noMoreExtents
) {
1642 *extentBTreeHint
= kNoHint
; // no hint, because not in the BTree
1643 foundExtentKey
->keyLength
= 0; // 0 = the FCB itself
1644 err
= fxRangeErr
; // There are no more extents, so must be beyond PEOF
1649 // Find the desired record, or the previous record if it is the same fork
1651 err
= FindExtentRecord(vcb
, (fcb
->fcbFlags
& fcbResourceMask
) ? kResourceForkType
: kDataForkType
,
1652 H_FILEID(fcb
), filePositionBlock
, true, foundExtentKey
, foundExtentData
, extentBTreeHint
);
1654 if (err
== btNotFound
) {
1656 // If we get here, the desired position is beyond the extents in the FCB, and there are no extents
1657 // in the extents file. Return the FCB's extents and a range error.
1659 *extentBTreeHint
= kNoHint
;
1660 foundExtentKey
->keyLength
= 0;
1661 err
= GetFCBExtentRecord(fcb
, foundExtentData
);
1662 // Note: foundExtentIndex and endingFABNPlusOne have already been set as a result of the very
1663 // first SearchExtentRecord call in this function (when searching in the FCB's extents, and
1664 // we got a range error).
1670 // If we get here, there was either a BTree error, or we found an appropriate record.
1671 // If we found a record, then search it for the correct index into the extents.
1674 // Find appropriate index into extent record
1675 err
= SearchExtentRecord(vcb
, filePositionBlock
, foundExtentData
, foundExtentKey
->startBlock
,
1676 foundExtentIndex
, endingFABNPlusOne
, &noMoreExtents
);
1685 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1686 // Routine: UpdateExtentRecord
1688 // Function: Write new extent data to an existing extent record with a given key.
1689 // If all of the extents are empty, and the extent record is in the
1690 // extents file, then the record is deleted.
1692 // Input: vcb - the volume containing the extents
1693 // fcb - the file that owns the extents
1694 // extentFileKey - pointer to extent key record (xkr)
1695 // If the key length is 0, then the extents are actually part
1696 // of the catalog record, stored in the FCB.
1697 // extentData - pointer to extent data record (xdr)
1698 // extentBTreeHint - hint for given key, or kNoHint
1700 // Result: noErr = ok
1701 // (other) = error from BTree
1702 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1704 static OSErr
UpdateExtentRecord (
1705 const ExtendedVCB
*vcb
,
1707 const HFSPlusExtentKey
*extentFileKey
,
1708 const HFSPlusExtentRecord extentData
,
1709 UInt32 extentBTreeHint
)
1711 BTreeIterator btIterator
;
1712 FSBufferDescriptor btRecord
;
1713 UInt16 btRecordSize
;
1717 if (extentFileKey
->keyLength
== 0) { // keyLength == 0 means the FCB's extent record
1718 BlockMoveData(extentData
, fcb
->fcbExtents
, sizeof(HFSPlusExtentRecord
));
1719 fcb
->fcbFlags
|= fcbModifiedMask
;
1723 // Need to find and change a record in Extents BTree
1725 btFCB
= GetFileControlBlock(vcb
->extentsRefNum
);
1727 if (vcb
->vcbSigWord
== kHFSSigWord
) {
1728 HFSExtentKey
* key
; // Actual extent key used on disk in HFS
1729 HFSExtentRecord foundData
; // The extent data actually found
1731 key
= (HFSExtentKey
*) &btIterator
.key
;
1732 key
->keyLength
= kHFSExtentKeyMaximumLength
;
1733 key
->forkType
= extentFileKey
->forkType
;
1734 key
->fileID
= extentFileKey
->fileID
;
1735 key
->startBlock
= extentFileKey
->startBlock
;
1737 btIterator
.hint
.index
= 0;
1738 btIterator
.hint
.nodeNum
= extentBTreeHint
;
1740 btRecord
.bufferAddress
= &foundData
;
1741 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
1742 btRecord
.itemCount
= 1;
1744 err
= BTSearchRecord(btFCB
, &btIterator
, kInvalidMRUCacheKey
, &btRecord
,
1745 &btRecordSize
, &btIterator
);
1748 err
= HFSPlusToHFSExtents(extentData
, (HFSExtentDescriptor
*)&foundData
);
1751 err
= BTReplaceRecord(btFCB
, &btIterator
, &btRecord
, btRecordSize
);
1753 else { // HFS Plus volume
1754 HFSPlusExtentRecord foundData
; // The extent data actually found
1756 BlockMoveData(extentFileKey
, &btIterator
.key
, sizeof(HFSPlusExtentKey
));
1758 btIterator
.hint
.index
= 0;
1759 btIterator
.hint
.nodeNum
= extentBTreeHint
;
1761 btRecord
.bufferAddress
= &foundData
;
1762 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
1763 btRecord
.itemCount
= 1;
1765 err
= BTSearchRecord(btFCB
, &btIterator
, kInvalidMRUCacheKey
, &btRecord
,
1766 &btRecordSize
, &btIterator
);
1769 BlockMoveData(extentData
, &foundData
, sizeof(HFSPlusExtentRecord
));
1770 err
= BTReplaceRecord(btFCB
, &btIterator
, &btRecord
, btRecordSize
);
1780 void HFSToHFSPlusExtents(
1781 const HFSExtentRecord oldExtents
,
1782 HFSPlusExtentRecord newExtents
)
1786 // copy the first 3 extents
1787 newExtents
[0].startBlock
= oldExtents
[0].startBlock
;
1788 newExtents
[0].blockCount
= oldExtents
[0].blockCount
;
1789 newExtents
[1].startBlock
= oldExtents
[1].startBlock
;
1790 newExtents
[1].blockCount
= oldExtents
[1].blockCount
;
1791 newExtents
[2].startBlock
= oldExtents
[2].startBlock
;
1792 newExtents
[2].blockCount
= oldExtents
[2].blockCount
;
1794 // zero out the remaining ones
1795 for (i
= 3; i
< kHFSPlusExtentDensity
; ++i
)
1797 newExtents
[i
].startBlock
= 0;
1798 newExtents
[i
].blockCount
= 0;
1804 OSErr
HFSPlusToHFSExtents(
1805 const HFSPlusExtentRecord oldExtents
,
1806 HFSExtentRecord newExtents
)
1812 // copy the first 3 extents
1813 newExtents
[0].startBlock
= oldExtents
[0].startBlock
;
1814 newExtents
[0].blockCount
= oldExtents
[0].blockCount
;
1815 newExtents
[1].startBlock
= oldExtents
[1].startBlock
;
1816 newExtents
[1].blockCount
= oldExtents
[1].blockCount
;
1817 newExtents
[2].startBlock
= oldExtents
[2].startBlock
;
1818 newExtents
[2].blockCount
= oldExtents
[2].blockCount
;
1821 if (oldExtents
[3].startBlock
|| oldExtents
[3].blockCount
) {
1822 DebugStr("\pExtentRecord with > 3 extents is invalid for HFS");
1833 OSErr
GetFCBExtentRecord(
1835 HFSPlusExtentRecord extents
)
1838 BlockMoveData(fcb
->fcbExtents
, extents
, sizeof(HFSPlusExtentRecord
));
1844 //_________________________________________________________________________________
1846 // Routine: ExtentsAreIntegral
1848 // Purpose: Ensure that each extent can hold an integral number of nodes
1849 // Called by the NodesAreContiguous function
1850 //_________________________________________________________________________________
1852 static Boolean
ExtentsAreIntegral(
1853 const HFSPlusExtentRecord extentRecord
,
1855 UInt32
*blocksChecked
,
1856 Boolean
*checkedLastExtent
)
1862 *checkedLastExtent
= false;
1864 for(extentIndex
= 0; extentIndex
< kHFSPlusExtentDensity
; extentIndex
++)
1866 blocks
= extentRecord
[extentIndex
].blockCount
;
1870 *checkedLastExtent
= true;
1874 *blocksChecked
+= blocks
;
1884 //_________________________________________________________________________________
1886 // Routine: NodesAreContiguous
1888 // Purpose: Ensure that all b-tree nodes are contiguous on disk
1889 // Called by BTOpenPath during volume mount
1890 //_________________________________________________________________________________
1892 Boolean
NodesAreContiguous(
1899 UInt32 blocksChecked
;
1901 HFSPlusExtentKey key
;
1902 HFSPlusExtentRecord extents
;
1904 Boolean lastExtentReached
;
1907 if (vcb
->blockSize
>= nodeSize
)
1910 mask
= (nodeSize
/ vcb
->blockSize
) - 1;
1912 // check the local extents
1913 (void) GetFCBExtentRecord(fcb
, extents
);
1914 if ( !ExtentsAreIntegral(extents
, mask
, &blocksChecked
, &lastExtentReached
) )
1917 if (lastExtentReached
|| (SInt64
)((SInt64
)blocksChecked
* (SInt64
)vcb
->blockSize
) >= fcb
->fcbPLen
)
1920 startBlock
= blocksChecked
;
1922 // check the overflow extents (if any)
1923 while ( !lastExtentReached
)
1925 result
= FindExtentRecord(vcb
, kDataForkType
, H_FILEID(fcb
), startBlock
, FALSE
, &key
, extents
, &hint
);
1928 if ( !ExtentsAreIntegral(extents
, mask
, &blocksChecked
, &lastExtentReached
) )
1931 startBlock
+= blocksChecked
;