2 * Copyright (c) 1999-2002, 2005, 2008 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 Contains: Routines to map file positions to volume positions, and manipulate the extents B-Tree.
30 Written by: Dave Heller, Mark Day
32 Copyright: © 1996-1999 by Apple Computer, Inc., all rights reserved.
37 #include "Scavenger.h"
40 ============================================================
41 Public (Exported) Routines:
42 ============================================================
43 DeallocateFile Deallocate all disk space allocated to a specified file.
44 Both forks are deallocated.
46 ExtendFileC Allocate more space to a given file.
49 MapFileBlockC Convert (map) an offset within a given file into a
50 physical disk address.
52 TruncateFileC Truncates the disk space allocated to a file. The file
53 space is truncated to a specified new physical EOF, rounded
54 up to the next allocation block boundry. There is an option
55 to truncate to the end of the extent containing the new EOF.
58 Flush the extents file for a given volume.
61 Copy EOF, physical length, and extent records from one FCB
62 to all other FCBs for that fork. This is used when a file is
63 grown or shrunk as the result of a Write, SetEOF, or Allocate.
66 Map some position in a file to a volume block number. Also
67 returns the number of contiguous bytes that are mapped there.
68 This is a queued HFSDispatch call that does the equivalent of
69 MapFileBlockC, using a parameter block.
72 If the extent record came from the extents file, write out
73 the updated record; otherwise, copy the updated record into
74 the FCB resident extent record. If the record has no extents,
75 and was in the extents file, then delete the record instead.
78 Deallocate all allocation blocks in all extents of an extent
80 ============================================================
82 ============================================================
84 Search the extents BTree for a particular extent record.
86 Search the FCB and extents file for an extent record that
87 contains a given file position (in bytes).
89 Search a given extent record to see if it contains a given
90 file position (in bytes). Used by SearchExtentFile.
92 Deallocate blocks and delete extent records for all allocation
93 blocks beyond a certain point in a file. The starting point
94 must be the first file allocation block for some extent record
97 Deallocate all allocation blocks belonging to a given fork.
102 kTwoGigSectors
= 0x00400000,
105 kResourceForkType
= 0xFF,
107 kPreviousRecord
= -1,
109 kSectorSize
= 512 // Size of a physical sector
112 static OSErr
ExtentsToExtDataRec(
113 HFSPlusExtentRecord oldExtents
,
114 HFSExtentRecord newExtents
);
116 OSErr
FindExtentRecord(
121 Boolean allowPrevious
,
122 HFSPlusExtentKey
*foundKey
,
123 HFSPlusExtentRecord foundData
,
126 OSErr
DeleteExtentRecord(
132 static OSErr
CreateExtentRecord(
134 HFSPlusExtentKey
*key
,
135 HFSPlusExtentRecord extents
,
138 OSErr
GetFCBExtentRecord(
141 HFSPlusExtentRecord extents
);
143 static OSErr
SetFCBExtentRecord(
146 HFSPlusExtentRecord extents
);
148 static OSErr
SearchExtentFile(
152 HFSPlusExtentKey
*foundExtentKey
,
153 HFSPlusExtentRecord foundExtentData
,
154 UInt32
*foundExtentDataIndex
,
155 UInt32
*extentBTreeHint
,
156 UInt32
*endingFABNPlusOne
);
158 static OSErr
SearchExtentRecord(
161 const HFSPlusExtentRecord extentData
,
162 UInt32 extentDataStartFABN
,
163 UInt32
*foundExtentDataOffset
,
164 UInt32
*endingFABNPlusOne
,
165 Boolean
*noMoreExtents
);
168 static OSErr
DeallocateFork(
170 HFSCatalogNodeID fileID
,
172 HFSPlusExtentRecord catalogExtents
,
173 Boolean
* recordDeleted
);
175 static OSErr
TruncateExtents(
180 Boolean
* recordDeleted
);
183 static OSErr
MapFileBlockFromFCB(
186 UInt64 offset
, // Desired offset in bytes from start of file
187 UInt32
*firstFABN
, // FABN of first block of found extent
188 UInt32
*firstBlock
, // Corresponding allocation block number
189 UInt32
*nextFABN
); // FABN of block after end of extent
191 static Boolean
ExtentsAreIntegral(
192 const HFSPlusExtentRecord extentRecord
,
194 UInt32
*blocksChecked
,
195 Boolean
*checkedLastExtent
);
197 //_________________________________________________________________________________
199 // Routine: FindExtentRecord
201 // Purpose: Search the extents BTree for an extent record matching the given
202 // FileID, fork, and starting file allocation block number.
205 // vcb Volume to search
206 // forkType 0 = data fork, -1 = resource fork
207 // fileID File's FileID (HFSCatalogNodeID)
208 // startBlock Starting file allocation block number
209 // allowPrevious If the desired record isn't found and this flag is set,
210 // then see if the previous record belongs to the same fork.
211 // If so, then return it.
214 // foundKey The key data for the record actually found
215 // foundData The extent record actually found (NOTE: on an HFS volume, the
216 // fourth entry will be zeroes.
217 // foundHint The BTree hint to find the node again
218 //_________________________________________________________________________________
219 OSErr
FindExtentRecord(
224 Boolean allowPrevious
,
225 HFSPlusExtentKey
*foundKey
,
226 HFSPlusExtentRecord foundData
,
234 if (vcb
->vcbSignature
== kHFSSigWord
) {
236 HFSExtentKey extentKey
;
237 HFSExtentRecord extentData
;
239 key
.keyLength
= kHFSExtentKeyMaximumLength
;
240 key
.forkType
= forkType
;
242 key
.startBlock
= startBlock
;
244 err
= SearchBTreeRecord(vcb
->vcbExtentsFile
, &key
, kNoHint
, &extentKey
, &extentData
,
245 &foundSize
, foundHint
);
247 if (err
== btNotFound
&& allowPrevious
) {
248 err
= GetBTreeRecord(vcb
->vcbExtentsFile
, kPreviousRecord
, &extentKey
, &extentData
,
249 &foundSize
, foundHint
);
251 // A previous record may not exist, so just return btNotFound (like we would if
252 // it was for the wrong file/fork).
253 if (err
== (OSErr
) fsBTStartOfIterationErr
) //¥¥ fsBTStartOfIterationErr is type unsigned long
257 // Found a previous record. Does it belong to the same fork of the same file?
258 if (extentKey
.fileID
!= fileID
|| extentKey
.forkType
!= forkType
)
266 // Copy the found key back for the caller
267 foundKey
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
268 foundKey
->forkType
= extentKey
.forkType
;
270 foundKey
->fileID
= extentKey
.fileID
;
271 foundKey
->startBlock
= extentKey
.startBlock
;
273 // Copy the found data back for the caller
274 foundData
[0].startBlock
= extentData
[0].startBlock
;
275 foundData
[0].blockCount
= extentData
[0].blockCount
;
276 foundData
[1].startBlock
= extentData
[1].startBlock
;
277 foundData
[1].blockCount
= extentData
[1].blockCount
;
278 foundData
[2].startBlock
= extentData
[2].startBlock
;
279 foundData
[2].blockCount
= extentData
[2].blockCount
;
281 for (i
= 3; i
< kHFSPlusExtentDensity
; ++i
)
283 foundData
[i
].startBlock
= 0;
284 foundData
[i
].blockCount
= 0;
288 else { // HFS Plus volume
289 HFSPlusExtentKey key
;
290 HFSPlusExtentKey extentKey
;
291 HFSPlusExtentRecord extentData
;
293 key
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
294 key
.forkType
= forkType
;
297 key
.startBlock
= startBlock
;
299 err
= SearchBTreeRecord(vcb
->vcbExtentsFile
, &key
, kNoHint
, &extentKey
, &extentData
,
300 &foundSize
, foundHint
);
302 if (err
== btNotFound
&& allowPrevious
) {
303 err
= GetBTreeRecord(vcb
->vcbExtentsFile
, kPreviousRecord
, &extentKey
, &extentData
,
304 &foundSize
, foundHint
);
306 // A previous record may not exist, so just return btNotFound (like we would if
307 // it was for the wrong file/fork).
308 if (err
== (OSErr
) fsBTStartOfIterationErr
) //¥¥ fsBTStartOfIterationErr is type unsigned long
312 // Found a previous record. Does it belong to the same fork of the same file?
313 if (extentKey
.fileID
!= fileID
|| extentKey
.forkType
!= forkType
)
319 // Copy the found key back for the caller
320 CopyMemory(&extentKey
, foundKey
, sizeof(HFSPlusExtentKey
));
321 // Copy the found data back for the caller
322 CopyMemory(&extentData
, foundData
, sizeof(HFSPlusExtentRecord
));
331 static OSErr
CreateExtentRecord(
333 HFSPlusExtentKey
*key
,
334 HFSPlusExtentRecord extents
,
341 if (vcb
->vcbSignature
== kHFSSigWord
) {
343 HFSExtentRecord data
;
345 hfsKey
.keyLength
= kHFSExtentKeyMaximumLength
;
346 hfsKey
.forkType
= key
->forkType
;
347 hfsKey
.fileID
= key
->fileID
;
348 hfsKey
.startBlock
= key
->startBlock
;
350 err
= ExtentsToExtDataRec(extents
, data
);
352 err
= InsertBTreeRecord(vcb
->vcbExtentsFile
, &hfsKey
, data
, sizeof(HFSExtentRecord
), hint
);
354 else { // HFS Plus volume
355 err
= InsertBTreeRecord(vcb
->vcbExtentsFile
, key
, extents
, sizeof(HFSPlusExtentRecord
), hint
);
362 OSErr
DeleteExtentRecord(
372 if (vcb
->vcbSignature
== kHFSSigWord
) {
375 key
.keyLength
= kHFSExtentKeyMaximumLength
;
376 key
.forkType
= forkType
;
378 key
.startBlock
= startBlock
;
380 err
= DeleteBTreeRecord( vcb
->vcbExtentsFile
, &key
);
382 else { // HFS Plus volume
383 HFSPlusExtentKey key
;
385 key
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
386 key
.forkType
= forkType
;
389 key
.startBlock
= startBlock
;
391 err
= DeleteBTreeRecord( vcb
->vcbExtentsFile
, &key
);
399 //_________________________________________________________________________________
401 // Routine: MapFileBlock
403 // Function: Maps a file position into a physical disk address.
405 // Input: A2.L - VCB pointer
406 // (A1,D1.W) - FCB pointer
407 // D4.L - number of bytes desired
408 // D5.L - file position (byte address)
410 // Output: D3.L - physical start block
411 // D6.L - number of contiguous bytes available (up to D4 bytes)
412 // D0.L - result code <01Oct85>
414 // FXRangeErr = file position beyond mapped range <17Oct85>
415 // FXOvFlErr = extents file overflow <17Oct85>
416 // other = error <17Oct85>
418 // Called By: Log2Phys (read/write in place), Cache (map a file block).
419 //_________________________________________________________________________________
421 OSErr
MapFileBlockC (
422 SVCB
*vcb
, // volume that file resides on
423 SFCB
*fcb
, // FCB of file
424 UInt32 numberOfBytes
, // number of contiguous bytes desired
425 UInt64 sectorOffset
, // starting offset within file (in 512-byte sectors)
426 UInt64
*startSector
, // first 512-byte volume sector (NOT an allocation block)
427 UInt32
*availableBytes
) // number of contiguous bytes (up to numberOfBytes)
430 UInt32 allocBlockSize
; // Size of the volume's allocation block, in sectors
431 HFSPlusExtentKey foundKey
;
432 HFSPlusExtentRecord foundData
;
435 UInt32 firstFABN
= 0; // file allocation block of first block in found extent
436 UInt32 nextFABN
; // file allocation block of block after end of found extent
437 UInt64 dataEnd
; // (offset) end of range that is contiguous (in sectors)
438 UInt32 startBlock
= 0; // volume allocation block corresponding to firstFABN
442 // LogStartTime(kTraceMapFileBlock);
444 allocBlockSize
= vcb
->vcbBlockSize
>> kSectorShift
;
446 err
= MapFileBlockFromFCB(vcb
, fcb
, sectorOffset
, &firstFABN
, &startBlock
, &nextFABN
);
448 err
= SearchExtentFile(vcb
, fcb
, sectorOffset
, &foundKey
, foundData
, &foundIndex
, &hint
, &nextFABN
);
450 startBlock
= foundData
[foundIndex
].startBlock
;
451 firstFABN
= nextFABN
- foundData
[foundIndex
].blockCount
;
457 // LogEndTime(kTraceMapFileBlock, err);
463 // Determine the end of the available space. It will either be the end of the extent,
464 // or the file's PEOF, whichever is smaller.
467 // Get fork's physical size, in sectors
468 temp
= fcb
->fcbPhysicalSize
>> kSectorShift
;
469 dataEnd
= (UInt64
) nextFABN
* allocBlockSize
; // Assume valid data through end of this extent
470 if (temp
< dataEnd
) // Is PEOF shorter?
471 dataEnd
= temp
; // Yes, so only map up to PEOF
474 // Compute the absolute sector number that contains the offset of the given file
476 temp
= sectorOffset
- ((UInt64
) firstFABN
* allocBlockSize
); // offset in sectors from start of this extent
477 temp
+= (UInt64
)startBlock
* (UInt64
)allocBlockSize
; // offset in sectors from start of allocation block space
478 if (vcb
->vcbSignature
== kHFSPlusSigWord
)
479 temp
+= vcb
->vcbEmbeddedOffset
/512; // offset into the wrapper
481 temp
+= vcb
->vcbAlBlSt
; // offset in sectors from start of volume
483 // Return the desired sector for file position "offset"
487 // Determine the number of contiguous sectors until the end of the extent
488 // (or the amount they asked for, whichever comes first). In any case,
489 // we never map more than 2GB per call.
491 temp
= dataEnd
- sectorOffset
;
492 if (temp
>= kTwoGigSectors
)
493 temp
= kTwoGigSectors
-1; // never map more than 2GB per call
494 temp
<<= kSectorShift
; // convert sectors to bytes
495 if (temp
> numberOfBytes
)
496 *availableBytes
= numberOfBytes
; // more there than they asked for, so pin the output
498 *availableBytes
= temp
;
500 // LogEndTime(kTraceMapFileBlock, noErr);
506 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
507 // Routine: ReleaseExtents
509 // Function: Release the extents of a single extent data record.
510 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
513 OSErr
ReleaseExtents(
515 const HFSPlusExtentRecord extentRecord
,
516 UInt32
*numReleasedAllocationBlocks
,
517 Boolean
*releasedLastExtent
)
520 UInt32 numberOfExtents
;
523 *numReleasedAllocationBlocks
= 0;
524 *releasedLastExtent
= false;
526 if (vcb
->vcbSignature
== kHFSPlusSigWord
)
527 numberOfExtents
= kHFSPlusExtentDensity
;
529 numberOfExtents
= kHFSExtentDensity
;
531 for( extentIndex
= 0; extentIndex
< numberOfExtents
; extentIndex
++)
533 UInt32 numAllocationBlocks
;
535 // Loop over the extent record and release the blocks associated with each extent.
537 numAllocationBlocks
= extentRecord
[extentIndex
].blockCount
;
538 if ( numAllocationBlocks
== 0 )
540 *releasedLastExtent
= true;
544 err
= ReleaseBitmapBits( extentRecord
[extentIndex
].startBlock
, numAllocationBlocks
);
548 *numReleasedAllocationBlocks
+= numAllocationBlocks
; // bump FABN to beg of next extent
556 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
557 // Routine: TruncateExtents
559 // Purpose: Delete extent records whose starting file allocation block number
560 // is greater than or equal to a given starting block number. The
561 // allocation blocks represented by the extents are deallocated.
564 // vcb Volume to operate on
565 // fileID Which file to operate on
566 // startBlock Starting file allocation block number for first extent
570 // recordDeleted Set to true if any extents B-tree record was deleted.
571 // Unchanged otherwise.
572 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
574 static OSErr
TruncateExtents(
579 Boolean
* recordDeleted
)
582 Boolean releasedLastExtent
;
583 UInt32 numberExtentsReleased
;
585 HFSPlusExtentKey key
;
586 HFSPlusExtentRecord extents
;
589 err
= FindExtentRecord(vcb
, forkType
, fileID
, startBlock
, false, &key
, extents
, &hint
);
591 if (err
== btNotFound
)
596 err
= ReleaseExtents( vcb
, extents
, &numberExtentsReleased
, &releasedLastExtent
);
597 if (err
!= noErr
) break;
599 err
= DeleteExtentRecord(vcb
, forkType
, fileID
, startBlock
);
600 if (err
!= noErr
) break;
602 *recordDeleted
= true; // We did delete a record
603 startBlock
+= numberExtentsReleased
;
610 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
611 // Routine: DeallocateFork
613 // Function: De-allocates all disk space allocated to a specified fork.
614 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
615 static OSErr
DeallocateFork(
617 HFSCatalogNodeID fileID
,
619 HFSPlusExtentRecord catalogExtents
,
620 Boolean
* recordDeleted
) // set to true if any record was deleted
623 UInt32 numReleasedAllocationBlocks
;
624 Boolean releasedLastExtent
;
626 // Release the catalog extents
627 err
= ReleaseExtents( vcb
, catalogExtents
, &numReleasedAllocationBlocks
, &releasedLastExtent
);
629 // Release the extra extents, if present
630 if (err
== noErr
&& !releasedLastExtent
)
631 err
= TruncateExtents(vcb
, forkType
, fileID
, numReleasedAllocationBlocks
, recordDeleted
);
637 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
638 // Routine: FlushExtentFile
640 // Function: Flushes the extent file for a specified volume
641 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
643 OSErr
FlushExtentFile( SVCB
*vcb
)
647 err
= BTFlushPath(vcb
->vcbExtentsFile
);
650 // If the FCB for the extent "file" is dirty, mark the VCB as dirty.
652 if( ( vcb
->vcbExtentsFile
->fcbFlags
& fcbModifiedMask
) != 0 )
654 (void) MarkVCBDirty( vcb
);
655 err
= FlushVolumeControlBlock( vcb
);
663 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
664 // Routine: DeallocateFile
666 // Function: De-allocates all disk space allocated to a specified file.
667 // The space occupied by both forks is deallocated.
669 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
671 OSErr
DeallocateFile(SVCB
*vcb
, CatalogRecord
* fileRec
)
675 Boolean recordDeleted
= false;
679 if (fileRec
->recordType
== kHFSFileRecord
) {
680 HFSPlusExtentRecord dataForkExtents
;
681 HFSPlusExtentRecord rsrcForkExtents
;
683 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
684 dataForkExtents
[i
].startBlock
=
685 (UInt32
) (fileRec
->hfsFile
.dataExtents
[i
].startBlock
);
686 dataForkExtents
[i
].blockCount
=
687 (UInt32
) (fileRec
->hfsFile
.dataExtents
[i
].blockCount
);
689 rsrcForkExtents
[i
].startBlock
=
690 (UInt32
) (fileRec
->hfsFile
.rsrcExtents
[i
].startBlock
);
691 rsrcForkExtents
[i
].blockCount
=
692 (UInt32
) (fileRec
->hfsFile
.rsrcExtents
[i
].blockCount
);
694 ClearMemory(&dataForkExtents
[i
].startBlock
,
695 sizeof(HFSPlusExtentRecord
) - sizeof(HFSExtentRecord
));
697 ClearMemory(&rsrcForkExtents
[i
].startBlock
,
698 sizeof(HFSPlusExtentRecord
) - sizeof(HFSExtentRecord
));
700 errDF
= DeallocateFork(vcb
, fileRec
->hfsFile
.fileID
, kDataForkType
,
701 dataForkExtents
, &recordDeleted
);
703 errRF
= DeallocateFork(vcb
, fileRec
->hfsFile
.fileID
, kResourceForkType
,
704 rsrcForkExtents
, &recordDeleted
);
706 else if (fileRec
->recordType
== kHFSPlusFileRecord
) {
707 errDF
= DeallocateFork(vcb
, fileRec
->hfsPlusFile
.fileID
, kDataForkType
,
708 fileRec
->hfsPlusFile
.dataFork
.extents
, &recordDeleted
);
710 errRF
= DeallocateFork(vcb
, fileRec
->hfsPlusFile
.fileID
, kResourceForkType
,
711 fileRec
->hfsPlusFile
.resourceFork
.extents
, &recordDeleted
);
715 (void) FlushExtentFile(vcb
);
719 return (errDF
? errDF
: errRF
);
723 //_________________________________________________________________________________
725 // Routine: Extendfile
727 // Function: Extends the disk space allocated to a file.
729 // Input: A2.L - VCB pointer
730 // A1.L - pointer to FCB array
731 // D1.W - file refnum
732 // D3.B - option flags
733 // kEFContigMask - force contiguous allocation
734 // kEFAllMask - allocate all requested bytes or none
735 // NOTE: You may not set both options.
736 // D4.L - number of additional bytes to allocate
738 // Output: D0.W - result code
741 // D6.L - number of bytes allocated
743 // Called by: FileAloc,FileWrite,SetEof
745 // Note: ExtendFile updates the PEOF in the FCB.
746 //_________________________________________________________________________________
749 SVCB
*vcb
, // volume that file resides on
750 SFCB
*fcb
, // FCB of file to truncate
751 UInt32 sectorsToAdd
, // number of sectors to allocate
752 UInt32 flags
, // EFContig and/or EFAll
753 UInt32
*actualSectorsAdded
)// number of bytes actually allocated
758 UInt32 sectorsPerBlock
;
759 UInt32 blocksToAdd
; // number of blocks we'd like to add
760 UInt32 blocksPerClump
; // number of blocks in clump size
761 UInt32 maxBlocksToAdd
; // max blocks we want to add
762 UInt32 eofBlocks
; // current EOF in blocks
763 HFSPlusExtentKey foundKey
; // from SearchExtentFile
764 HFSPlusExtentRecord foundData
;
765 UInt32 foundIndex
; // from SearchExtentFile
766 UInt32 hint
; // from SearchExtentFile
767 UInt32 nextBlock
; // from SearchExtentFile
769 UInt32 actualStartBlock
;
770 UInt32 actualNumBlocks
;
771 UInt32 numExtentsPerRecord
;
774 needsFlush
= false; // Assume the B-tree header doesn't need to be updated
776 *actualSectorsAdded
= 0;
778 if (vcb
->vcbSignature
== kHFSPlusSigWord
)
779 numExtentsPerRecord
= kHFSPlusExtentDensity
;
781 numExtentsPerRecord
= kHFSExtentDensity
;
784 // Round up the request to whole allocation blocks
786 sectorsPerBlock
= vcb
->vcbBlockSize
>> kSectorShift
;
787 blocksToAdd
= DivideAndRoundUp(sectorsToAdd
, sectorsPerBlock
);
790 // Determine the physical EOF in allocation blocks
792 eofBlocks
= fcb
->fcbPhysicalSize
/ vcb
->vcbBlockSize
;
795 // Make sure the request won't make the file too big (>=2GB).
796 // [2350148] Always limit HFS files.
797 // ¥¥ Shouldn't really fail if allOrNothing is false
798 // ¥¥ Adjust for clump size here?
800 if ( vcb
->vcbSignature
== kHFSPlusSigWord
)
802 // Allow it to grow beyond 2GB.
806 UInt32 maxFileBlocks
; // max legal EOF, in blocks
807 maxFileBlocks
= (kTwoGigSectors
-1) / sectorsPerBlock
;
808 if (blocksToAdd
> maxFileBlocks
|| (blocksToAdd
+ eofBlocks
) > maxFileBlocks
) {
815 // If allocation is all-or-nothing, then make sure there
816 // are enough free blocks. (A quick test)
818 if ((flags
& kEFAllMask
) && blocksToAdd
> vcb
->vcbFreeBlocks
) {
824 // There may be blocks allocated beyond the physical EOF
825 // (because we allocated the rest of the clump size, or
826 // because of a PBAllocate or PBAllocContig call).
827 // If these extra blocks exist, then use them to satisfy
828 // part or all of the request.
830 // ¥¥ What, if anything, would break if the physical EOF always
831 // ¥¥ represented ALL extents allocated to the file (including
832 // ¥¥ the clump size roundup)?
834 // Note: (blocks * sectorsPerBlock - 1) is the sector offset
835 // of the last sector in the last block.
837 err
= SearchExtentFile(vcb
, fcb
, (eofBlocks
+blocksToAdd
) * sectorsPerBlock
- 1, &foundKey
, foundData
, &foundIndex
, &hint
, &nextBlock
);
839 // Enough blocks are already allocated. Just update the FCB to reflect the new length.
840 eofBlocks
+= blocksToAdd
; // new EOF, in blocks
841 blocksAdded
+= blocksToAdd
;
844 if (err
!= fxRangeErr
) // Any real error?
845 goto ErrorExit
; // Yes, so exit immediately
848 // There wasn't enough already allocated. But there might have been
849 // a few allocated blocks beyond the physical EOF. So, set the physical
850 // EOF to match the end of the last extent.
852 if (nextBlock
> eofBlocks
) {
853 // There were (nextBlock - eofBlocks) extra blocks past physical EOF
854 blocksAdded
+= nextBlock
- eofBlocks
;
855 blocksToAdd
-= nextBlock
- eofBlocks
;
856 eofBlocks
= nextBlock
;
860 // We still need to allocate more blocks.
862 // First try a contiguous allocation (of the whole amount).
863 // If that fails, get whatever we can.
864 // If forceContig, then take whatever we got
865 // else, keep getting bits and pieces (non-contig)
867 // ¥¥ Need to do clump size calculations
869 blocksPerClump
= fcb
->fcbClumpSize
/ vcb
->vcbBlockSize
;
870 if (blocksPerClump
== 0)
876 // Make maxBlocksToAdd equal to blocksToAdd rounded up to a multiple
877 // of the file's clump size. This gives the file room to grow some
878 // more without fragmenting.
879 if (flags
& kEFNoClumpMask
) {
880 // Caller said not to round up, so only allocate what was asked for.
881 maxBlocksToAdd
= blocksToAdd
;
884 // Round up to multiple of clump size
885 maxBlocksToAdd
= DivideAndRoundUp(blocksToAdd
, blocksPerClump
);
886 maxBlocksToAdd
*= blocksPerClump
;
889 // Try to allocate the new space contiguous with the end of the previous
890 // extent. If this succeeds, the last extent grows and the file does not
891 // become any more fragmented.
892 startBlock
= foundData
[foundIndex
].startBlock
+ foundData
[foundIndex
].blockCount
;
893 err
= BlockAllocate(vcb
, startBlock
, blocksToAdd
, maxBlocksToAdd
, wantContig
, &actualStartBlock
, &actualNumBlocks
);
894 if (err
== dskFulErr
) {
895 if (flags
& kEFContigMask
)
896 break; // AllocContig failed because not enough contiguous space
898 // Couldn't get one big chunk, so get whatever we can.
903 if (actualNumBlocks
!= 0)
907 // Add the new extent to the existing extent record, or create a new one.
908 if (actualStartBlock
== startBlock
) {
909 // We grew the file's last extent, so just adjust the number of blocks.
910 foundData
[foundIndex
].blockCount
+= actualNumBlocks
;
911 err
= UpdateExtentRecord(vcb
, fcb
, &foundKey
, foundData
, hint
);
912 if (err
!= noErr
) break;
917 // Need to add a new extent. See if there is room in the current record.
918 if (foundData
[foundIndex
].blockCount
!= 0) // Is current extent free to use?
919 ++foundIndex
; // No, so use the next one.
920 if (foundIndex
== numExtentsPerRecord
) {
921 // This record is full. Need to create a new one.
922 if (fcb
->fcbFileID
== kHFSExtentsFileID
|| (flags
& kEFNoExtOvflwMask
)) {
923 (void) BlockDeallocate(vcb
, actualStartBlock
, actualNumBlocks
);
924 err
= fxOvFlErr
; // Oops. Can't extend extents file past first record.
928 foundKey
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
929 if (fcb
->fcbFlags
& fcbResourceMask
)
930 foundKey
.forkType
= kResourceForkType
;
932 foundKey
.forkType
= kDataForkType
;
934 foundKey
.fileID
= fcb
->fcbFileID
;
935 foundKey
.startBlock
= nextBlock
;
937 foundData
[0].startBlock
= actualStartBlock
;
938 foundData
[0].blockCount
= actualNumBlocks
;
940 // zero out remaining extents...
941 for (i
= 1; i
< kHFSPlusExtentDensity
; ++i
)
943 foundData
[i
].startBlock
= 0;
944 foundData
[i
].blockCount
= 0;
949 err
= CreateExtentRecord(vcb
, &foundKey
, foundData
, &hint
);
950 if (err
== fxOvFlErr
) {
951 // We couldn't create an extent record because extents B-tree
952 // couldn't grow. Dellocate the extent just allocated and
953 // return a disk full error.
954 (void) BlockDeallocate(vcb
, actualStartBlock
, actualNumBlocks
);
957 if (err
!= noErr
) break;
959 needsFlush
= true; // We need to update the B-tree header
962 // Add a new extent into this record and update.
963 foundData
[foundIndex
].startBlock
= actualStartBlock
;
964 foundData
[foundIndex
].blockCount
= actualNumBlocks
;
965 err
= UpdateExtentRecord(vcb
, fcb
, &foundKey
, foundData
, hint
);
966 if (err
!= noErr
) break;
970 // Figure out how many bytes were actually allocated.
971 // NOTE: BlockAllocate could have allocated more than the minimum
972 // we asked for (up to our requested maximum).
973 // Don't set the PEOF beyond what our client asked for.
974 nextBlock
+= actualNumBlocks
;
975 if (actualNumBlocks
> blocksToAdd
) {
976 blocksAdded
+= blocksToAdd
;
977 eofBlocks
+= blocksToAdd
;
981 blocksAdded
+= actualNumBlocks
;
982 blocksToAdd
-= actualNumBlocks
;
983 eofBlocks
+= actualNumBlocks
;
986 // If contiguous allocation was requested, then we've already got one contiguous
987 // chunk. If we didn't get all we wanted, then adjust the error to disk full.
988 if (flags
& kEFContigMask
) {
989 if (blocksToAdd
!= 0)
991 break; // We've already got everything that's contiguous
994 } while (err
== noErr
&& blocksToAdd
);
998 *actualSectorsAdded
= blocksAdded
* sectorsPerBlock
;
1000 fcb
->fcbPhysicalSize
= (UInt64
)eofBlocks
* (UInt64
)vcb
->vcbBlockSize
;
1001 fcb
->fcbFlags
|= fcbModifiedMask
;
1004 // [2355121] If we created a new extent record, then update the B-tree header
1006 (void) FlushExtentFile(vcb
);
1013 //_________________________________________________________________________________
1015 // Routine: TruncateFileC
1017 // Function: Truncates the disk space allocated to a file. The file space is
1018 // truncated to a specified new PEOF rounded up to the next allocation
1019 // block boundry. If the 'TFTrunExt' option is specified, the file is
1020 // truncated to the end of the extent containing the new PEOF.
1022 // Input: A2.L - VCB pointer
1023 // A1.L - pointer to FCB array
1024 // D1.W - file refnum
1025 // D2.B - option flags
1026 // TFTrunExt - truncate to the extent containing new PEOF
1029 // Output: D0.W - result code
1033 // Note: TruncateFile updates the PEOF in the FCB.
1034 //_________________________________________________________________________________
1037 OSErr
TruncateFileC (
1038 SVCB
*vcb
, // volume that file resides on
1039 SFCB
*fcb
, // FCB of file to truncate
1040 UInt32 eofSectors
, // new physical size for file
1041 Boolean truncateToExtent
) // if true, truncate to end of extent containing newPEOF
1044 UInt32 nextBlock
; // next file allocation block to consider
1045 UInt32 startBlock
; // Physical (volume) allocation block number of start of a range
1046 UInt32 physNumBlocks
; // Number of allocation blocks in file (according to PEOF)
1048 HFSPlusExtentKey key
; // key for current extent record; key->keyLength == 0 if FCB's extent record
1049 UInt32 hint
; // BTree hint corresponding to key
1050 HFSPlusExtentRecord extentRecord
;
1052 UInt32 extentNextBlock
;
1053 UInt32 numExtentsPerRecord
;
1054 UInt32 sectorsPerBlock
;
1056 Boolean extentChanged
; // true if we actually changed an extent
1057 Boolean recordDeleted
; // true if an extent record got deleted
1059 recordDeleted
= false;
1060 sectorsPerBlock
= vcb
->vcbBlockSize
>> kSectorShift
;
1062 if (vcb
->vcbSignature
== kHFSPlusSigWord
)
1063 numExtentsPerRecord
= kHFSPlusExtentDensity
;
1065 numExtentsPerRecord
= kHFSExtentDensity
;
1067 if (fcb
->fcbFlags
& fcbResourceMask
)
1068 forkType
= kResourceForkType
;
1070 forkType
= kDataForkType
;
1072 // Compute number of allocation blocks currently in file
1073 physNumBlocks
= fcb
->fcbPhysicalSize
/ vcb
->vcbBlockSize
;
1076 // Round newPEOF up to a multiple of the allocation block size. If new size is
1077 // two gigabytes or more, then round down by one allocation block (??? really?
1078 // shouldn't that be an error?).
1080 nextBlock
= DivideAndRoundUp(eofSectors
, sectorsPerBlock
); // number of allocation blocks to remain in file
1081 eofSectors
= nextBlock
* sectorsPerBlock
; // rounded up to multiple of block size
1082 if ((fcb
->fcbFlags
& fcbLargeFileMask
) == 0 && eofSectors
>= kTwoGigSectors
) {
1084 DebugStr("\pHFS: Trying to truncate a file to 2GB or more");
1086 err
= fileBoundsErr
;
1091 // Update FCB's length
1093 fcb
->fcbPhysicalSize
= (UInt64
)nextBlock
* (UInt64
)vcb
->vcbBlockSize
;
1094 fcb
->fcbFlags
|= fcbModifiedMask
;
1097 // If the new PEOF is 0, then truncateToExtent has no meaning (we should always deallocate
1100 if (eofSectors
== 0) {
1103 // Find the catalog extent record
1104 err
= GetFCBExtentRecord(vcb
, fcb
, extentRecord
);
1105 if (err
!= noErr
) goto ErrorExit
; // got some error, so return it
1107 // Deallocate all the extents for this fork
1108 err
= DeallocateFork(vcb
, fcb
->fcbFileID
, forkType
, extentRecord
, &recordDeleted
);
1109 if (err
!= noErr
) goto ErrorExit
; // got some error, so return it
1111 // Update the catalog extent record (making sure it's zeroed out)
1113 for (i
=0; i
< numExtentsPerRecord
; i
++) {
1114 extentRecord
[i
].startBlock
= 0;
1115 extentRecord
[i
].blockCount
= 0;
1118 err
= SetFCBExtentRecord((VCB
*) vcb
, fcb
, extentRecord
);
1123 // Find the extent containing byte (peof-1). This is the last extent we'll keep.
1124 // (If truncateToExtent is true, we'll keep the whole extent; otherwise, we'll only
1125 // keep up through peof). The search will tell us how many allocation blocks exist
1126 // in the found extent plus all previous extents.
1128 err
= SearchExtentFile(vcb
, fcb
, eofSectors
-1, &key
, extentRecord
, &extentIndex
, &hint
, &extentNextBlock
);
1129 if (err
!= noErr
) goto ErrorExit
;
1131 extentChanged
= false; // haven't changed the extent yet
1133 if (!truncateToExtent
) {
1135 // Shorten this extent. It may be the case that the entire extent gets
1138 numBlocks
= extentNextBlock
- nextBlock
; // How many blocks in this extent to free up
1139 if (numBlocks
!= 0) {
1140 // Compute first volume allocation block to free
1141 startBlock
= extentRecord
[extentIndex
].startBlock
+ extentRecord
[extentIndex
].blockCount
- numBlocks
;
1142 // Free the blocks in bitmap
1143 err
= BlockDeallocate(vcb
, startBlock
, numBlocks
);
1144 if (err
!= noErr
) goto ErrorExit
;
1145 // Adjust length of this extent
1146 extentRecord
[extentIndex
].blockCount
-= numBlocks
;
1147 // If extent is empty, set start block to 0
1148 if (extentRecord
[extentIndex
].blockCount
== 0)
1149 extentRecord
[extentIndex
].startBlock
= 0;
1150 // Remember that we changed the extent record
1151 extentChanged
= true;
1156 // Now move to the next extent in the record, and set up the file allocation block number
1158 nextBlock
= extentNextBlock
; // Next file allocation block to free
1159 ++extentIndex
; // Its index within the extent record
1162 // Release all following extents in this extent record. Update the record.
1164 while (extentIndex
< numExtentsPerRecord
&& extentRecord
[extentIndex
].blockCount
!= 0) {
1165 numBlocks
= extentRecord
[extentIndex
].blockCount
;
1166 // Deallocate this extent
1167 err
= BlockDeallocate(vcb
, extentRecord
[extentIndex
].startBlock
, numBlocks
);
1168 if (err
!= noErr
) goto ErrorExit
;
1169 // Update next file allocation block number
1170 nextBlock
+= numBlocks
;
1171 // Zero out start and length of this extent to delete it from record
1172 extentRecord
[extentIndex
].startBlock
= 0;
1173 extentRecord
[extentIndex
].blockCount
= 0;
1174 // Remember that we changed an extent
1175 extentChanged
= true;
1176 // Move to next extent in record
1181 // If any of the extents in the current record were changed, then update that
1182 // record (in the FCB, or extents file).
1184 if (extentChanged
) {
1185 err
= UpdateExtentRecord(vcb
, fcb
, &key
, extentRecord
, hint
);
1186 if (err
!= noErr
) goto ErrorExit
;
1190 // If there are any following allocation blocks, then we need
1191 // to seach for their extent records and delete those allocation
1194 if (nextBlock
< physNumBlocks
)
1195 err
= TruncateExtents(vcb
, forkType
, fcb
->fcbFileID
, nextBlock
, &recordDeleted
);
1201 if (err
== fxRangeErr
)
1202 DebugStr("\pAbout to return fxRangeErr");
1205 // [2355121] If we actually deleted extent records, then update the B-tree header
1207 (void) FlushExtentFile(vcb
);
1214 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1215 // Routine: SearchExtentRecord (was XRSearch)
1217 // Function: Searches extent record for the extent mapping a given file
1218 // allocation block number (FABN).
1220 // Input: searchFABN - desired FABN
1221 // extentData - pointer to extent data record (xdr)
1222 // extentDataStartFABN - beginning FABN for extent record
1224 // Output: foundExtentDataOffset - offset to extent entry within xdr
1225 // result = noErr, offset to extent mapping desired FABN
1226 // result = FXRangeErr, offset to last extent in record
1227 // endingFABNPlusOne - ending FABN +1
1228 // noMoreExtents - True if the extent was not found, and the
1229 // extent record was not full (so don't bother
1230 // looking in subsequent records); false otherwise.
1232 // Result: noErr = ok
1233 // FXRangeErr = desired FABN > last mapped FABN in record
1234 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1236 static OSErr
SearchExtentRecord(
1239 const HFSPlusExtentRecord extentData
,
1240 UInt32 extentDataStartFABN
,
1241 UInt32
*foundExtentIndex
,
1242 UInt32
*endingFABNPlusOne
,
1243 Boolean
*noMoreExtents
)
1247 UInt32 numberOfExtents
;
1248 UInt32 numAllocationBlocks
;
1249 Boolean foundExtent
;
1251 *endingFABNPlusOne
= extentDataStartFABN
;
1252 *noMoreExtents
= false;
1253 foundExtent
= false;
1255 if (vcb
->vcbSignature
== kHFSPlusSigWord
)
1256 numberOfExtents
= kHFSPlusExtentDensity
;
1258 numberOfExtents
= kHFSExtentDensity
;
1260 for( extentIndex
= 0; extentIndex
< numberOfExtents
; ++extentIndex
)
1263 // Loop over the extent record and find the search FABN.
1265 numAllocationBlocks
= extentData
[extentIndex
].blockCount
;
1266 if ( numAllocationBlocks
== 0 )
1271 *endingFABNPlusOne
+= numAllocationBlocks
;
1273 if( searchFABN
< *endingFABNPlusOne
)
1275 // Found the extent.
1283 // Found the extent. Note the extent offset
1284 *foundExtentIndex
= extentIndex
;
1288 // Did not find the extent. Set foundExtentDataOffset accordingly
1289 if( extentIndex
> 0 )
1291 *foundExtentIndex
= extentIndex
- 1;
1295 *foundExtentIndex
= 0;
1298 // If we found an empty extent, then set noMoreExtents.
1299 if (extentIndex
< numberOfExtents
)
1300 *noMoreExtents
= true;
1302 // Finally, return an error to the caller
1309 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1310 // Routine: SearchExtentFile (was XFSearch)
1312 // Function: Searches extent file (including the FCB resident extent record)
1313 // for the extent mapping a given file position.
1315 // Input: vcb - VCB pointer
1316 // fcb - FCB pointer
1317 // filePosition - file position (byte address)
1319 // Output: foundExtentKey - extent key record (xkr)
1320 // If extent was found in the FCB's resident extent record,
1321 // then foundExtentKey->keyLength will be set to 0.
1322 // foundExtentData - extent data record(xdr)
1323 // foundExtentIndex - index to extent entry in xdr
1324 // result = 0, offset to extent mapping desired FABN
1325 // result = FXRangeErr, offset to last extent in record
1326 // (i.e., kNumExtentsPerRecord-1)
1327 // extentBTreeHint - BTree hint for extent record
1328 // kNoHint = Resident extent record
1329 // endingFABNPlusOne - ending FABN +1
1332 // noErr Found an extent that contains the given file position
1333 // FXRangeErr Given position is beyond the last allocated extent
1334 // (other) (some other internal I/O error)
1335 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1337 static OSErr
SearchExtentFile(
1340 UInt64 sectorOffset
,
1341 HFSPlusExtentKey
*foundExtentKey
,
1342 HFSPlusExtentRecord foundExtentData
,
1343 UInt32
*foundExtentIndex
,
1344 UInt32
*extentBTreeHint
,
1345 UInt32
*endingFABNPlusOne
)
1348 UInt32 filePositionBlock
;
1349 Boolean noMoreExtents
= true;
1351 filePositionBlock
= sectorOffset
/ (vcb
->vcbBlockSize
>> kSectorShift
);
1353 // Search the resident FCB first.
1354 err
= GetFCBExtentRecord(vcb
, fcb
, foundExtentData
);
1356 err
= SearchExtentRecord( vcb
, filePositionBlock
, foundExtentData
, 0,
1357 foundExtentIndex
, endingFABNPlusOne
, &noMoreExtents
);
1359 if( err
== noErr
) {
1360 // Found the extent. Set results accordingly
1361 *extentBTreeHint
= kNoHint
; // no hint, because not in the BTree
1362 foundExtentKey
->keyLength
= 0; // 0 = the FCB itself
1367 // Didn't find extent in FCB. If FCB's extent record wasn't full, there's no point
1368 // in searching the extents file. Note that SearchExtentRecord left us pointing at
1369 // the last valid extent (or the first one, if none were valid). This means we need
1370 // to fill in the hint and key outputs, just like the "if" statement above.
1371 if ( noMoreExtents
) {
1372 *extentBTreeHint
= kNoHint
; // no hint, because not in the BTree
1373 foundExtentKey
->keyLength
= 0; // 0 = the FCB itself
1374 err
= fxRangeErr
; // There are no more extents, so must be beyond PEOF
1379 // Find the desired record, or the previous record if it is the same fork
1381 err
= FindExtentRecord(vcb
, (fcb
->fcbFlags
& fcbResourceMask
) ? kResourceForkType
: kDataForkType
,
1382 fcb
->fcbFileID
, filePositionBlock
, true, foundExtentKey
, foundExtentData
, extentBTreeHint
);
1384 if (err
== btNotFound
) {
1386 // If we get here, the desired position is beyond the extents in the FCB, and there are no extents
1387 // in the extents file. Return the FCB's extents and a range error.
1389 *extentBTreeHint
= kNoHint
;
1390 foundExtentKey
->keyLength
= 0;
1391 err
= GetFCBExtentRecord(vcb
, fcb
, foundExtentData
);
1392 // Note: foundExtentIndex and endingFABNPlusOne have already been set as a result of the very
1393 // first SearchExtentRecord call in this function (when searching in the FCB's extents, and
1394 // we got a range error).
1400 // If we get here, there was either a BTree error, or we found an appropriate record.
1401 // If we found a record, then search it for the correct index into the extents.
1404 // Find appropriate index into extent record
1405 err
= SearchExtentRecord(vcb
, filePositionBlock
, foundExtentData
, foundExtentKey
->startBlock
,
1406 foundExtentIndex
, endingFABNPlusOne
, &noMoreExtents
);
1415 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1416 // Routine: UpdateExtentRecord
1418 // Function: Write new extent data to an existing extent record with a given key.
1419 // If all of the extents are empty, and the extent record is in the
1420 // extents file, then the record is deleted.
1422 // Input: vcb - the volume containing the extents
1423 // fcb - the file that owns the extents
1424 // extentFileKey - pointer to extent key record (xkr)
1425 // If the key length is 0, then the extents are actually part
1426 // of the catalog record, stored in the FCB.
1427 // extentData - pointer to extent data record (xdr)
1428 // extentBTreeHint - hint for given key, or kNoHint
1430 // Result: noErr = ok
1431 // (other) = error from BTree
1432 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1434 OSErr
UpdateExtentRecord (
1437 const HFSPlusExtentKey
*extentFileKey
,
1438 HFSPlusExtentRecord extentData
,
1439 UInt32 extentBTreeHint
)
1443 UInt16 foundDataSize
;
1445 if (extentFileKey
->keyLength
== 0) { // keyLength == 0 means the FCB's extent record
1446 err
= SetFCBExtentRecord(vcb
, fcb
, extentData
);
1447 fcb
->fcbFlags
|= fcbModifiedMask
;
1451 // Need to find and change a record in Extents BTree
1453 if (vcb
->vcbSignature
== kHFSSigWord
) {
1454 HFSExtentKey key
; // Actual extent key used on disk in HFS
1455 HFSExtentKey foundKey
; // The key actually found during search
1456 HFSExtentRecord foundData
; // The extent data actually found
1458 key
.keyLength
= kHFSExtentKeyMaximumLength
;
1459 key
.forkType
= extentFileKey
->forkType
;
1460 key
.fileID
= extentFileKey
->fileID
;
1461 key
.startBlock
= extentFileKey
->startBlock
;
1463 err
= SearchBTreeRecord(vcb
->vcbExtentsFile
, &key
, extentBTreeHint
,
1464 &foundKey
, &foundData
, &foundDataSize
, &foundHint
);
1467 err
= ExtentsToExtDataRec(extentData
, (HFSExtentDescriptor
*)&foundData
);
1470 err
= ReplaceBTreeRecord(vcb
->vcbExtentsFile
, &foundKey
, foundHint
, &foundData
, foundDataSize
, &foundHint
);
1472 else { // HFS Plus volume
1473 HFSPlusExtentKey foundKey
; // The key actually found during search
1474 HFSPlusExtentRecord foundData
; // The extent data actually found
1476 err
= SearchBTreeRecord(vcb
->vcbExtentsFile
, extentFileKey
, extentBTreeHint
,
1477 &foundKey
, &foundData
, &foundDataSize
, &foundHint
);
1480 CopyMemory(extentData
, &foundData
, sizeof(HFSPlusExtentRecord
));
1483 err
= ReplaceBTreeRecord(vcb
->vcbExtentsFile
, &foundKey
, foundHint
, &foundData
, foundDataSize
, &foundHint
);
1491 void ExtDataRecToExtents(
1492 const HFSExtentRecord oldExtents
,
1493 HFSPlusExtentRecord newExtents
)
1497 // copy the first 3 extents
1498 newExtents
[0].startBlock
= oldExtents
[0].startBlock
;
1499 newExtents
[0].blockCount
= oldExtents
[0].blockCount
;
1500 newExtents
[1].startBlock
= oldExtents
[1].startBlock
;
1501 newExtents
[1].blockCount
= oldExtents
[1].blockCount
;
1502 newExtents
[2].startBlock
= oldExtents
[2].startBlock
;
1503 newExtents
[2].blockCount
= oldExtents
[2].blockCount
;
1505 // zero out the remaining ones
1506 for (i
= 3; i
< kHFSPlusExtentDensity
; ++i
)
1508 newExtents
[i
].startBlock
= 0;
1509 newExtents
[i
].blockCount
= 0;
1515 static OSErr
ExtentsToExtDataRec(
1516 HFSPlusExtentRecord oldExtents
,
1517 HFSExtentRecord newExtents
)
1523 // copy the first 3 extents
1524 newExtents
[0].startBlock
= oldExtents
[0].startBlock
;
1525 newExtents
[0].blockCount
= oldExtents
[0].blockCount
;
1526 newExtents
[1].startBlock
= oldExtents
[1].startBlock
;
1527 newExtents
[1].blockCount
= oldExtents
[1].blockCount
;
1528 newExtents
[2].startBlock
= oldExtents
[2].startBlock
;
1529 newExtents
[2].blockCount
= oldExtents
[2].blockCount
;
1532 if (oldExtents
[3].startBlock
|| oldExtents
[3].blockCount
) {
1533 DebugStr("\pExtentRecord with > 3 extents is invalid for HFS");
1542 OSErr
GetFCBExtentRecord(
1545 HFSPlusExtentRecord extents
)
1547 if (vcb
->vcbSignature
== kHFSPlusSigWord
)
1548 CopyMemory(fcb
->fcbExtents32
, extents
, sizeof(HFSPlusExtentRecord
));
1550 ExtDataRecToExtents(fcb
->fcbExtents16
, extents
);
1556 static OSErr
SetFCBExtentRecord(
1559 HFSPlusExtentRecord extents
)
1563 if (fcb
->fcbVolume
!= vcb
)
1564 DebugStr("\pVCB does not match FCB");
1567 if (vcb
->vcbSignature
== kHFSPlusSigWord
)
1568 CopyMemory(extents
, fcb
->fcbExtents32
, sizeof(HFSPlusExtentRecord
));
1570 (void) ExtentsToExtDataRec(extents
, fcb
->fcbExtents16
);
1577 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1578 // Routine: MapFileBlockFromFCB
1580 // Function: Determine if the given file offset is within the set of extents
1581 // stored in the FCB. If so, return the file allocation
1582 // block number of the start of the extent, volume allocation block number
1583 // of the start of the extent, and file allocation block number immediately
1584 // following the extent.
1586 // Input: vcb - the volume containing the extents
1587 // fcb - the file that owns the extents
1588 // offset - desired offset in 512-byte sectors
1590 // Output: firstFABN - file alloc block number of start of extent
1591 // firstBlock - volume alloc block number of start of extent
1592 // nextFABN - file alloc block number of next extent
1594 // Result: noErr = ok
1595 // fxRangeErr = beyond FCB's extents
1596 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1597 static OSErr
MapFileBlockFromFCB(
1600 UInt64 sectorOffset
, // Desired offset in sectors from start of file
1601 UInt32
*firstFABN
, // FABN of first block of found extent
1602 UInt32
*firstBlock
, // Corresponding allocation block number
1603 UInt32
*nextFABN
) // FABN of block after end of extent
1606 UInt32 offsetBlocks
;
1608 offsetBlocks
= sectorOffset
/ (vcb
->vcbBlockSize
>> kSectorShift
);
1610 if (vcb
->vcbSignature
== kHFSSigWord
) {
1611 const HFSExtentDescriptor
*extent
;
1615 extent
= fcb
->fcbExtents16
;
1618 for (index
=0; index
<kHFSExtentDensity
; index
++) {
1620 blockCount
= extent
->blockCount
;
1622 if (blockCount
== 0)
1623 return fxRangeErr
; // ran out of extents!
1625 // Is it in this extent?
1626 if (offsetBlocks
< blockCount
) {
1627 *firstFABN
= currentFABN
;
1628 *firstBlock
= extent
->startBlock
;
1629 currentFABN
+= blockCount
; // faster to add these as UInt16 first, then extend to UInt32
1630 *nextFABN
= currentFABN
;
1631 return noErr
; // found the right extent
1634 // Not in current extent, so adjust counters and loop again
1635 offsetBlocks
-= blockCount
;
1636 currentFABN
+= blockCount
;
1641 const HFSPlusExtentDescriptor
*extent
;
1645 extent
= fcb
->fcbExtents32
;
1648 for (index
=0; index
<kHFSPlusExtentDensity
; index
++) {
1650 blockCount
= extent
->blockCount
;
1652 if (blockCount
== 0)
1653 return fxRangeErr
; // ran out of extents!
1655 // Is it in this extent?
1656 if (offsetBlocks
< blockCount
) {
1657 *firstFABN
= currentFABN
;
1658 *firstBlock
= extent
->startBlock
;
1659 *nextFABN
= currentFABN
+ blockCount
;
1660 return noErr
; // found the right extent
1663 // Not in current extent, so adjust counters and loop again
1664 offsetBlocks
-= blockCount
;
1665 currentFABN
+= blockCount
;
1670 // If we fall through here, the extent record was full, but the offset was
1671 // beyond those extents.
1677 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1678 // Routine: ZeroFileBlocks
1680 // Function: Write all zeros to a range of a file. Currently used when
1681 // extending a B-Tree, so that all the new allocation blocks
1682 // contain zeros (to prevent them from accidentally looking
1685 // Input: vcb - the volume
1687 // startingSector - the first 512-byte sector to write
1688 // numberOfSectors - the number of sectors to zero
1690 // Result: noErr = ok
1691 // fxRangeErr = beyond FCB's extents
1692 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1693 #define FSBufferSize 32768
1695 OSErr
ZeroFileBlocks( SVCB
*vcb
, SFCB
*fcb
, UInt32 startingSector
, UInt32 numberOfSectors
)
1700 UInt32 requestedBytes
;
1701 UInt32 actualBytes
; // Bytes actually read by CacheReadInPlace
1702 UInt32 bufferSizeSectors
= FSBufferSize
>> kSectorShift
;
1703 UInt64 currentPosition
= startingSector
<< kSectorShift
;
1705 buffer
= AllocateMemory(FSBufferSize
);
1706 if ( buffer
== NULL
)
1707 return( fileBoundsErr
);
1709 ClearMemory( buffer
, FSBufferSize
); // Zero our buffer
1710 ClearMemory( &iopb
, sizeof(iopb
) ); // Zero our param block
1712 iopb
.ioRefNum
= ResolveFileRefNum( fcb
);
1713 iopb
.ioBuffer
= buffer
;
1714 iopb
.ioPosMode
|= noCacheMask
; // OR with the high byte
1718 if ( numberOfSectors
> bufferSizeSectors
)
1719 requestedBytes
= FSBufferSize
;
1721 requestedBytes
= numberOfSectors
<< kSectorShift
;
1723 err
= CacheWriteInPlace( vcb
, iopb
.ioRefNum
, &iopb
, currentPosition
, requestedBytes
, &actualBytes
);
1725 if ( err
|| actualBytes
== 0 )
1728 // Don't update ioActCount to force writing from beginning of zero buffer
1729 currentPosition
+= actualBytes
;
1730 numberOfSectors
-= (actualBytes
>> kSectorShift
);
1732 } while( numberOfSectors
> 0 );
1735 DisposeMemory(buffer
);
1737 if ( err
== noErr
&& numberOfSectors
!= 0 )
1743 //_________________________________________________________________________________
1745 // Routine: ExtentsAreIntegral
1747 // Purpose: Ensure that each extent can hold an integral number of nodes
1748 // Called by the NodesAreContiguous function
1749 //_________________________________________________________________________________
1751 static Boolean
ExtentsAreIntegral(
1752 const HFSPlusExtentRecord extentRecord
,
1754 UInt32
*blocksChecked
,
1755 Boolean
*checkedLastExtent
)
1761 *checkedLastExtent
= false;
1763 for(extentIndex
= 0; extentIndex
< kHFSPlusExtentDensity
; extentIndex
++)
1765 blocks
= extentRecord
[extentIndex
].blockCount
;
1769 *checkedLastExtent
= true;
1773 *blocksChecked
+= blocks
;
1782 //_________________________________________________________________________________
1784 // Routine: NodesAreContiguous
1786 // Purpose: Ensure that all b-tree nodes are contiguous on disk
1787 // Called by BTOpenPath during volume mount
1788 //_________________________________________________________________________________
1790 Boolean
NodesAreContiguous(
1797 UInt32 blocksChecked
;
1799 HFSPlusExtentKey key
;
1800 HFSPlusExtentRecord extents
;
1802 Boolean lastExtentReached
;
1805 vcb
= (SVCB
*)fcb
->fcbVolume
;
1807 if (vcb
->vcbBlockSize
>= nodeSize
)
1810 mask
= (nodeSize
/ vcb
->vcbBlockSize
) - 1;
1812 // check the local extents
1813 (void) GetFCBExtentRecord(vcb
, fcb
, extents
);
1814 if ( !ExtentsAreIntegral(extents
, mask
, &blocksChecked
, &lastExtentReached
) )
1817 if (lastExtentReached
|| ((UInt64
)blocksChecked
* (UInt64
)vcb
->vcbBlockSize
) >= fcb
->fcbPhysicalSize
)
1820 startBlock
= blocksChecked
;
1822 // check the overflow extents (if any)
1823 while ( !lastExtentReached
)
1825 result
= FindExtentRecord(vcb
, kDataForkType
, fcb
->fcbFileID
, startBlock
, false, &key
, extents
, &hint
);
1828 if ( !ExtentsAreIntegral(extents
, mask
, &blocksChecked
, &lastExtentReached
) )
1831 startBlock
+= blocksChecked
;