2 * Copyright (c) 2000-2003 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,
254 static OSErr
HFSPlusToHFSExtents(
255 const HFSPlusExtentRecord oldExtents
,
256 HFSExtentRecord newExtents
);
258 static OSErr
FindExtentRecord(
259 const ExtendedVCB
*vcb
,
263 Boolean allowPrevious
,
264 HFSPlusExtentKey
*foundKey
,
265 HFSPlusExtentRecord foundData
,
268 static OSErr
DeleteExtentRecord(
269 const ExtendedVCB
*vcb
,
274 static OSErr
CreateExtentRecord(
275 const ExtendedVCB
*vcb
,
276 HFSPlusExtentKey
*key
,
277 HFSPlusExtentRecord extents
,
281 static 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 static OSErr
FindExtentRecord(
360 const ExtendedVCB
*vcb
,
364 Boolean allowPrevious
,
365 HFSPlusExtentKey
*foundKey
,
366 HFSPlusExtentRecord foundData
,
370 BTreeIterator
*btIterator
;
371 FSBufferDescriptor btRecord
;
378 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
380 MALLOC(btIterator
, BTreeIterator
*, sizeof(*btIterator
), M_TEMP
, M_WAITOK
);
381 bzero(btIterator
, sizeof(*btIterator
));
383 if (vcb
->vcbSigWord
== kHFSSigWord
) {
384 HFSExtentKey
* extentKeyPtr
;
385 HFSExtentRecord extentData
;
387 extentKeyPtr
= (HFSExtentKey
*) &btIterator
->key
;
388 extentKeyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
389 extentKeyPtr
->forkType
= forkType
;
390 extentKeyPtr
->fileID
= fileID
;
391 extentKeyPtr
->startBlock
= startBlock
;
393 btRecord
.bufferAddress
= &extentData
;
394 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
395 btRecord
.itemCount
= 1;
397 err
= BTSearchRecord(fcb
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
399 if (err
== btNotFound
&& allowPrevious
) {
400 err
= BTIterateRecord(fcb
, kBTreePrevRecord
, btIterator
, &btRecord
, &btRecordSize
);
402 // A previous record may not exist, so just return btNotFound (like we would if
403 // it was for the wrong file/fork).
404 if (err
== (OSErr
) fsBTStartOfIterationErr
) //¥¥ fsBTStartOfIterationErr is type unsigned long
408 // Found a previous record. Does it belong to the same fork of the same file?
409 if (extentKeyPtr
->fileID
!= fileID
|| extentKeyPtr
->forkType
!= forkType
)
417 // Copy the found key back for the caller
419 foundKey
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
420 foundKey
->forkType
= extentKeyPtr
->forkType
;
422 foundKey
->fileID
= extentKeyPtr
->fileID
;
423 foundKey
->startBlock
= extentKeyPtr
->startBlock
;
425 // Copy the found data back for the caller
426 foundData
[0].startBlock
= extentData
[0].startBlock
;
427 foundData
[0].blockCount
= extentData
[0].blockCount
;
428 foundData
[1].startBlock
= extentData
[1].startBlock
;
429 foundData
[1].blockCount
= extentData
[1].blockCount
;
430 foundData
[2].startBlock
= extentData
[2].startBlock
;
431 foundData
[2].blockCount
= extentData
[2].blockCount
;
433 for (i
= 3; i
< kHFSPlusExtentDensity
; ++i
)
435 foundData
[i
].startBlock
= 0;
436 foundData
[i
].blockCount
= 0;
440 else { // HFS Plus volume
441 HFSPlusExtentKey
* extentKeyPtr
;
442 HFSPlusExtentRecord extentData
;
444 extentKeyPtr
= (HFSPlusExtentKey
*) &btIterator
->key
;
445 extentKeyPtr
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
446 extentKeyPtr
->forkType
= forkType
;
447 extentKeyPtr
->pad
= 0;
448 extentKeyPtr
->fileID
= fileID
;
449 extentKeyPtr
->startBlock
= startBlock
;
451 btRecord
.bufferAddress
= &extentData
;
452 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
453 btRecord
.itemCount
= 1;
455 err
= BTSearchRecord(fcb
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
457 if (err
== btNotFound
&& allowPrevious
) {
458 err
= BTIterateRecord(fcb
, kBTreePrevRecord
, btIterator
, &btRecord
, &btRecordSize
);
460 // A previous record may not exist, so just return btNotFound (like we would if
461 // it was for the wrong file/fork).
462 if (err
== (OSErr
) fsBTStartOfIterationErr
) //¥¥ fsBTStartOfIterationErr is type unsigned long
466 // Found a previous record. Does it belong to the same fork of the same file?
467 if (extentKeyPtr
->fileID
!= fileID
|| extentKeyPtr
->forkType
!= forkType
)
473 // 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
));
482 *foundHint
= btIterator
->hint
.nodeNum
;
483 FREE(btIterator
, M_TEMP
);
489 static OSErr
CreateExtentRecord(
490 const ExtendedVCB
*vcb
,
491 HFSPlusExtentKey
*key
,
492 HFSPlusExtentRecord extents
,
495 BTreeIterator
* btIterator
;
496 FSBufferDescriptor btRecord
;
503 MALLOC(btIterator
, BTreeIterator
*, sizeof(*btIterator
), M_TEMP
, M_WAITOK
);
504 bzero(btIterator
, sizeof(*btIterator
));
506 if (vcb
->vcbSigWord
== kHFSSigWord
) {
507 HFSExtentKey
* keyPtr
;
508 HFSExtentRecord data
;
510 btRecordSize
= sizeof(HFSExtentRecord
);
511 btRecord
.bufferAddress
= &data
;
512 btRecord
.itemSize
= btRecordSize
;
513 btRecord
.itemCount
= 1;
515 keyPtr
= (HFSExtentKey
*) &btIterator
->key
;
516 keyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
517 keyPtr
->forkType
= key
->forkType
;
518 keyPtr
->fileID
= key
->fileID
;
519 keyPtr
->startBlock
= key
->startBlock
;
521 err
= HFSPlusToHFSExtents(extents
, data
);
523 else { // HFS Plus volume
524 btRecordSize
= sizeof(HFSPlusExtentRecord
);
525 btRecord
.bufferAddress
= extents
;
526 btRecord
.itemSize
= btRecordSize
;
527 btRecord
.itemCount
= 1;
529 BlockMoveData(key
, &btIterator
->key
, sizeof(HFSPlusExtentKey
));
533 err
= BTInsertRecord(GetFileControlBlock(vcb
->extentsRefNum
), btIterator
, &btRecord
, btRecordSize
);
536 *hint
= btIterator
->hint
.nodeNum
;
538 (void) BTFlushPath(GetFileControlBlock(vcb
->extentsRefNum
));
540 FREE(btIterator
, M_TEMP
);
545 static OSErr
DeleteExtentRecord(
546 const ExtendedVCB
*vcb
,
551 BTreeIterator
* btIterator
;
556 MALLOC(btIterator
, BTreeIterator
*, sizeof(*btIterator
), M_TEMP
, M_WAITOK
);
557 bzero(btIterator
, sizeof(*btIterator
));
559 if (vcb
->vcbSigWord
== kHFSSigWord
) {
560 HFSExtentKey
* keyPtr
;
562 keyPtr
= (HFSExtentKey
*) &btIterator
->key
;
563 keyPtr
->keyLength
= kHFSExtentKeyMaximumLength
;
564 keyPtr
->forkType
= forkType
;
565 keyPtr
->fileID
= fileID
;
566 keyPtr
->startBlock
= startBlock
;
568 else { // HFS Plus volume
569 HFSPlusExtentKey
* keyPtr
;
571 keyPtr
= (HFSPlusExtentKey
*) &btIterator
->key
;
572 keyPtr
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
573 keyPtr
->forkType
= forkType
;
575 keyPtr
->fileID
= fileID
;
576 keyPtr
->startBlock
= startBlock
;
579 err
= BTDeleteRecord(GetFileControlBlock(vcb
->extentsRefNum
), btIterator
);
580 (void) BTFlushPath(GetFileControlBlock(vcb
->extentsRefNum
));
582 FREE(btIterator
, M_TEMP
);
588 //_________________________________________________________________________________
590 // Routine: MapFileBlock
592 // Function: Maps a file position into a physical disk address.
594 // Input: A2.L - VCB pointer
595 // (A1,D1.W) - FCB pointer
596 // D4.L - number of bytes desired
597 // D5.L - file position (byte address)
599 // Output: D3.L - physical start block
600 // D6.L - number of contiguous bytes available (up to D4 bytes)
601 // D0.L - result code <01Oct85>
603 // FXRangeErr = file position beyond mapped range <17Oct85>
604 // FXOvFlErr = extents file overflow <17Oct85>
605 // other = error <17Oct85>
607 // Called By: Log2Phys (read/write in place), Cache (map a file block).
608 //_________________________________________________________________________________
611 OSErr
MapFileBlockC (
612 ExtendedVCB
*vcb
, // volume that file resides on
613 FCB
*fcb
, // FCB of file
614 size_t numberOfBytes
, // number of contiguous bytes desired
615 off_t offset
, // starting offset within file (in bytes)
616 daddr_t
*startSector
, // first sector (NOT an allocation block)
617 size_t *availableBytes
) // number of contiguous bytes (up to numberOfBytes)
620 UInt32 allocBlockSize
; // Size of the volume's allocation block
622 HFSPlusExtentKey foundKey
;
623 HFSPlusExtentRecord foundData
;
626 UInt32 firstFABN
; // file allocation block of first block in found extent
627 UInt32 nextFABN
; // file allocation block of block after end of found extent
628 off_t dataEnd
; // (offset) end of range that is contiguous
629 UInt32 sectorsPerBlock
; // Number of sectors per allocation block
630 UInt32 startBlock
; // volume allocation block corresponding to firstFABN
634 allocBlockSize
= vcb
->blockSize
;
635 sectorSize
= VCBTOHFS(vcb
)->hfs_phys_block_size
;
637 err
= SearchExtentFile(vcb
, fcb
, offset
, &foundKey
, foundData
, &foundIndex
, &hint
, &nextFABN
);
639 startBlock
= foundData
[foundIndex
].startBlock
;
640 firstFABN
= nextFABN
- foundData
[foundIndex
].blockCount
;
649 // Determine the end of the available space. It will either be the end of the extent,
650 // or the file's PEOF, whichever is smaller.
652 dataEnd
= (off_t
)((off_t
)(nextFABN
) * (off_t
)(allocBlockSize
)); // Assume valid data through end of this extent
653 if (((off_t
)fcb
->ff_blocks
* (off_t
)allocBlockSize
) < dataEnd
) // Is PEOF shorter?
654 dataEnd
= (off_t
)fcb
->ff_blocks
* (off_t
)allocBlockSize
; // Yes, so only map up to PEOF
656 // Compute the number of sectors in an allocation block
657 sectorsPerBlock
= allocBlockSize
/ sectorSize
; // sectors per allocation block
660 // Compute the absolute sector number that contains the offset of the given file
661 // offset in sectors from start of the extent +
662 // offset in sectors from start of allocation block space
664 temp
= (daddr_t
)((offset
- (off_t
)((off_t
)(firstFABN
) * (off_t
)(allocBlockSize
)))/sectorSize
);
665 temp
+= startBlock
* sectorsPerBlock
;
667 /* Add in any volume offsets */
668 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
669 temp
+= vcb
->hfsPlusIOPosOffset
/ sectorSize
;
671 temp
+= vcb
->vcbAlBlSt
;
673 // Return the desired sector for file position "offset"
677 // Determine the number of contiguous bytes until the end of the extent
678 // (or the amount they asked for, whichever comes first).
682 tmpOff
= dataEnd
- offset
;
683 if (tmpOff
> (off_t
)(numberOfBytes
))
684 *availableBytes
= numberOfBytes
; // more there than they asked for, so pin the output
686 *availableBytes
= tmpOff
;
692 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
693 // Routine: ReleaseExtents
695 // Function: Release the extents of a single extent data record.
696 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
698 static OSErr
ReleaseExtents(
700 const HFSPlusExtentRecord extentRecord
,
701 UInt32
*numReleasedAllocationBlocks
,
702 Boolean
*releasedLastExtent
)
705 UInt32 numberOfExtents
;
708 *numReleasedAllocationBlocks
= 0;
709 *releasedLastExtent
= false;
711 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
712 numberOfExtents
= kHFSPlusExtentDensity
;
714 numberOfExtents
= kHFSExtentDensity
;
716 for( extentIndex
= 0; extentIndex
< numberOfExtents
; extentIndex
++)
718 UInt32 numAllocationBlocks
;
720 // Loop over the extent record and release the blocks associated with each extent.
722 numAllocationBlocks
= extentRecord
[extentIndex
].blockCount
;
723 if ( numAllocationBlocks
== 0 )
725 *releasedLastExtent
= true;
729 err
= BlockDeallocate( vcb
, extentRecord
[extentIndex
].startBlock
, numAllocationBlocks
);
733 *numReleasedAllocationBlocks
+= numAllocationBlocks
; // bump FABN to beg of next extent
741 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
742 // Routine: TruncateExtents
744 // Purpose: Delete extent records whose starting file allocation block number
745 // is greater than or equal to a given starting block number. The
746 // allocation blocks represented by the extents are deallocated.
749 // vcb Volume to operate on
750 // fileID Which file to operate on
751 // startBlock Starting file allocation block number for first extent
753 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
755 static OSErr
TruncateExtents(
760 Boolean
* recordDeleted
)
763 UInt32 numberExtentsReleased
;
764 Boolean releasedLastExtent
;
766 HFSPlusExtentKey key
;
767 HFSPlusExtentRecord extents
;
770 err
= FindExtentRecord(vcb
, forkType
, fileID
, startBlock
, false, &key
, extents
, &hint
);
772 if (err
== btNotFound
)
777 err
= ReleaseExtents( vcb
, extents
, &numberExtentsReleased
, &releasedLastExtent
);
778 if (err
!= noErr
) break;
780 err
= DeleteExtentRecord(vcb
, forkType
, fileID
, startBlock
);
781 if (err
!= noErr
) break;
783 *recordDeleted
= true;
784 startBlock
+= numberExtentsReleased
;
792 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
793 // Routine: DeallocateFork
795 // Function: De-allocates all disk space allocated to a specified fork.
796 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
798 static OSErr
DeallocateFork(
800 HFSCatalogNodeID fileID
,
802 HFSPlusExtentRecord catalogExtents
,
803 Boolean
* recordDeleted
) /* true if a record was deleted */
806 UInt32 numReleasedAllocationBlocks
;
807 Boolean releasedLastExtent
;
809 // Release the catalog extents
810 err
= ReleaseExtents( vcb
, catalogExtents
, &numReleasedAllocationBlocks
, &releasedLastExtent
);
811 // Release the extra extents, if present
812 if (err
== noErr
&& !releasedLastExtent
)
813 err
= TruncateExtents(vcb
, forkType
, fileID
, numReleasedAllocationBlocks
, recordDeleted
);
818 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
819 // Routine: FlushExtentFile
821 // Function: Flushes the extent file for a specified volume
822 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\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 OSErr
FlushExtentFile( ExtendedVCB
*vcb
)
830 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
831 err
= BTFlushPath(fcb
);
834 // If the FCB for the extent "file" is dirty, mark the VCB as dirty.
836 if (FTOC(fcb
)->c_flag
& C_MODIFIED
)
839 // err = FlushVolumeControlBlock( vcb );
847 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
848 // Routine: CompareExtentKeys
850 // Function: Compares two extent file keys (a search key and a trial key) for
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
855 SInt32
CompareExtentKeys( const HFSExtentKey
*searchKey
, const HFSExtentKey
*trialKey
)
857 SInt32 result
; // ± 1
860 if (searchKey
->keyLength
!= kHFSExtentKeyMaximumLength
)
861 DebugStr("\pHFS: search Key is wrong length");
862 if (trialKey
->keyLength
!= kHFSExtentKeyMaximumLength
)
863 DebugStr("\pHFS: trial Key is wrong length");
866 result
= -1; // assume searchKey < trialKey
868 if (searchKey
->fileID
== trialKey
->fileID
) {
870 // FileNum's are equal; compare fork types
872 if (searchKey
->forkType
== trialKey
->forkType
) {
874 // Fork types are equal; compare allocation block number
876 if (searchKey
->startBlock
== trialKey
->startBlock
) {
878 // Everything is equal
884 // Allocation block numbers differ; determine sign
886 if (searchKey
->startBlock
> trialKey
->startBlock
)
892 // Fork types differ; determine sign
894 if (searchKey
->forkType
> trialKey
->forkType
)
900 // FileNums differ; determine sign
902 if (searchKey
->fileID
> trialKey
->fileID
)
911 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
912 // Routine: CompareExtentKeysPlus
914 // Function: Compares two extent file keys (a search key and a trial key) for
916 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
919 SInt32
CompareExtentKeysPlus( const HFSPlusExtentKey
*searchKey
, const HFSPlusExtentKey
*trialKey
)
921 SInt32 result
; // ± 1
924 if (searchKey
->keyLength
!= kHFSPlusExtentKeyMaximumLength
)
925 DebugStr("\pHFS: search Key is wrong length");
926 if (trialKey
->keyLength
!= kHFSPlusExtentKeyMaximumLength
)
927 DebugStr("\pHFS: trial Key is wrong length");
930 result
= -1; // assume searchKey < trialKey
932 if (searchKey
->fileID
== trialKey
->fileID
) {
934 // FileNum's are equal; compare fork types
936 if (searchKey
->forkType
== trialKey
->forkType
) {
938 // Fork types are equal; compare allocation block number
940 if (searchKey
->startBlock
== trialKey
->startBlock
) {
942 // Everything is equal
948 // Allocation block numbers differ; determine sign
950 if (searchKey
->startBlock
> trialKey
->startBlock
)
956 // Fork types differ; determine sign
958 if (searchKey
->forkType
> trialKey
->forkType
)
964 // FileNums differ; determine sign
966 if (searchKey
->fileID
> trialKey
->fileID
)
974 * Add a file extent to a file.
976 * Used by hfs_extendfs to extend the volume allocation bitmap file.
981 AddFileExtent(ExtendedVCB
*vcb
, FCB
*fcb
, UInt32 startBlock
, UInt32 blockCount
)
983 HFSPlusExtentKey foundKey
;
984 HFSPlusExtentRecord foundData
;
992 peof
= (SInt64
)(fcb
->ff_blocks
+ blockCount
) * (SInt64
)vcb
->blockSize
;
994 error
= SearchExtentFile(vcb
, fcb
, peof
-1, &foundKey
, foundData
, &foundIndex
, &hint
, &nextBlock
);
995 if (error
!= fxRangeErr
)
999 * Add new extent. See if there is room in the current record.
1001 if (foundData
[foundIndex
].blockCount
!= 0)
1003 if (foundIndex
== kHFSPlusExtentDensity
) {
1005 * Existing record is full so create a new one.
1007 foundKey
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
1008 foundKey
.forkType
= kDataForkType
;
1010 foundKey
.fileID
= FTOC(fcb
)->c_fileid
;
1011 foundKey
.startBlock
= nextBlock
;
1013 foundData
[0].startBlock
= startBlock
;
1014 foundData
[0].blockCount
= blockCount
;
1016 /* zero out remaining extents. */
1017 for (i
= 1; i
< kHFSPlusExtentDensity
; ++i
) {
1018 foundData
[i
].startBlock
= 0;
1019 foundData
[i
].blockCount
= 0;
1024 error
= CreateExtentRecord(vcb
, &foundKey
, foundData
, &hint
);
1025 if (error
== fxOvFlErr
)
1029 * Add a new extent into existing record.
1031 foundData
[foundIndex
].startBlock
= startBlock
;
1032 foundData
[foundIndex
].blockCount
= blockCount
;
1033 error
= UpdateExtentRecord(vcb
, fcb
, &foundKey
, foundData
, hint
);
1035 (void) FlushExtentFile(vcb
);
1041 //_________________________________________________________________________________
1043 // Routine: Extendfile
1045 // Function: Extends the disk space allocated to a file.
1047 // Input: A2.L - VCB pointer
1048 // A1.L - pointer to FCB array
1049 // D1.W - file refnum
1050 // D3.B - option flags
1051 // kEFContigMask - force contiguous allocation
1052 // kEFAllMask - allocate all requested bytes or none
1053 // NOTE: You may not set both options.
1054 // D4.L - number of additional bytes to allocate
1056 // Output: D0.W - result code
1059 // D6.L - number of bytes allocated
1061 // Called by: FileAloc,FileWrite,SetEof
1063 // Note: ExtendFile updates the PEOF in the FCB.
1064 //_________________________________________________________________________________
1068 ExtendedVCB
*vcb
, // volume that file resides on
1069 FCB
*fcb
, // FCB of file to truncate
1070 SInt64 bytesToAdd
, // number of bytes to allocate
1071 UInt32 blockHint
, // desired starting allocation block
1072 UInt32 flags
, // EFContig and/or EFAll
1073 SInt64
*actualBytesAdded
) // number of bytes actually allocated
1076 UInt32 volumeBlockSize
;
1078 SInt64 bytesThisExtent
;
1079 HFSPlusExtentKey foundKey
;
1080 HFSPlusExtentRecord foundData
;
1085 Boolean allOrNothing
;
1086 Boolean forceContig
;
1088 Boolean useMetaZone
;
1090 UInt32 actualStartBlock
;
1091 UInt32 actualNumBlocks
;
1092 UInt32 numExtentsPerRecord
;
1093 SInt64 maximumBytes
;
1099 *actualBytesAdded
= 0;
1100 volumeBlockSize
= vcb
->blockSize
;
1101 allOrNothing
= ((flags
& kEFAllMask
) != 0);
1102 forceContig
= ((flags
& kEFContigMask
) != 0);
1103 prevblocks
= fcb
->ff_blocks
;
1105 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1106 numExtentsPerRecord
= kHFSPlusExtentDensity
;
1108 numExtentsPerRecord
= kHFSExtentDensity
;
1111 // Make sure the request and new PEOF are less than 2GB if HFS.
1113 if (vcb
->vcbSigWord
== kHFSSigWord
) {
1114 if (bytesToAdd
>= kTwoGigabytes
)
1116 if ((((SInt64
)fcb
->ff_blocks
* (SInt64
)volumeBlockSize
) + bytesToAdd
) >= kTwoGigabytes
)
1120 // Determine how many blocks need to be allocated.
1121 // Round up the number of desired bytes to add.
1123 blocksToAdd
= howmany(bytesToAdd
, volumeBlockSize
);
1124 bytesToAdd
= (SInt64
)((SInt64
)blocksToAdd
* (SInt64
)volumeBlockSize
);
1127 * For deferred allocations just reserve the blocks.
1129 if ((flags
& kEFDeferMask
)
1130 && (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1131 && (bytesToAdd
< (SInt64
)HFS_MAX_DEFERED_ALLOC
)
1132 && (blocksToAdd
< hfs_freeblks(VCBTOHFS(vcb
), 1))) {
1133 fcb
->ff_unallocblocks
+= blocksToAdd
;
1134 vcb
->loanedBlocks
+= blocksToAdd
;
1135 FTOC(fcb
)->c_blocks
+= blocksToAdd
;
1136 fcb
->ff_blocks
+= blocksToAdd
;
1138 FTOC(fcb
)->c_flag
|= C_MODIFIED
| C_FORCEUPDATE
;
1139 *actualBytesAdded
= bytesToAdd
;
1143 * Give back any unallocated blocks before doing real allocations.
1145 if (fcb
->ff_unallocblocks
> 0) {
1146 blocksToAdd
+= fcb
->ff_unallocblocks
;
1147 bytesToAdd
= (SInt64
)blocksToAdd
* (SInt64
)volumeBlockSize
;
1149 vcb
->loanedBlocks
-= fcb
->ff_unallocblocks
;
1150 FTOC(fcb
)->c_blocks
-= fcb
->ff_unallocblocks
;
1151 fcb
->ff_blocks
-= fcb
->ff_unallocblocks
;
1152 fcb
->ff_unallocblocks
= 0;
1156 // If the file's clump size is larger than the allocation block size,
1157 // then set the maximum number of bytes to the requested number of bytes
1158 // rounded up to a multiple of the clump size.
1160 if ((vcb
->vcbClpSiz
> volumeBlockSize
)
1161 && (bytesToAdd
< (SInt64
)HFS_MAX_DEFERED_ALLOC
)
1162 && (flags
& kEFNoClumpMask
) == 0) {
1163 maximumBytes
= (SInt64
)howmany(bytesToAdd
, vcb
->vcbClpSiz
);
1164 maximumBytes
*= vcb
->vcbClpSiz
;
1166 maximumBytes
= bytesToAdd
;
1170 // Compute new physical EOF, rounded up to a multiple of a block.
1172 if ((vcb
->vcbSigWord
== kHFSSigWord
) && ((((SInt64
)fcb
->ff_blocks
* (SInt64
)volumeBlockSize
) + bytesToAdd
) >= kTwoGigabytes
)) // Too big?
1173 if (allOrNothing
) // Yes, must they have it all?
1174 goto Overflow
; // Yes, can't have it
1176 --blocksToAdd
; // No, give give 'em one block less
1177 bytesToAdd
-= volumeBlockSize
;
1181 // If allocation is all-or-nothing, make sure there are
1182 // enough free blocks on the volume (quick test).
1185 (blocksToAdd
> hfs_freeblks(VCBTOHFS(vcb
), flags
& kEFReserveMask
))) {
1191 // See if there are already enough blocks allocated to the file.
1193 peof
= ((SInt64
)fcb
->ff_blocks
* (SInt64
)volumeBlockSize
) + bytesToAdd
; // potential new PEOF
1194 err
= SearchExtentFile(vcb
, fcb
, peof
-1, &foundKey
, foundData
, &foundIndex
, &hint
, &nextBlock
);
1196 // Enough blocks are already allocated. Just update the FCB to reflect the new length.
1197 fcb
->ff_blocks
= peof
/ volumeBlockSize
;
1198 FTOC(fcb
)->c_blocks
+= (bytesToAdd
/ volumeBlockSize
);
1199 FTOC(fcb
)->c_flag
|= C_MODIFIED
| C_FORCEUPDATE
;
1202 if (err
!= fxRangeErr
) // Any real error?
1203 goto ErrorExit
; // Yes, so exit immediately
1206 // Adjust the PEOF to the end of the last extent.
1208 peof
= (SInt64
)((SInt64
)nextBlock
* (SInt64
)volumeBlockSize
); // currently allocated PEOF
1209 bytesThisExtent
= (SInt64
)(nextBlock
- fcb
->ff_blocks
) * (SInt64
)volumeBlockSize
;
1210 if (bytesThisExtent
!= 0) {
1211 fcb
->ff_blocks
= nextBlock
;
1212 FTOC(fcb
)->c_blocks
+= (bytesThisExtent
/ volumeBlockSize
);
1213 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1214 bytesToAdd
-= bytesThisExtent
;
1218 // Allocate some more space.
1220 // First try a contiguous allocation (of the whole amount).
1221 // If that fails, get whatever we can.
1222 // If forceContig, then take whatever we got
1223 // else, keep getting bits and pieces (non-contig)
1226 useMetaZone
= flags
& kEFMetadataMask
;
1227 vcb
->vcbFreeExtCnt
= 0; /* For now, force rebuild of free extent list */
1230 startBlock
= blockHint
;
1232 startBlock
= foundData
[foundIndex
].startBlock
+ foundData
[foundIndex
].blockCount
;
1234 /* Force reserve checking if requested. */
1235 if (flags
& kEFReserveMask
) {
1238 actualNumBlocks
= 0;
1239 actualStartBlock
= 0;
1241 availbytes
= (SInt64
)hfs_freeblks(VCBTOHFS(vcb
), 1) *
1242 (SInt64
)volumeBlockSize
;
1243 if (availbytes
<= 0) {
1246 if (wantContig
&& (availbytes
< bytesToAdd
))
1249 err
= BlockAllocate(
1252 howmany(MIN(bytesToAdd
, availbytes
), volumeBlockSize
),
1253 howmany(MIN(maximumBytes
, availbytes
), volumeBlockSize
),
1261 err
= BlockAllocate(vcb
, startBlock
, howmany(bytesToAdd
, volumeBlockSize
),
1262 howmany(maximumBytes
, volumeBlockSize
), wantContig
, useMetaZone
,
1263 &actualStartBlock
, &actualNumBlocks
);
1265 if (err
== dskFulErr
) {
1267 break; // AllocContig failed because not enough contiguous space
1269 // Couldn't get one big chunk, so get whatever we can.
1274 if (actualNumBlocks
!= 0)
1276 if (useMetaZone
== 0) {
1277 /* Couldn't get anything so dip into metadat zone */
1284 if (actualNumBlocks
!= 0) {
1285 // this catalog entry *must* get forced to disk when
1286 // hfs_update() is called
1287 FTOC(fcb
)->c_flag
|= C_FORCEUPDATE
;
1290 // Add the new extent to the existing extent record, or create a new one.
1291 if ((actualStartBlock
== startBlock
) && (blockHint
== 0)) {
1292 // We grew the file's last extent, so just adjust the number of blocks.
1293 foundData
[foundIndex
].blockCount
+= actualNumBlocks
;
1294 err
= UpdateExtentRecord(vcb
, fcb
, &foundKey
, foundData
, hint
);
1295 if (err
!= noErr
) break;
1300 // Need to add a new extent. See if there is room in the current record.
1301 if (foundData
[foundIndex
].blockCount
!= 0) // Is current extent free to use?
1302 ++foundIndex
; // No, so use the next one.
1303 if (foundIndex
== numExtentsPerRecord
) {
1304 // This record is full. Need to create a new one.
1305 if (FTOC(fcb
)->c_fileid
== kHFSExtentsFileID
) {
1306 (void) BlockDeallocate(vcb
, actualStartBlock
, actualNumBlocks
);
1307 err
= dskFulErr
; // Oops. Can't extend extents file past first record.
1311 foundKey
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
1312 if (FORK_IS_RSRC(fcb
))
1313 foundKey
.forkType
= kResourceForkType
;
1315 foundKey
.forkType
= kDataForkType
;
1317 foundKey
.fileID
= FTOC(fcb
)->c_fileid
;
1318 foundKey
.startBlock
= nextBlock
;
1320 foundData
[0].startBlock
= actualStartBlock
;
1321 foundData
[0].blockCount
= actualNumBlocks
;
1323 // zero out remaining extents...
1324 for (i
= 1; i
< kHFSPlusExtentDensity
; ++i
)
1326 foundData
[i
].startBlock
= 0;
1327 foundData
[i
].blockCount
= 0;
1332 err
= CreateExtentRecord(vcb
, &foundKey
, foundData
, &hint
);
1333 if (err
== fxOvFlErr
) {
1334 // We couldn't create an extent record because extents B-tree
1335 // couldn't grow. Dellocate the extent just allocated and
1336 // return a disk full error.
1337 (void) BlockDeallocate(vcb
, actualStartBlock
, actualNumBlocks
);
1340 if (err
!= noErr
) break;
1342 needsFlush
= true; // We need to update the B-tree header
1345 // Add a new extent into this record and update.
1346 foundData
[foundIndex
].startBlock
= actualStartBlock
;
1347 foundData
[foundIndex
].blockCount
= actualNumBlocks
;
1348 err
= UpdateExtentRecord(vcb
, fcb
, &foundKey
, foundData
, hint
);
1349 if (err
!= noErr
) break;
1353 // Figure out how many bytes were actually allocated.
1354 // NOTE: BlockAllocate could have allocated more than we asked for.
1355 // Don't set the PEOF beyond what our client asked for.
1356 nextBlock
+= actualNumBlocks
;
1357 bytesThisExtent
= (SInt64
)((SInt64
)actualNumBlocks
* (SInt64
)volumeBlockSize
);
1358 if (bytesThisExtent
> bytesToAdd
) {
1362 bytesToAdd
-= bytesThisExtent
;
1363 maximumBytes
-= bytesThisExtent
;
1365 fcb
->ff_blocks
+= (bytesThisExtent
/ volumeBlockSize
);
1366 FTOC(fcb
)->c_blocks
+= (bytesThisExtent
/ volumeBlockSize
);
1367 FTOC(fcb
)->c_flag
|= C_MODIFIED
| C_FORCEUPDATE
;
1369 // If contiguous allocation was requested, then we've already got one contiguous
1370 // chunk. If we didn't get all we wanted, then adjust the error to disk full.
1372 if (bytesToAdd
!= 0)
1374 break; // We've already got everything that's contiguous
1377 } while (err
== noErr
&& bytesToAdd
);
1381 if (VCBTOHFS(vcb
)->hfs_flags
& HFS_METADATA_ZONE
) {
1382 /* Keep the roving allocator out of the metadata zone. */
1383 if (vcb
->nextAllocation
>= VCBTOHFS(vcb
)->hfs_metazone_start
&&
1384 vcb
->nextAllocation
<= VCBTOHFS(vcb
)->hfs_metazone_end
) {
1385 vcb
->nextAllocation
= VCBTOHFS(vcb
)->hfs_metazone_end
+ 1;
1388 *actualBytesAdded
= (SInt64
)(fcb
->ff_blocks
- prevblocks
) * (SInt64
)volumeBlockSize
;
1391 (void) FlushExtentFile(vcb
);
1396 err
= fileBoundsErr
;
1402 //_________________________________________________________________________________
1404 // Routine: TruncateFileC
1406 // Function: Truncates the disk space allocated to a file. The file space is
1407 // truncated to a specified new PEOF rounded up to the next allocation
1408 // block boundry. If the 'TFTrunExt' option is specified, the file is
1409 // truncated to the end of the extent containing the new PEOF.
1411 // Input: A2.L - VCB pointer
1412 // A1.L - pointer to FCB array
1413 // D1.W - file refnum
1414 // D2.B - option flags
1415 // TFTrunExt - truncate to the extent containing new PEOF
1418 // Output: D0.W - result code
1422 // Note: TruncateFile updates the PEOF in the FCB.
1423 //_________________________________________________________________________________
1426 OSErr
TruncateFileC (
1427 ExtendedVCB
*vcb
, // volume that file resides on
1428 FCB
*fcb
, // FCB of file to truncate
1429 SInt64 peof
, // new physical size for file
1430 Boolean truncateToExtent
) // if true, truncate to end of extent containing newPEOF
1433 UInt32 nextBlock
; // next file allocation block to consider
1434 UInt32 startBlock
; // Physical (volume) allocation block number of start of a range
1435 UInt32 physNumBlocks
; // Number of allocation blocks in file (according to PEOF)
1437 HFSPlusExtentKey key
; // key for current extent record; key->keyLength == 0 if FCB's extent record
1438 UInt32 hint
; // BTree hint corresponding to key
1439 HFSPlusExtentRecord extentRecord
;
1441 UInt32 extentNextBlock
;
1442 UInt32 numExtentsPerRecord
;
1445 Boolean extentChanged
; // true if we actually changed an extent
1446 Boolean recordDeleted
; // true if an extent record got deleted
1449 recordDeleted
= false;
1451 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1452 numExtentsPerRecord
= kHFSPlusExtentDensity
;
1454 numExtentsPerRecord
= kHFSExtentDensity
;
1456 if (FORK_IS_RSRC(fcb
))
1457 forkType
= kResourceForkType
;
1459 forkType
= kDataForkType
;
1461 temp64
= fcb
->ff_blocks
;
1462 physNumBlocks
= (UInt32
)temp64
;
1465 // Round newPEOF up to a multiple of the allocation block size. If new size is
1466 // two gigabytes or more, then round down by one allocation block (??? really?
1467 // shouldn't that be an error?).
1469 nextBlock
= howmany(peof
, vcb
->blockSize
); // number of allocation blocks to remain in file
1470 peof
= (SInt64
)((SInt64
)nextBlock
* (SInt64
)vcb
->blockSize
); // number of bytes in those blocks
1471 if ((vcb
->vcbSigWord
== kHFSSigWord
) && (peof
>= kTwoGigabytes
)) {
1473 DebugStr("\pHFS: Trying to truncate a file to 2GB or more");
1475 err
= fileBoundsErr
;
1480 // Update FCB's length
1483 * XXX Any errors could cause ff_blocks and c_blocks to get out of sync...
1485 numBlocks
= peof
/ vcb
->blockSize
;
1486 FTOC(fcb
)->c_blocks
-= (fcb
->ff_blocks
- numBlocks
);
1487 fcb
->ff_blocks
= numBlocks
;
1489 // this catalog entry is modified and *must* get forced
1490 // to disk when hfs_update() is called
1491 FTOC(fcb
)->c_flag
|= C_MODIFIED
| C_FORCEUPDATE
;
1494 // If the new PEOF is 0, then truncateToExtent has no meaning (we should always deallocate
1500 // Deallocate all the extents for this fork
1501 err
= DeallocateFork(vcb
, FTOC(fcb
)->c_fileid
, forkType
, fcb
->fcbExtents
, &recordDeleted
);
1502 if (err
!= noErr
) goto ErrorExit
; // got some error, so return it
1504 // Update the catalog extent record (making sure it's zeroed out)
1506 for (i
=0; i
< kHFSPlusExtentDensity
; i
++) {
1507 fcb
->fcbExtents
[i
].startBlock
= 0;
1508 fcb
->fcbExtents
[i
].blockCount
= 0;
1515 // Find the extent containing byte (peof-1). This is the last extent we'll keep.
1516 // (If truncateToExtent is true, we'll keep the whole extent; otherwise, we'll only
1517 // keep up through peof). The search will tell us how many allocation blocks exist
1518 // in the found extent plus all previous extents.
1520 err
= SearchExtentFile(vcb
, fcb
, peof
-1, &key
, extentRecord
, &extentIndex
, &hint
, &extentNextBlock
);
1521 if (err
!= noErr
) goto ErrorExit
;
1523 extentChanged
= false; // haven't changed the extent yet
1525 if (!truncateToExtent
) {
1527 // Shorten this extent. It may be the case that the entire extent gets
1530 numBlocks
= extentNextBlock
- nextBlock
; // How many blocks in this extent to free up
1531 if (numBlocks
!= 0) {
1532 // Compute first volume allocation block to free
1533 startBlock
= extentRecord
[extentIndex
].startBlock
+ extentRecord
[extentIndex
].blockCount
- numBlocks
;
1534 // Free the blocks in bitmap
1535 err
= BlockDeallocate(vcb
, startBlock
, numBlocks
);
1536 if (err
!= noErr
) goto ErrorExit
;
1537 // Adjust length of this extent
1538 extentRecord
[extentIndex
].blockCount
-= numBlocks
;
1539 // If extent is empty, set start block to 0
1540 if (extentRecord
[extentIndex
].blockCount
== 0)
1541 extentRecord
[extentIndex
].startBlock
= 0;
1542 // Remember that we changed the extent record
1543 extentChanged
= true;
1548 // Now move to the next extent in the record, and set up the file allocation block number
1550 nextBlock
= extentNextBlock
; // Next file allocation block to free
1551 ++extentIndex
; // Its index within the extent record
1554 // Release all following extents in this extent record. Update the record.
1556 while (extentIndex
< numExtentsPerRecord
&& extentRecord
[extentIndex
].blockCount
!= 0) {
1557 numBlocks
= extentRecord
[extentIndex
].blockCount
;
1558 // Deallocate this extent
1559 err
= BlockDeallocate(vcb
, extentRecord
[extentIndex
].startBlock
, numBlocks
);
1560 if (err
!= noErr
) goto ErrorExit
;
1561 // Update next file allocation block number
1562 nextBlock
+= numBlocks
;
1563 // Zero out start and length of this extent to delete it from record
1564 extentRecord
[extentIndex
].startBlock
= 0;
1565 extentRecord
[extentIndex
].blockCount
= 0;
1566 // Remember that we changed an extent
1567 extentChanged
= true;
1568 // Move to next extent in record
1573 // If any of the extents in the current record were changed, then update that
1574 // record (in the FCB, or extents file).
1576 if (extentChanged
) {
1577 err
= UpdateExtentRecord(vcb
, fcb
, &key
, extentRecord
, hint
);
1578 if (err
!= noErr
) goto ErrorExit
;
1582 // If there are any following allocation blocks, then we need
1583 // to seach for their extent records and delete those allocation
1586 if (nextBlock
< physNumBlocks
)
1587 err
= TruncateExtents(vcb
, forkType
, FTOC(fcb
)->c_fileid
, nextBlock
, &recordDeleted
);
1593 (void) FlushExtentFile(vcb
);
1604 OSErr
HeadTruncateFile (
1609 HFSPlusExtentRecord extents
;
1610 HFSPlusExtentRecord tailExtents
;
1611 HFSCatalogNodeID fileID
;
1620 if (vcb
->vcbSigWord
!= kHFSPlusSigWord
)
1623 forkType
= FORK_IS_RSRC(fcb
) ? kResourceForkType
: kDataForkType
;
1624 fileID
= FTOC(fcb
)->c_fileid
;
1625 bzero(tailExtents
, sizeof(tailExtents
));
1631 * Process catalog resident extents
1633 for (i
= 0, j
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
1634 blkcnt
= fcb
->fcbExtents
[i
].blockCount
;
1636 break; /* end of extents */
1638 if (blksfreed
< headblks
) {
1639 error
= BlockDeallocate(vcb
, fcb
->fcbExtents
[i
].startBlock
, blkcnt
);
1641 * Any errors after the first BlockDeallocate
1642 * must be ignored so we can put the file in
1647 goto ErrorExit
; /* uh oh */
1650 printf("HeadTruncateFile: problems deallocating %s (%d)\n",
1651 FTOC(fcb
)->c_desc
.cd_nameptr
? FTOC(fcb
)->c_desc
.cd_nameptr
: "", error
);
1655 blksfreed
+= blkcnt
;
1656 fcb
->fcbExtents
[i
].startBlock
= 0;
1657 fcb
->fcbExtents
[i
].blockCount
= 0;
1659 tailExtents
[j
].startBlock
= fcb
->fcbExtents
[i
].startBlock
;
1660 tailExtents
[j
].blockCount
= blkcnt
;
1670 * Process overflow extents
1675 error
= FindExtentRecord(vcb
, forkType
, fileID
, startblk
, false, NULL
, extents
, NULL
);
1678 * Any errors after the first BlockDeallocate
1679 * must be ignored so we can put the file in
1682 if (error
!= btNotFound
)
1683 printf("HeadTruncateFile: problems finding extents %s (%d)\n",
1684 FTOC(fcb
)->c_desc
.cd_nameptr
? FTOC(fcb
)->c_desc
.cd_nameptr
: "", error
);
1689 for(i
= 0, extblks
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
1690 blkcnt
= extents
[i
].blockCount
;
1692 break; /* end of extents */
1694 if (blksfreed
< headblks
) {
1695 error
= BlockDeallocate(vcb
, extents
[i
].startBlock
, blkcnt
);
1697 printf("HeadTruncateFile: problems deallocating %s (%d)\n",
1698 FTOC(fcb
)->c_desc
.cd_nameptr
? FTOC(fcb
)->c_desc
.cd_nameptr
: "", error
);
1701 blksfreed
+= blkcnt
;
1703 tailExtents
[j
].startBlock
= extents
[i
].startBlock
;
1704 tailExtents
[j
].blockCount
= blkcnt
;
1710 error
= DeleteExtentRecord(vcb
, forkType
, fileID
, startblk
);
1712 printf("HeadTruncateFile: problems deallocating %s (%d)\n",
1713 FTOC(fcb
)->c_desc
.cd_nameptr
? FTOC(fcb
)->c_desc
.cd_nameptr
: "", error
);
1718 break; /* all done */
1720 startblk
+= extblks
;
1725 bcopy(tailExtents
, fcb
->fcbExtents
, sizeof(tailExtents
));
1726 blkcnt
= fcb
->ff_blocks
- headblks
;
1727 FTOC(fcb
)->c_blocks
-= blkcnt
;
1728 fcb
->ff_blocks
= blkcnt
;
1730 FTOC(fcb
)->c_flag
|= C_CHANGE
| C_FORCEUPDATE
;
1732 (void) FlushExtentFile(vcb
);
1736 return MacToVFSError(error
);
1741 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1742 // Routine: SearchExtentRecord (was XRSearch)
1744 // Function: Searches extent record for the extent mapping a given file
1745 // allocation block number (FABN).
1747 // Input: searchFABN - desired FABN
1748 // extentData - pointer to extent data record (xdr)
1749 // extentDataStartFABN - beginning FABN for extent record
1751 // Output: foundExtentDataOffset - offset to extent entry within xdr
1752 // result = noErr, offset to extent mapping desired FABN
1753 // result = FXRangeErr, offset to last extent in record
1754 // endingFABNPlusOne - ending FABN +1
1755 // noMoreExtents - True if the extent was not found, and the
1756 // extent record was not full (so don't bother
1757 // looking in subsequent records); false otherwise.
1759 // Result: noErr = ok
1760 // FXRangeErr = desired FABN > last mapped FABN in record
1761 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1763 static OSErr
SearchExtentRecord(
1764 const ExtendedVCB
*vcb
,
1766 const HFSPlusExtentRecord extentData
,
1767 UInt32 extentDataStartFABN
,
1768 UInt32
*foundExtentIndex
,
1769 UInt32
*endingFABNPlusOne
,
1770 Boolean
*noMoreExtents
)
1774 UInt32 numberOfExtents
;
1775 UInt32 numAllocationBlocks
;
1776 Boolean foundExtent
;
1778 *endingFABNPlusOne
= extentDataStartFABN
;
1779 *noMoreExtents
= false;
1780 foundExtent
= false;
1782 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1783 numberOfExtents
= kHFSPlusExtentDensity
;
1785 numberOfExtents
= kHFSExtentDensity
;
1787 for( extentIndex
= 0; extentIndex
< numberOfExtents
; ++extentIndex
)
1790 // Loop over the extent record and find the search FABN.
1792 numAllocationBlocks
= extentData
[extentIndex
].blockCount
;
1793 if ( numAllocationBlocks
== 0 )
1798 *endingFABNPlusOne
+= numAllocationBlocks
;
1800 if( searchFABN
< *endingFABNPlusOne
)
1802 // Found the extent.
1810 // Found the extent. Note the extent offset
1811 *foundExtentIndex
= extentIndex
;
1815 // Did not find the extent. Set foundExtentDataOffset accordingly
1816 if( extentIndex
> 0 )
1818 *foundExtentIndex
= extentIndex
- 1;
1822 *foundExtentIndex
= 0;
1825 // If we found an empty extent, then set noMoreExtents.
1826 if (extentIndex
< numberOfExtents
)
1827 *noMoreExtents
= true;
1829 // Finally, return an error to the caller
1836 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1837 // Routine: SearchExtentFile (was XFSearch)
1839 // Function: Searches extent file (including the FCB resident extent record)
1840 // for the extent mapping a given file position.
1842 // Input: vcb - VCB pointer
1843 // fcb - FCB pointer
1844 // filePosition - file position (byte address)
1846 // Output: foundExtentKey - extent key record (xkr)
1847 // If extent was found in the FCB's resident extent record,
1848 // then foundExtentKey->keyLength will be set to 0.
1849 // foundExtentData - extent data record(xdr)
1850 // foundExtentIndex - index to extent entry in xdr
1851 // result = 0, offset to extent mapping desired FABN
1852 // result = FXRangeErr, offset to last extent in record
1853 // (i.e., kNumExtentsPerRecord-1)
1854 // extentBTreeHint - BTree hint for extent record
1855 // kNoHint = Resident extent record
1856 // endingFABNPlusOne - ending FABN +1
1859 // noErr Found an extent that contains the given file position
1860 // FXRangeErr Given position is beyond the last allocated extent
1861 // (other) (some other internal I/O error)
1862 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1864 static OSErr
SearchExtentFile(
1865 const ExtendedVCB
*vcb
,
1867 SInt64 filePosition
,
1868 HFSPlusExtentKey
*foundExtentKey
,
1869 HFSPlusExtentRecord foundExtentData
,
1870 UInt32
*foundExtentIndex
,
1871 UInt32
*extentBTreeHint
,
1872 UInt32
*endingFABNPlusOne
)
1875 UInt32 filePositionBlock
;
1877 Boolean noMoreExtents
;
1879 temp64
= filePosition
/ (SInt64
)vcb
->blockSize
;
1880 filePositionBlock
= (UInt32
)temp64
;
1882 bcopy ( fcb
->fcbExtents
, foundExtentData
, sizeof(HFSPlusExtentRecord
));
1884 // Search the resident FCB first.
1885 err
= SearchExtentRecord( vcb
, filePositionBlock
, foundExtentData
, 0,
1886 foundExtentIndex
, endingFABNPlusOne
, &noMoreExtents
);
1888 if( err
== noErr
) {
1889 // Found the extent. Set results accordingly
1890 *extentBTreeHint
= kNoHint
; // no hint, because not in the BTree
1891 foundExtentKey
->keyLength
= 0; // 0 = the FCB itself
1896 // Didn't find extent in FCB. If FCB's extent record wasn't full, there's no point
1897 // in searching the extents file. Note that SearchExtentRecord left us pointing at
1898 // the last valid extent (or the first one, if none were valid). This means we need
1899 // to fill in the hint and key outputs, just like the "if" statement above.
1900 if ( noMoreExtents
) {
1901 *extentBTreeHint
= kNoHint
; // no hint, because not in the BTree
1902 foundExtentKey
->keyLength
= 0; // 0 = the FCB itself
1903 err
= fxRangeErr
; // There are no more extents, so must be beyond PEOF
1908 // Find the desired record, or the previous record if it is the same fork
1910 err
= FindExtentRecord(vcb
, FORK_IS_RSRC(fcb
) ? kResourceForkType
: kDataForkType
,
1911 FTOC(fcb
)->c_fileid
, filePositionBlock
, true, foundExtentKey
, foundExtentData
, extentBTreeHint
);
1913 if (err
== btNotFound
) {
1915 // If we get here, the desired position is beyond the extents in the FCB, and there are no extents
1916 // in the extents file. Return the FCB's extents and a range error.
1918 *extentBTreeHint
= kNoHint
;
1919 foundExtentKey
->keyLength
= 0;
1920 err
= GetFCBExtentRecord(fcb
, foundExtentData
);
1921 // Note: foundExtentIndex and endingFABNPlusOne have already been set as a result of the very
1922 // first SearchExtentRecord call in this function (when searching in the FCB's extents, and
1923 // we got a range error).
1929 // If we get here, there was either a BTree error, or we found an appropriate record.
1930 // If we found a record, then search it for the correct index into the extents.
1933 // Find appropriate index into extent record
1934 err
= SearchExtentRecord(vcb
, filePositionBlock
, foundExtentData
, foundExtentKey
->startBlock
,
1935 foundExtentIndex
, endingFABNPlusOne
, &noMoreExtents
);
1944 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1945 // Routine: UpdateExtentRecord
1947 // Function: Write new extent data to an existing extent record with a given key.
1948 // If all of the extents are empty, and the extent record is in the
1949 // extents file, then the record is deleted.
1951 // Input: vcb - the volume containing the extents
1952 // fcb - the file that owns the extents
1953 // extentFileKey - pointer to extent key record (xkr)
1954 // If the key length is 0, then the extents are actually part
1955 // of the catalog record, stored in the FCB.
1956 // extentData - pointer to extent data record (xdr)
1957 // extentBTreeHint - hint for given key, or kNoHint
1959 // Result: noErr = ok
1960 // (other) = error from BTree
1961 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1963 static OSErr
UpdateExtentRecord (
1964 const ExtendedVCB
*vcb
,
1966 const HFSPlusExtentKey
*extentFileKey
,
1967 const HFSPlusExtentRecord extentData
,
1968 UInt32 extentBTreeHint
)
1972 if (extentFileKey
->keyLength
== 0) { // keyLength == 0 means the FCB's extent record
1973 BlockMoveData(extentData
, fcb
->fcbExtents
, sizeof(HFSPlusExtentRecord
));
1974 FTOC(fcb
)->c_flag
|= C_MODIFIED
;
1977 BTreeIterator
* btIterator
;
1978 FSBufferDescriptor btRecord
;
1979 UInt16 btRecordSize
;
1983 // Need to find and change a record in Extents BTree
1985 btFCB
= GetFileControlBlock(vcb
->extentsRefNum
);
1987 MALLOC(btIterator
, BTreeIterator
*, sizeof(*btIterator
), M_TEMP
, M_WAITOK
);
1988 bzero(btIterator
, sizeof(*btIterator
));
1990 if (vcb
->vcbSigWord
== kHFSSigWord
) {
1991 HFSExtentKey
* key
; // Actual extent key used on disk in HFS
1992 HFSExtentRecord foundData
; // The extent data actually found
1994 key
= (HFSExtentKey
*) &btIterator
->key
;
1995 key
->keyLength
= kHFSExtentKeyMaximumLength
;
1996 key
->forkType
= extentFileKey
->forkType
;
1997 key
->fileID
= extentFileKey
->fileID
;
1998 key
->startBlock
= extentFileKey
->startBlock
;
2000 btIterator
->hint
.index
= 0;
2001 btIterator
->hint
.nodeNum
= extentBTreeHint
;
2003 btRecord
.bufferAddress
= &foundData
;
2004 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
2005 btRecord
.itemCount
= 1;
2007 err
= BTSearchRecord(btFCB
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
2010 err
= HFSPlusToHFSExtents(extentData
, (HFSExtentDescriptor
*)&foundData
);
2013 err
= BTReplaceRecord(btFCB
, btIterator
, &btRecord
, btRecordSize
);
2014 (void) BTFlushPath(btFCB
);
2016 else { // HFS Plus volume
2017 HFSPlusExtentRecord foundData
; // The extent data actually found
2019 BlockMoveData(extentFileKey
, &btIterator
->key
, sizeof(HFSPlusExtentKey
));
2021 btIterator
->hint
.index
= 0;
2022 btIterator
->hint
.nodeNum
= extentBTreeHint
;
2024 btRecord
.bufferAddress
= &foundData
;
2025 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
2026 btRecord
.itemCount
= 1;
2028 err
= BTSearchRecord(btFCB
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
2031 BlockMoveData(extentData
, &foundData
, sizeof(HFSPlusExtentRecord
));
2032 err
= BTReplaceRecord(btFCB
, btIterator
, &btRecord
, btRecordSize
);
2034 (void) BTFlushPath(btFCB
);
2036 FREE(btIterator
, M_TEMP
);
2045 static OSErr
HFSPlusToHFSExtents(
2046 const HFSPlusExtentRecord oldExtents
,
2047 HFSExtentRecord newExtents
)
2053 // copy the first 3 extents
2054 newExtents
[0].startBlock
= oldExtents
[0].startBlock
;
2055 newExtents
[0].blockCount
= oldExtents
[0].blockCount
;
2056 newExtents
[1].startBlock
= oldExtents
[1].startBlock
;
2057 newExtents
[1].blockCount
= oldExtents
[1].blockCount
;
2058 newExtents
[2].startBlock
= oldExtents
[2].startBlock
;
2059 newExtents
[2].blockCount
= oldExtents
[2].blockCount
;
2062 if (oldExtents
[3].startBlock
|| oldExtents
[3].blockCount
) {
2063 DebugStr("\pExtentRecord with > 3 extents is invalid for HFS");
2074 static OSErr
GetFCBExtentRecord(
2076 HFSPlusExtentRecord extents
)
2079 BlockMoveData(fcb
->fcbExtents
, extents
, sizeof(HFSPlusExtentRecord
));
2085 //_________________________________________________________________________________
2087 // Routine: ExtentsAreIntegral
2089 // Purpose: Ensure that each extent can hold an integral number of nodes
2090 // Called by the NodesAreContiguous function
2091 //_________________________________________________________________________________
2093 static Boolean
ExtentsAreIntegral(
2094 const HFSPlusExtentRecord extentRecord
,
2096 UInt32
*blocksChecked
,
2097 Boolean
*checkedLastExtent
)
2103 *checkedLastExtent
= false;
2105 for(extentIndex
= 0; extentIndex
< kHFSPlusExtentDensity
; extentIndex
++)
2107 blocks
= extentRecord
[extentIndex
].blockCount
;
2111 *checkedLastExtent
= true;
2115 *blocksChecked
+= blocks
;
2125 //_________________________________________________________________________________
2127 // Routine: NodesAreContiguous
2129 // Purpose: Ensure that all b-tree nodes are contiguous on disk
2130 // Called by BTOpenPath during volume mount
2131 //_________________________________________________________________________________
2134 Boolean
NodesAreContiguous(
2141 UInt32 blocksChecked
;
2143 HFSPlusExtentKey key
;
2144 HFSPlusExtentRecord extents
;
2146 Boolean lastExtentReached
;
2149 if (vcb
->blockSize
>= nodeSize
)
2152 mask
= (nodeSize
/ vcb
->blockSize
) - 1;
2154 // check the local extents
2155 (void) GetFCBExtentRecord(fcb
, extents
);
2156 if ( !ExtentsAreIntegral(extents
, mask
, &blocksChecked
, &lastExtentReached
) )
2159 if (lastExtentReached
|| (SInt64
)((SInt64
)blocksChecked
* (SInt64
)vcb
->blockSize
) >= fcb
->ff_size
)
2162 startBlock
= blocksChecked
;
2164 // check the overflow extents (if any)
2165 while ( !lastExtentReached
)
2167 result
= FindExtentRecord(vcb
, kDataForkType
, fcb
->ff_cp
->c_fileid
, startBlock
, FALSE
, &key
, extents
, &hint
);
2170 if ( !ExtentsAreIntegral(extents
, mask
, &blocksChecked
, &lastExtentReached
) )
2173 startBlock
+= blocksChecked
;