]> git.saurik.com Git - apple/hfs.git/blob - fsck_hfs/dfalib/SExtents.c
hfs-522.100.5.tar.gz
[apple/hfs.git] / fsck_hfs / dfalib / SExtents.c
1 /*
2 * Copyright (c) 1999-2002, 2005, 2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /*
24 File: SExtents.c
25
26 Contains: Routines to map file positions to volume positions, and manipulate the extents B-Tree.
27
28 Version: HFS Plus 1.0
29
30 Written by: Dave Heller, Mark Day
31
32 Copyright: © 1996-1999 by Apple Computer, Inc., all rights reserved.
33 */
34
35
36 #include "BTree.h"
37 #include "Scavenger.h"
38
39 /*
40 ============================================================
41 Public (Exported) Routines:
42 ============================================================
43 DeallocateFile Deallocate all disk space allocated to a specified file.
44 Both forks are deallocated.
45
46 ExtendFileC Allocate more space to a given file.
47
48
49 MapFileBlockC Convert (map) an offset within a given file into a
50 physical disk address.
51
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.
56
57 FlushExtentFile
58 Flush the extents file for a given volume.
59
60 AdjustEOF
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.
64
65 MapLogicalToPhysical
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.
70
71 UpdateExtentRecord
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.
76
77 ReleaseExtents
78 Deallocate all allocation blocks in all extents of an extent
79 data record.
80 ============================================================
81 Internal Routines:
82 ============================================================
83 FindExtentRecord
84 Search the extents BTree for a particular extent record.
85 SearchExtentFile
86 Search the FCB and extents file for an extent record that
87 contains a given file position (in bytes).
88 SearchExtentRecord
89 Search a given extent record to see if it contains a given
90 file position (in bytes). Used by SearchExtentFile.
91 TruncateExtents
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
95 for the file.
96 DeallocateFork
97 Deallocate all allocation blocks belonging to a given fork.
98 */
99
100 enum
101 {
102 kTwoGigSectors = 0x00400000,
103
104 kDataForkType = 0,
105 kResourceForkType = 0xFF,
106
107 kPreviousRecord = -1,
108
109 kSectorSize = 512 // Size of a physical sector
110 };
111
112 static OSErr ExtentsToExtDataRec(
113 HFSPlusExtentRecord oldExtents,
114 HFSExtentRecord newExtents);
115
116 OSErr FindExtentRecord(
117 const SVCB *vcb,
118 UInt8 forkType,
119 UInt32 fileID,
120 UInt32 startBlock,
121 Boolean allowPrevious,
122 HFSPlusExtentKey *foundKey,
123 HFSPlusExtentRecord foundData,
124 UInt32 *foundHint);
125
126 OSErr DeleteExtentRecord(
127 const SVCB *vcb,
128 UInt8 forkType,
129 UInt32 fileID,
130 UInt32 startBlock);
131
132 static OSErr CreateExtentRecord(
133 const SVCB *vcb,
134 HFSPlusExtentKey *key,
135 HFSPlusExtentRecord extents,
136 UInt32 *hint);
137
138 OSErr GetFCBExtentRecord(
139 const SVCB *vcb,
140 const SFCB *fcb,
141 HFSPlusExtentRecord extents);
142
143 static OSErr SetFCBExtentRecord(
144 const SVCB *vcb,
145 SFCB *fcb,
146 HFSPlusExtentRecord extents);
147
148 static OSErr SearchExtentFile(
149 const SVCB *vcb,
150 const SFCB *fcb,
151 UInt64 filePosition,
152 HFSPlusExtentKey *foundExtentKey,
153 HFSPlusExtentRecord foundExtentData,
154 UInt32 *foundExtentDataIndex,
155 UInt32 *extentBTreeHint,
156 UInt32 *endingFABNPlusOne );
157
158 static OSErr SearchExtentRecord(
159 const SVCB *vcb,
160 UInt32 searchFABN,
161 const HFSPlusExtentRecord extentData,
162 UInt32 extentDataStartFABN,
163 UInt32 *foundExtentDataOffset,
164 UInt32 *endingFABNPlusOne,
165 Boolean *noMoreExtents);
166
167 #if 0
168 static OSErr DeallocateFork(
169 SVCB *vcb,
170 HFSCatalogNodeID fileID,
171 UInt8 forkType,
172 HFSPlusExtentRecord catalogExtents,
173 Boolean * recordDeleted);
174
175 static OSErr TruncateExtents(
176 SVCB *vcb,
177 UInt8 forkType,
178 UInt32 fileID,
179 UInt32 startBlock,
180 Boolean * recordDeleted);
181 #endif
182
183 static OSErr MapFileBlockFromFCB(
184 const SVCB *vcb,
185 const SFCB *fcb,
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
190
191 static Boolean ExtentsAreIntegral(
192 const HFSPlusExtentRecord extentRecord,
193 UInt32 mask,
194 UInt32 *blocksChecked,
195 Boolean *checkedLastExtent);
196
197 //_________________________________________________________________________________
198 //
199 // Routine: FindExtentRecord
200 //
201 // Purpose: Search the extents BTree for an extent record matching the given
202 // FileID, fork, and starting file allocation block number.
203 //
204 // Inputs:
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.
212 //
213 // Outputs:
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(
220 const SVCB *vcb,
221 UInt8 forkType,
222 UInt32 fileID,
223 UInt32 startBlock,
224 Boolean allowPrevious,
225 HFSPlusExtentKey *foundKey,
226 HFSPlusExtentRecord foundData,
227 UInt32 *foundHint)
228 {
229 OSErr err;
230 UInt16 foundSize;
231
232 err = noErr;
233
234 if (vcb->vcbSignature == kHFSSigWord) {
235 HFSExtentKey key;
236 HFSExtentKey extentKey;
237 HFSExtentRecord extentData;
238
239 key.keyLength = kHFSExtentKeyMaximumLength;
240 key.forkType = forkType;
241 key.fileID = fileID;
242 key.startBlock = startBlock;
243
244 err = SearchBTreeRecord(vcb->vcbExtentsFile, &key, kNoHint, &extentKey, &extentData,
245 &foundSize, foundHint);
246
247 if (err == btNotFound && allowPrevious) {
248 err = GetBTreeRecord(vcb->vcbExtentsFile, kPreviousRecord, &extentKey, &extentData,
249 &foundSize, foundHint);
250
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
254 err = btNotFound;
255
256 if (err == noErr) {
257 // Found a previous record. Does it belong to the same fork of the same file?
258 if (extentKey.fileID != fileID || extentKey.forkType != forkType)
259 err = btNotFound;
260 }
261 }
262
263 if (err == noErr) {
264 UInt16 i;
265
266 // Copy the found key back for the caller
267 foundKey->keyLength = kHFSPlusExtentKeyMaximumLength;
268 foundKey->forkType = extentKey.forkType;
269 foundKey->pad = 0;
270 foundKey->fileID = extentKey.fileID;
271 foundKey->startBlock = extentKey.startBlock;
272
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;
280
281 for (i = 3; i < kHFSPlusExtentDensity; ++i)
282 {
283 foundData[i].startBlock = 0;
284 foundData[i].blockCount = 0;
285 }
286 }
287 }
288 else { // HFS Plus volume
289 HFSPlusExtentKey key;
290 HFSPlusExtentKey extentKey;
291 HFSPlusExtentRecord extentData;
292
293 key.keyLength = kHFSPlusExtentKeyMaximumLength;
294 key.forkType = forkType;
295 key.pad = 0;
296 key.fileID = fileID;
297 key.startBlock = startBlock;
298
299 err = SearchBTreeRecord(vcb->vcbExtentsFile, &key, kNoHint, &extentKey, &extentData,
300 &foundSize, foundHint);
301
302 if (err == btNotFound && allowPrevious) {
303 err = GetBTreeRecord(vcb->vcbExtentsFile, kPreviousRecord, &extentKey, &extentData,
304 &foundSize, foundHint);
305
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
309 err = btNotFound;
310
311 if (err == noErr) {
312 // Found a previous record. Does it belong to the same fork of the same file?
313 if (extentKey.fileID != fileID || extentKey.forkType != forkType)
314 err = btNotFound;
315 }
316 }
317
318 if (err == noErr) {
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));
323 }
324 }
325
326 return err;
327 }
328
329
330
331 static OSErr CreateExtentRecord(
332 const SVCB *vcb,
333 HFSPlusExtentKey *key,
334 HFSPlusExtentRecord extents,
335 UInt32 *hint)
336 {
337 OSErr err;
338
339 err = noErr;
340
341 if (vcb->vcbSignature == kHFSSigWord) {
342 HFSExtentKey hfsKey;
343 HFSExtentRecord data;
344
345 hfsKey.keyLength = kHFSExtentKeyMaximumLength;
346 hfsKey.forkType = key->forkType;
347 hfsKey.fileID = key->fileID;
348 hfsKey.startBlock = key->startBlock;
349
350 err = ExtentsToExtDataRec(extents, data);
351 if (err == noErr)
352 err = InsertBTreeRecord(vcb->vcbExtentsFile, &hfsKey, data, sizeof(HFSExtentRecord), hint);
353 }
354 else { // HFS Plus volume
355 err = InsertBTreeRecord(vcb->vcbExtentsFile, key, extents, sizeof(HFSPlusExtentRecord), hint);
356 }
357
358 return err;
359 }
360
361
362 OSErr DeleteExtentRecord(
363 const SVCB *vcb,
364 UInt8 forkType,
365 UInt32 fileID,
366 UInt32 startBlock)
367 {
368 OSErr err;
369
370 err = noErr;
371
372 if (vcb->vcbSignature == kHFSSigWord) {
373 HFSExtentKey key;
374
375 key.keyLength = kHFSExtentKeyMaximumLength;
376 key.forkType = forkType;
377 key.fileID = fileID;
378 key.startBlock = startBlock;
379
380 err = DeleteBTreeRecord( vcb->vcbExtentsFile, &key );
381 }
382 else { // HFS Plus volume
383 HFSPlusExtentKey key;
384
385 key.keyLength = kHFSPlusExtentKeyMaximumLength;
386 key.forkType = forkType;
387 key.pad = 0;
388 key.fileID = fileID;
389 key.startBlock = startBlock;
390
391 err = DeleteBTreeRecord( vcb->vcbExtentsFile, &key );
392 }
393
394 return err;
395 }
396
397
398
399 //_________________________________________________________________________________
400 //
401 // Routine: MapFileBlock
402 //
403 // Function: Maps a file position into a physical disk address.
404 //
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)
409 //
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>
413 // 0 = ok
414 // FXRangeErr = file position beyond mapped range <17Oct85>
415 // FXOvFlErr = extents file overflow <17Oct85>
416 // other = error <17Oct85>
417 //
418 // Called By: Log2Phys (read/write in place), Cache (map a file block).
419 //_________________________________________________________________________________
420
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)
428 {
429 OSErr err;
430 UInt32 allocBlockSize; // Size of the volume's allocation block, in sectors
431 HFSPlusExtentKey foundKey;
432 HFSPlusExtentRecord foundData;
433 UInt32 foundIndex;
434 UInt32 hint;
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
439 UInt64 temp;
440
441
442 // LogStartTime(kTraceMapFileBlock);
443
444 allocBlockSize = vcb->vcbBlockSize >> kSectorShift;
445
446 err = MapFileBlockFromFCB(vcb, fcb, sectorOffset, &firstFABN, &startBlock, &nextFABN);
447 if (err != noErr) {
448 err = SearchExtentFile(vcb, fcb, sectorOffset, &foundKey, foundData, &foundIndex, &hint, &nextFABN);
449 if (err == noErr) {
450 startBlock = foundData[foundIndex].startBlock;
451 firstFABN = nextFABN - foundData[foundIndex].blockCount;
452 }
453 }
454
455 if (err != noErr)
456 {
457 // LogEndTime(kTraceMapFileBlock, err);
458
459 return err;
460 }
461
462 //
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.
465 //
466
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
472
473 //
474 // Compute the absolute sector number that contains the offset of the given file
475 //
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
480 else
481 temp += vcb->vcbAlBlSt; // offset in sectors from start of volume
482
483 // Return the desired sector for file position "offset"
484 *startSector = temp;
485
486 //
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.
490 //
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
497 else
498 *availableBytes = temp;
499
500 // LogEndTime(kTraceMapFileBlock, noErr);
501
502 return noErr;
503 }
504
505
506 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
507 // Routine: ReleaseExtents
508 //
509 // Function: Release the extents of a single extent data record.
510 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
511
512 #if 1
513 OSErr ReleaseExtents(
514 SVCB *vcb,
515 const HFSPlusExtentRecord extentRecord,
516 UInt32 *numReleasedAllocationBlocks,
517 Boolean *releasedLastExtent)
518 {
519 UInt32 extentIndex;
520 UInt32 numberOfExtents;
521 OSErr err = noErr;
522
523 *numReleasedAllocationBlocks = 0;
524 *releasedLastExtent = false;
525
526 if (vcb->vcbSignature == kHFSPlusSigWord)
527 numberOfExtents = kHFSPlusExtentDensity;
528 else
529 numberOfExtents = kHFSExtentDensity;
530
531 for( extentIndex = 0; extentIndex < numberOfExtents; extentIndex++)
532 {
533 UInt32 numAllocationBlocks;
534
535 // Loop over the extent record and release the blocks associated with each extent.
536
537 numAllocationBlocks = extentRecord[extentIndex].blockCount;
538 if ( numAllocationBlocks == 0 )
539 {
540 *releasedLastExtent = true;
541 break;
542 }
543
544 err = ReleaseBitmapBits( extentRecord[extentIndex].startBlock, numAllocationBlocks );
545 if ( err != noErr )
546 break;
547
548 *numReleasedAllocationBlocks += numAllocationBlocks; // bump FABN to beg of next extent
549 }
550
551 return( err );
552 }
553 #endif
554
555
556 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
557 // Routine: TruncateExtents
558 //
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.
562 //
563 // Inputs:
564 // vcb Volume to operate on
565 // fileID Which file to operate on
566 // startBlock Starting file allocation block number for first extent
567 // record to delete.
568 //
569 // Outputs:
570 // recordDeleted Set to true if any extents B-tree record was deleted.
571 // Unchanged otherwise.
572 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
573
574 static OSErr TruncateExtents(
575 SVCB *vcb,
576 UInt8 forkType,
577 UInt32 fileID,
578 UInt32 startBlock,
579 Boolean * recordDeleted)
580 {
581 OSErr err;
582 Boolean releasedLastExtent;
583 UInt32 numberExtentsReleased;
584 UInt32 hint;
585 HFSPlusExtentKey key;
586 HFSPlusExtentRecord extents;
587
588 while (true) {
589 err = FindExtentRecord(vcb, forkType, fileID, startBlock, false, &key, extents, &hint);
590 if (err != noErr) {
591 if (err == btNotFound)
592 err = noErr;
593 break;
594 }
595
596 err = ReleaseExtents( vcb, extents, &numberExtentsReleased, &releasedLastExtent );
597 if (err != noErr) break;
598
599 err = DeleteExtentRecord(vcb, forkType, fileID, startBlock);
600 if (err != noErr) break;
601
602 *recordDeleted = true; // We did delete a record
603 startBlock += numberExtentsReleased;
604 }
605
606 return err;
607 }
608
609
610 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
611 // Routine: DeallocateFork
612 //
613 // Function: De-allocates all disk space allocated to a specified fork.
614 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
615 static OSErr DeallocateFork(
616 SVCB *vcb,
617 HFSCatalogNodeID fileID,
618 UInt8 forkType,
619 HFSPlusExtentRecord catalogExtents,
620 Boolean * recordDeleted) // set to true if any record was deleted
621 {
622 OSErr err;
623 UInt32 numReleasedAllocationBlocks;
624 Boolean releasedLastExtent;
625
626 // Release the catalog extents
627 err = ReleaseExtents( vcb, catalogExtents, &numReleasedAllocationBlocks, &releasedLastExtent );
628
629 // Release the extra extents, if present
630 if (err == noErr && !releasedLastExtent)
631 err = TruncateExtents(vcb, forkType, fileID, numReleasedAllocationBlocks, recordDeleted);
632
633 return( err );
634 }
635
636
637 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
638 // Routine: FlushExtentFile
639 //
640 // Function: Flushes the extent file for a specified volume
641 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
642
643 OSErr FlushExtentFile( SVCB *vcb )
644 {
645 OSErr err;
646
647 err = BTFlushPath(vcb->vcbExtentsFile);
648 if ( err == noErr )
649 {
650 // If the FCB for the extent "file" is dirty, mark the VCB as dirty.
651
652 if( ( vcb->vcbExtentsFile->fcbFlags & fcbModifiedMask ) != 0 )
653 {
654 (void) MarkVCBDirty( vcb );
655 err = FlushVolumeControlBlock( vcb );
656 }
657 }
658
659 return( err );
660 }
661
662
663 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
664 // Routine: DeallocateFile
665 //
666 // Function: De-allocates all disk space allocated to a specified file.
667 // The space occupied by both forks is deallocated.
668 //
669 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
670
671 OSErr DeallocateFile(SVCB *vcb, CatalogRecord * fileRec)
672 {
673 int i;
674 OSErr errDF, errRF;
675 Boolean recordDeleted = false;
676
677 errDF = errRF = 0;
678
679 if (fileRec->recordType == kHFSFileRecord) {
680 HFSPlusExtentRecord dataForkExtents;
681 HFSPlusExtentRecord rsrcForkExtents;
682
683 ClearMemory(&dataForkExtents, sizeof(dataForkExtents));
684 ClearMemory(&rsrcForkExtents, sizeof(rsrcForkExtents));
685
686 for (i = 0; i < kHFSExtentDensity; ++i) {
687 dataForkExtents[i].startBlock =
688 (UInt32) (fileRec->hfsFile.dataExtents[i].startBlock);
689 dataForkExtents[i].blockCount =
690 (UInt32) (fileRec->hfsFile.dataExtents[i].blockCount);
691
692 rsrcForkExtents[i].startBlock =
693 (UInt32) (fileRec->hfsFile.rsrcExtents[i].startBlock);
694 rsrcForkExtents[i].blockCount =
695 (UInt32) (fileRec->hfsFile.rsrcExtents[i].blockCount);
696 }
697
698 errDF = DeallocateFork(vcb, fileRec->hfsFile.fileID, kDataForkType,
699 dataForkExtents, &recordDeleted );
700
701 errRF = DeallocateFork(vcb, fileRec->hfsFile.fileID, kResourceForkType,
702 rsrcForkExtents, &recordDeleted );
703 }
704 else if (fileRec->recordType == kHFSPlusFileRecord) {
705 errDF = DeallocateFork(vcb, fileRec->hfsPlusFile.fileID, kDataForkType,
706 fileRec->hfsPlusFile.dataFork.extents, &recordDeleted );
707
708 errRF = DeallocateFork(vcb, fileRec->hfsPlusFile.fileID, kResourceForkType,
709 fileRec->hfsPlusFile.resourceFork.extents, &recordDeleted );
710 }
711
712 if (recordDeleted)
713 (void) FlushExtentFile(vcb);
714
715 MarkVCBDirty(vcb);
716
717 return (errDF ? errDF : errRF);
718 }
719
720
721 //_________________________________________________________________________________
722 //
723 // Routine: Extendfile
724 //
725 // Function: Extends the disk space allocated to a file.
726 //
727 // Input: A2.L - VCB pointer
728 // A1.L - pointer to FCB array
729 // D1.W - file refnum
730 // D3.B - option flags
731 // kEFContigMask - force contiguous allocation
732 // kEFAllMask - allocate all requested bytes or none
733 // NOTE: You may not set both options.
734 // D4.L - number of additional bytes to allocate
735 //
736 // Output: D0.W - result code
737 // 0 = ok
738 // -n = IO error
739 // D6.L - number of bytes allocated
740 //
741 // Called by: FileAloc,FileWrite,SetEof
742 //
743 // Note: ExtendFile updates the PEOF in the FCB.
744 //_________________________________________________________________________________
745
746 OSErr ExtendFileC (
747 SVCB *vcb, // volume that file resides on
748 SFCB *fcb, // FCB of file to truncate
749 UInt32 sectorsToAdd, // number of sectors to allocate
750 UInt32 flags, // EFContig and/or EFAll
751 UInt32 *actualSectorsAdded)// number of bytes actually allocated
752 {
753 OSErr err;
754 Boolean wantContig;
755 Boolean needsFlush;
756 UInt32 sectorsPerBlock;
757 UInt32 blocksToAdd; // number of blocks we'd like to add
758 UInt32 blocksPerClump; // number of blocks in clump size
759 UInt32 maxBlocksToAdd; // max blocks we want to add
760 UInt32 eofBlocks; // current EOF in blocks
761 HFSPlusExtentKey foundKey; // from SearchExtentFile
762 HFSPlusExtentRecord foundData;
763 UInt32 foundIndex; // from SearchExtentFile
764 UInt32 hint; // from SearchExtentFile
765 UInt32 nextBlock; // from SearchExtentFile
766 UInt32 startBlock;
767 UInt32 actualStartBlock;
768 UInt32 actualNumBlocks;
769 UInt32 numExtentsPerRecord;
770 UInt32 blocksAdded;
771
772 needsFlush = false; // Assume the B-tree header doesn't need to be updated
773 blocksAdded = 0;
774 *actualSectorsAdded = 0;
775
776 if (vcb->vcbSignature == kHFSPlusSigWord)
777 numExtentsPerRecord = kHFSPlusExtentDensity;
778 else
779 numExtentsPerRecord = kHFSExtentDensity;
780
781 //
782 // Round up the request to whole allocation blocks
783 //
784 sectorsPerBlock = vcb->vcbBlockSize >> kSectorShift;
785 blocksToAdd = DivideAndRoundUp(sectorsToAdd, sectorsPerBlock);
786
787 //
788 // Determine the physical EOF in allocation blocks
789 //
790 eofBlocks = fcb->fcbPhysicalSize / vcb->vcbBlockSize;
791
792 //
793 // Make sure the request won't make the file too big (>=2GB).
794 // [2350148] Always limit HFS files.
795 // ¥¥ Shouldn't really fail if allOrNothing is false
796 // ¥¥ Adjust for clump size here?
797 //
798 if ( vcb->vcbSignature == kHFSPlusSigWord )
799 {
800 // Allow it to grow beyond 2GB.
801 }
802 else
803 {
804 UInt32 maxFileBlocks; // max legal EOF, in blocks
805 maxFileBlocks = (kTwoGigSectors-1) / sectorsPerBlock;
806 if (blocksToAdd > maxFileBlocks || (blocksToAdd + eofBlocks) > maxFileBlocks) {
807 err = fileBoundsErr;
808 goto ErrorExit;
809 }
810 }
811
812 //
813 // If allocation is all-or-nothing, then make sure there
814 // are enough free blocks. (A quick test)
815 //
816 if ((flags & kEFAllMask) && blocksToAdd > vcb->vcbFreeBlocks) {
817 err = dskFulErr;
818 goto ErrorExit;
819 }
820
821 //
822 // There may be blocks allocated beyond the physical EOF
823 // (because we allocated the rest of the clump size, or
824 // because of a PBAllocate or PBAllocContig call).
825 // If these extra blocks exist, then use them to satisfy
826 // part or all of the request.
827 //
828 // ¥¥ What, if anything, would break if the physical EOF always
829 // ¥¥ represented ALL extents allocated to the file (including
830 // ¥¥ the clump size roundup)?
831 //
832 // Note: (blocks * sectorsPerBlock - 1) is the sector offset
833 // of the last sector in the last block.
834 //
835 err = SearchExtentFile(vcb, fcb, (eofBlocks+blocksToAdd) * sectorsPerBlock - 1, &foundKey, foundData, &foundIndex, &hint, &nextBlock);
836 if (err == noErr) {
837 // Enough blocks are already allocated. Just update the FCB to reflect the new length.
838 eofBlocks += blocksToAdd; // new EOF, in blocks
839 blocksAdded += blocksToAdd;
840 goto Exit;
841 }
842 if (err != fxRangeErr) // Any real error?
843 goto ErrorExit; // Yes, so exit immediately
844
845 //
846 // There wasn't enough already allocated. But there might have been
847 // a few allocated blocks beyond the physical EOF. So, set the physical
848 // EOF to match the end of the last extent.
849 //
850 if (nextBlock > eofBlocks) {
851 // There were (nextBlock - eofBlocks) extra blocks past physical EOF
852 blocksAdded += nextBlock - eofBlocks;
853 blocksToAdd -= nextBlock - eofBlocks;
854 eofBlocks = nextBlock;
855 }
856
857 //
858 // We still need to allocate more blocks.
859 //
860 // First try a contiguous allocation (of the whole amount).
861 // If that fails, get whatever we can.
862 // If forceContig, then take whatever we got
863 // else, keep getting bits and pieces (non-contig)
864 //
865 // ¥¥ Need to do clump size calculations
866 //
867 blocksPerClump = fcb->fcbClumpSize / vcb->vcbBlockSize;
868 if (blocksPerClump == 0)
869 blocksPerClump = 1;
870
871 err = noErr;
872 wantContig = true;
873 do {
874 // Make maxBlocksToAdd equal to blocksToAdd rounded up to a multiple
875 // of the file's clump size. This gives the file room to grow some
876 // more without fragmenting.
877 if (flags & kEFNoClumpMask) {
878 // Caller said not to round up, so only allocate what was asked for.
879 maxBlocksToAdd = blocksToAdd;
880 }
881 else {
882 // Round up to multiple of clump size
883 maxBlocksToAdd = DivideAndRoundUp(blocksToAdd, blocksPerClump);
884 maxBlocksToAdd *= blocksPerClump;
885 }
886
887 // Try to allocate the new space contiguous with the end of the previous
888 // extent. If this succeeds, the last extent grows and the file does not
889 // become any more fragmented.
890 startBlock = foundData[foundIndex].startBlock + foundData[foundIndex].blockCount;
891 err = BlockAllocate(vcb, startBlock, blocksToAdd, maxBlocksToAdd, wantContig, &actualStartBlock, &actualNumBlocks);
892 if (err == dskFulErr) {
893 if (flags & kEFContigMask)
894 break; // AllocContig failed because not enough contiguous space
895 if (wantContig) {
896 // Couldn't get one big chunk, so get whatever we can.
897 err = noErr;
898 wantContig = false;
899 continue;
900 }
901 if (actualNumBlocks != 0)
902 err = noErr;
903 }
904 if (err == noErr) {
905 // Add the new extent to the existing extent record, or create a new one.
906 if (actualStartBlock == startBlock) {
907 // We grew the file's last extent, so just adjust the number of blocks.
908 foundData[foundIndex].blockCount += actualNumBlocks;
909 err = UpdateExtentRecord(vcb, fcb, &foundKey, foundData, hint);
910 if (err != noErr) break;
911 }
912 else {
913 UInt16 i;
914
915 // Need to add a new extent. See if there is room in the current record.
916 if (foundData[foundIndex].blockCount != 0) // Is current extent free to use?
917 ++foundIndex; // No, so use the next one.
918 if (foundIndex == numExtentsPerRecord) {
919 // This record is full. Need to create a new one.
920 if (fcb->fcbFileID == kHFSExtentsFileID || (flags & kEFNoExtOvflwMask)) {
921 (void) BlockDeallocate(vcb, actualStartBlock, actualNumBlocks);
922 err = fxOvFlErr; // Oops. Can't extend extents file past first record.
923 break;
924 }
925
926 foundKey.keyLength = kHFSPlusExtentKeyMaximumLength;
927 if (fcb->fcbFlags & fcbResourceMask)
928 foundKey.forkType = kResourceForkType;
929 else
930 foundKey.forkType = kDataForkType;
931 foundKey.pad = 0;
932 foundKey.fileID = fcb->fcbFileID;
933 foundKey.startBlock = nextBlock;
934
935 foundData[0].startBlock = actualStartBlock;
936 foundData[0].blockCount = actualNumBlocks;
937
938 // zero out remaining extents...
939 for (i = 1; i < kHFSPlusExtentDensity; ++i)
940 {
941 foundData[i].startBlock = 0;
942 foundData[i].blockCount = 0;
943 }
944
945 foundIndex = 0;
946
947 err = CreateExtentRecord(vcb, &foundKey, foundData, &hint);
948 if (err == fxOvFlErr) {
949 // We couldn't create an extent record because extents B-tree
950 // couldn't grow. Dellocate the extent just allocated and
951 // return a disk full error.
952 (void) BlockDeallocate(vcb, actualStartBlock, actualNumBlocks);
953 err = dskFulErr;
954 }
955 if (err != noErr) break;
956
957 needsFlush = true; // We need to update the B-tree header
958 }
959 else {
960 // Add a new extent into this record and update.
961 foundData[foundIndex].startBlock = actualStartBlock;
962 foundData[foundIndex].blockCount = actualNumBlocks;
963 err = UpdateExtentRecord(vcb, fcb, &foundKey, foundData, hint);
964 if (err != noErr) break;
965 }
966 }
967
968 // Figure out how many bytes were actually allocated.
969 // NOTE: BlockAllocate could have allocated more than the minimum
970 // we asked for (up to our requested maximum).
971 // Don't set the PEOF beyond what our client asked for.
972 nextBlock += actualNumBlocks;
973 if (actualNumBlocks > blocksToAdd) {
974 blocksAdded += blocksToAdd;
975 eofBlocks += blocksToAdd;
976 blocksToAdd = 0;
977 }
978 else {
979 blocksAdded += actualNumBlocks;
980 blocksToAdd -= actualNumBlocks;
981 eofBlocks += actualNumBlocks;
982 }
983
984 // If contiguous allocation was requested, then we've already got one contiguous
985 // chunk. If we didn't get all we wanted, then adjust the error to disk full.
986 if (flags & kEFContigMask) {
987 if (blocksToAdd != 0)
988 err = dskFulErr;
989 break; // We've already got everything that's contiguous
990 }
991 }
992 } while (err == noErr && blocksToAdd);
993
994 ErrorExit:
995 Exit:
996 *actualSectorsAdded = blocksAdded * sectorsPerBlock;
997 if (blocksAdded) {
998 fcb->fcbPhysicalSize = (UInt64)eofBlocks * (UInt64)vcb->vcbBlockSize;
999 fcb->fcbFlags |= fcbModifiedMask;
1000 }
1001
1002 // [2355121] If we created a new extent record, then update the B-tree header
1003 if (needsFlush)
1004 (void) FlushExtentFile(vcb);
1005
1006 return err;
1007 }
1008
1009
1010
1011 //_________________________________________________________________________________
1012 //
1013 // Routine: TruncateFileC
1014 //
1015 // Function: Truncates the disk space allocated to a file. The file space is
1016 // truncated to a specified new PEOF rounded up to the next allocation
1017 // block boundry. If the 'TFTrunExt' option is specified, the file is
1018 // truncated to the end of the extent containing the new PEOF.
1019 //
1020 // Input: A2.L - VCB pointer
1021 // A1.L - pointer to FCB array
1022 // D1.W - file refnum
1023 // D2.B - option flags
1024 // TFTrunExt - truncate to the extent containing new PEOF
1025 // D3.L - new PEOF
1026 //
1027 // Output: D0.W - result code
1028 // 0 = ok
1029 // -n = IO error
1030 //
1031 // Note: TruncateFile updates the PEOF in the FCB.
1032 //_________________________________________________________________________________
1033
1034 #if 0
1035 OSErr TruncateFileC (
1036 SVCB *vcb, // volume that file resides on
1037 SFCB *fcb, // FCB of file to truncate
1038 UInt32 eofSectors, // new physical size for file
1039 Boolean truncateToExtent) // if true, truncate to end of extent containing newPEOF
1040 {
1041 OSErr err;
1042 UInt32 nextBlock; // next file allocation block to consider
1043 UInt32 startBlock; // Physical (volume) allocation block number of start of a range
1044 UInt32 physNumBlocks; // Number of allocation blocks in file (according to PEOF)
1045 UInt32 numBlocks;
1046 HFSPlusExtentKey key; // key for current extent record; key->keyLength == 0 if FCB's extent record
1047 UInt32 hint; // BTree hint corresponding to key
1048 HFSPlusExtentRecord extentRecord;
1049 UInt32 extentIndex;
1050 UInt32 extentNextBlock;
1051 UInt32 numExtentsPerRecord;
1052 UInt32 sectorsPerBlock;
1053 UInt8 forkType;
1054 Boolean extentChanged; // true if we actually changed an extent
1055 Boolean recordDeleted; // true if an extent record got deleted
1056
1057 recordDeleted = false;
1058 sectorsPerBlock = vcb->vcbBlockSize >> kSectorShift;
1059
1060 if (vcb->vcbSignature == kHFSPlusSigWord)
1061 numExtentsPerRecord = kHFSPlusExtentDensity;
1062 else
1063 numExtentsPerRecord = kHFSExtentDensity;
1064
1065 if (fcb->fcbFlags & fcbResourceMask)
1066 forkType = kResourceForkType;
1067 else
1068 forkType = kDataForkType;
1069
1070 // Compute number of allocation blocks currently in file
1071 physNumBlocks = fcb->fcbPhysicalSize / vcb->vcbBlockSize;
1072
1073 //
1074 // Round newPEOF up to a multiple of the allocation block size. If new size is
1075 // two gigabytes or more, then round down by one allocation block (??? really?
1076 // shouldn't that be an error?).
1077 //
1078 nextBlock = DivideAndRoundUp(eofSectors, sectorsPerBlock); // number of allocation blocks to remain in file
1079 eofSectors = nextBlock * sectorsPerBlock; // rounded up to multiple of block size
1080 if ((fcb->fcbFlags & fcbLargeFileMask) == 0 && eofSectors >= kTwoGigSectors) {
1081 #if DEBUG_BUILD
1082 DebugStr("\pHFS: Trying to truncate a file to 2GB or more");
1083 #endif
1084 err = fileBoundsErr;
1085 goto ErrorExit;
1086 }
1087
1088 //
1089 // Update FCB's length
1090 //
1091 fcb->fcbPhysicalSize = (UInt64)nextBlock * (UInt64)vcb->vcbBlockSize;
1092 fcb->fcbFlags |= fcbModifiedMask;
1093
1094 //
1095 // If the new PEOF is 0, then truncateToExtent has no meaning (we should always deallocate
1096 // all storage).
1097 //
1098 if (eofSectors == 0) {
1099 int i;
1100
1101 // Find the catalog extent record
1102 err = GetFCBExtentRecord(vcb, fcb, extentRecord);
1103 if (err != noErr) goto ErrorExit; // got some error, so return it
1104
1105 // Deallocate all the extents for this fork
1106 err = DeallocateFork(vcb, fcb->fcbFileID, forkType, extentRecord, &recordDeleted);
1107 if (err != noErr) goto ErrorExit; // got some error, so return it
1108
1109 // Update the catalog extent record (making sure it's zeroed out)
1110 if (err == noErr) {
1111 for (i=0; i < numExtentsPerRecord; i++) {
1112 extentRecord[i].startBlock = 0;
1113 extentRecord[i].blockCount = 0;
1114 }
1115 }
1116 err = SetFCBExtentRecord((VCB *) vcb, fcb, extentRecord);
1117 goto Done;
1118 }
1119
1120 //
1121 // Find the extent containing byte (peof-1). This is the last extent we'll keep.
1122 // (If truncateToExtent is true, we'll keep the whole extent; otherwise, we'll only
1123 // keep up through peof). The search will tell us how many allocation blocks exist
1124 // in the found extent plus all previous extents.
1125 //
1126 err = SearchExtentFile(vcb, fcb, eofSectors-1, &key, extentRecord, &extentIndex, &hint, &extentNextBlock);
1127 if (err != noErr) goto ErrorExit;
1128
1129 extentChanged = false; // haven't changed the extent yet
1130
1131 if (!truncateToExtent) {
1132 //
1133 // Shorten this extent. It may be the case that the entire extent gets
1134 // freed here.
1135 //
1136 numBlocks = extentNextBlock - nextBlock; // How many blocks in this extent to free up
1137 if (numBlocks != 0) {
1138 // Compute first volume allocation block to free
1139 startBlock = extentRecord[extentIndex].startBlock + extentRecord[extentIndex].blockCount - numBlocks;
1140 // Free the blocks in bitmap
1141 err = BlockDeallocate(vcb, startBlock, numBlocks);
1142 if (err != noErr) goto ErrorExit;
1143 // Adjust length of this extent
1144 extentRecord[extentIndex].blockCount -= numBlocks;
1145 // If extent is empty, set start block to 0
1146 if (extentRecord[extentIndex].blockCount == 0)
1147 extentRecord[extentIndex].startBlock = 0;
1148 // Remember that we changed the extent record
1149 extentChanged = true;
1150 }
1151 }
1152
1153 //
1154 // Now move to the next extent in the record, and set up the file allocation block number
1155 //
1156 nextBlock = extentNextBlock; // Next file allocation block to free
1157 ++extentIndex; // Its index within the extent record
1158
1159 //
1160 // Release all following extents in this extent record. Update the record.
1161 //
1162 while (extentIndex < numExtentsPerRecord && extentRecord[extentIndex].blockCount != 0) {
1163 numBlocks = extentRecord[extentIndex].blockCount;
1164 // Deallocate this extent
1165 err = BlockDeallocate(vcb, extentRecord[extentIndex].startBlock, numBlocks);
1166 if (err != noErr) goto ErrorExit;
1167 // Update next file allocation block number
1168 nextBlock += numBlocks;
1169 // Zero out start and length of this extent to delete it from record
1170 extentRecord[extentIndex].startBlock = 0;
1171 extentRecord[extentIndex].blockCount = 0;
1172 // Remember that we changed an extent
1173 extentChanged = true;
1174 // Move to next extent in record
1175 ++extentIndex;
1176 }
1177
1178 //
1179 // If any of the extents in the current record were changed, then update that
1180 // record (in the FCB, or extents file).
1181 //
1182 if (extentChanged) {
1183 err = UpdateExtentRecord(vcb, fcb, &key, extentRecord, hint);
1184 if (err != noErr) goto ErrorExit;
1185 }
1186
1187 //
1188 // If there are any following allocation blocks, then we need
1189 // to seach for their extent records and delete those allocation
1190 // blocks.
1191 //
1192 if (nextBlock < physNumBlocks)
1193 err = TruncateExtents(vcb, forkType, fcb->fcbFileID, nextBlock, &recordDeleted);
1194
1195 Done:
1196 ErrorExit:
1197
1198 #if DEBUG_BUILD
1199 if (err == fxRangeErr)
1200 DebugStr("\pAbout to return fxRangeErr");
1201 #endif
1202
1203 // [2355121] If we actually deleted extent records, then update the B-tree header
1204 if (recordDeleted)
1205 (void) FlushExtentFile(vcb);
1206
1207 return err;
1208 }
1209 #endif
1210
1211
1212 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1213 // Routine: SearchExtentRecord (was XRSearch)
1214 //
1215 // Function: Searches extent record for the extent mapping a given file
1216 // allocation block number (FABN).
1217 //
1218 // Input: searchFABN - desired FABN
1219 // extentData - pointer to extent data record (xdr)
1220 // extentDataStartFABN - beginning FABN for extent record
1221 //
1222 // Output: foundExtentDataOffset - offset to extent entry within xdr
1223 // result = noErr, offset to extent mapping desired FABN
1224 // result = FXRangeErr, offset to last extent in record
1225 // endingFABNPlusOne - ending FABN +1
1226 // noMoreExtents - True if the extent was not found, and the
1227 // extent record was not full (so don't bother
1228 // looking in subsequent records); false otherwise.
1229 //
1230 // Result: noErr = ok
1231 // FXRangeErr = desired FABN > last mapped FABN in record
1232 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1233
1234 static OSErr SearchExtentRecord(
1235 const SVCB *vcb,
1236 UInt32 searchFABN,
1237 const HFSPlusExtentRecord extentData,
1238 UInt32 extentDataStartFABN,
1239 UInt32 *foundExtentIndex,
1240 UInt32 *endingFABNPlusOne,
1241 Boolean *noMoreExtents)
1242 {
1243 OSErr err = noErr;
1244 UInt32 extentIndex;
1245 UInt32 numberOfExtents;
1246 UInt32 numAllocationBlocks;
1247 Boolean foundExtent;
1248
1249 *endingFABNPlusOne = extentDataStartFABN;
1250 *noMoreExtents = false;
1251 foundExtent = false;
1252
1253 if (vcb->vcbSignature == kHFSPlusSigWord)
1254 numberOfExtents = kHFSPlusExtentDensity;
1255 else
1256 numberOfExtents = kHFSExtentDensity;
1257
1258 for( extentIndex = 0; extentIndex < numberOfExtents; ++extentIndex )
1259 {
1260
1261 // Loop over the extent record and find the search FABN.
1262
1263 numAllocationBlocks = extentData[extentIndex].blockCount;
1264 if ( numAllocationBlocks == 0 )
1265 {
1266 break;
1267 }
1268
1269 *endingFABNPlusOne += numAllocationBlocks;
1270
1271 if( searchFABN < *endingFABNPlusOne )
1272 {
1273 // Found the extent.
1274 foundExtent = true;
1275 break;
1276 }
1277 }
1278
1279 if( foundExtent )
1280 {
1281 // Found the extent. Note the extent offset
1282 *foundExtentIndex = extentIndex;
1283 }
1284 else
1285 {
1286 // Did not find the extent. Set foundExtentDataOffset accordingly
1287 if( extentIndex > 0 )
1288 {
1289 *foundExtentIndex = extentIndex - 1;
1290 }
1291 else
1292 {
1293 *foundExtentIndex = 0;
1294 }
1295
1296 // If we found an empty extent, then set noMoreExtents.
1297 if (extentIndex < numberOfExtents)
1298 *noMoreExtents = true;
1299
1300 // Finally, return an error to the caller
1301 err = fxRangeErr;
1302 }
1303
1304 return( err );
1305 }
1306
1307 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1308 // Routine: SearchExtentFile (was XFSearch)
1309 //
1310 // Function: Searches extent file (including the FCB resident extent record)
1311 // for the extent mapping a given file position.
1312 //
1313 // Input: vcb - VCB pointer
1314 // fcb - FCB pointer
1315 // filePosition - file position (byte address)
1316 //
1317 // Output: foundExtentKey - extent key record (xkr)
1318 // If extent was found in the FCB's resident extent record,
1319 // then foundExtentKey->keyLength will be set to 0.
1320 // foundExtentData - extent data record(xdr)
1321 // foundExtentIndex - index to extent entry in xdr
1322 // result = 0, offset to extent mapping desired FABN
1323 // result = FXRangeErr, offset to last extent in record
1324 // (i.e., kNumExtentsPerRecord-1)
1325 // extentBTreeHint - BTree hint for extent record
1326 // kNoHint = Resident extent record
1327 // endingFABNPlusOne - ending FABN +1
1328 //
1329 // Result:
1330 // noErr Found an extent that contains the given file position
1331 // FXRangeErr Given position is beyond the last allocated extent
1332 // (other) (some other internal I/O error)
1333 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1334
1335 static OSErr SearchExtentFile(
1336 const SVCB *vcb,
1337 const SFCB *fcb,
1338 UInt64 sectorOffset,
1339 HFSPlusExtentKey *foundExtentKey,
1340 HFSPlusExtentRecord foundExtentData,
1341 UInt32 *foundExtentIndex,
1342 UInt32 *extentBTreeHint,
1343 UInt32 *endingFABNPlusOne )
1344 {
1345 OSErr err;
1346 UInt32 filePositionBlock;
1347 Boolean noMoreExtents = true;
1348
1349 filePositionBlock = sectorOffset / (vcb->vcbBlockSize >> kSectorShift);
1350
1351 // Search the resident FCB first.
1352 err = GetFCBExtentRecord(vcb, fcb, foundExtentData);
1353 if (err == noErr)
1354 err = SearchExtentRecord( vcb, filePositionBlock, foundExtentData, 0,
1355 foundExtentIndex, endingFABNPlusOne, &noMoreExtents );
1356
1357 if( err == noErr ) {
1358 // Found the extent. Set results accordingly
1359 *extentBTreeHint = kNoHint; // no hint, because not in the BTree
1360 foundExtentKey->keyLength = 0; // 0 = the FCB itself
1361
1362 goto Exit;
1363 }
1364
1365 // Didn't find extent in FCB. If FCB's extent record wasn't full, there's no point
1366 // in searching the extents file. Note that SearchExtentRecord left us pointing at
1367 // the last valid extent (or the first one, if none were valid). This means we need
1368 // to fill in the hint and key outputs, just like the "if" statement above.
1369 if ( noMoreExtents ) {
1370 *extentBTreeHint = kNoHint; // no hint, because not in the BTree
1371 foundExtentKey->keyLength = 0; // 0 = the FCB itself
1372 err = fxRangeErr; // There are no more extents, so must be beyond PEOF
1373 goto Exit;
1374 }
1375
1376 //
1377 // Find the desired record, or the previous record if it is the same fork
1378 //
1379 err = FindExtentRecord(vcb, (fcb->fcbFlags & fcbResourceMask) ? kResourceForkType : kDataForkType,
1380 fcb->fcbFileID, filePositionBlock, true, foundExtentKey, foundExtentData, extentBTreeHint);
1381
1382 if (err == btNotFound) {
1383 //
1384 // If we get here, the desired position is beyond the extents in the FCB, and there are no extents
1385 // in the extents file. Return the FCB's extents and a range error.
1386 //
1387 *extentBTreeHint = kNoHint;
1388 foundExtentKey->keyLength = 0;
1389 err = GetFCBExtentRecord(vcb, fcb, foundExtentData);
1390 // Note: foundExtentIndex and endingFABNPlusOne have already been set as a result of the very
1391 // first SearchExtentRecord call in this function (when searching in the FCB's extents, and
1392 // we got a range error).
1393
1394 return fxRangeErr;
1395 }
1396
1397 //
1398 // If we get here, there was either a BTree error, or we found an appropriate record.
1399 // If we found a record, then search it for the correct index into the extents.
1400 //
1401 if (err == noErr) {
1402 // Find appropriate index into extent record
1403 err = SearchExtentRecord(vcb, filePositionBlock, foundExtentData, foundExtentKey->startBlock,
1404 foundExtentIndex, endingFABNPlusOne, &noMoreExtents);
1405 }
1406
1407 Exit:
1408 return err;
1409 }
1410
1411
1412
1413 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1414 // Routine: UpdateExtentRecord
1415 //
1416 // Function: Write new extent data to an existing extent record with a given key.
1417 // If all of the extents are empty, and the extent record is in the
1418 // extents file, then the record is deleted.
1419 //
1420 // Input: vcb - the volume containing the extents
1421 // fcb - the file that owns the extents
1422 // extentFileKey - pointer to extent key record (xkr)
1423 // If the key length is 0, then the extents are actually part
1424 // of the catalog record, stored in the FCB.
1425 // extentData - pointer to extent data record (xdr)
1426 // extentBTreeHint - hint for given key, or kNoHint
1427 //
1428 // Result: noErr = ok
1429 // (other) = error from BTree
1430 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1431
1432 OSErr UpdateExtentRecord (
1433 const SVCB *vcb,
1434 SFCB *fcb,
1435 const HFSPlusExtentKey *extentFileKey,
1436 HFSPlusExtentRecord extentData,
1437 UInt32 extentBTreeHint)
1438 {
1439 OSErr err;
1440 UInt32 foundHint;
1441 UInt16 foundDataSize;
1442
1443 if (extentFileKey->keyLength == 0) { // keyLength == 0 means the FCB's extent record
1444 err = SetFCBExtentRecord(vcb, fcb, extentData);
1445 fcb->fcbFlags |= fcbModifiedMask;
1446 }
1447 else {
1448 //
1449 // Need to find and change a record in Extents BTree
1450 //
1451 if (vcb->vcbSignature == kHFSSigWord) {
1452 HFSExtentKey key; // Actual extent key used on disk in HFS
1453 HFSExtentKey foundKey; // The key actually found during search
1454 HFSExtentRecord foundData; // The extent data actually found
1455
1456 key.keyLength = kHFSExtentKeyMaximumLength;
1457 key.forkType = extentFileKey->forkType;
1458 key.fileID = extentFileKey->fileID;
1459 key.startBlock = extentFileKey->startBlock;
1460
1461 err = SearchBTreeRecord(vcb->vcbExtentsFile, &key, extentBTreeHint,
1462 &foundKey, &foundData, &foundDataSize, &foundHint);
1463
1464 if (err == noErr)
1465 err = ExtentsToExtDataRec(extentData, (HFSExtentDescriptor *)&foundData);
1466
1467 if (err == noErr)
1468 err = ReplaceBTreeRecord(vcb->vcbExtentsFile, &foundKey, foundHint, &foundData, foundDataSize, &foundHint);
1469 }
1470 else { // HFS Plus volume
1471 HFSPlusExtentKey foundKey; // The key actually found during search
1472 HFSPlusExtentRecord foundData; // The extent data actually found
1473
1474 err = SearchBTreeRecord(vcb->vcbExtentsFile, extentFileKey, extentBTreeHint,
1475 &foundKey, &foundData, &foundDataSize, &foundHint);
1476
1477 if (err == noErr)
1478 CopyMemory(extentData, &foundData, sizeof(HFSPlusExtentRecord));
1479
1480 if (err == noErr)
1481 err = ReplaceBTreeRecord(vcb->vcbExtentsFile, &foundKey, foundHint, &foundData, foundDataSize, &foundHint);
1482 }
1483 }
1484
1485 return err;
1486 }
1487
1488
1489 void ExtDataRecToExtents(
1490 const HFSExtentRecord oldExtents,
1491 HFSPlusExtentRecord newExtents)
1492 {
1493 UInt32 i;
1494
1495 // copy the first 3 extents
1496 newExtents[0].startBlock = oldExtents[0].startBlock;
1497 newExtents[0].blockCount = oldExtents[0].blockCount;
1498 newExtents[1].startBlock = oldExtents[1].startBlock;
1499 newExtents[1].blockCount = oldExtents[1].blockCount;
1500 newExtents[2].startBlock = oldExtents[2].startBlock;
1501 newExtents[2].blockCount = oldExtents[2].blockCount;
1502
1503 // zero out the remaining ones
1504 for (i = 3; i < kHFSPlusExtentDensity; ++i)
1505 {
1506 newExtents[i].startBlock = 0;
1507 newExtents[i].blockCount = 0;
1508 }
1509 }
1510
1511
1512
1513 static OSErr ExtentsToExtDataRec(
1514 HFSPlusExtentRecord oldExtents,
1515 HFSExtentRecord newExtents)
1516 {
1517 OSErr err;
1518
1519 err = noErr;
1520
1521 // copy the first 3 extents
1522 newExtents[0].startBlock = oldExtents[0].startBlock;
1523 newExtents[0].blockCount = oldExtents[0].blockCount;
1524 newExtents[1].startBlock = oldExtents[1].startBlock;
1525 newExtents[1].blockCount = oldExtents[1].blockCount;
1526 newExtents[2].startBlock = oldExtents[2].startBlock;
1527 newExtents[2].blockCount = oldExtents[2].blockCount;
1528
1529 #if DEBUG_BUILD
1530 if (oldExtents[3].startBlock || oldExtents[3].blockCount) {
1531 DebugStr("\pExtentRecord with > 3 extents is invalid for HFS");
1532 err = fsDSIntErr;
1533 }
1534 #endif
1535
1536 return err;
1537 }
1538
1539
1540 OSErr GetFCBExtentRecord(
1541 const SVCB *vcb,
1542 const SFCB *fcb,
1543 HFSPlusExtentRecord extents)
1544 {
1545 if (vcb->vcbSignature == kHFSPlusSigWord)
1546 CopyMemory(fcb->fcbExtents32, extents, sizeof(HFSPlusExtentRecord));
1547 else
1548 ExtDataRecToExtents(fcb->fcbExtents16, extents);
1549 return noErr;
1550 }
1551
1552
1553
1554 static OSErr SetFCBExtentRecord(
1555 const SVCB *vcb,
1556 SFCB *fcb,
1557 HFSPlusExtentRecord extents)
1558 {
1559
1560 #if DEBUG_BUILD
1561 if (fcb->fcbVolume != vcb)
1562 DebugStr("\pVCB does not match FCB");
1563 #endif
1564
1565 if (vcb->vcbSignature == kHFSPlusSigWord)
1566 CopyMemory(extents, fcb->fcbExtents32, sizeof(HFSPlusExtentRecord));
1567 else
1568 (void) ExtentsToExtDataRec(extents, fcb->fcbExtents16);
1569
1570 return noErr;
1571 }
1572
1573
1574
1575 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1576 // Routine: MapFileBlockFromFCB
1577 //
1578 // Function: Determine if the given file offset is within the set of extents
1579 // stored in the FCB. If so, return the file allocation
1580 // block number of the start of the extent, volume allocation block number
1581 // of the start of the extent, and file allocation block number immediately
1582 // following the extent.
1583 //
1584 // Input: vcb - the volume containing the extents
1585 // fcb - the file that owns the extents
1586 // offset - desired offset in 512-byte sectors
1587 //
1588 // Output: firstFABN - file alloc block number of start of extent
1589 // firstBlock - volume alloc block number of start of extent
1590 // nextFABN - file alloc block number of next extent
1591 //
1592 // Result: noErr = ok
1593 // fxRangeErr = beyond FCB's extents
1594 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1595 static OSErr MapFileBlockFromFCB(
1596 const SVCB *vcb,
1597 const SFCB *fcb,
1598 UInt64 sectorOffset, // Desired offset in sectors from start of file
1599 UInt32 *firstFABN, // FABN of first block of found extent
1600 UInt32 *firstBlock, // Corresponding allocation block number
1601 UInt32 *nextFABN) // FABN of block after end of extent
1602 {
1603 UInt32 index;
1604 UInt32 offsetBlocks;
1605
1606 offsetBlocks = sectorOffset / (vcb->vcbBlockSize >> kSectorShift);
1607
1608 if (vcb->vcbSignature == kHFSSigWord) {
1609 const HFSExtentDescriptor *extent;
1610 UInt32 blockCount;
1611 UInt32 currentFABN;
1612
1613 extent = fcb->fcbExtents16;
1614 currentFABN = 0;
1615
1616 for (index=0; index<kHFSExtentDensity; index++) {
1617
1618 blockCount = extent->blockCount;
1619
1620 if (blockCount == 0)
1621 return fxRangeErr; // ran out of extents!
1622
1623 // Is it in this extent?
1624 if (offsetBlocks < blockCount) {
1625 *firstFABN = currentFABN;
1626 *firstBlock = extent->startBlock;
1627 currentFABN += blockCount; // faster to add these as UInt16 first, then extend to UInt32
1628 *nextFABN = currentFABN;
1629 return noErr; // found the right extent
1630 }
1631
1632 // Not in current extent, so adjust counters and loop again
1633 offsetBlocks -= blockCount;
1634 currentFABN += blockCount;
1635 extent++;
1636 }
1637 }
1638 else {
1639 const HFSPlusExtentDescriptor *extent;
1640 UInt32 blockCount;
1641 UInt32 currentFABN;
1642
1643 extent = fcb->fcbExtents32;
1644 currentFABN = 0;
1645
1646 for (index=0; index<kHFSPlusExtentDensity; index++) {
1647
1648 blockCount = extent->blockCount;
1649
1650 if (blockCount == 0)
1651 return fxRangeErr; // ran out of extents!
1652
1653 // Is it in this extent?
1654 if (offsetBlocks < blockCount) {
1655 *firstFABN = currentFABN;
1656 *firstBlock = extent->startBlock;
1657 *nextFABN = currentFABN + blockCount;
1658 return noErr; // found the right extent
1659 }
1660
1661 // Not in current extent, so adjust counters and loop again
1662 offsetBlocks -= blockCount;
1663 currentFABN += blockCount;
1664 extent++;
1665 }
1666 }
1667
1668 // If we fall through here, the extent record was full, but the offset was
1669 // beyond those extents.
1670
1671 return fxRangeErr;
1672 }
1673
1674
1675 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1676 // Routine: ZeroFileBlocks
1677 //
1678 // Function: Write all zeros to a range of a file. Currently used when
1679 // extending a B-Tree, so that all the new allocation blocks
1680 // contain zeros (to prevent them from accidentally looking
1681 // like real data).
1682 //
1683 // Input: vcb - the volume
1684 // fcb - the file
1685 // startingSector - the first 512-byte sector to write
1686 // numberOfSectors - the number of sectors to zero
1687 //
1688 // Result: noErr = ok
1689 // fxRangeErr = beyond FCB's extents
1690 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
1691 #define FSBufferSize 32768
1692
1693 OSErr ZeroFileBlocks( SVCB *vcb, SFCB *fcb, UInt32 startingSector, UInt32 numberOfSectors )
1694 {
1695 Ptr buffer;
1696 OSErr err;
1697 HIOParam iopb;
1698 UInt32 requestedBytes;
1699 UInt32 actualBytes; // Bytes actually read by CacheReadInPlace
1700 UInt32 bufferSizeSectors = FSBufferSize >> kSectorShift;
1701 UInt64 currentPosition = startingSector << kSectorShift;
1702
1703 buffer = AllocateMemory(FSBufferSize);
1704 if ( buffer == NULL )
1705 return( fileBoundsErr );
1706
1707 ClearMemory( buffer, FSBufferSize ); // Zero our buffer
1708 ClearMemory( &iopb, sizeof(iopb) ); // Zero our param block
1709
1710 iopb.ioRefNum = ResolveFileRefNum( fcb );
1711 iopb.ioBuffer = buffer;
1712 iopb.ioPosMode |= noCacheMask; // OR with the high byte
1713
1714 do
1715 {
1716 if ( numberOfSectors > bufferSizeSectors )
1717 requestedBytes = FSBufferSize;
1718 else
1719 requestedBytes = numberOfSectors << kSectorShift;
1720
1721 err = CacheWriteInPlace( vcb, iopb.ioRefNum, &iopb, currentPosition, requestedBytes, &actualBytes );
1722
1723 if ( err || actualBytes == 0 )
1724 goto BAIL;
1725
1726 // Don't update ioActCount to force writing from beginning of zero buffer
1727 currentPosition += actualBytes;
1728 numberOfSectors -= (actualBytes >> kSectorShift);
1729
1730 } while( numberOfSectors > 0 );
1731
1732 BAIL:
1733 DisposeMemory(buffer);
1734
1735 if ( err == noErr && numberOfSectors != 0 )
1736 err = eofErr;
1737
1738 return( err );
1739 }
1740
1741 //_________________________________________________________________________________
1742 //
1743 // Routine: ExtentsAreIntegral
1744 //
1745 // Purpose: Ensure that each extent can hold an integral number of nodes
1746 // Called by the NodesAreContiguous function
1747 //_________________________________________________________________________________
1748
1749 static Boolean ExtentsAreIntegral(
1750 const HFSPlusExtentRecord extentRecord,
1751 UInt32 mask,
1752 UInt32 *blocksChecked,
1753 Boolean *checkedLastExtent)
1754 {
1755 UInt32 blocks;
1756 UInt32 extentIndex;
1757
1758 *blocksChecked = 0;
1759 *checkedLastExtent = false;
1760
1761 for(extentIndex = 0; extentIndex < kHFSPlusExtentDensity; extentIndex++)
1762 {
1763 blocks = extentRecord[extentIndex].blockCount;
1764
1765 if ( blocks == 0 )
1766 {
1767 *checkedLastExtent = true;
1768 break;
1769 }
1770
1771 *blocksChecked += blocks;
1772
1773 if (blocks & mask)
1774 return false;
1775 }
1776
1777 return true;
1778 }
1779
1780 //_________________________________________________________________________________
1781 //
1782 // Routine: NodesAreContiguous
1783 //
1784 // Purpose: Ensure that all b-tree nodes are contiguous on disk
1785 // Called by BTOpenPath during volume mount
1786 //_________________________________________________________________________________
1787
1788 Boolean NodesAreContiguous(
1789 SFCB *fcb,
1790 UInt32 nodeSize)
1791 {
1792 SVCB *vcb;
1793 UInt32 mask;
1794 UInt32 startBlock;
1795 UInt32 blocksChecked;
1796 UInt32 hint;
1797 HFSPlusExtentKey key;
1798 HFSPlusExtentRecord extents;
1799 OSErr result;
1800 Boolean lastExtentReached;
1801
1802
1803 vcb = (SVCB *)fcb->fcbVolume;
1804
1805 if (vcb->vcbBlockSize >= nodeSize)
1806 return true;
1807
1808 mask = (nodeSize / vcb->vcbBlockSize) - 1;
1809
1810 // check the local extents
1811 (void) GetFCBExtentRecord(vcb, fcb, extents);
1812 if ( !ExtentsAreIntegral(extents, mask, &blocksChecked, &lastExtentReached) )
1813 return false;
1814
1815 if (lastExtentReached || ((UInt64)blocksChecked * (UInt64)vcb->vcbBlockSize) >= fcb->fcbPhysicalSize)
1816 return true;
1817
1818 startBlock = blocksChecked;
1819
1820 // check the overflow extents (if any)
1821 while ( !lastExtentReached )
1822 {
1823 result = FindExtentRecord(vcb, kDataForkType, fcb->fcbFileID, startBlock, false, &key, extents, &hint);
1824 if (result) break;
1825
1826 if ( !ExtentsAreIntegral(extents, mask, &blocksChecked, &lastExtentReached) )
1827 return false;
1828
1829 startBlock += blocksChecked;
1830 }
1831
1832 return true;
1833 }