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,
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 (void) BTInvalidateHint(&btIterator
);
381 if (vcb
->vcbSigWord
== kHFSSigWord
) {
382 HFSExtentKey
* extentKeyPtr
;
383 HFSExtentRecord extentData
;
385 extentKeyPtr
= (HFSExtentKey
*) &btIterator
.key
;
386 extentKeyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
387 extentKeyPtr
->forkType
= forkType
;
388 extentKeyPtr
->fileID
= fileID
;
389 extentKeyPtr
->startBlock
= startBlock
;
391 btRecord
.bufferAddress
= &extentData
;
392 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
393 btRecord
.itemCount
= 1;
395 err
= BTSearchRecord(fcb
, &btIterator
, kInvalidMRUCacheKey
, &btRecord
, &btRecordSize
, &btIterator
);
397 if (err
== btNotFound
&& allowPrevious
) {
398 err
= BTIterateRecord(fcb
, kBTreePrevRecord
, &btIterator
, &btRecord
, &btRecordSize
);
400 // A previous record may not exist, so just return btNotFound (like we would if
401 // it was for the wrong file/fork).
402 if (err
== (OSErr
) fsBTStartOfIterationErr
) //¥¥ fsBTStartOfIterationErr is type unsigned long
406 // Found a previous record. Does it belong to the same fork of the same file?
407 if (extentKeyPtr
->fileID
!= fileID
|| extentKeyPtr
->forkType
!= forkType
)
415 // Copy the found key back for the caller
416 foundKey
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
417 foundKey
->forkType
= extentKeyPtr
->forkType
;
419 foundKey
->fileID
= extentKeyPtr
->fileID
;
420 foundKey
->startBlock
= extentKeyPtr
->startBlock
;
422 // Copy the found data back for the caller
423 foundData
[0].startBlock
= extentData
[0].startBlock
;
424 foundData
[0].blockCount
= extentData
[0].blockCount
;
425 foundData
[1].startBlock
= extentData
[1].startBlock
;
426 foundData
[1].blockCount
= extentData
[1].blockCount
;
427 foundData
[2].startBlock
= extentData
[2].startBlock
;
428 foundData
[2].blockCount
= extentData
[2].blockCount
;
430 for (i
= 3; i
< kHFSPlusExtentDensity
; ++i
)
432 foundData
[i
].startBlock
= 0;
433 foundData
[i
].blockCount
= 0;
437 else { // HFS Plus volume
438 HFSPlusExtentKey
* extentKeyPtr
;
439 HFSPlusExtentRecord extentData
;
441 extentKeyPtr
= (HFSPlusExtentKey
*) &btIterator
.key
;
442 extentKeyPtr
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
443 extentKeyPtr
->forkType
= forkType
;
444 extentKeyPtr
->pad
= 0;
445 extentKeyPtr
->fileID
= fileID
;
446 extentKeyPtr
->startBlock
= startBlock
;
448 btRecord
.bufferAddress
= &extentData
;
449 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
450 btRecord
.itemCount
= 1;
452 err
= BTSearchRecord(fcb
, &btIterator
, kInvalidMRUCacheKey
, &btRecord
, &btRecordSize
, &btIterator
);
454 if (err
== btNotFound
&& allowPrevious
) {
455 err
= BTIterateRecord(fcb
, kBTreePrevRecord
, &btIterator
, &btRecord
, &btRecordSize
);
457 // A previous record may not exist, so just return btNotFound (like we would if
458 // it was for the wrong file/fork).
459 if (err
== (OSErr
) fsBTStartOfIterationErr
) //¥¥ fsBTStartOfIterationErr is type unsigned long
463 // Found a previous record. Does it belong to the same fork of the same file?
464 if (extentKeyPtr
->fileID
!= fileID
|| extentKeyPtr
->forkType
!= forkType
)
470 // Copy the found key back for the caller
471 BlockMoveData(extentKeyPtr
, foundKey
, sizeof(HFSPlusExtentKey
));
472 // Copy the found data back for the caller
473 BlockMoveData(&extentData
, foundData
, sizeof(HFSPlusExtentRecord
));
477 *foundHint
= btIterator
.hint
.nodeNum
;
483 static OSErr
CreateExtentRecord(
484 const ExtendedVCB
*vcb
,
485 HFSPlusExtentKey
*key
,
486 HFSPlusExtentRecord extents
,
489 BTreeIterator btIterator
;
490 FSBufferDescriptor btRecord
;
496 (void) BTInvalidateHint(&btIterator
);
498 if (vcb
->vcbSigWord
== kHFSSigWord
) {
499 HFSExtentKey
* keyPtr
;
500 HFSExtentRecord data
;
502 btRecordSize
= sizeof(HFSExtentRecord
);
503 btRecord
.bufferAddress
= &data
;
504 btRecord
.itemSize
= btRecordSize
;
505 btRecord
.itemCount
= 1;
507 keyPtr
= (HFSExtentKey
*) &btIterator
.key
;
508 keyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
509 keyPtr
->forkType
= key
->forkType
;
510 keyPtr
->fileID
= key
->fileID
;
511 keyPtr
->startBlock
= key
->startBlock
;
513 err
= HFSPlusToHFSExtents(extents
, data
);
515 else { // HFS Plus volume
516 btRecordSize
= sizeof(HFSPlusExtentRecord
);
517 btRecord
.bufferAddress
= extents
;
518 btRecord
.itemSize
= btRecordSize
;
519 btRecord
.itemCount
= 1;
521 BlockMoveData(key
, &btIterator
.key
, sizeof(HFSPlusExtentKey
));
525 err
= BTInsertRecord(GetFileControlBlock(vcb
->extentsRefNum
), &btIterator
, &btRecord
, btRecordSize
);
528 *hint
= btIterator
.hint
.nodeNum
;
534 OSErr
DeleteExtentRecord(
535 const ExtendedVCB
*vcb
,
540 BTreeIterator btIterator
;
544 (void) BTInvalidateHint(&btIterator
);
546 if (vcb
->vcbSigWord
== kHFSSigWord
) {
547 HFSExtentKey
* keyPtr
;
549 keyPtr
= (HFSExtentKey
*) &btIterator
.key
;
550 keyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
551 keyPtr
->forkType
= forkType
;
552 keyPtr
->fileID
= fileID
;
553 keyPtr
->startBlock
= startBlock
;
555 else { // HFS Plus volume
556 HFSPlusExtentKey
* keyPtr
;
558 keyPtr
= (HFSPlusExtentKey
*) &btIterator
.key
;
559 keyPtr
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
560 keyPtr
->forkType
= forkType
;
562 keyPtr
->fileID
= fileID
;
563 keyPtr
->startBlock
= startBlock
;
566 err
= BTDeleteRecord(GetFileControlBlock(vcb
->extentsRefNum
), &btIterator
);
573 //_________________________________________________________________________________
575 // Routine: MapFileBlock
577 // Function: Maps a file position into a physical disk address.
579 // Input: A2.L - VCB pointer
580 // (A1,D1.W) - FCB pointer
581 // D4.L - number of bytes desired
582 // D5.L - file position (byte address)
584 // Output: D3.L - physical start block
585 // D6.L - number of contiguous bytes available (up to D4 bytes)
586 // D0.L - result code <01Oct85>
588 // FXRangeErr = file position beyond mapped range <17Oct85>
589 // FXOvFlErr = extents file overflow <17Oct85>
590 // other = error <17Oct85>
592 // Called By: Log2Phys (read/write in place), Cache (map a file block).
593 //_________________________________________________________________________________
595 OSErr
MapFileBlockC (
596 ExtendedVCB
*vcb
, // volume that file resides on
597 FCB
*fcb
, // FCB of file
598 size_t numberOfBytes
, // number of contiguous bytes desired
599 off_t offset
, // starting offset within file (in bytes)
600 daddr_t
*startSector
, // first sector (NOT an allocation block)
601 size_t *availableBytes
) // number of contiguous bytes (up to numberOfBytes)
604 UInt32 allocBlockSize
; // Size of the volume's allocation block
606 HFSPlusExtentKey foundKey
;
607 HFSPlusExtentRecord foundData
;
610 UInt32 firstFABN
; // file allocation block of first block in found extent
611 UInt32 nextFABN
; // file allocation block of block after end of found extent
612 off_t dataEnd
; // (offset) end of range that is contiguous
613 UInt32 sectorsPerBlock
; // Number of sectors per allocation block
614 UInt32 startBlock
; // volume allocation block corresponding to firstFABN
618 allocBlockSize
= vcb
->blockSize
;
619 sectorSize
= VCBTOHFS(vcb
)->hfs_phys_block_size
;
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
/ sectorSize
; // sectors per allocation block
644 // Compute the absolute sector number that contains the offset of the given file
647 // offset in sectors from start of the extent
648 temp
= (daddr_t
)((offset
- (off_t
)((off_t
)(firstFABN
) * (off_t
)(allocBlockSize
)))/sectorSize
);
649 // offset in sectors from start of allocation block space
650 temp
+= startBlock
* sectorsPerBlock
; // offset in sectors from start of allocation block space
651 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
652 temp
+= vcb
->hfsPlusIOPosOffset
/ sectorSize
; /* offset inside wrapper */
654 temp
+= vcb
->vcbAlBlSt
; /* offset in sectors from start of volume */
656 // Return the desired sector for file position "offset"
660 // Determine the number of contiguous bytes until the end of the extent
661 // (or the amount they asked for, whichever comes first).
663 tmpOff
= dataEnd
- offset
;
664 if (tmpOff
> (off_t
)(numberOfBytes
))
665 *availableBytes
= numberOfBytes
; // more there than they asked for, so pin the output
667 *availableBytes
= tmpOff
;
673 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
674 // Routine: ReleaseExtents
676 // Function: Release the extents of a single extent data record.
677 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
679 static OSErr
ReleaseExtents(
681 const HFSPlusExtentRecord extentRecord
,
682 UInt32
*numReleasedAllocationBlocks
,
683 Boolean
*releasedLastExtent
)
686 UInt32 numberOfExtents
;
689 *numReleasedAllocationBlocks
= 0;
690 *releasedLastExtent
= false;
692 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
693 numberOfExtents
= kHFSPlusExtentDensity
;
695 numberOfExtents
= kHFSExtentDensity
;
697 for( extentIndex
= 0; extentIndex
< numberOfExtents
; extentIndex
++)
699 UInt32 numAllocationBlocks
;
701 // Loop over the extent record and release the blocks associated with each extent.
703 numAllocationBlocks
= extentRecord
[extentIndex
].blockCount
;
704 if ( numAllocationBlocks
== 0 )
706 *releasedLastExtent
= true;
710 err
= BlockDeallocate( vcb
, extentRecord
[extentIndex
].startBlock
, numAllocationBlocks
);
714 *numReleasedAllocationBlocks
+= numAllocationBlocks
; // bump FABN to beg of next extent
722 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
723 // Routine: TruncateExtents
725 // Purpose: Delete extent records whose starting file allocation block number
726 // is greater than or equal to a given starting block number. The
727 // allocation blocks represented by the extents are deallocated.
730 // vcb Volume to operate on
731 // fileID Which file to operate on
732 // startBlock Starting file allocation block number for first extent
734 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
736 static OSErr
TruncateExtents(
741 Boolean
* recordDeleted
)
744 UInt32 numberExtentsReleased
;
745 Boolean releasedLastExtent
;
747 HFSPlusExtentKey key
;
748 HFSPlusExtentRecord extents
;
751 err
= FindExtentRecord(vcb
, forkType
, fileID
, startBlock
, false, &key
, extents
, &hint
);
753 if (err
== btNotFound
)
758 err
= ReleaseExtents( vcb
, extents
, &numberExtentsReleased
, &releasedLastExtent
);
759 if (err
!= noErr
) break;
761 err
= DeleteExtentRecord(vcb
, forkType
, fileID
, startBlock
);
762 if (err
!= noErr
) break;
764 *recordDeleted
= true;
765 startBlock
+= numberExtentsReleased
;
773 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
774 // Routine: DeallocateFork
776 // Function: De-allocates all disk space allocated to a specified fork.
777 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
779 static OSErr
DeallocateFork(
781 HFSCatalogNodeID fileID
,
783 HFSPlusExtentRecord catalogExtents
,
784 Boolean
* recordDeleted
) /* true if a record was deleted */
787 UInt32 numReleasedAllocationBlocks
;
788 Boolean releasedLastExtent
;
790 // Release the catalog extents
791 err
= ReleaseExtents( vcb
, catalogExtents
, &numReleasedAllocationBlocks
, &releasedLastExtent
);
792 // Release the extra extents, if present
793 if (err
== noErr
&& !releasedLastExtent
)
794 err
= TruncateExtents(vcb
, forkType
, fileID
, numReleasedAllocationBlocks
, recordDeleted
);
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
800 // Routine: FlushExtentFile
802 // Function: Flushes the extent file for a specified volume
803 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
805 OSErr
FlushExtentFile( ExtendedVCB
*vcb
)
810 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
811 err
= BTFlushPath(fcb
);
814 // If the FCB for the extent "file" is dirty, mark the VCB as dirty.
816 if ((fcb
->fcbFlags
& fcbModifiedMask
) != 0)
819 err
= FlushVolumeControlBlock( vcb
);
826 //-------------------------------------------------------------------------------
827 // Routine: DeleteFile
829 // Function: De-allocates all disk space allocated to a specified file
830 // including the space used by the catalog (ie the catalog record).
831 // The space occupied by both forks is also deallocated.
833 //-------------------------------------------------------------------------------
835 OSErr
DeleteFile( ExtendedVCB
*vcb
, HFSCatalogNodeID parDirID
, ConstUTF8Param catalogName
, UInt32 catalogHint
)
839 CatalogNodeData catalogData
;
840 Boolean recordDeleted
;
842 recordDeleted
= false;
844 INIT_CATALOGDATA(&catalogData
, kCatNameNoCopyName
);
846 // Find catalog data in catalog
847 err
= GetCatalogNode( vcb
, parDirID
, catalogName
, kUndefinedStrLen
, catalogHint
, &catalogData
, &catalogHint
);
852 // Check to make sure record is for a file
853 if ( catalogData
.cnd_type
!= kCatalogFileNode
)
860 // Always delete the Catalog record first (to minimize disk corruption)
862 err
= DeleteCatalogNode(vcb
, parDirID
, catalogName
, catalogHint
);
867 // Note: we don't report errors from DeallocateFork since the
868 // file no longer exists (since DeleteCatalogNode succeeded).
869 // Any errors mean that there are possibly some orphaned disk
870 // blocks but from the clients perspective the file was deleted.
873 // Deallocate data fork extents
874 errDF
= DeallocateFork( vcb
, catalogData
.cnd_nodeID
, kDataForkType
,
875 catalogData
.cnd_datafork
.extents
, &recordDeleted
);
877 // Deallocate resource fork extents
878 errRF
= DeallocateFork( vcb
, catalogData
.cnd_nodeID
, kResourceForkType
,
879 catalogData
.cnd_rsrcfork
.extents
, &recordDeleted
);
882 err
= FlushExtentFile( vcb
);
884 CLEAN_CATALOGDATA(&catalogData
);
885 return (errDF
? errDF
: (errRF
? errRF
: err
));
888 CLEAN_CATALOGDATA(&catalogData
);
892 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
893 // Routine: CompareExtentKeys
895 // Function: Compares two extent file keys (a search key and a trial key) for
897 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
899 SInt32
CompareExtentKeys( const HFSExtentKey
*searchKey
, const HFSExtentKey
*trialKey
)
901 SInt32 result
; // ± 1
904 if (searchKey
->keyLength
!= kHFSExtentKeyMaximumLength
)
905 DebugStr("\pHFS: search Key is wrong length");
906 if (trialKey
->keyLength
!= kHFSExtentKeyMaximumLength
)
907 DebugStr("\pHFS: trial Key is wrong length");
910 result
= -1; // assume searchKey < trialKey
912 if (searchKey
->fileID
== trialKey
->fileID
) {
914 // FileNum's are equal; compare fork types
916 if (searchKey
->forkType
== trialKey
->forkType
) {
918 // Fork types are equal; compare allocation block number
920 if (searchKey
->startBlock
== trialKey
->startBlock
) {
922 // Everything is equal
928 // Allocation block numbers differ; determine sign
930 if (searchKey
->startBlock
> trialKey
->startBlock
)
936 // Fork types differ; determine sign
938 if (searchKey
->forkType
> trialKey
->forkType
)
944 // FileNums differ; determine sign
946 if (searchKey
->fileID
> trialKey
->fileID
)
955 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
956 // Routine: CompareExtentKeysPlus
958 // Function: Compares two extent file keys (a search key and a trial key) for
960 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
962 SInt32
CompareExtentKeysPlus( const HFSPlusExtentKey
*searchKey
, const HFSPlusExtentKey
*trialKey
)
964 SInt32 result
; // ± 1
967 if (searchKey
->keyLength
!= kHFSPlusExtentKeyMaximumLength
)
968 DebugStr("\pHFS: search Key is wrong length");
969 if (trialKey
->keyLength
!= kHFSPlusExtentKeyMaximumLength
)
970 DebugStr("\pHFS: trial Key is wrong length");
973 result
= -1; // assume searchKey < trialKey
975 if (searchKey
->fileID
== trialKey
->fileID
) {
977 // FileNum's are equal; compare fork types
979 if (searchKey
->forkType
== trialKey
->forkType
) {
981 // Fork types are equal; compare allocation block number
983 if (searchKey
->startBlock
== trialKey
->startBlock
) {
985 // Everything is equal
991 // Allocation block numbers differ; determine sign
993 if (searchKey
->startBlock
> trialKey
->startBlock
)
999 // Fork types differ; determine sign
1001 if (searchKey
->forkType
> trialKey
->forkType
)
1007 // FileNums differ; determine sign
1009 if (searchKey
->fileID
> trialKey
->fileID
)
1018 //_________________________________________________________________________________
1020 // Routine: Extendfile
1022 // Function: Extends the disk space allocated to a file.
1024 // Input: A2.L - VCB pointer
1025 // A1.L - pointer to FCB array
1026 // D1.W - file refnum
1027 // D3.B - option flags
1028 // kEFContigMask - force contiguous allocation
1029 // kEFAllMask - allocate all requested bytes or none
1030 // NOTE: You may not set both options.
1031 // D4.L - number of additional bytes to allocate
1033 // Output: D0.W - result code
1036 // D6.L - number of bytes allocated
1038 // Called by: FileAloc,FileWrite,SetEof
1040 // Note: ExtendFile updates the PEOF in the FCB.
1041 //_________________________________________________________________________________
1044 ExtendedVCB
*vcb
, // volume that file resides on
1045 FCB
*fcb
, // FCB of file to truncate
1046 SInt64 bytesToAdd
, // number of bytes to allocate
1047 UInt32 blockHint
, // desired starting allocation block
1048 UInt32 flags
, // EFContig and/or EFAll
1049 SInt64
*actualBytesAdded
) // number of bytes actually allocated
1052 UInt32 volumeBlockSize
;
1054 SInt64 bytesThisExtent
;
1055 HFSPlusExtentKey foundKey
;
1056 HFSPlusExtentRecord foundData
;
1061 Boolean allOrNothing
;
1062 Boolean forceContig
;
1065 UInt32 actualStartBlock
;
1066 UInt32 actualNumBlocks
;
1067 UInt32 numExtentsPerRecord
;
1068 SInt64 maximumBytes
;
1070 SInt64 previousPEOF
;
1074 *actualBytesAdded
= 0;
1075 volumeBlockSize
= vcb
->blockSize
;
1076 allOrNothing
= ((flags
& kEFAllMask
) != 0);
1077 forceContig
= ((flags
& kEFContigMask
) != 0);
1078 previousPEOF
= fcb
->fcbPLen
;
1080 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1081 numExtentsPerRecord
= kHFSPlusExtentDensity
;
1083 numExtentsPerRecord
= kHFSExtentDensity
;
1086 // Make sure the request and new PEOF are less than 2GB if HFS.
1088 if (vcb
->vcbSigWord
== kHFSSigWord
) {
1089 if (bytesToAdd
>= kTwoGigabytes
)
1091 if ((fcb
->fcbPLen
+ bytesToAdd
) >= kTwoGigabytes
)
1095 // Determine how many blocks need to be allocated.
1096 // Round up the number of desired bytes to add.
1098 blocksToAdd
= FileBytesToBlocks(bytesToAdd
, volumeBlockSize
);
1099 bytesToAdd
= (SInt64
)((SInt64
)blocksToAdd
* (SInt64
)volumeBlockSize
);
1102 // If the file's clump size is larger than the allocation block size,
1103 // then set the maximum number of bytes to the requested number of bytes
1104 // rounded up to a multiple of the clump size.
1106 if (fcb
->fcbClmpSize
> volumeBlockSize
) {
1107 maximumBytes
= (SInt64
)FileBytesToBlocks(bytesToAdd
, fcb
->fcbClmpSize
);
1108 maximumBytes
*= fcb
->fcbClmpSize
;
1111 maximumBytes
= bytesToAdd
;
1115 // Compute new physical EOF, rounded up to a multiple of a block.
1117 if ((vcb
->vcbSigWord
== kHFSSigWord
) && ((fcb
->fcbPLen
+ bytesToAdd
) >= (SInt64
) kTwoGigabytes
)) // Too big?
1118 if (allOrNothing
) // Yes, must they have it all?
1119 goto Overflow
; // Yes, can't have it
1121 --blocksToAdd
; // No, give give 'em one block less
1122 bytesToAdd
-= volumeBlockSize
;
1126 // If allocation is all-or-nothing, make sure there are
1127 // enough free blocks on the volume (quick test).
1129 if (allOrNothing
&& (blocksToAdd
> (SInt64
)vcb
->freeBlocks
)) {
1135 // See if there are already enough blocks allocated to the file.
1137 peof
= fcb
->fcbPLen
+ bytesToAdd
; // potential new PEOF
1138 err
= SearchExtentFile(vcb
, fcb
, peof
-1, &foundKey
, foundData
, &foundIndex
, &hint
, &nextBlock
);
1140 // Enough blocks are already allocated. Just update the FCB to reflect the new length.
1141 fcb
->fcbPLen
= peof
;
1142 H_EXTENDSIZE(fcb
, bytesToAdd
);
1143 fcb
->fcbFlags
|= fcbModifiedMask
;
1146 if (err
!= fxRangeErr
) // Any real error?
1147 goto ErrorExit
; // Yes, so exit immediately
1150 // Adjust the PEOF to the end of the last extent.
1152 peof
= (SInt64
)((SInt64
)nextBlock
* (SInt64
)volumeBlockSize
); // currently allocated PEOF
1153 bytesThisExtent
= peof
- fcb
->fcbPLen
;
1154 if (bytesThisExtent
!= 0) {
1155 fcb
->fcbPLen
= peof
;
1156 H_EXTENDSIZE(fcb
, bytesThisExtent
);
1157 fcb
->fcbFlags
|= fcbModifiedMask
;
1158 bytesToAdd
-= bytesThisExtent
;
1162 // Allocate some more space.
1164 // First try a contiguous allocation (of the whole amount).
1165 // If that fails, get whatever we can.
1166 // If forceContig, then take whatever we got
1167 // else, keep getting bits and pieces (non-contig)
1170 vcb
->vcbFreeExtCnt
= 0; /* For now, force rebuild of free extent list */
1173 startBlock
= blockHint
;
1175 startBlock
= foundData
[foundIndex
].startBlock
+ foundData
[foundIndex
].blockCount
;
1176 err
= BlockAllocate(vcb
, startBlock
, bytesToAdd
, maximumBytes
, wantContig
, &actualStartBlock
, &actualNumBlocks
);
1177 if (err
== dskFulErr
) {
1179 break; // AllocContig failed because not enough contiguous space
1181 // Couldn't get one big chunk, so get whatever we can.
1186 if (actualNumBlocks
!= 0)
1190 // Add the new extent to the existing extent record, or create a new one.
1191 if ((actualStartBlock
== startBlock
) && (blockHint
== 0)) {
1192 // We grew the file's last extent, so just adjust the number of blocks.
1193 foundData
[foundIndex
].blockCount
+= actualNumBlocks
;
1194 err
= UpdateExtentRecord(vcb
, fcb
, &foundKey
, foundData
, hint
);
1195 if (err
!= noErr
) break;
1200 // Need to add a new extent. See if there is room in the current record.
1201 if (foundData
[foundIndex
].blockCount
!= 0) // Is current extent free to use?
1202 ++foundIndex
; // No, so use the next one.
1203 if (foundIndex
== numExtentsPerRecord
) {
1204 // This record is full. Need to create a new one.
1205 if (H_FILEID(fcb
) == kHFSExtentsFileID
) {
1206 (void) BlockDeallocate(vcb
, actualStartBlock
, actualNumBlocks
);
1207 err
= dskFulErr
; // Oops. Can't extend extents file past first record.
1211 foundKey
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
1212 if (fcb
->fcbFlags
& fcbResourceMask
)
1213 foundKey
.forkType
= kResourceForkType
;
1215 foundKey
.forkType
= kDataForkType
;
1217 foundKey
.fileID
= H_FILEID(fcb
);
1218 foundKey
.startBlock
= nextBlock
;
1220 foundData
[0].startBlock
= actualStartBlock
;
1221 foundData
[0].blockCount
= actualNumBlocks
;
1223 // zero out remaining extents...
1224 for (i
= 1; i
< kHFSPlusExtentDensity
; ++i
)
1226 foundData
[i
].startBlock
= 0;
1227 foundData
[i
].blockCount
= 0;
1232 err
= CreateExtentRecord(vcb
, &foundKey
, foundData
, &hint
);
1233 if (err
== fxOvFlErr
) {
1234 // We couldn't create an extent record because extents B-tree
1235 // couldn't grow. Dellocate the extent just allocated and
1236 // return a disk full error.
1237 (void) BlockDeallocate(vcb
, actualStartBlock
, actualNumBlocks
);
1240 if (err
!= noErr
) break;
1242 needsFlush
= true; // We need to update the B-tree header
1245 // Add a new extent into this record and update.
1246 foundData
[foundIndex
].startBlock
= actualStartBlock
;
1247 foundData
[foundIndex
].blockCount
= actualNumBlocks
;
1248 err
= UpdateExtentRecord(vcb
, fcb
, &foundKey
, foundData
, hint
);
1249 if (err
!= noErr
) break;
1253 // Figure out how many bytes were actually allocated.
1254 // NOTE: BlockAllocate could have allocated more than we asked for.
1255 // Don't set the PEOF beyond what our client asked for.
1256 nextBlock
+= actualNumBlocks
;
1257 bytesThisExtent
= (SInt64
)((SInt64
)actualNumBlocks
* (SInt64
)volumeBlockSize
);
1258 if (bytesThisExtent
> bytesToAdd
) {
1262 bytesToAdd
-= bytesThisExtent
;
1263 maximumBytes
-= bytesThisExtent
;
1265 fcb
->fcbPLen
+= bytesThisExtent
;
1266 H_EXTENDSIZE(fcb
, bytesThisExtent
);
1267 fcb
->fcbFlags
|= fcbModifiedMask
;
1269 // If contiguous allocation was requested, then we've already got one contiguous
1270 // chunk. If we didn't get all we wanted, then adjust the error to disk full.
1272 if (bytesToAdd
!= 0)
1274 break; // We've already got everything that's contiguous
1277 } while (err
== noErr
&& bytesToAdd
);
1281 *actualBytesAdded
= fcb
->fcbPLen
- previousPEOF
;
1284 (void) FlushExtentFile(vcb
);
1289 err
= fileBoundsErr
;
1295 //_________________________________________________________________________________
1297 // Routine: TruncateFileC
1299 // Function: Truncates the disk space allocated to a file. The file space is
1300 // truncated to a specified new PEOF rounded up to the next allocation
1301 // block boundry. If the 'TFTrunExt' option is specified, the file is
1302 // truncated to the end of the extent containing the new PEOF.
1304 // Input: A2.L - VCB pointer
1305 // A1.L - pointer to FCB array
1306 // D1.W - file refnum
1307 // D2.B - option flags
1308 // TFTrunExt - truncate to the extent containing new PEOF
1311 // Output: D0.W - result code
1315 // Note: TruncateFile updates the PEOF in the FCB.
1316 //_________________________________________________________________________________
1318 OSErr
TruncateFileC (
1319 ExtendedVCB
*vcb
, // volume that file resides on
1320 FCB
*fcb
, // FCB of file to truncate
1321 SInt64 peof
, // new physical size for file
1322 Boolean truncateToExtent
) // if true, truncate to end of extent containing newPEOF
1325 UInt32 nextBlock
; // next file allocation block to consider
1326 UInt32 startBlock
; // Physical (volume) allocation block number of start of a range
1327 UInt32 physNumBlocks
; // Number of allocation blocks in file (according to PEOF)
1329 HFSPlusExtentKey key
; // key for current extent record; key->keyLength == 0 if FCB's extent record
1330 UInt32 hint
; // BTree hint corresponding to key
1331 HFSPlusExtentRecord extentRecord
;
1333 UInt32 extentNextBlock
;
1334 UInt32 numExtentsPerRecord
;
1337 Boolean extentChanged
; // true if we actually changed an extent
1338 Boolean recordDeleted
; // true if an extent record got deleted
1341 recordDeleted
= false;
1343 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1344 numExtentsPerRecord
= kHFSPlusExtentDensity
;
1346 numExtentsPerRecord
= kHFSExtentDensity
;
1348 if (fcb
->fcbFlags
& fcbResourceMask
)
1349 forkType
= kResourceForkType
;
1351 forkType
= kDataForkType
;
1353 temp64
= fcb
->fcbPLen
/ (SInt64
)vcb
->blockSize
; // number of allocation blocks currently in file
1354 physNumBlocks
= (UInt32
)temp64
;
1357 // Round newPEOF up to a multiple of the allocation block size. If new size is
1358 // two gigabytes or more, then round down by one allocation block (??? really?
1359 // shouldn't that be an error?).
1361 nextBlock
= FileBytesToBlocks(peof
, vcb
->blockSize
); // number of allocation blocks to remain in file
1362 peof
= (SInt64
)((SInt64
)nextBlock
* (SInt64
)vcb
->blockSize
); // number of bytes in those blocks
1363 if ((vcb
->vcbSigWord
== kHFSSigWord
) && (peof
>= (UInt32
) kTwoGigabytes
)) {
1365 DebugStr("\pHFS: Trying to truncate a file to 2GB or more");
1367 err
= fileBoundsErr
;
1372 // Update FCB's length
1374 H_TRUNCSIZE(fcb
, fcb
->fcbPLen
- peof
);
1375 fcb
->fcbPLen
= peof
;
1376 fcb
->fcbFlags
|= fcbModifiedMask
;
1379 // If the new PEOF is 0, then truncateToExtent has no meaning (we should always deallocate
1385 // Deallocate all the extents for this fork
1386 err
= DeallocateFork(vcb
, H_FILEID(fcb
), forkType
, fcb
->fcbExtents
, &recordDeleted
);
1387 if (err
!= noErr
) goto ErrorExit
; // got some error, so return it
1389 // Update the catalog extent record (making sure it's zeroed out)
1391 for (i
=0; i
< kHFSPlusExtentDensity
; i
++) {
1392 fcb
->fcbExtents
[i
].startBlock
= 0;
1393 fcb
->fcbExtents
[i
].blockCount
= 0;
1400 // Find the extent containing byte (peof-1). This is the last extent we'll keep.
1401 // (If truncateToExtent is true, we'll keep the whole extent; otherwise, we'll only
1402 // keep up through peof). The search will tell us how many allocation blocks exist
1403 // in the found extent plus all previous extents.
1405 err
= SearchExtentFile(vcb
, fcb
, peof
-1, &key
, extentRecord
, &extentIndex
, &hint
, &extentNextBlock
);
1406 if (err
!= noErr
) goto ErrorExit
;
1408 extentChanged
= false; // haven't changed the extent yet
1410 if (!truncateToExtent
) {
1412 // Shorten this extent. It may be the case that the entire extent gets
1415 numBlocks
= extentNextBlock
- nextBlock
; // How many blocks in this extent to free up
1416 if (numBlocks
!= 0) {
1417 // Compute first volume allocation block to free
1418 startBlock
= extentRecord
[extentIndex
].startBlock
+ extentRecord
[extentIndex
].blockCount
- numBlocks
;
1419 // Free the blocks in bitmap
1420 err
= BlockDeallocate(vcb
, startBlock
, numBlocks
);
1421 if (err
!= noErr
) goto ErrorExit
;
1422 // Adjust length of this extent
1423 extentRecord
[extentIndex
].blockCount
-= numBlocks
;
1424 // If extent is empty, set start block to 0
1425 if (extentRecord
[extentIndex
].blockCount
== 0)
1426 extentRecord
[extentIndex
].startBlock
= 0;
1427 // Remember that we changed the extent record
1428 extentChanged
= true;
1433 // Now move to the next extent in the record, and set up the file allocation block number
1435 nextBlock
= extentNextBlock
; // Next file allocation block to free
1436 ++extentIndex
; // Its index within the extent record
1439 // Release all following extents in this extent record. Update the record.
1441 while (extentIndex
< numExtentsPerRecord
&& extentRecord
[extentIndex
].blockCount
!= 0) {
1442 numBlocks
= extentRecord
[extentIndex
].blockCount
;
1443 // Deallocate this extent
1444 err
= BlockDeallocate(vcb
, extentRecord
[extentIndex
].startBlock
, numBlocks
);
1445 if (err
!= noErr
) goto ErrorExit
;
1446 // Update next file allocation block number
1447 nextBlock
+= numBlocks
;
1448 // Zero out start and length of this extent to delete it from record
1449 extentRecord
[extentIndex
].startBlock
= 0;
1450 extentRecord
[extentIndex
].blockCount
= 0;
1451 // Remember that we changed an extent
1452 extentChanged
= true;
1453 // Move to next extent in record
1458 // If any of the extents in the current record were changed, then update that
1459 // record (in the FCB, or extents file).
1461 if (extentChanged
) {
1462 err
= UpdateExtentRecord(vcb
, fcb
, &key
, extentRecord
, hint
);
1463 if (err
!= noErr
) goto ErrorExit
;
1467 // If there are any following allocation blocks, then we need
1468 // to seach for their extent records and delete those allocation
1471 if (nextBlock
< physNumBlocks
)
1472 err
= TruncateExtents(vcb
, forkType
, H_FILEID(fcb
), nextBlock
, &recordDeleted
);
1478 (void) FlushExtentFile(vcb
);
1485 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1486 // Routine: SearchExtentRecord (was XRSearch)
1488 // Function: Searches extent record for the extent mapping a given file
1489 // allocation block number (FABN).
1491 // Input: searchFABN - desired FABN
1492 // extentData - pointer to extent data record (xdr)
1493 // extentDataStartFABN - beginning FABN for extent record
1495 // Output: foundExtentDataOffset - offset to extent entry within xdr
1496 // result = noErr, offset to extent mapping desired FABN
1497 // result = FXRangeErr, offset to last extent in record
1498 // endingFABNPlusOne - ending FABN +1
1499 // noMoreExtents - True if the extent was not found, and the
1500 // extent record was not full (so don't bother
1501 // looking in subsequent records); false otherwise.
1503 // Result: noErr = ok
1504 // FXRangeErr = desired FABN > last mapped FABN in record
1505 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1507 static OSErr
SearchExtentRecord(
1508 const ExtendedVCB
*vcb
,
1510 const HFSPlusExtentRecord extentData
,
1511 UInt32 extentDataStartFABN
,
1512 UInt32
*foundExtentIndex
,
1513 UInt32
*endingFABNPlusOne
,
1514 Boolean
*noMoreExtents
)
1518 UInt32 numberOfExtents
;
1519 UInt32 numAllocationBlocks
;
1520 Boolean foundExtent
;
1522 *endingFABNPlusOne
= extentDataStartFABN
;
1523 *noMoreExtents
= false;
1524 foundExtent
= false;
1526 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1527 numberOfExtents
= kHFSPlusExtentDensity
;
1529 numberOfExtents
= kHFSExtentDensity
;
1531 for( extentIndex
= 0; extentIndex
< numberOfExtents
; ++extentIndex
)
1534 // Loop over the extent record and find the search FABN.
1536 numAllocationBlocks
= extentData
[extentIndex
].blockCount
;
1537 if ( numAllocationBlocks
== 0 )
1542 *endingFABNPlusOne
+= numAllocationBlocks
;
1544 if( searchFABN
< *endingFABNPlusOne
)
1546 // Found the extent.
1554 // Found the extent. Note the extent offset
1555 *foundExtentIndex
= extentIndex
;
1559 // Did not find the extent. Set foundExtentDataOffset accordingly
1560 if( extentIndex
> 0 )
1562 *foundExtentIndex
= extentIndex
- 1;
1566 *foundExtentIndex
= 0;
1569 // If we found an empty extent, then set noMoreExtents.
1570 if (extentIndex
< numberOfExtents
)
1571 *noMoreExtents
= true;
1573 // Finally, return an error to the caller
1580 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1581 // Routine: SearchExtentFile (was XFSearch)
1583 // Function: Searches extent file (including the FCB resident extent record)
1584 // for the extent mapping a given file position.
1586 // Input: vcb - VCB pointer
1587 // fcb - FCB pointer
1588 // filePosition - file position (byte address)
1590 // Output: foundExtentKey - extent key record (xkr)
1591 // If extent was found in the FCB's resident extent record,
1592 // then foundExtentKey->keyLength will be set to 0.
1593 // foundExtentData - extent data record(xdr)
1594 // foundExtentIndex - index to extent entry in xdr
1595 // result = 0, offset to extent mapping desired FABN
1596 // result = FXRangeErr, offset to last extent in record
1597 // (i.e., kNumExtentsPerRecord-1)
1598 // extentBTreeHint - BTree hint for extent record
1599 // kNoHint = Resident extent record
1600 // endingFABNPlusOne - ending FABN +1
1603 // noErr Found an extent that contains the given file position
1604 // FXRangeErr Given position is beyond the last allocated extent
1605 // (other) (some other internal I/O error)
1606 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1608 static OSErr
SearchExtentFile(
1609 const ExtendedVCB
*vcb
,
1611 SInt64 filePosition
,
1612 HFSPlusExtentKey
*foundExtentKey
,
1613 HFSPlusExtentRecord foundExtentData
,
1614 UInt32
*foundExtentIndex
,
1615 UInt32
*extentBTreeHint
,
1616 UInt32
*endingFABNPlusOne
)
1619 UInt32 filePositionBlock
;
1621 Boolean noMoreExtents
;
1623 temp64
= filePosition
/ (SInt64
)vcb
->blockSize
;
1624 filePositionBlock
= (UInt32
)temp64
;
1626 bcopy ( fcb
->fcbExtents
, foundExtentData
, sizeof(HFSPlusExtentRecord
));
1628 // Search the resident FCB first.
1629 err
= SearchExtentRecord( vcb
, filePositionBlock
, foundExtentData
, 0,
1630 foundExtentIndex
, endingFABNPlusOne
, &noMoreExtents
);
1632 if( err
== noErr
) {
1633 // Found the extent. Set results accordingly
1634 *extentBTreeHint
= kNoHint
; // no hint, because not in the BTree
1635 foundExtentKey
->keyLength
= 0; // 0 = the FCB itself
1640 // Didn't find extent in FCB. If FCB's extent record wasn't full, there's no point
1641 // in searching the extents file. Note that SearchExtentRecord left us pointing at
1642 // the last valid extent (or the first one, if none were valid). This means we need
1643 // to fill in the hint and key outputs, just like the "if" statement above.
1644 if ( noMoreExtents
) {
1645 *extentBTreeHint
= kNoHint
; // no hint, because not in the BTree
1646 foundExtentKey
->keyLength
= 0; // 0 = the FCB itself
1647 err
= fxRangeErr
; // There are no more extents, so must be beyond PEOF
1652 // Find the desired record, or the previous record if it is the same fork
1654 err
= FindExtentRecord(vcb
, (fcb
->fcbFlags
& fcbResourceMask
) ? kResourceForkType
: kDataForkType
,
1655 H_FILEID(fcb
), filePositionBlock
, true, foundExtentKey
, foundExtentData
, extentBTreeHint
);
1657 if (err
== btNotFound
) {
1659 // If we get here, the desired position is beyond the extents in the FCB, and there are no extents
1660 // in the extents file. Return the FCB's extents and a range error.
1662 *extentBTreeHint
= kNoHint
;
1663 foundExtentKey
->keyLength
= 0;
1664 err
= GetFCBExtentRecord(fcb
, foundExtentData
);
1665 // Note: foundExtentIndex and endingFABNPlusOne have already been set as a result of the very
1666 // first SearchExtentRecord call in this function (when searching in the FCB's extents, and
1667 // we got a range error).
1673 // If we get here, there was either a BTree error, or we found an appropriate record.
1674 // If we found a record, then search it for the correct index into the extents.
1677 // Find appropriate index into extent record
1678 err
= SearchExtentRecord(vcb
, filePositionBlock
, foundExtentData
, foundExtentKey
->startBlock
,
1679 foundExtentIndex
, endingFABNPlusOne
, &noMoreExtents
);
1688 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1689 // Routine: UpdateExtentRecord
1691 // Function: Write new extent data to an existing extent record with a given key.
1692 // If all of the extents are empty, and the extent record is in the
1693 // extents file, then the record is deleted.
1695 // Input: vcb - the volume containing the extents
1696 // fcb - the file that owns the extents
1697 // extentFileKey - pointer to extent key record (xkr)
1698 // If the key length is 0, then the extents are actually part
1699 // of the catalog record, stored in the FCB.
1700 // extentData - pointer to extent data record (xdr)
1701 // extentBTreeHint - hint for given key, or kNoHint
1703 // Result: noErr = ok
1704 // (other) = error from BTree
1705 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\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 static OSErr
UpdateExtentRecord (
1708 const ExtendedVCB
*vcb
,
1710 const HFSPlusExtentKey
*extentFileKey
,
1711 const HFSPlusExtentRecord extentData
,
1712 UInt32 extentBTreeHint
)
1714 BTreeIterator btIterator
;
1715 FSBufferDescriptor btRecord
;
1716 UInt16 btRecordSize
;
1720 if (extentFileKey
->keyLength
== 0) { // keyLength == 0 means the FCB's extent record
1721 BlockMoveData(extentData
, fcb
->fcbExtents
, sizeof(HFSPlusExtentRecord
));
1722 fcb
->fcbFlags
|= fcbModifiedMask
;
1726 // Need to find and change a record in Extents BTree
1728 btFCB
= GetFileControlBlock(vcb
->extentsRefNum
);
1730 if (vcb
->vcbSigWord
== kHFSSigWord
) {
1731 HFSExtentKey
* key
; // Actual extent key used on disk in HFS
1732 HFSExtentRecord foundData
; // The extent data actually found
1734 key
= (HFSExtentKey
*) &btIterator
.key
;
1735 key
->keyLength
= kHFSExtentKeyMaximumLength
;
1736 key
->forkType
= extentFileKey
->forkType
;
1737 key
->fileID
= extentFileKey
->fileID
;
1738 key
->startBlock
= extentFileKey
->startBlock
;
1740 btIterator
.hint
.index
= 0;
1741 btIterator
.hint
.nodeNum
= extentBTreeHint
;
1743 btRecord
.bufferAddress
= &foundData
;
1744 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
1745 btRecord
.itemCount
= 1;
1747 err
= BTSearchRecord(btFCB
, &btIterator
, kInvalidMRUCacheKey
, &btRecord
,
1748 &btRecordSize
, &btIterator
);
1751 err
= HFSPlusToHFSExtents(extentData
, (HFSExtentDescriptor
*)&foundData
);
1754 err
= BTReplaceRecord(btFCB
, &btIterator
, &btRecord
, btRecordSize
);
1756 else { // HFS Plus volume
1757 HFSPlusExtentRecord foundData
; // The extent data actually found
1759 BlockMoveData(extentFileKey
, &btIterator
.key
, sizeof(HFSPlusExtentKey
));
1761 btIterator
.hint
.index
= 0;
1762 btIterator
.hint
.nodeNum
= extentBTreeHint
;
1764 btRecord
.bufferAddress
= &foundData
;
1765 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
1766 btRecord
.itemCount
= 1;
1768 err
= BTSearchRecord(btFCB
, &btIterator
, kInvalidMRUCacheKey
, &btRecord
,
1769 &btRecordSize
, &btIterator
);
1772 BlockMoveData(extentData
, &foundData
, sizeof(HFSPlusExtentRecord
));
1773 err
= BTReplaceRecord(btFCB
, &btIterator
, &btRecord
, btRecordSize
);
1783 void HFSToHFSPlusExtents(
1784 const HFSExtentRecord oldExtents
,
1785 HFSPlusExtentRecord newExtents
)
1789 // copy the first 3 extents
1790 newExtents
[0].startBlock
= oldExtents
[0].startBlock
;
1791 newExtents
[0].blockCount
= oldExtents
[0].blockCount
;
1792 newExtents
[1].startBlock
= oldExtents
[1].startBlock
;
1793 newExtents
[1].blockCount
= oldExtents
[1].blockCount
;
1794 newExtents
[2].startBlock
= oldExtents
[2].startBlock
;
1795 newExtents
[2].blockCount
= oldExtents
[2].blockCount
;
1797 // zero out the remaining ones
1798 for (i
= 3; i
< kHFSPlusExtentDensity
; ++i
)
1800 newExtents
[i
].startBlock
= 0;
1801 newExtents
[i
].blockCount
= 0;
1807 OSErr
HFSPlusToHFSExtents(
1808 const HFSPlusExtentRecord oldExtents
,
1809 HFSExtentRecord newExtents
)
1815 // copy the first 3 extents
1816 newExtents
[0].startBlock
= oldExtents
[0].startBlock
;
1817 newExtents
[0].blockCount
= oldExtents
[0].blockCount
;
1818 newExtents
[1].startBlock
= oldExtents
[1].startBlock
;
1819 newExtents
[1].blockCount
= oldExtents
[1].blockCount
;
1820 newExtents
[2].startBlock
= oldExtents
[2].startBlock
;
1821 newExtents
[2].blockCount
= oldExtents
[2].blockCount
;
1824 if (oldExtents
[3].startBlock
|| oldExtents
[3].blockCount
) {
1825 DebugStr("\pExtentRecord with > 3 extents is invalid for HFS");
1836 OSErr
GetFCBExtentRecord(
1838 HFSPlusExtentRecord extents
)
1841 BlockMoveData(fcb
->fcbExtents
, extents
, sizeof(HFSPlusExtentRecord
));
1847 //_________________________________________________________________________________
1849 // Routine: ExtentsAreIntegral
1851 // Purpose: Ensure that each extent can hold an integral number of nodes
1852 // Called by the NodesAreContiguous function
1853 //_________________________________________________________________________________
1855 static Boolean
ExtentsAreIntegral(
1856 const HFSPlusExtentRecord extentRecord
,
1858 UInt32
*blocksChecked
,
1859 Boolean
*checkedLastExtent
)
1865 *checkedLastExtent
= false;
1867 for(extentIndex
= 0; extentIndex
< kHFSPlusExtentDensity
; extentIndex
++)
1869 blocks
= extentRecord
[extentIndex
].blockCount
;
1873 *checkedLastExtent
= true;
1877 *blocksChecked
+= blocks
;
1887 //_________________________________________________________________________________
1889 // Routine: NodesAreContiguous
1891 // Purpose: Ensure that all b-tree nodes are contiguous on disk
1892 // Called by BTOpenPath during volume mount
1893 //_________________________________________________________________________________
1895 Boolean
NodesAreContiguous(
1902 UInt32 blocksChecked
;
1904 HFSPlusExtentKey key
;
1905 HFSPlusExtentRecord extents
;
1907 Boolean lastExtentReached
;
1910 if (vcb
->blockSize
>= nodeSize
)
1913 mask
= (nodeSize
/ vcb
->blockSize
) - 1;
1915 // check the local extents
1916 (void) GetFCBExtentRecord(fcb
, extents
);
1917 if ( !ExtentsAreIntegral(extents
, mask
, &blocksChecked
, &lastExtentReached
) )
1920 if (lastExtentReached
|| (SInt64
)((SInt64
)blocksChecked
* (SInt64
)vcb
->blockSize
) >= fcb
->fcbPLen
)
1923 startBlock
= blocksChecked
;
1925 // check the overflow extents (if any)
1926 while ( !lastExtentReached
)
1928 result
= FindExtentRecord(vcb
, kDataForkType
, H_FILEID(fcb
), startBlock
, FALSE
, &key
, extents
, &hint
);
1931 if ( !ExtentsAreIntegral(extents
, mask
, &blocksChecked
, &lastExtentReached
) )
1934 startBlock
+= blocksChecked
;