2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 File: FileExtentMapping.c
28 Contains: xxx put contents here xxx
32 Written by: Dave Heller, Mark Day
34 Copyright: © 1996-1999 by Apple Computer, Inc., all rights reserved.
40 Other Contact: xxx put other contact here xxx
42 Technology: xxx put technology here xxx
50 Change History (most recent first):
51 <MacOSX> 9/9/99 djb Fix fcbModifiedMask flag testing logic.
52 <MacOSX> 8/25/98 djb Flush extents b-tree header if dirty (2371088).
53 <MacOSX> 6/30/98 djb Add functions NodesAreContiguous and ExtentsAreIntegral (for radar #2249539).
54 <MacOSX> 6/23/98 djb Changed DeallocFile to DeleteFile which now deletes the catalog record.
55 Fixed UpdateExtentRecord to pass correct fcb to Btree routines. Fixed
56 hfs+ bug in CreateExtentRecord (double dereference).
57 <MacOSX> 5/20/98 djb In ExtendFileC don't lie about the peof! (radar #2230094).
58 <MacOSX> 4/17/98 djb Add VCB locking.
59 <MacOSX> 4/2/98 djb Switch over to real BTree interface (no more BTreeWrapper.c).
60 <MacOSX> 3/31/98 djb Sync up with final HFSVolumes.h header file.
62 <CS24> 1/23/98 msd Bug 2208024: AllocContig is actually allocating one extent even
63 though there is not enough contiguous space.
64 <CS23> 12/2/97 DSH GetFCBExtentRecord no longer static so DFA can use it.
65 <CS22> 10/20/97 msd When allocating more space for a file, do the clump size
66 calculations in ExtendFileC, not BlockAllocate. Undo change from
68 <CS21> 10/17/97 msd Conditionalize DebugStrs.
69 <CS20> 10/16/97 msd Simplify the code path for MapFileBlockC (logical to physical
70 block mapping) in the typical case where the file isn't
71 fragmented so badly that it has extents in the extents B-tree.
72 Simplified some of the calculations for all cases.
73 <CS19> 10/13/97 DSH FindExtentRecord & DeleteExtentRecord are also being used by DFA
75 <CS18> 10/6/97 msd When extending a file, set the physical EOF to include any extra
76 space allocated due to a file's clump size.
77 <CS17> 9/19/97 msd Remove the MapLogicalToPhysical SPI. It was never used and is
78 not being tested anyway.
79 <CS16> 9/5/97 msd In CompareExtentKeys and CompareExtentKeysPlus, use the symbolic
80 constants for key length. Don't DebugStr unless DEBUG_BUILD is
82 <CS15> 7/24/97 djb Add instrumentation to MapFileBlockC
83 <CS14> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
85 <CS13> 7/15/97 DSH AdjEOF() mark the FCB as modified. (1664389)
86 <CS12> 7/8/97 DSH Loading PrecompiledHeaders from define passed in on C line
87 <CS11> 7/3/97 msd Bug #1663518. Remove DebugStr when setting the FCB extent record
88 for a volume control file.
89 <CS10> 6/27/97 msd Moved enum kFirstFileRefnum to FilesInternal.
90 <CS9> 6/24/97 djb Include "CatalogPrivate.h"
91 <CS8> 6/16/97 msd Finish implementation of CreateLargeFile SPI.
92 <CS7> 6/12/97 msd Add stub for CreateLargeFile SPI.
93 <CS6> 6/5/97 msd Add MapLogicalToPhysical.
94 <CS5> 6/2/97 msd In TruncateFileC, don't update the extent record unless it was
95 actually changed (prevents extra updates when truncating to the
96 end of the extent, and it is the last extent of the file.) Added
97 an AdjustEOF routine called by the assembly AdjEOF routine. It
98 copies the EOF, physical length, and extent information from one
99 FCB to all other FCBs for that fork.
100 <CS4> 5/20/97 DSH Removed const declaration in MapFileBlocC, const is benign when
101 passing by value, and SC requires it to match prototype.
102 <CS3> 5/15/97 msd Change enum kResourceForkType from -1 to 0xFF since it is now
103 unsigned. Change all forkType parameters to UInt8.
104 <CS2> 5/7/97 msd When checking for an unused extent descriptor, check the length,
105 not the starting block.
106 <CS1> 4/24/97 djb first checked in
107 <HFS25> 4/11/97 DSH use extended VCB fields catalogRefNum, and extentsRefNum.
108 <HFS24> 4/4/97 djb Get in sync with volume format changes.
109 <HFS23> 3/17/97 DSH Casting to compile with SC.
110 <HFS22> 2/26/97 msd Add instrumentation in ExtendFileC and TruncateFileC. In
111 CompareExtentKeys and CompareExtentKeysPlus, make sure the key
113 <HFS21> 2/5/97 msd The comparison with fsBTStartOfIterationErr didn't work because
114 the enum is an unsigned long; it is now casted to an OSErr
116 <HFS20> 1/31/97 msd In FindExtentRecord, turn an fsBTStartOfIterationErr error into
118 <HFS19> 1/28/97 msd Fixed bug in MapFileBlockC where it returned the wrong number of
119 bytes available at the given block number. This could
120 potentially cause programs to read or write over other files.
121 <HFS18> 1/16/97 djb Extent key compare procs now return SInt32. Fixed
122 UpdateExtentRecord - it was passing a pointer to an ExtentKey
124 <HFS17> 1/10/97 msd Change TruncateFileC to call DellocateFork when the new PEOF is
125 0. Fixes a fxRangeErr returned when no extents existed.
126 <HFS16> 1/6/97 msd Previous change prevents extent records from being removed if
127 the files new PEOF is in the local (FCB/catalog) extents.
128 <HFS15> 1/3/97 djb Temp fix in TruncateFileC to prevent unwanted calls to
130 <HFS14> 12/23/96 msd Previous change to SearchExtentFile didn't set up the outputs
131 for hint and key when the FCB extent record wasn't full.
132 <HFS13> 12/20/96 msd In SearchExtentFile, don't bother searching the extents file if
133 the FCB's extent record wasn't full, or if the FCB was for the
134 extents file itself. Modified SearchExtentRecord to return a
135 Boolean to indicate that the record was not full.
136 <HFS12> 12/19/96 DSH Changed refs from VCB to ExtendedVCB
137 <HFS11> 12/19/96 djb Updated for new B-tree Manager interface.
138 <HFS10> 12/12/96 djb Really use new SPI for GetCatalogNode.
139 <HFS9> 12/12/96 djb Use new Catalog SPI for GetCatalogNode. Added Mark's changes to
141 <HFS8> 12/11/96 msd TruncateFileC must always release extents, even if PEOF hasn't
142 changed (since allocation may have been rounded up due to clump
144 <HFS7> 12/10/96 msd Check PRAGMA_LOAD_SUPPORTED before loading precompiled headers.
145 <HFS6> 12/4/96 DSH Precompiled headers
146 <HFS5> 11/26/96 msd Add an exported routine to grow the parallel FCB table to
147 accomodate the HFS+ ExtentRecord.
148 <HFS4> 11/26/96 msd Convert internal routines to use ExtentKey and ExtentRecord
149 (instead of the raw HFS structures).
150 <HFS3> 11/21/96 msd Added CompareExtentKeysPlus().
151 <HFS2> 11/20/96 msd Finish porting FXM to C.
152 <HFS1> 11/6/96 DKH first checked in
157 #include "../../hfs.h"
158 #include "../../hfs_format.h"
159 #include "../../hfs_endian.h"
161 #include "../headers/FileMgrInternal.h"
162 #include "../headers/BTreesInternal.h"
163 #include "../headers/CatalogPrivate.h" // calling a private catalog routine (LocateCatalogNode)
165 #include <sys/malloc.h>
168 ============================================================
169 Public (Exported) Routines:
170 ============================================================
171 DeAllocFile Deallocate all disk space allocated to a specified file.
172 Both forks are deallocated.
174 ExtendFileC Allocate more space to a given file.
177 Compare two extents file keys (a search key and a trial
178 key). Used by the BTree manager when searching for,
179 adding, or deleting keys in the extents file of an HFS
182 CompareExtentKeysPlus
183 Compare two extents file keys (a search key and a trial
184 key). Used by the BTree manager when searching for,
185 adding, or deleting keys in the extents file of an HFS+
188 MapFileBlockC Convert (map) an offset within a given file into a
189 physical disk address.
191 TruncateFileC Truncates the disk space allocated to a file. The file
192 space is truncated to a specified new physical EOF, rounded
193 up to the next allocation block boundry. There is an option
194 to truncate to the end of the extent containing the new EOF.
197 Flush the extents file for a given volume.
200 Make sure the parallel FCB entries are big enough to support
201 the HFS+ ExtentRecord. If not, the array is grown and the
202 pre-existing data copied over.
205 Copy EOF, physical length, and extent records from one FCB
206 to all other FCBs for that fork. This is used when a file is
207 grown or shrunk as the result of a Write, SetEOF, or Allocate.
210 Map some position in a file to a volume block number. Also
211 returns the number of contiguous bytes that are mapped there.
212 This is a queued HFSDispatch call that does the equivalent of
213 MapFileBlockC, using a parameter block.
215 ============================================================
217 ============================================================
219 Search the extents BTree for a particular extent record.
221 Search the FCB and extents file for an extent record that
222 contains a given file position (in bytes).
224 Search a given extent record to see if it contains a given
225 file position (in bytes). Used by SearchExtentFile.
227 Deallocate all allocation blocks in all extents of an extent
230 Deallocate blocks and delete extent records for all allocation
231 blocks beyond a certain point in a file. The starting point
232 must be the first file allocation block for some extent record
235 Deallocate all allocation blocks belonging to a given fork.
237 If the extent record came from the extents file, write out
238 the updated record; otherwise, copy the updated record into
239 the FCB resident extent record. If the record has no extents,
240 and was in the extents file, then delete the record instead.
243 static const SInt64 kTwoGigabytes
= 0x80000000LL
;
248 kResourceForkType
= 0xFF,
253 void HFSToHFSPlusExtents(
254 const HFSExtentRecord oldExtents
,
255 HFSPlusExtentRecord newExtents
);
257 OSErr
HFSPlusToHFSExtents(
258 const HFSPlusExtentRecord oldExtents
,
259 HFSExtentRecord newExtents
);
261 OSErr
FindExtentRecord(
262 const ExtendedVCB
*vcb
,
266 Boolean allowPrevious
,
267 HFSPlusExtentKey
*foundKey
,
268 HFSPlusExtentRecord foundData
,
271 OSErr
DeleteExtentRecord(
272 const ExtendedVCB
*vcb
,
277 static OSErr
CreateExtentRecord(
278 const ExtendedVCB
*vcb
,
279 HFSPlusExtentKey
*key
,
280 HFSPlusExtentRecord extents
,
284 OSErr
GetFCBExtentRecord(
286 HFSPlusExtentRecord extents
);
288 static OSErr
SearchExtentFile(
289 const ExtendedVCB
*vcb
,
292 HFSPlusExtentKey
*foundExtentKey
,
293 HFSPlusExtentRecord foundExtentData
,
294 UInt32
*foundExtentDataIndex
,
295 UInt32
*extentBTreeHint
,
296 UInt32
*endingFABNPlusOne
);
298 static OSErr
SearchExtentRecord(
299 const ExtendedVCB
*vcb
,
301 const HFSPlusExtentRecord extentData
,
302 UInt32 extentDataStartFABN
,
303 UInt32
*foundExtentDataOffset
,
304 UInt32
*endingFABNPlusOne
,
305 Boolean
*noMoreExtents
);
307 static OSErr
ReleaseExtents(
309 const HFSPlusExtentRecord extentRecord
,
310 UInt32
*numReleasedAllocationBlocks
,
311 Boolean
*releasedLastExtent
);
313 static OSErr
DeallocateFork(
315 HFSCatalogNodeID fileID
,
317 HFSPlusExtentRecord catalogExtents
,
318 Boolean
* recordDeleted
);
320 static OSErr
TruncateExtents(
325 Boolean
* recordDeleted
);
327 static OSErr
UpdateExtentRecord (
328 const ExtendedVCB
*vcb
,
330 const HFSPlusExtentKey
*extentFileKey
,
331 const HFSPlusExtentRecord extentData
,
332 UInt32 extentBTreeHint
);
334 static Boolean
ExtentsAreIntegral(
335 const HFSPlusExtentRecord extentRecord
,
337 UInt32
*blocksChecked
,
338 Boolean
*checkedLastExtent
);
340 //_________________________________________________________________________________
342 // Routine: FindExtentRecord
344 // Purpose: Search the extents BTree for an extent record matching the given
345 // FileID, fork, and starting file allocation block number.
348 // vcb Volume to search
349 // forkType 0 = data fork, -1 = resource fork
350 // fileID File's FileID (CatalogNodeID)
351 // startBlock Starting file allocation block number
352 // allowPrevious If the desired record isn't found and this flag is set,
353 // then see if the previous record belongs to the same fork.
354 // If so, then return it.
357 // foundKey The key data for the record actually found
358 // foundData The extent record actually found (NOTE: on an HFS volume, the
359 // fourth entry will be zeroes.
360 // foundHint The BTree hint to find the node again
361 //_________________________________________________________________________________
362 OSErr
FindExtentRecord(
363 const ExtendedVCB
*vcb
,
367 Boolean allowPrevious
,
368 HFSPlusExtentKey
*foundKey
,
369 HFSPlusExtentRecord foundData
,
373 BTreeIterator
*btIterator
;
374 FSBufferDescriptor btRecord
;
380 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
382 MALLOC(btIterator
, BTreeIterator
*, sizeof(*btIterator
), M_TEMP
, M_WAITOK
);
383 bzero(btIterator
, sizeof(*btIterator
));
385 if (vcb
->vcbSigWord
== kHFSSigWord
) {
386 HFSExtentKey
* extentKeyPtr
;
387 HFSExtentRecord extentData
;
389 extentKeyPtr
= (HFSExtentKey
*) &btIterator
->key
;
390 extentKeyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
391 extentKeyPtr
->forkType
= forkType
;
392 extentKeyPtr
->fileID
= fileID
;
393 extentKeyPtr
->startBlock
= startBlock
;
395 btRecord
.bufferAddress
= &extentData
;
396 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
397 btRecord
.itemCount
= 1;
399 err
= BTSearchRecord(fcb
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
401 if (err
== btNotFound
&& allowPrevious
) {
402 err
= BTIterateRecord(fcb
, kBTreePrevRecord
, btIterator
, &btRecord
, &btRecordSize
);
404 // A previous record may not exist, so just return btNotFound (like we would if
405 // it was for the wrong file/fork).
406 if (err
== (OSErr
) fsBTStartOfIterationErr
) //¥¥ fsBTStartOfIterationErr is type unsigned long
410 // Found a previous record. Does it belong to the same fork of the same file?
411 if (extentKeyPtr
->fileID
!= fileID
|| extentKeyPtr
->forkType
!= forkType
)
419 // Copy the found key back for the caller
420 foundKey
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
421 foundKey
->forkType
= extentKeyPtr
->forkType
;
423 foundKey
->fileID
= extentKeyPtr
->fileID
;
424 foundKey
->startBlock
= extentKeyPtr
->startBlock
;
426 // Copy the found data back for the caller
427 foundData
[0].startBlock
= extentData
[0].startBlock
;
428 foundData
[0].blockCount
= extentData
[0].blockCount
;
429 foundData
[1].startBlock
= extentData
[1].startBlock
;
430 foundData
[1].blockCount
= extentData
[1].blockCount
;
431 foundData
[2].startBlock
= extentData
[2].startBlock
;
432 foundData
[2].blockCount
= extentData
[2].blockCount
;
434 for (i
= 3; i
< kHFSPlusExtentDensity
; ++i
)
436 foundData
[i
].startBlock
= 0;
437 foundData
[i
].blockCount
= 0;
441 else { // HFS Plus volume
442 HFSPlusExtentKey
* extentKeyPtr
;
443 HFSPlusExtentRecord extentData
;
445 extentKeyPtr
= (HFSPlusExtentKey
*) &btIterator
->key
;
446 extentKeyPtr
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
447 extentKeyPtr
->forkType
= forkType
;
448 extentKeyPtr
->pad
= 0;
449 extentKeyPtr
->fileID
= fileID
;
450 extentKeyPtr
->startBlock
= startBlock
;
452 btRecord
.bufferAddress
= &extentData
;
453 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
454 btRecord
.itemCount
= 1;
456 err
= BTSearchRecord(fcb
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
458 if (err
== btNotFound
&& allowPrevious
) {
459 err
= BTIterateRecord(fcb
, kBTreePrevRecord
, btIterator
, &btRecord
, &btRecordSize
);
461 // A previous record may not exist, so just return btNotFound (like we would if
462 // it was for the wrong file/fork).
463 if (err
== (OSErr
) fsBTStartOfIterationErr
) //¥¥ fsBTStartOfIterationErr is type unsigned long
467 // Found a previous record. Does it belong to the same fork of the same file?
468 if (extentKeyPtr
->fileID
!= fileID
|| extentKeyPtr
->forkType
!= forkType
)
474 // Copy the found key back for the caller
475 BlockMoveData(extentKeyPtr
, foundKey
, sizeof(HFSPlusExtentKey
));
476 // Copy the found data back for the caller
477 BlockMoveData(&extentData
, foundData
, sizeof(HFSPlusExtentRecord
));
481 *foundHint
= btIterator
->hint
.nodeNum
;
482 FREE(btIterator
, M_TEMP
);
488 static OSErr
CreateExtentRecord(
489 const ExtendedVCB
*vcb
,
490 HFSPlusExtentKey
*key
,
491 HFSPlusExtentRecord extents
,
494 BTreeIterator
* btIterator
;
495 FSBufferDescriptor btRecord
;
502 // XXXdbg - preflight that there's enough space
503 err
= BTCheckFreeSpace(GetFileControlBlock(vcb
->extentsRefNum
));
507 MALLOC(btIterator
, BTreeIterator
*, sizeof(*btIterator
), M_TEMP
, M_WAITOK
);
508 bzero(btIterator
, sizeof(*btIterator
));
510 if (vcb
->vcbSigWord
== kHFSSigWord
) {
511 HFSExtentKey
* keyPtr
;
512 HFSExtentRecord data
;
514 btRecordSize
= sizeof(HFSExtentRecord
);
515 btRecord
.bufferAddress
= &data
;
516 btRecord
.itemSize
= btRecordSize
;
517 btRecord
.itemCount
= 1;
519 keyPtr
= (HFSExtentKey
*) &btIterator
->key
;
520 keyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
521 keyPtr
->forkType
= key
->forkType
;
522 keyPtr
->fileID
= key
->fileID
;
523 keyPtr
->startBlock
= key
->startBlock
;
525 err
= HFSPlusToHFSExtents(extents
, data
);
527 else { // HFS Plus volume
528 btRecordSize
= sizeof(HFSPlusExtentRecord
);
529 btRecord
.bufferAddress
= extents
;
530 btRecord
.itemSize
= btRecordSize
;
531 btRecord
.itemCount
= 1;
533 BlockMoveData(key
, &btIterator
->key
, sizeof(HFSPlusExtentKey
));
537 err
= BTInsertRecord(GetFileControlBlock(vcb
->extentsRefNum
), btIterator
, &btRecord
, btRecordSize
);
540 *hint
= btIterator
->hint
.nodeNum
;
542 (void) BTFlushPath(GetFileControlBlock(vcb
->extentsRefNum
));
544 FREE(btIterator
, M_TEMP
);
549 OSErr
DeleteExtentRecord(
550 const ExtendedVCB
*vcb
,
555 BTreeIterator
* btIterator
;
560 // XXXdbg - preflight that there's enough space
561 err
= BTCheckFreeSpace(GetFileControlBlock(vcb
->extentsRefNum
));
565 MALLOC(btIterator
, BTreeIterator
*, sizeof(*btIterator
), M_TEMP
, M_WAITOK
);
566 bzero(btIterator
, sizeof(*btIterator
));
568 if (vcb
->vcbSigWord
== kHFSSigWord
) {
569 HFSExtentKey
* keyPtr
;
571 keyPtr
= (HFSExtentKey
*) &btIterator
->key
;
572 keyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
573 keyPtr
->forkType
= forkType
;
574 keyPtr
->fileID
= fileID
;
575 keyPtr
->startBlock
= startBlock
;
577 else { // HFS Plus volume
578 HFSPlusExtentKey
* keyPtr
;
580 keyPtr
= (HFSPlusExtentKey
*) &btIterator
->key
;
581 keyPtr
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
582 keyPtr
->forkType
= forkType
;
584 keyPtr
->fileID
= fileID
;
585 keyPtr
->startBlock
= startBlock
;
588 err
= BTDeleteRecord(GetFileControlBlock(vcb
->extentsRefNum
), btIterator
);
589 (void) BTFlushPath(GetFileControlBlock(vcb
->extentsRefNum
));
591 FREE(btIterator
, M_TEMP
);
597 //_________________________________________________________________________________
599 // Routine: MapFileBlock
601 // Function: Maps a file position into a physical disk address.
603 // Input: A2.L - VCB pointer
604 // (A1,D1.W) - FCB pointer
605 // D4.L - number of bytes desired
606 // D5.L - file position (byte address)
608 // Output: D3.L - physical start block
609 // D6.L - number of contiguous bytes available (up to D4 bytes)
610 // D0.L - result code <01Oct85>
612 // FXRangeErr = file position beyond mapped range <17Oct85>
613 // FXOvFlErr = extents file overflow <17Oct85>
614 // other = error <17Oct85>
616 // Called By: Log2Phys (read/write in place), Cache (map a file block).
617 //_________________________________________________________________________________
619 OSErr
MapFileBlockC (
620 ExtendedVCB
*vcb
, // volume that file resides on
621 FCB
*fcb
, // FCB of file
622 size_t numberOfBytes
, // number of contiguous bytes desired
623 off_t offset
, // starting offset within file (in bytes)
624 daddr_t
*startSector
, // first sector (NOT an allocation block)
625 size_t *availableBytes
) // number of contiguous bytes (up to numberOfBytes)
628 UInt32 allocBlockSize
; // Size of the volume's allocation block
630 HFSPlusExtentKey foundKey
;
631 HFSPlusExtentRecord foundData
;
634 UInt32 firstFABN
; // file allocation block of first block in found extent
635 UInt32 nextFABN
; // file allocation block of block after end of found extent
636 off_t dataEnd
; // (offset) end of range that is contiguous
637 UInt32 sectorsPerBlock
; // Number of sectors per allocation block
638 UInt32 startBlock
; // volume allocation block corresponding to firstFABN
642 allocBlockSize
= vcb
->blockSize
;
643 sectorSize
= VCBTOHFS(vcb
)->hfs_phys_block_size
;
645 err
= SearchExtentFile(vcb
, fcb
, offset
, &foundKey
, foundData
, &foundIndex
, &hint
, &nextFABN
);
647 startBlock
= foundData
[foundIndex
].startBlock
;
648 firstFABN
= nextFABN
- foundData
[foundIndex
].blockCount
;
657 // Determine the end of the available space. It will either be the end of the extent,
658 // or the file's PEOF, whichever is smaller.
660 dataEnd
= (off_t
)((off_t
)(nextFABN
) * (off_t
)(allocBlockSize
)); // Assume valid data through end of this extent
661 if (((off_t
)fcb
->ff_blocks
* (off_t
)allocBlockSize
) < dataEnd
) // Is PEOF shorter?
662 dataEnd
= (off_t
)fcb
->ff_blocks
* (off_t
)allocBlockSize
; // Yes, so only map up to PEOF
664 // Compute the number of sectors in an allocation block
665 sectorsPerBlock
= allocBlockSize
/ sectorSize
; // sectors per allocation block
668 // Compute the absolute sector number that contains the offset of the given file
669 // offset in sectors from start of the extent +
670 // offset in sectors from start of allocation block space
672 temp
= (daddr_t
)((offset
- (off_t
)((off_t
)(firstFABN
) * (off_t
)(allocBlockSize
)))/sectorSize
);
673 temp
+= startBlock
* sectorsPerBlock
;
675 /* Add in any volume offsets */
676 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
677 temp
+= vcb
->hfsPlusIOPosOffset
/ sectorSize
;
679 temp
+= vcb
->vcbAlBlSt
;
681 // Return the desired sector for file position "offset"
685 // Determine the number of contiguous bytes until the end of the extent
686 // (or the amount they asked for, whichever comes first).
688 tmpOff
= dataEnd
- offset
;
689 if (tmpOff
> (off_t
)(numberOfBytes
))
690 *availableBytes
= numberOfBytes
; // more there than they asked for, so pin the output
692 *availableBytes
= tmpOff
;
698 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
699 // Routine: ReleaseExtents
701 // Function: Release the extents of a single extent data record.
702 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
704 static OSErr
ReleaseExtents(
706 const HFSPlusExtentRecord extentRecord
,
707 UInt32
*numReleasedAllocationBlocks
,
708 Boolean
*releasedLastExtent
)
711 UInt32 numberOfExtents
;
714 *numReleasedAllocationBlocks
= 0;
715 *releasedLastExtent
= false;
717 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
718 numberOfExtents
= kHFSPlusExtentDensity
;
720 numberOfExtents
= kHFSExtentDensity
;
722 for( extentIndex
= 0; extentIndex
< numberOfExtents
; extentIndex
++)
724 UInt32 numAllocationBlocks
;
726 // Loop over the extent record and release the blocks associated with each extent.
728 numAllocationBlocks
= extentRecord
[extentIndex
].blockCount
;
729 if ( numAllocationBlocks
== 0 )
731 *releasedLastExtent
= true;
735 err
= BlockDeallocate( vcb
, extentRecord
[extentIndex
].startBlock
, numAllocationBlocks
);
739 *numReleasedAllocationBlocks
+= numAllocationBlocks
; // bump FABN to beg of next extent
747 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
748 // Routine: TruncateExtents
750 // Purpose: Delete extent records whose starting file allocation block number
751 // is greater than or equal to a given starting block number. The
752 // allocation blocks represented by the extents are deallocated.
755 // vcb Volume to operate on
756 // fileID Which file to operate on
757 // startBlock Starting file allocation block number for first extent
759 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
761 static OSErr
TruncateExtents(
766 Boolean
* recordDeleted
)
769 UInt32 numberExtentsReleased
;
770 Boolean releasedLastExtent
;
772 HFSPlusExtentKey key
;
773 HFSPlusExtentRecord extents
;
776 err
= FindExtentRecord(vcb
, forkType
, fileID
, startBlock
, false, &key
, extents
, &hint
);
778 if (err
== btNotFound
)
783 err
= ReleaseExtents( vcb
, extents
, &numberExtentsReleased
, &releasedLastExtent
);
784 if (err
!= noErr
) break;
786 err
= DeleteExtentRecord(vcb
, forkType
, fileID
, startBlock
);
787 if (err
!= noErr
) break;
789 *recordDeleted
= true;
790 startBlock
+= numberExtentsReleased
;
798 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
799 // Routine: DeallocateFork
801 // Function: De-allocates all disk space allocated to a specified fork.
802 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
804 static OSErr
DeallocateFork(
806 HFSCatalogNodeID fileID
,
808 HFSPlusExtentRecord catalogExtents
,
809 Boolean
* recordDeleted
) /* true if a record was deleted */
812 UInt32 numReleasedAllocationBlocks
;
813 Boolean releasedLastExtent
;
815 // Release the catalog extents
816 err
= ReleaseExtents( vcb
, catalogExtents
, &numReleasedAllocationBlocks
, &releasedLastExtent
);
817 // Release the extra extents, if present
818 if (err
== noErr
&& !releasedLastExtent
)
819 err
= TruncateExtents(vcb
, forkType
, fileID
, numReleasedAllocationBlocks
, recordDeleted
);
824 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
825 // Routine: FlushExtentFile
827 // Function: Flushes the extent file for a specified volume
828 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
830 OSErr
FlushExtentFile( ExtendedVCB
*vcb
)
835 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
836 err
= BTFlushPath(fcb
);
839 // If the FCB for the extent "file" is dirty, mark the VCB as dirty.
841 if (FTOC(fcb
)->c_flag
& C_MODIFIED
)
844 // err = FlushVolumeControlBlock( vcb );
852 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
853 // Routine: CompareExtentKeys
855 // Function: Compares two extent file keys (a search key and a trial key) for
857 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
859 SInt32
CompareExtentKeys( const HFSExtentKey
*searchKey
, const HFSExtentKey
*trialKey
)
861 SInt32 result
; // ± 1
864 if (searchKey
->keyLength
!= kHFSExtentKeyMaximumLength
)
865 DebugStr("\pHFS: search Key is wrong length");
866 if (trialKey
->keyLength
!= kHFSExtentKeyMaximumLength
)
867 DebugStr("\pHFS: trial Key is wrong length");
870 result
= -1; // assume searchKey < trialKey
872 if (searchKey
->fileID
== trialKey
->fileID
) {
874 // FileNum's are equal; compare fork types
876 if (searchKey
->forkType
== trialKey
->forkType
) {
878 // Fork types are equal; compare allocation block number
880 if (searchKey
->startBlock
== trialKey
->startBlock
) {
882 // Everything is equal
888 // Allocation block numbers differ; determine sign
890 if (searchKey
->startBlock
> trialKey
->startBlock
)
896 // Fork types differ; determine sign
898 if (searchKey
->forkType
> trialKey
->forkType
)
904 // FileNums differ; determine sign
906 if (searchKey
->fileID
> trialKey
->fileID
)
915 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
916 // Routine: CompareExtentKeysPlus
918 // Function: Compares two extent file keys (a search key and a trial key) for
920 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
922 SInt32
CompareExtentKeysPlus( const HFSPlusExtentKey
*searchKey
, const HFSPlusExtentKey
*trialKey
)
924 SInt32 result
; // ± 1
927 if (searchKey
->keyLength
!= kHFSPlusExtentKeyMaximumLength
)
928 DebugStr("\pHFS: search Key is wrong length");
929 if (trialKey
->keyLength
!= kHFSPlusExtentKeyMaximumLength
)
930 DebugStr("\pHFS: trial Key is wrong length");
933 result
= -1; // assume searchKey < trialKey
935 if (searchKey
->fileID
== trialKey
->fileID
) {
937 // FileNum's are equal; compare fork types
939 if (searchKey
->forkType
== trialKey
->forkType
) {
941 // Fork types are equal; compare allocation block number
943 if (searchKey
->startBlock
== trialKey
->startBlock
) {
945 // Everything is equal
951 // Allocation block numbers differ; determine sign
953 if (searchKey
->startBlock
> trialKey
->startBlock
)
959 // Fork types differ; determine sign
961 if (searchKey
->forkType
> trialKey
->forkType
)
967 // FileNums differ; determine sign
969 if (searchKey
->fileID
> trialKey
->fileID
)
978 //_________________________________________________________________________________
980 // Routine: Extendfile
982 // Function: Extends the disk space allocated to a file.
984 // Input: A2.L - VCB pointer
985 // A1.L - pointer to FCB array
986 // D1.W - file refnum
987 // D3.B - option flags
988 // kEFContigMask - force contiguous allocation
989 // kEFAllMask - allocate all requested bytes or none
990 // NOTE: You may not set both options.
991 // D4.L - number of additional bytes to allocate
993 // Output: D0.W - result code
996 // D6.L - number of bytes allocated
998 // Called by: FileAloc,FileWrite,SetEof
1000 // Note: ExtendFile updates the PEOF in the FCB.
1001 //_________________________________________________________________________________
1004 ExtendedVCB
*vcb
, // volume that file resides on
1005 FCB
*fcb
, // FCB of file to truncate
1006 SInt64 bytesToAdd
, // number of bytes to allocate
1007 UInt32 blockHint
, // desired starting allocation block
1008 UInt32 flags
, // EFContig and/or EFAll
1009 SInt64
*actualBytesAdded
) // number of bytes actually allocated
1012 UInt32 volumeBlockSize
;
1014 SInt64 bytesThisExtent
;
1015 HFSPlusExtentKey foundKey
;
1016 HFSPlusExtentRecord foundData
;
1021 Boolean allOrNothing
;
1022 Boolean forceContig
;
1025 UInt32 actualStartBlock
;
1026 UInt32 actualNumBlocks
;
1027 UInt32 numExtentsPerRecord
;
1028 SInt64 maximumBytes
;
1034 *actualBytesAdded
= 0;
1035 volumeBlockSize
= vcb
->blockSize
;
1036 allOrNothing
= ((flags
& kEFAllMask
) != 0);
1037 forceContig
= ((flags
& kEFContigMask
) != 0);
1038 prevblocks
= fcb
->ff_blocks
;
1040 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1041 numExtentsPerRecord
= kHFSPlusExtentDensity
;
1043 numExtentsPerRecord
= kHFSExtentDensity
;
1046 // Make sure the request and new PEOF are less than 2GB if HFS.
1048 if (vcb
->vcbSigWord
== kHFSSigWord
) {
1049 if (bytesToAdd
>= kTwoGigabytes
)
1051 if ((((SInt64
)fcb
->ff_blocks
* (SInt64
)volumeBlockSize
) + bytesToAdd
) >= kTwoGigabytes
)
1055 // Determine how many blocks need to be allocated.
1056 // Round up the number of desired bytes to add.
1058 blocksToAdd
= FileBytesToBlocks(bytesToAdd
, volumeBlockSize
);
1059 bytesToAdd
= (SInt64
)((SInt64
)blocksToAdd
* (SInt64
)volumeBlockSize
);
1062 * For deferred allocations just reserve the blocks.
1064 if ((flags
& kEFDeferMask
)
1065 && (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1066 && (bytesToAdd
< (SInt64
)HFS_MAX_DEFERED_ALLOC
)
1067 && (blocksToAdd
< hfs_freeblks(VCBTOHFS(vcb
), 1))) {
1068 fcb
->ff_unallocblocks
+= blocksToAdd
;
1069 vcb
->loanedBlocks
+= blocksToAdd
;
1070 FTOC(fcb
)->c_blocks
+= blocksToAdd
;
1071 fcb
->ff_blocks
+= blocksToAdd
;
1073 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1074 *actualBytesAdded
= bytesToAdd
;
1078 * Give back any unallocated blocks before doing real allocations.
1080 if (fcb
->ff_unallocblocks
> 0) {
1081 blocksToAdd
+= fcb
->ff_unallocblocks
;
1082 bytesToAdd
= (SInt64
)blocksToAdd
* (SInt64
)volumeBlockSize
;
1084 vcb
->loanedBlocks
-= fcb
->ff_unallocblocks
;
1085 FTOC(fcb
)->c_blocks
-= fcb
->ff_unallocblocks
;
1086 fcb
->ff_blocks
-= fcb
->ff_unallocblocks
;
1087 fcb
->ff_unallocblocks
= 0;
1091 // If the file's clump size is larger than the allocation block size,
1092 // then set the maximum number of bytes to the requested number of bytes
1093 // rounded up to a multiple of the clump size.
1095 if ((fcb
->fcbClmpSize
> volumeBlockSize
)
1096 && (bytesToAdd
< (SInt64
)HFS_MAX_DEFERED_ALLOC
)
1097 && (flags
& kEFNoClumpMask
) == 0) {
1098 maximumBytes
= (SInt64
)FileBytesToBlocks(bytesToAdd
, fcb
->fcbClmpSize
);
1099 maximumBytes
*= fcb
->fcbClmpSize
;
1101 maximumBytes
= bytesToAdd
;
1105 // Compute new physical EOF, rounded up to a multiple of a block.
1107 if ((vcb
->vcbSigWord
== kHFSSigWord
) && ((((SInt64
)fcb
->ff_blocks
* (SInt64
)volumeBlockSize
) + bytesToAdd
) >= kTwoGigabytes
)) // Too big?
1108 if (allOrNothing
) // Yes, must they have it all?
1109 goto Overflow
; // Yes, can't have it
1111 --blocksToAdd
; // No, give give 'em one block less
1112 bytesToAdd
-= volumeBlockSize
;
1116 // If allocation is all-or-nothing, make sure there are
1117 // enough free blocks on the volume (quick test).
1120 (blocksToAdd
> hfs_freeblks(VCBTOHFS(vcb
), flags
& kEFReserveMask
))) {
1126 // See if there are already enough blocks allocated to the file.
1128 peof
= ((SInt64
)fcb
->ff_blocks
* (SInt64
)volumeBlockSize
) + bytesToAdd
; // potential new PEOF
1129 err
= SearchExtentFile(vcb
, fcb
, peof
-1, &foundKey
, foundData
, &foundIndex
, &hint
, &nextBlock
);
1131 // Enough blocks are already allocated. Just update the FCB to reflect the new length.
1132 fcb
->ff_blocks
= peof
/ volumeBlockSize
;
1133 FTOC(fcb
)->c_blocks
+= (bytesToAdd
/ volumeBlockSize
);
1134 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1137 if (err
!= fxRangeErr
) // Any real error?
1138 goto ErrorExit
; // Yes, so exit immediately
1141 // Adjust the PEOF to the end of the last extent.
1143 peof
= (SInt64
)((SInt64
)nextBlock
* (SInt64
)volumeBlockSize
); // currently allocated PEOF
1144 bytesThisExtent
= (SInt64
)(nextBlock
- fcb
->ff_blocks
) * (SInt64
)volumeBlockSize
;
1145 if (bytesThisExtent
!= 0) {
1146 fcb
->ff_blocks
= nextBlock
;
1147 FTOC(fcb
)->c_blocks
+= (bytesThisExtent
/ volumeBlockSize
);
1148 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1149 bytesToAdd
-= bytesThisExtent
;
1153 // Allocate some more space.
1155 // First try a contiguous allocation (of the whole amount).
1156 // If that fails, get whatever we can.
1157 // If forceContig, then take whatever we got
1158 // else, keep getting bits and pieces (non-contig)
1161 vcb
->vcbFreeExtCnt
= 0; /* For now, force rebuild of free extent list */
1164 startBlock
= blockHint
;
1166 startBlock
= foundData
[foundIndex
].startBlock
+ foundData
[foundIndex
].blockCount
;
1168 /* Force reserve checking if requested. */
1169 if (flags
& kEFReserveMask
) {
1172 actualNumBlocks
= 0;
1173 actualStartBlock
= 0;
1175 availbytes
= (SInt64
)hfs_freeblks(VCBTOHFS(vcb
), 1) *
1176 (SInt64
)volumeBlockSize
;
1177 if (availbytes
<= 0) {
1180 if (wantContig
&& (availbytes
< bytesToAdd
))
1183 err
= BlockAllocate(
1186 MIN(bytesToAdd
, availbytes
),
1187 MIN(maximumBytes
, availbytes
),
1194 err
= BlockAllocate(vcb
, startBlock
, bytesToAdd
, maximumBytes
,
1195 wantContig
, &actualStartBlock
, &actualNumBlocks
);
1197 if (err
== dskFulErr
) {
1199 break; // AllocContig failed because not enough contiguous space
1201 // Couldn't get one big chunk, so get whatever we can.
1206 if (actualNumBlocks
!= 0)
1210 // Add the new extent to the existing extent record, or create a new one.
1211 if ((actualStartBlock
== startBlock
) && (blockHint
== 0)) {
1212 // We grew the file's last extent, so just adjust the number of blocks.
1213 foundData
[foundIndex
].blockCount
+= actualNumBlocks
;
1214 err
= UpdateExtentRecord(vcb
, fcb
, &foundKey
, foundData
, hint
);
1215 if (err
!= noErr
) break;
1220 // Need to add a new extent. See if there is room in the current record.
1221 if (foundData
[foundIndex
].blockCount
!= 0) // Is current extent free to use?
1222 ++foundIndex
; // No, so use the next one.
1223 if (foundIndex
== numExtentsPerRecord
) {
1224 // This record is full. Need to create a new one.
1225 if (FTOC(fcb
)->c_fileid
== kHFSExtentsFileID
) {
1226 (void) BlockDeallocate(vcb
, actualStartBlock
, actualNumBlocks
);
1227 err
= dskFulErr
; // Oops. Can't extend extents file past first record.
1231 foundKey
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
1232 if (FORK_IS_RSRC(fcb
))
1233 foundKey
.forkType
= kResourceForkType
;
1235 foundKey
.forkType
= kDataForkType
;
1237 foundKey
.fileID
= FTOC(fcb
)->c_fileid
;
1238 foundKey
.startBlock
= nextBlock
;
1240 foundData
[0].startBlock
= actualStartBlock
;
1241 foundData
[0].blockCount
= actualNumBlocks
;
1243 // zero out remaining extents...
1244 for (i
= 1; i
< kHFSPlusExtentDensity
; ++i
)
1246 foundData
[i
].startBlock
= 0;
1247 foundData
[i
].blockCount
= 0;
1252 err
= CreateExtentRecord(vcb
, &foundKey
, foundData
, &hint
);
1253 if (err
== fxOvFlErr
) {
1254 // We couldn't create an extent record because extents B-tree
1255 // couldn't grow. Dellocate the extent just allocated and
1256 // return a disk full error.
1257 (void) BlockDeallocate(vcb
, actualStartBlock
, actualNumBlocks
);
1260 if (err
!= noErr
) break;
1262 needsFlush
= true; // We need to update the B-tree header
1265 // Add a new extent into this record and update.
1266 foundData
[foundIndex
].startBlock
= actualStartBlock
;
1267 foundData
[foundIndex
].blockCount
= actualNumBlocks
;
1268 err
= UpdateExtentRecord(vcb
, fcb
, &foundKey
, foundData
, hint
);
1269 if (err
!= noErr
) break;
1273 // Figure out how many bytes were actually allocated.
1274 // NOTE: BlockAllocate could have allocated more than we asked for.
1275 // Don't set the PEOF beyond what our client asked for.
1276 nextBlock
+= actualNumBlocks
;
1277 bytesThisExtent
= (SInt64
)((SInt64
)actualNumBlocks
* (SInt64
)volumeBlockSize
);
1278 if (bytesThisExtent
> bytesToAdd
) {
1282 bytesToAdd
-= bytesThisExtent
;
1283 maximumBytes
-= bytesThisExtent
;
1285 fcb
->ff_blocks
+= (bytesThisExtent
/ volumeBlockSize
);
1286 FTOC(fcb
)->c_blocks
+= (bytesThisExtent
/ volumeBlockSize
);
1287 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1289 // If contiguous allocation was requested, then we've already got one contiguous
1290 // chunk. If we didn't get all we wanted, then adjust the error to disk full.
1292 if (bytesToAdd
!= 0)
1294 break; // We've already got everything that's contiguous
1297 } while (err
== noErr
&& bytesToAdd
);
1301 *actualBytesAdded
= (SInt64
)(fcb
->ff_blocks
- prevblocks
) * (SInt64
)volumeBlockSize
;
1304 (void) FlushExtentFile(vcb
);
1309 err
= fileBoundsErr
;
1315 //_________________________________________________________________________________
1317 // Routine: TruncateFileC
1319 // Function: Truncates the disk space allocated to a file. The file space is
1320 // truncated to a specified new PEOF rounded up to the next allocation
1321 // block boundry. If the 'TFTrunExt' option is specified, the file is
1322 // truncated to the end of the extent containing the new PEOF.
1324 // Input: A2.L - VCB pointer
1325 // A1.L - pointer to FCB array
1326 // D1.W - file refnum
1327 // D2.B - option flags
1328 // TFTrunExt - truncate to the extent containing new PEOF
1331 // Output: D0.W - result code
1335 // Note: TruncateFile updates the PEOF in the FCB.
1336 //_________________________________________________________________________________
1338 OSErr
TruncateFileC (
1339 ExtendedVCB
*vcb
, // volume that file resides on
1340 FCB
*fcb
, // FCB of file to truncate
1341 SInt64 peof
, // new physical size for file
1342 Boolean truncateToExtent
) // if true, truncate to end of extent containing newPEOF
1345 UInt32 nextBlock
; // next file allocation block to consider
1346 UInt32 startBlock
; // Physical (volume) allocation block number of start of a range
1347 UInt32 physNumBlocks
; // Number of allocation blocks in file (according to PEOF)
1349 HFSPlusExtentKey key
; // key for current extent record; key->keyLength == 0 if FCB's extent record
1350 UInt32 hint
; // BTree hint corresponding to key
1351 HFSPlusExtentRecord extentRecord
;
1353 UInt32 extentNextBlock
;
1354 UInt32 numExtentsPerRecord
;
1357 Boolean extentChanged
; // true if we actually changed an extent
1358 Boolean recordDeleted
; // true if an extent record got deleted
1361 recordDeleted
= false;
1363 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1364 numExtentsPerRecord
= kHFSPlusExtentDensity
;
1366 numExtentsPerRecord
= kHFSExtentDensity
;
1368 if (FORK_IS_RSRC(fcb
))
1369 forkType
= kResourceForkType
;
1371 forkType
= kDataForkType
;
1373 temp64
= fcb
->ff_blocks
;
1374 physNumBlocks
= (UInt32
)temp64
;
1377 // Round newPEOF up to a multiple of the allocation block size. If new size is
1378 // two gigabytes or more, then round down by one allocation block (??? really?
1379 // shouldn't that be an error?).
1381 nextBlock
= FileBytesToBlocks(peof
, vcb
->blockSize
); // number of allocation blocks to remain in file
1382 peof
= (SInt64
)((SInt64
)nextBlock
* (SInt64
)vcb
->blockSize
); // number of bytes in those blocks
1383 if ((vcb
->vcbSigWord
== kHFSSigWord
) && (peof
>= kTwoGigabytes
)) {
1385 DebugStr("\pHFS: Trying to truncate a file to 2GB or more");
1387 err
= fileBoundsErr
;
1392 // Update FCB's length
1394 numBlocks
= peof
/ vcb
->blockSize
;
1395 FTOC(fcb
)->c_blocks
-= (fcb
->ff_blocks
- numBlocks
);
1396 fcb
->ff_blocks
= numBlocks
;
1397 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1400 // If the new PEOF is 0, then truncateToExtent has no meaning (we should always deallocate
1406 // Deallocate all the extents for this fork
1407 err
= DeallocateFork(vcb
, FTOC(fcb
)->c_fileid
, forkType
, fcb
->fcbExtents
, &recordDeleted
);
1408 if (err
!= noErr
) goto ErrorExit
; // got some error, so return it
1410 // Update the catalog extent record (making sure it's zeroed out)
1412 for (i
=0; i
< kHFSPlusExtentDensity
; i
++) {
1413 fcb
->fcbExtents
[i
].startBlock
= 0;
1414 fcb
->fcbExtents
[i
].blockCount
= 0;
1421 // Find the extent containing byte (peof-1). This is the last extent we'll keep.
1422 // (If truncateToExtent is true, we'll keep the whole extent; otherwise, we'll only
1423 // keep up through peof). The search will tell us how many allocation blocks exist
1424 // in the found extent plus all previous extents.
1426 err
= SearchExtentFile(vcb
, fcb
, peof
-1, &key
, extentRecord
, &extentIndex
, &hint
, &extentNextBlock
);
1427 if (err
!= noErr
) goto ErrorExit
;
1429 extentChanged
= false; // haven't changed the extent yet
1431 if (!truncateToExtent
) {
1433 // Shorten this extent. It may be the case that the entire extent gets
1436 numBlocks
= extentNextBlock
- nextBlock
; // How many blocks in this extent to free up
1437 if (numBlocks
!= 0) {
1438 // Compute first volume allocation block to free
1439 startBlock
= extentRecord
[extentIndex
].startBlock
+ extentRecord
[extentIndex
].blockCount
- numBlocks
;
1440 // Free the blocks in bitmap
1441 err
= BlockDeallocate(vcb
, startBlock
, numBlocks
);
1442 if (err
!= noErr
) goto ErrorExit
;
1443 // Adjust length of this extent
1444 extentRecord
[extentIndex
].blockCount
-= numBlocks
;
1445 // If extent is empty, set start block to 0
1446 if (extentRecord
[extentIndex
].blockCount
== 0)
1447 extentRecord
[extentIndex
].startBlock
= 0;
1448 // Remember that we changed the extent record
1449 extentChanged
= true;
1454 // Now move to the next extent in the record, and set up the file allocation block number
1456 nextBlock
= extentNextBlock
; // Next file allocation block to free
1457 ++extentIndex
; // Its index within the extent record
1460 // Release all following extents in this extent record. Update the record.
1462 while (extentIndex
< numExtentsPerRecord
&& extentRecord
[extentIndex
].blockCount
!= 0) {
1463 numBlocks
= extentRecord
[extentIndex
].blockCount
;
1464 // Deallocate this extent
1465 err
= BlockDeallocate(vcb
, extentRecord
[extentIndex
].startBlock
, numBlocks
);
1466 if (err
!= noErr
) goto ErrorExit
;
1467 // Update next file allocation block number
1468 nextBlock
+= numBlocks
;
1469 // Zero out start and length of this extent to delete it from record
1470 extentRecord
[extentIndex
].startBlock
= 0;
1471 extentRecord
[extentIndex
].blockCount
= 0;
1472 // Remember that we changed an extent
1473 extentChanged
= true;
1474 // Move to next extent in record
1479 // If any of the extents in the current record were changed, then update that
1480 // record (in the FCB, or extents file).
1482 if (extentChanged
) {
1483 err
= UpdateExtentRecord(vcb
, fcb
, &key
, extentRecord
, hint
);
1484 if (err
!= noErr
) goto ErrorExit
;
1488 // If there are any following allocation blocks, then we need
1489 // to seach for their extent records and delete those allocation
1492 if (nextBlock
< physNumBlocks
)
1493 err
= TruncateExtents(vcb
, forkType
, FTOC(fcb
)->c_fileid
, nextBlock
, &recordDeleted
);
1499 (void) FlushExtentFile(vcb
);
1506 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\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 // Routine: SearchExtentRecord (was XRSearch)
1509 // Function: Searches extent record for the extent mapping a given file
1510 // allocation block number (FABN).
1512 // Input: searchFABN - desired FABN
1513 // extentData - pointer to extent data record (xdr)
1514 // extentDataStartFABN - beginning FABN for extent record
1516 // Output: foundExtentDataOffset - offset to extent entry within xdr
1517 // result = noErr, offset to extent mapping desired FABN
1518 // result = FXRangeErr, offset to last extent in record
1519 // endingFABNPlusOne - ending FABN +1
1520 // noMoreExtents - True if the extent was not found, and the
1521 // extent record was not full (so don't bother
1522 // looking in subsequent records); false otherwise.
1524 // Result: noErr = ok
1525 // FXRangeErr = desired FABN > last mapped FABN in record
1526 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1528 static OSErr
SearchExtentRecord(
1529 const ExtendedVCB
*vcb
,
1531 const HFSPlusExtentRecord extentData
,
1532 UInt32 extentDataStartFABN
,
1533 UInt32
*foundExtentIndex
,
1534 UInt32
*endingFABNPlusOne
,
1535 Boolean
*noMoreExtents
)
1539 UInt32 numberOfExtents
;
1540 UInt32 numAllocationBlocks
;
1541 Boolean foundExtent
;
1543 *endingFABNPlusOne
= extentDataStartFABN
;
1544 *noMoreExtents
= false;
1545 foundExtent
= false;
1547 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1548 numberOfExtents
= kHFSPlusExtentDensity
;
1550 numberOfExtents
= kHFSExtentDensity
;
1552 for( extentIndex
= 0; extentIndex
< numberOfExtents
; ++extentIndex
)
1555 // Loop over the extent record and find the search FABN.
1557 numAllocationBlocks
= extentData
[extentIndex
].blockCount
;
1558 if ( numAllocationBlocks
== 0 )
1563 *endingFABNPlusOne
+= numAllocationBlocks
;
1565 if( searchFABN
< *endingFABNPlusOne
)
1567 // Found the extent.
1575 // Found the extent. Note the extent offset
1576 *foundExtentIndex
= extentIndex
;
1580 // Did not find the extent. Set foundExtentDataOffset accordingly
1581 if( extentIndex
> 0 )
1583 *foundExtentIndex
= extentIndex
- 1;
1587 *foundExtentIndex
= 0;
1590 // If we found an empty extent, then set noMoreExtents.
1591 if (extentIndex
< numberOfExtents
)
1592 *noMoreExtents
= true;
1594 // Finally, return an error to the caller
1601 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1602 // Routine: SearchExtentFile (was XFSearch)
1604 // Function: Searches extent file (including the FCB resident extent record)
1605 // for the extent mapping a given file position.
1607 // Input: vcb - VCB pointer
1608 // fcb - FCB pointer
1609 // filePosition - file position (byte address)
1611 // Output: foundExtentKey - extent key record (xkr)
1612 // If extent was found in the FCB's resident extent record,
1613 // then foundExtentKey->keyLength will be set to 0.
1614 // foundExtentData - extent data record(xdr)
1615 // foundExtentIndex - index to extent entry in xdr
1616 // result = 0, offset to extent mapping desired FABN
1617 // result = FXRangeErr, offset to last extent in record
1618 // (i.e., kNumExtentsPerRecord-1)
1619 // extentBTreeHint - BTree hint for extent record
1620 // kNoHint = Resident extent record
1621 // endingFABNPlusOne - ending FABN +1
1624 // noErr Found an extent that contains the given file position
1625 // FXRangeErr Given position is beyond the last allocated extent
1626 // (other) (some other internal I/O error)
1627 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1629 static OSErr
SearchExtentFile(
1630 const ExtendedVCB
*vcb
,
1632 SInt64 filePosition
,
1633 HFSPlusExtentKey
*foundExtentKey
,
1634 HFSPlusExtentRecord foundExtentData
,
1635 UInt32
*foundExtentIndex
,
1636 UInt32
*extentBTreeHint
,
1637 UInt32
*endingFABNPlusOne
)
1640 UInt32 filePositionBlock
;
1642 Boolean noMoreExtents
;
1644 temp64
= filePosition
/ (SInt64
)vcb
->blockSize
;
1645 filePositionBlock
= (UInt32
)temp64
;
1647 bcopy ( fcb
->fcbExtents
, foundExtentData
, sizeof(HFSPlusExtentRecord
));
1649 // Search the resident FCB first.
1650 err
= SearchExtentRecord( vcb
, filePositionBlock
, foundExtentData
, 0,
1651 foundExtentIndex
, endingFABNPlusOne
, &noMoreExtents
);
1653 if( err
== noErr
) {
1654 // Found the extent. Set results accordingly
1655 *extentBTreeHint
= kNoHint
; // no hint, because not in the BTree
1656 foundExtentKey
->keyLength
= 0; // 0 = the FCB itself
1661 // Didn't find extent in FCB. If FCB's extent record wasn't full, there's no point
1662 // in searching the extents file. Note that SearchExtentRecord left us pointing at
1663 // the last valid extent (or the first one, if none were valid). This means we need
1664 // to fill in the hint and key outputs, just like the "if" statement above.
1665 if ( noMoreExtents
) {
1666 *extentBTreeHint
= kNoHint
; // no hint, because not in the BTree
1667 foundExtentKey
->keyLength
= 0; // 0 = the FCB itself
1668 err
= fxRangeErr
; // There are no more extents, so must be beyond PEOF
1673 // Find the desired record, or the previous record if it is the same fork
1675 err
= FindExtentRecord(vcb
, FORK_IS_RSRC(fcb
) ? kResourceForkType
: kDataForkType
,
1676 FTOC(fcb
)->c_fileid
, filePositionBlock
, true, foundExtentKey
, foundExtentData
, extentBTreeHint
);
1678 if (err
== btNotFound
) {
1680 // If we get here, the desired position is beyond the extents in the FCB, and there are no extents
1681 // in the extents file. Return the FCB's extents and a range error.
1683 *extentBTreeHint
= kNoHint
;
1684 foundExtentKey
->keyLength
= 0;
1685 err
= GetFCBExtentRecord(fcb
, foundExtentData
);
1686 // Note: foundExtentIndex and endingFABNPlusOne have already been set as a result of the very
1687 // first SearchExtentRecord call in this function (when searching in the FCB's extents, and
1688 // we got a range error).
1694 // If we get here, there was either a BTree error, or we found an appropriate record.
1695 // If we found a record, then search it for the correct index into the extents.
1698 // Find appropriate index into extent record
1699 err
= SearchExtentRecord(vcb
, filePositionBlock
, foundExtentData
, foundExtentKey
->startBlock
,
1700 foundExtentIndex
, endingFABNPlusOne
, &noMoreExtents
);
1709 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1710 // Routine: UpdateExtentRecord
1712 // Function: Write new extent data to an existing extent record with a given key.
1713 // If all of the extents are empty, and the extent record is in the
1714 // extents file, then the record is deleted.
1716 // Input: vcb - the volume containing the extents
1717 // fcb - the file that owns the extents
1718 // extentFileKey - pointer to extent key record (xkr)
1719 // If the key length is 0, then the extents are actually part
1720 // of the catalog record, stored in the FCB.
1721 // extentData - pointer to extent data record (xdr)
1722 // extentBTreeHint - hint for given key, or kNoHint
1724 // Result: noErr = ok
1725 // (other) = error from BTree
1726 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1728 static OSErr
UpdateExtentRecord (
1729 const ExtendedVCB
*vcb
,
1731 const HFSPlusExtentKey
*extentFileKey
,
1732 const HFSPlusExtentRecord extentData
,
1733 UInt32 extentBTreeHint
)
1737 if (extentFileKey
->keyLength
== 0) { // keyLength == 0 means the FCB's extent record
1738 BlockMoveData(extentData
, fcb
->fcbExtents
, sizeof(HFSPlusExtentRecord
));
1739 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1742 BTreeIterator
* btIterator
;
1743 FSBufferDescriptor btRecord
;
1744 UInt16 btRecordSize
;
1748 // Need to find and change a record in Extents BTree
1750 btFCB
= GetFileControlBlock(vcb
->extentsRefNum
);
1752 // XXXdbg - preflight that there's enough space
1753 err
= BTCheckFreeSpace(btFCB
);
1757 MALLOC(btIterator
, BTreeIterator
*, sizeof(*btIterator
), M_TEMP
, M_WAITOK
);
1758 bzero(btIterator
, sizeof(*btIterator
));
1760 if (vcb
->vcbSigWord
== kHFSSigWord
) {
1761 HFSExtentKey
* key
; // Actual extent key used on disk in HFS
1762 HFSExtentRecord foundData
; // The extent data actually found
1764 key
= (HFSExtentKey
*) &btIterator
->key
;
1765 key
->keyLength
= kHFSExtentKeyMaximumLength
;
1766 key
->forkType
= extentFileKey
->forkType
;
1767 key
->fileID
= extentFileKey
->fileID
;
1768 key
->startBlock
= extentFileKey
->startBlock
;
1770 btIterator
->hint
.index
= 0;
1771 btIterator
->hint
.nodeNum
= extentBTreeHint
;
1773 btRecord
.bufferAddress
= &foundData
;
1774 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
1775 btRecord
.itemCount
= 1;
1777 err
= BTSearchRecord(btFCB
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
1780 err
= HFSPlusToHFSExtents(extentData
, (HFSExtentDescriptor
*)&foundData
);
1783 err
= BTReplaceRecord(btFCB
, btIterator
, &btRecord
, btRecordSize
);
1784 (void) BTFlushPath(btFCB
);
1786 else { // HFS Plus volume
1787 HFSPlusExtentRecord foundData
; // The extent data actually found
1789 BlockMoveData(extentFileKey
, &btIterator
->key
, sizeof(HFSPlusExtentKey
));
1791 btIterator
->hint
.index
= 0;
1792 btIterator
->hint
.nodeNum
= extentBTreeHint
;
1794 btRecord
.bufferAddress
= &foundData
;
1795 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
1796 btRecord
.itemCount
= 1;
1798 err
= BTSearchRecord(btFCB
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
1801 BlockMoveData(extentData
, &foundData
, sizeof(HFSPlusExtentRecord
));
1802 err
= BTReplaceRecord(btFCB
, btIterator
, &btRecord
, btRecordSize
);
1804 (void) BTFlushPath(btFCB
);
1806 FREE(btIterator
, M_TEMP
);
1814 void HFSToHFSPlusExtents(
1815 const HFSExtentRecord oldExtents
,
1816 HFSPlusExtentRecord newExtents
)
1820 // copy the first 3 extents
1821 newExtents
[0].startBlock
= oldExtents
[0].startBlock
;
1822 newExtents
[0].blockCount
= oldExtents
[0].blockCount
;
1823 newExtents
[1].startBlock
= oldExtents
[1].startBlock
;
1824 newExtents
[1].blockCount
= oldExtents
[1].blockCount
;
1825 newExtents
[2].startBlock
= oldExtents
[2].startBlock
;
1826 newExtents
[2].blockCount
= oldExtents
[2].blockCount
;
1828 // zero out the remaining ones
1829 for (i
= 3; i
< kHFSPlusExtentDensity
; ++i
)
1831 newExtents
[i
].startBlock
= 0;
1832 newExtents
[i
].blockCount
= 0;
1838 OSErr
HFSPlusToHFSExtents(
1839 const HFSPlusExtentRecord oldExtents
,
1840 HFSExtentRecord newExtents
)
1846 // copy the first 3 extents
1847 newExtents
[0].startBlock
= oldExtents
[0].startBlock
;
1848 newExtents
[0].blockCount
= oldExtents
[0].blockCount
;
1849 newExtents
[1].startBlock
= oldExtents
[1].startBlock
;
1850 newExtents
[1].blockCount
= oldExtents
[1].blockCount
;
1851 newExtents
[2].startBlock
= oldExtents
[2].startBlock
;
1852 newExtents
[2].blockCount
= oldExtents
[2].blockCount
;
1855 if (oldExtents
[3].startBlock
|| oldExtents
[3].blockCount
) {
1856 DebugStr("\pExtentRecord with > 3 extents is invalid for HFS");
1867 OSErr
GetFCBExtentRecord(
1869 HFSPlusExtentRecord extents
)
1872 BlockMoveData(fcb
->fcbExtents
, extents
, sizeof(HFSPlusExtentRecord
));
1878 //_________________________________________________________________________________
1880 // Routine: ExtentsAreIntegral
1882 // Purpose: Ensure that each extent can hold an integral number of nodes
1883 // Called by the NodesAreContiguous function
1884 //_________________________________________________________________________________
1886 static Boolean
ExtentsAreIntegral(
1887 const HFSPlusExtentRecord extentRecord
,
1889 UInt32
*blocksChecked
,
1890 Boolean
*checkedLastExtent
)
1896 *checkedLastExtent
= false;
1898 for(extentIndex
= 0; extentIndex
< kHFSPlusExtentDensity
; extentIndex
++)
1900 blocks
= extentRecord
[extentIndex
].blockCount
;
1904 *checkedLastExtent
= true;
1908 *blocksChecked
+= blocks
;
1918 //_________________________________________________________________________________
1920 // Routine: NodesAreContiguous
1922 // Purpose: Ensure that all b-tree nodes are contiguous on disk
1923 // Called by BTOpenPath during volume mount
1924 //_________________________________________________________________________________
1926 Boolean
NodesAreContiguous(
1933 UInt32 blocksChecked
;
1935 HFSPlusExtentKey key
;
1936 HFSPlusExtentRecord extents
;
1938 Boolean lastExtentReached
;
1941 if (vcb
->blockSize
>= nodeSize
)
1944 mask
= (nodeSize
/ vcb
->blockSize
) - 1;
1946 // check the local extents
1947 (void) GetFCBExtentRecord(fcb
, extents
);
1948 if ( !ExtentsAreIntegral(extents
, mask
, &blocksChecked
, &lastExtentReached
) )
1951 if (lastExtentReached
|| (SInt64
)((SInt64
)blocksChecked
* (SInt64
)vcb
->blockSize
) >= fcb
->ff_size
)
1954 startBlock
= blocksChecked
;
1956 // check the overflow extents (if any)
1957 while ( !lastExtentReached
)
1959 result
= FindExtentRecord(vcb
, kDataForkType
, fcb
->ff_cp
->c_fileid
, startBlock
, FALSE
, &key
, extents
, &hint
);
1962 if ( !ExtentsAreIntegral(extents
, mask
, &blocksChecked
, &lastExtentReached
) )
1965 startBlock
+= blocksChecked
;