]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfscommon/Misc/FileExtentMapping.c
d989e134a2bb6e3516309873f00921a84e1239e6
[apple/xnu.git] / bsd / hfs / hfscommon / Misc / FileExtentMapping.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 File: FileExtentMapping.c
24
25 Contains: xxx put contents here xxx
26
27 Version: HFS Plus 1.0
28
29 Written by: Dave Heller, Mark Day
30
31 Copyright: © 1996-1999 by Apple Computer, Inc., all rights reserved.
32
33 File Ownership:
34
35 DRI: Mark Day
36
37 Other Contact: xxx put other contact here xxx
38
39 Technology: xxx put technology here xxx
40
41 Writers:
42
43 (DSH) Deric Horn
44 (msd) Mark Day
45 (djb) Don Brady
46
47 Change History (most recent first):
48 <MacOSX> 9/9/99 djb Fix fcbModifiedMask flag testing logic.
49 <MacOSX> 8/25/98 djb Flush extents b-tree header if dirty (2371088).
50 <MacOSX> 6/30/98 djb Add functions NodesAreContiguous and ExtentsAreIntegral (for radar #2249539).
51 <MacOSX> 6/23/98 djb Changed DeallocFile to DeleteFile which now deletes the catalog record.
52 Fixed UpdateExtentRecord to pass correct fcb to Btree routines. Fixed
53 hfs+ bug in CreateExtentRecord (double dereference).
54 <MacOSX> 5/20/98 djb In ExtendFileC don't lie about the peof! (radar #2230094).
55 <MacOSX> 4/17/98 djb Add VCB locking.
56 <MacOSX> 4/2/98 djb Switch over to real BTree interface (no more BTreeWrapper.c).
57 <MacOSX> 3/31/98 djb Sync up with final HFSVolumes.h header file.
58
59 <CS24> 1/23/98 msd Bug 2208024: AllocContig is actually allocating one extent even
60 though there is not enough contiguous space.
61 <CS23> 12/2/97 DSH GetFCBExtentRecord no longer static so DFA can use it.
62 <CS22> 10/20/97 msd When allocating more space for a file, do the clump size
63 calculations in ExtendFileC, not BlockAllocate. Undo change from
64 <CS18>.
65 <CS21> 10/17/97 msd Conditionalize DebugStrs.
66 <CS20> 10/16/97 msd Simplify the code path for MapFileBlockC (logical to physical
67 block mapping) in the typical case where the file isn't
68 fragmented so badly that it has extents in the extents B-tree.
69 Simplified some of the calculations for all cases.
70 <CS19> 10/13/97 DSH FindExtentRecord & DeleteExtentRecord are also being used by DFA
71 no longer static.
72 <CS18> 10/6/97 msd When extending a file, set the physical EOF to include any extra
73 space allocated due to a file's clump size.
74 <CS17> 9/19/97 msd Remove the MapLogicalToPhysical SPI. It was never used and is
75 not being tested anyway.
76 <CS16> 9/5/97 msd In CompareExtentKeys and CompareExtentKeysPlus, use the symbolic
77 constants for key length. Don't DebugStr unless DEBUG_BUILD is
78 set.
79 <CS15> 7/24/97 djb Add instrumentation to MapFileBlockC
80 <CS14> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
81 collision
82 <CS13> 7/15/97 DSH AdjEOF() mark the FCB as modified. (1664389)
83 <CS12> 7/8/97 DSH Loading PrecompiledHeaders from define passed in on C line
84 <CS11> 7/3/97 msd Bug #1663518. Remove DebugStr when setting the FCB extent record
85 for a volume control file.
86 <CS10> 6/27/97 msd Moved enum kFirstFileRefnum to FilesInternal.
87 <CS9> 6/24/97 djb Include "CatalogPrivate.h"
88 <CS8> 6/16/97 msd Finish implementation of CreateLargeFile SPI.
89 <CS7> 6/12/97 msd Add stub for CreateLargeFile SPI.
90 <CS6> 6/5/97 msd Add MapLogicalToPhysical.
91 <CS5> 6/2/97 msd In TruncateFileC, don't update the extent record unless it was
92 actually changed (prevents extra updates when truncating to the
93 end of the extent, and it is the last extent of the file.) Added
94 an AdjustEOF routine called by the assembly AdjEOF routine. It
95 copies the EOF, physical length, and extent information from one
96 FCB to all other FCBs for that fork.
97 <CS4> 5/20/97 DSH Removed const declaration in MapFileBlocC, const is benign when
98 passing by value, and SC requires it to match prototype.
99 <CS3> 5/15/97 msd Change enum kResourceForkType from -1 to 0xFF since it is now
100 unsigned. Change all forkType parameters to UInt8.
101 <CS2> 5/7/97 msd When checking for an unused extent descriptor, check the length,
102 not the starting block.
103 <CS1> 4/24/97 djb first checked in
104 <HFS25> 4/11/97 DSH use extended VCB fields catalogRefNum, and extentsRefNum.
105 <HFS24> 4/4/97 djb Get in sync with volume format changes.
106 <HFS23> 3/17/97 DSH Casting to compile with SC.
107 <HFS22> 2/26/97 msd Add instrumentation in ExtendFileC and TruncateFileC. In
108 CompareExtentKeys and CompareExtentKeysPlus, make sure the key
109 lengths are correct.
110 <HFS21> 2/5/97 msd The comparison with fsBTStartOfIterationErr didn't work because
111 the enum is an unsigned long; it is now casted to an OSErr
112 before comparing.
113 <HFS20> 1/31/97 msd In FindExtentRecord, turn an fsBTStartOfIterationErr error into
114 btNotFound.
115 <HFS19> 1/28/97 msd Fixed bug in MapFileBlockC where it returned the wrong number of
116 bytes available at the given block number. This could
117 potentially cause programs to read or write over other files.
118 <HFS18> 1/16/97 djb Extent key compare procs now return SInt32. Fixed
119 UpdateExtentRecord - it was passing a pointer to an ExtentKey
120 pointer.
121 <HFS17> 1/10/97 msd Change TruncateFileC to call DellocateFork when the new PEOF is
122 0. Fixes a fxRangeErr returned when no extents existed.
123 <HFS16> 1/6/97 msd Previous change prevents extent records from being removed if
124 the files new PEOF is in the local (FCB/catalog) extents.
125 <HFS15> 1/3/97 djb Temp fix in TruncateFileC to prevent unwanted calls to
126 TruncateExtents.
127 <HFS14> 12/23/96 msd Previous change to SearchExtentFile didn't set up the outputs
128 for hint and key when the FCB extent record wasn't full.
129 <HFS13> 12/20/96 msd In SearchExtentFile, don't bother searching the extents file if
130 the FCB's extent record wasn't full, or if the FCB was for the
131 extents file itself. Modified SearchExtentRecord to return a
132 Boolean to indicate that the record was not full.
133 <HFS12> 12/19/96 DSH Changed refs from VCB to ExtendedVCB
134 <HFS11> 12/19/96 djb Updated for new B-tree Manager interface.
135 <HFS10> 12/12/96 djb Really use new SPI for GetCatalogNode.
136 <HFS9> 12/12/96 djb Use new Catalog SPI for GetCatalogNode. Added Mark's changes to
137 MapFileBlockC.
138 <HFS8> 12/11/96 msd TruncateFileC must always release extents, even if PEOF hasn't
139 changed (since allocation may have been rounded up due to clump
140 size).
141 <HFS7> 12/10/96 msd Check PRAGMA_LOAD_SUPPORTED before loading precompiled headers.
142 <HFS6> 12/4/96 DSH Precompiled headers
143 <HFS5> 11/26/96 msd Add an exported routine to grow the parallel FCB table to
144 accomodate the HFS+ ExtentRecord.
145 <HFS4> 11/26/96 msd Convert internal routines to use ExtentKey and ExtentRecord
146 (instead of the raw HFS structures).
147 <HFS3> 11/21/96 msd Added CompareExtentKeysPlus().
148 <HFS2> 11/20/96 msd Finish porting FXM to C.
149 <HFS1> 11/6/96 DKH first checked in
150
151 */
152
153
154 #include "../../hfs.h"
155 #include "../../hfs_format.h"
156 #include "../../hfs_endian.h"
157
158 #include "../headers/FileMgrInternal.h"
159 #include "../headers/BTreesInternal.h"
160 #include "../headers/CatalogPrivate.h" // calling a private catalog routine (LocateCatalogNode)
161
162 #include <sys/malloc.h>
163
164 /*
165 ============================================================
166 Public (Exported) Routines:
167 ============================================================
168 DeAllocFile Deallocate all disk space allocated to a specified file.
169 Both forks are deallocated.
170
171 ExtendFileC Allocate more space to a given file.
172
173 CompareExtentKeys
174 Compare two extents file keys (a search key and a trial
175 key). Used by the BTree manager when searching for,
176 adding, or deleting keys in the extents file of an HFS
177 volume.
178
179 CompareExtentKeysPlus
180 Compare two extents file keys (a search key and a trial
181 key). Used by the BTree manager when searching for,
182 adding, or deleting keys in the extents file of an HFS+
183 volume.
184
185 MapFileBlockC Convert (map) an offset within a given file into a
186 physical disk address.
187
188 TruncateFileC Truncates the disk space allocated to a file. The file
189 space is truncated to a specified new physical EOF, rounded
190 up to the next allocation block boundry. There is an option
191 to truncate to the end of the extent containing the new EOF.
192
193 FlushExtentFile
194 Flush the extents file for a given volume.
195
196 GrowParallelFCBs
197 Make sure the parallel FCB entries are big enough to support
198 the HFS+ ExtentRecord. If not, the array is grown and the
199 pre-existing data copied over.
200
201 AdjustEOF
202 Copy EOF, physical length, and extent records from one FCB
203 to all other FCBs for that fork. This is used when a file is
204 grown or shrunk as the result of a Write, SetEOF, or Allocate.
205
206 MapLogicalToPhysical
207 Map some position in a file to a volume block number. Also
208 returns the number of contiguous bytes that are mapped there.
209 This is a queued HFSDispatch call that does the equivalent of
210 MapFileBlockC, using a parameter block.
211
212 ============================================================
213 Internal Routines:
214 ============================================================
215 FindExtentRecord
216 Search the extents BTree for a particular extent record.
217 SearchExtentFile
218 Search the FCB and extents file for an extent record that
219 contains a given file position (in bytes).
220 SearchExtentRecord
221 Search a given extent record to see if it contains a given
222 file position (in bytes). Used by SearchExtentFile.
223 ReleaseExtents
224 Deallocate all allocation blocks in all extents of an extent
225 data record.
226 TruncateExtents
227 Deallocate blocks and delete extent records for all allocation
228 blocks beyond a certain point in a file. The starting point
229 must be the first file allocation block for some extent record
230 for the file.
231 DeallocateFork
232 Deallocate all allocation blocks belonging to a given fork.
233 UpdateExtentRecord
234 If the extent record came from the extents file, write out
235 the updated record; otherwise, copy the updated record into
236 the FCB resident extent record. If the record has no extents,
237 and was in the extents file, then delete the record instead.
238 */
239
240 enum
241 {
242 kTwoGigabytes = (UInt32) 0x80000000,
243
244 kDataForkType = 0,
245 kResourceForkType = 0xFF,
246
247 kPreviousRecord = -1
248 };
249
250 void HFSToHFSPlusExtents(
251 const HFSExtentRecord oldExtents,
252 HFSPlusExtentRecord newExtents);
253
254 OSErr HFSPlusToHFSExtents(
255 const HFSPlusExtentRecord oldExtents,
256 HFSExtentRecord newExtents);
257
258 OSErr FindExtentRecord(
259 const ExtendedVCB *vcb,
260 UInt8 forkType,
261 UInt32 fileID,
262 UInt32 startBlock,
263 Boolean allowPrevious,
264 HFSPlusExtentKey *foundKey,
265 HFSPlusExtentRecord foundData,
266 UInt32 *foundHint);
267
268 OSErr DeleteExtentRecord(
269 const ExtendedVCB *vcb,
270 UInt8 forkType,
271 UInt32 fileID,
272 UInt32 startBlock);
273
274 static OSErr CreateExtentRecord(
275 const ExtendedVCB *vcb,
276 HFSPlusExtentKey *key,
277 HFSPlusExtentRecord extents,
278 UInt32 *hint);
279
280
281 OSErr GetFCBExtentRecord(
282 const FCB *fcb,
283 HFSPlusExtentRecord extents);
284
285 static OSErr SearchExtentFile(
286 const ExtendedVCB *vcb,
287 const FCB *fcb,
288 SInt64 filePosition,
289 HFSPlusExtentKey *foundExtentKey,
290 HFSPlusExtentRecord foundExtentData,
291 UInt32 *foundExtentDataIndex,
292 UInt32 *extentBTreeHint,
293 UInt32 *endingFABNPlusOne );
294
295 static OSErr SearchExtentRecord(
296 const ExtendedVCB *vcb,
297 UInt32 searchFABN,
298 const HFSPlusExtentRecord extentData,
299 UInt32 extentDataStartFABN,
300 UInt32 *foundExtentDataOffset,
301 UInt32 *endingFABNPlusOne,
302 Boolean *noMoreExtents);
303
304 static OSErr ReleaseExtents(
305 ExtendedVCB *vcb,
306 const HFSPlusExtentRecord extentRecord,
307 UInt32 *numReleasedAllocationBlocks,
308 Boolean *releasedLastExtent);
309
310 static OSErr DeallocateFork(
311 ExtendedVCB *vcb,
312 HFSCatalogNodeID fileID,
313 UInt8 forkType,
314 HFSPlusExtentRecord catalogExtents,
315 Boolean * recordDeleted);
316
317 static OSErr TruncateExtents(
318 ExtendedVCB *vcb,
319 UInt8 forkType,
320 UInt32 fileID,
321 UInt32 startBlock,
322 Boolean * recordDeleted);
323
324 static OSErr UpdateExtentRecord (
325 const ExtendedVCB *vcb,
326 FCB *fcb,
327 const HFSPlusExtentKey *extentFileKey,
328 const HFSPlusExtentRecord extentData,
329 UInt32 extentBTreeHint);
330
331 static Boolean ExtentsAreIntegral(
332 const HFSPlusExtentRecord extentRecord,
333 UInt32 mask,
334 UInt32 *blocksChecked,
335 Boolean *checkedLastExtent);
336
337 //_________________________________________________________________________________
338 //
339 // Routine: FindExtentRecord
340 //
341 // Purpose: Search the extents BTree for an extent record matching the given
342 // FileID, fork, and starting file allocation block number.
343 //
344 // Inputs:
345 // vcb Volume to search
346 // forkType 0 = data fork, -1 = resource fork
347 // fileID File's FileID (CatalogNodeID)
348 // startBlock Starting file allocation block number
349 // allowPrevious If the desired record isn't found and this flag is set,
350 // then see if the previous record belongs to the same fork.
351 // If so, then return it.
352 //
353 // Outputs:
354 // foundKey The key data for the record actually found
355 // foundData The extent record actually found (NOTE: on an HFS volume, the
356 // fourth entry will be zeroes.
357 // foundHint The BTree hint to find the node again
358 //_________________________________________________________________________________
359 OSErr FindExtentRecord(
360 const ExtendedVCB *vcb,
361 UInt8 forkType,
362 UInt32 fileID,
363 UInt32 startBlock,
364 Boolean allowPrevious,
365 HFSPlusExtentKey *foundKey,
366 HFSPlusExtentRecord foundData,
367 UInt32 *foundHint)
368 {
369 FCB * fcb;
370 BTreeIterator btIterator;
371 FSBufferDescriptor btRecord;
372 OSErr err;
373 UInt16 btRecordSize;
374
375 err = noErr;
376 *foundHint = 0;
377 fcb = GetFileControlBlock(vcb->extentsRefNum);
378
379 (void) BTInvalidateHint(&btIterator);
380
381 if (vcb->vcbSigWord == kHFSSigWord) {
382 HFSExtentKey * extentKeyPtr;
383 HFSExtentRecord extentData;
384
385 extentKeyPtr = (HFSExtentKey*) &btIterator.key;
386 extentKeyPtr->keyLength = kHFSExtentKeyMaximumLength;
387 extentKeyPtr->forkType = forkType;
388 extentKeyPtr->fileID = fileID;
389 extentKeyPtr->startBlock = startBlock;
390
391 btRecord.bufferAddress = &extentData;
392 btRecord.itemSize = sizeof(HFSExtentRecord);
393 btRecord.itemCount = 1;
394
395 err = BTSearchRecord(fcb, &btIterator, kInvalidMRUCacheKey, &btRecord, &btRecordSize, &btIterator);
396
397 if (err == btNotFound && allowPrevious) {
398 err = BTIterateRecord(fcb, kBTreePrevRecord, &btIterator, &btRecord, &btRecordSize);
399
400 // A previous record may not exist, so just return btNotFound (like we would if
401 // it was for the wrong file/fork).
402 if (err == (OSErr) fsBTStartOfIterationErr) //¥¥ fsBTStartOfIterationErr is type unsigned long
403 err = btNotFound;
404
405 if (err == noErr) {
406 // Found a previous record. Does it belong to the same fork of the same file?
407 if (extentKeyPtr->fileID != fileID || extentKeyPtr->forkType != forkType)
408 err = btNotFound;
409 }
410 }
411
412 if (err == noErr) {
413 UInt16 i;
414
415 // Copy the found key back for the caller
416 foundKey->keyLength = kHFSPlusExtentKeyMaximumLength;
417 foundKey->forkType = extentKeyPtr->forkType;
418 foundKey->pad = 0;
419 foundKey->fileID = extentKeyPtr->fileID;
420 foundKey->startBlock = extentKeyPtr->startBlock;
421
422 // Copy the found data back for the caller
423 foundData[0].startBlock = extentData[0].startBlock;
424 foundData[0].blockCount = extentData[0].blockCount;
425 foundData[1].startBlock = extentData[1].startBlock;
426 foundData[1].blockCount = extentData[1].blockCount;
427 foundData[2].startBlock = extentData[2].startBlock;
428 foundData[2].blockCount = extentData[2].blockCount;
429
430 for (i = 3; i < kHFSPlusExtentDensity; ++i)
431 {
432 foundData[i].startBlock = 0;
433 foundData[i].blockCount = 0;
434 }
435 }
436 }
437 else { // HFS Plus volume
438 HFSPlusExtentKey * extentKeyPtr;
439 HFSPlusExtentRecord extentData;
440
441 extentKeyPtr = (HFSPlusExtentKey*) &btIterator.key;
442 extentKeyPtr->keyLength = kHFSPlusExtentKeyMaximumLength;
443 extentKeyPtr->forkType = forkType;
444 extentKeyPtr->pad = 0;
445 extentKeyPtr->fileID = fileID;
446 extentKeyPtr->startBlock = startBlock;
447
448 btRecord.bufferAddress = &extentData;
449 btRecord.itemSize = sizeof(HFSPlusExtentRecord);
450 btRecord.itemCount = 1;
451
452 err = BTSearchRecord(fcb, &btIterator, kInvalidMRUCacheKey, &btRecord, &btRecordSize, &btIterator);
453
454 if (err == btNotFound && allowPrevious) {
455 err = BTIterateRecord(fcb, kBTreePrevRecord, &btIterator, &btRecord, &btRecordSize);
456
457 // A previous record may not exist, so just return btNotFound (like we would if
458 // it was for the wrong file/fork).
459 if (err == (OSErr) fsBTStartOfIterationErr) //¥¥ fsBTStartOfIterationErr is type unsigned long
460 err = btNotFound;
461
462 if (err == noErr) {
463 // Found a previous record. Does it belong to the same fork of the same file?
464 if (extentKeyPtr->fileID != fileID || extentKeyPtr->forkType != forkType)
465 err = btNotFound;
466 }
467 }
468
469 if (err == noErr) {
470 // Copy the found key back for the caller
471 BlockMoveData(extentKeyPtr, foundKey, sizeof(HFSPlusExtentKey));
472 // Copy the found data back for the caller
473 BlockMoveData(&extentData, foundData, sizeof(HFSPlusExtentRecord));
474 }
475 }
476
477 *foundHint = btIterator.hint.nodeNum;
478 return err;
479 }
480
481
482
483 static OSErr CreateExtentRecord(
484 const ExtendedVCB *vcb,
485 HFSPlusExtentKey *key,
486 HFSPlusExtentRecord extents,
487 UInt32 *hint)
488 {
489 BTreeIterator btIterator;
490 FSBufferDescriptor btRecord;
491 UInt16 btRecordSize;
492 OSErr err;
493
494 err = noErr;
495 *hint = 0;
496 (void) BTInvalidateHint(&btIterator);
497
498 if (vcb->vcbSigWord == kHFSSigWord) {
499 HFSExtentKey * keyPtr;
500 HFSExtentRecord data;
501
502 btRecordSize = sizeof(HFSExtentRecord);
503 btRecord.bufferAddress = &data;
504 btRecord.itemSize = btRecordSize;
505 btRecord.itemCount = 1;
506
507 keyPtr = (HFSExtentKey*) &btIterator.key;
508 keyPtr->keyLength = kHFSExtentKeyMaximumLength;
509 keyPtr->forkType = key->forkType;
510 keyPtr->fileID = key->fileID;
511 keyPtr->startBlock = key->startBlock;
512
513 err = HFSPlusToHFSExtents(extents, data);
514 }
515 else { // HFS Plus volume
516 btRecordSize = sizeof(HFSPlusExtentRecord);
517 btRecord.bufferAddress = extents;
518 btRecord.itemSize = btRecordSize;
519 btRecord.itemCount = 1;
520
521 BlockMoveData(key, &btIterator.key, sizeof(HFSPlusExtentKey));
522 }
523
524 if (err == noErr)
525 err = BTInsertRecord(GetFileControlBlock(vcb->extentsRefNum), &btIterator, &btRecord, btRecordSize);
526
527 if (err == noErr)
528 *hint = btIterator.hint.nodeNum;
529
530 return err;
531 }
532
533
534 OSErr DeleteExtentRecord(
535 const ExtendedVCB *vcb,
536 UInt8 forkType,
537 UInt32 fileID,
538 UInt32 startBlock)
539 {
540 BTreeIterator btIterator;
541 OSErr err;
542
543 err = noErr;
544 (void) BTInvalidateHint(&btIterator);
545
546 if (vcb->vcbSigWord == kHFSSigWord) {
547 HFSExtentKey * keyPtr;
548
549 keyPtr = (HFSExtentKey*) &btIterator.key;
550 keyPtr->keyLength = kHFSExtentKeyMaximumLength;
551 keyPtr->forkType = forkType;
552 keyPtr->fileID = fileID;
553 keyPtr->startBlock = startBlock;
554 }
555 else { // HFS Plus volume
556 HFSPlusExtentKey * keyPtr;
557
558 keyPtr = (HFSPlusExtentKey*) &btIterator.key;
559 keyPtr->keyLength = kHFSPlusExtentKeyMaximumLength;
560 keyPtr->forkType = forkType;
561 keyPtr->pad = 0;
562 keyPtr->fileID = fileID;
563 keyPtr->startBlock = startBlock;
564 }
565
566 err = BTDeleteRecord(GetFileControlBlock(vcb->extentsRefNum), &btIterator);
567
568 return err;
569 }
570
571
572
573 //_________________________________________________________________________________
574 //
575 // Routine: MapFileBlock
576 //
577 // Function: Maps a file position into a physical disk address.
578 //
579 // Input: A2.L - VCB pointer
580 // (A1,D1.W) - FCB pointer
581 // D4.L - number of bytes desired
582 // D5.L - file position (byte address)
583 //
584 // Output: D3.L - physical start block
585 // D6.L - number of contiguous bytes available (up to D4 bytes)
586 // D0.L - result code <01Oct85>
587 // 0 = ok
588 // FXRangeErr = file position beyond mapped range <17Oct85>
589 // FXOvFlErr = extents file overflow <17Oct85>
590 // other = error <17Oct85>
591 //
592 // Called By: Log2Phys (read/write in place), Cache (map a file block).
593 //_________________________________________________________________________________
594
595 OSErr MapFileBlockC (
596 ExtendedVCB *vcb, // volume that file resides on
597 FCB *fcb, // FCB of file
598 size_t numberOfBytes, // number of contiguous bytes desired
599 off_t offset, // starting offset within file (in bytes)
600 daddr_t *startSector, // first sector (NOT an allocation block)
601 size_t *availableBytes) // number of contiguous bytes (up to numberOfBytes)
602 {
603 OSErr err;
604 UInt32 allocBlockSize; // Size of the volume's allocation block
605 UInt32 sectorSize;
606 HFSPlusExtentKey foundKey;
607 HFSPlusExtentRecord foundData;
608 UInt32 foundIndex;
609 UInt32 hint;
610 UInt32 firstFABN; // file allocation block of first block in found extent
611 UInt32 nextFABN; // file allocation block of block after end of found extent
612 off_t dataEnd; // (offset) end of range that is contiguous
613 UInt32 sectorsPerBlock; // Number of sectors per allocation block
614 UInt32 startBlock; // volume allocation block corresponding to firstFABN
615 daddr_t temp;
616 off_t tmpOff;
617
618 allocBlockSize = vcb->blockSize;
619 sectorSize = VCBTOHFS(vcb)->hfs_phys_block_size;
620
621 err = SearchExtentFile(vcb, fcb, offset, &foundKey, foundData, &foundIndex, &hint, &nextFABN);
622 if (err == noErr) {
623 startBlock = foundData[foundIndex].startBlock;
624 firstFABN = nextFABN - foundData[foundIndex].blockCount;
625 }
626
627 if (err != noErr)
628 {
629 return err;
630 }
631
632 //
633 // Determine the end of the available space. It will either be the end of the extent,
634 // or the file's PEOF, whichever is smaller.
635 //
636 dataEnd = (off_t)((off_t)(nextFABN) * (off_t)(allocBlockSize)); // Assume valid data through end of this extent
637 if (fcb->fcbPLen < dataEnd) // Is PEOF shorter?
638 dataEnd = fcb->fcbPLen; // Yes, so only map up to PEOF
639
640 // Compute the number of sectors in an allocation block
641 sectorsPerBlock = allocBlockSize / sectorSize; // sectors per allocation block
642
643 //
644 // Compute the absolute sector number that contains the offset of the given file
645 //
646
647 // offset in sectors from start of the extent
648 temp = (daddr_t)((offset - (off_t)((off_t)(firstFABN) * (off_t)(allocBlockSize)))/sectorSize);
649 // offset in sectors from start of allocation block space
650 temp += startBlock * sectorsPerBlock; // offset in sectors from start of allocation block space
651 if (vcb->vcbSigWord == kHFSPlusSigWord)
652 temp += vcb->hfsPlusIOPosOffset / sectorSize; /* offset inside wrapper */
653 else
654 temp += vcb->vcbAlBlSt; /* offset in sectors from start of volume */
655
656 // Return the desired sector for file position "offset"
657 *startSector = temp;
658
659 //
660 // Determine the number of contiguous bytes until the end of the extent
661 // (or the amount they asked for, whichever comes first).
662 //
663 tmpOff = dataEnd - offset;
664 if (tmpOff > (off_t)(numberOfBytes))
665 *availableBytes = numberOfBytes; // more there than they asked for, so pin the output
666 else
667 *availableBytes = tmpOff;
668
669 return noErr;
670 }
671
672
673 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
674 // Routine: ReleaseExtents
675 //
676 // Function: Release the extents of a single extent data record.
677 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
678
679 static OSErr ReleaseExtents(
680 ExtendedVCB *vcb,
681 const HFSPlusExtentRecord extentRecord,
682 UInt32 *numReleasedAllocationBlocks,
683 Boolean *releasedLastExtent)
684 {
685 UInt32 extentIndex;
686 UInt32 numberOfExtents;
687 OSErr err = noErr;
688
689 *numReleasedAllocationBlocks = 0;
690 *releasedLastExtent = false;
691
692 if (vcb->vcbSigWord == kHFSPlusSigWord)
693 numberOfExtents = kHFSPlusExtentDensity;
694 else
695 numberOfExtents = kHFSExtentDensity;
696
697 for( extentIndex = 0; extentIndex < numberOfExtents; extentIndex++)
698 {
699 UInt32 numAllocationBlocks;
700
701 // Loop over the extent record and release the blocks associated with each extent.
702
703 numAllocationBlocks = extentRecord[extentIndex].blockCount;
704 if ( numAllocationBlocks == 0 )
705 {
706 *releasedLastExtent = true;
707 break;
708 }
709
710 err = BlockDeallocate( vcb, extentRecord[extentIndex].startBlock, numAllocationBlocks );
711 if ( err != noErr )
712 break;
713
714 *numReleasedAllocationBlocks += numAllocationBlocks; // bump FABN to beg of next extent
715 }
716
717 return( err );
718 }
719
720
721
722 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
723 // Routine: TruncateExtents
724 //
725 // Purpose: Delete extent records whose starting file allocation block number
726 // is greater than or equal to a given starting block number. The
727 // allocation blocks represented by the extents are deallocated.
728 //
729 // Inputs:
730 // vcb Volume to operate on
731 // fileID Which file to operate on
732 // startBlock Starting file allocation block number for first extent
733 // record to delete.
734 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
735
736 static OSErr TruncateExtents(
737 ExtendedVCB *vcb,
738 UInt8 forkType,
739 UInt32 fileID,
740 UInt32 startBlock,
741 Boolean * recordDeleted)
742 {
743 OSErr err;
744 UInt32 numberExtentsReleased;
745 Boolean releasedLastExtent;
746 UInt32 hint;
747 HFSPlusExtentKey key;
748 HFSPlusExtentRecord extents;
749
750 while (true) {
751 err = FindExtentRecord(vcb, forkType, fileID, startBlock, false, &key, extents, &hint);
752 if (err != noErr) {
753 if (err == btNotFound)
754 err = noErr;
755 break;
756 }
757
758 err = ReleaseExtents( vcb, extents, &numberExtentsReleased, &releasedLastExtent );
759 if (err != noErr) break;
760
761 err = DeleteExtentRecord(vcb, forkType, fileID, startBlock);
762 if (err != noErr) break;
763
764 *recordDeleted = true;
765 startBlock += numberExtentsReleased;
766 }
767
768 return err;
769 }
770
771
772
773 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
774 // Routine: DeallocateFork
775 //
776 // Function: De-allocates all disk space allocated to a specified fork.
777 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
778
779 static OSErr DeallocateFork(
780 ExtendedVCB *vcb,
781 HFSCatalogNodeID fileID,
782 UInt8 forkType,
783 HFSPlusExtentRecord catalogExtents,
784 Boolean * recordDeleted) /* true if a record was deleted */
785 {
786 OSErr err;
787 UInt32 numReleasedAllocationBlocks;
788 Boolean releasedLastExtent;
789
790 // Release the catalog extents
791 err = ReleaseExtents( vcb, catalogExtents, &numReleasedAllocationBlocks, &releasedLastExtent );
792 // Release the extra extents, if present
793 if (err == noErr && !releasedLastExtent)
794 err = TruncateExtents(vcb, forkType, fileID, numReleasedAllocationBlocks, recordDeleted);
795
796 return( err );
797 }
798
799 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
800 // Routine: FlushExtentFile
801 //
802 // Function: Flushes the extent file for a specified volume
803 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
804
805 OSErr FlushExtentFile( ExtendedVCB *vcb )
806 {
807 FCB * fcb;
808 OSErr err;
809
810 fcb = GetFileControlBlock(vcb->extentsRefNum);
811 err = BTFlushPath(fcb);
812 if ( err == noErr )
813 {
814 // If the FCB for the extent "file" is dirty, mark the VCB as dirty.
815
816 if ((fcb->fcbFlags & fcbModifiedMask) != 0)
817 {
818 MarkVCBDirty( vcb );
819 err = FlushVolumeControlBlock( vcb );
820 }
821 }
822
823 return( err );
824 }
825
826 //-------------------------------------------------------------------------------
827 // Routine: DeleteFile
828 //
829 // Function: De-allocates all disk space allocated to a specified file
830 // including the space used by the catalog (ie the catalog record).
831 // The space occupied by both forks is also deallocated.
832 //
833 //-------------------------------------------------------------------------------
834
835 OSErr DeleteFile( ExtendedVCB *vcb, HFSCatalogNodeID parDirID, ConstUTF8Param catalogName, UInt32 catalogHint )
836 {
837 OSErr err;
838 OSErr errDF, errRF;
839 CatalogNodeData catalogData;
840 Boolean recordDeleted;
841
842 recordDeleted = false;
843
844 INIT_CATALOGDATA(&catalogData, kCatNameNoCopyName);
845
846 // Find catalog data in catalog
847 err = GetCatalogNode( vcb, parDirID, catalogName, kUndefinedStrLen, catalogHint, &catalogData, &catalogHint);
848 if( err != noErr )
849 goto Exit;
850
851
852 // Check to make sure record is for a file
853 if ( catalogData.cnd_type != kCatalogFileNode )
854 {
855 err = notAFileErr;
856 goto Exit;
857 }
858
859 //
860 // Always delete the Catalog record first (to minimize disk corruption)
861 //
862 err = DeleteCatalogNode(vcb, parDirID, catalogName, catalogHint);
863 if( err != noErr )
864 goto Exit;
865
866 //
867 // Note: we don't report errors from DeallocateFork since the
868 // file no longer exists (since DeleteCatalogNode succeeded).
869 // Any errors mean that there are possibly some orphaned disk
870 // blocks but from the clients perspective the file was deleted.
871 //
872
873 // Deallocate data fork extents
874 errDF = DeallocateFork( vcb, catalogData.cnd_nodeID, kDataForkType,
875 catalogData.cnd_datafork.extents, &recordDeleted );
876
877 // Deallocate resource fork extents
878 errRF = DeallocateFork( vcb, catalogData.cnd_nodeID, kResourceForkType,
879 catalogData.cnd_rsrcfork.extents, &recordDeleted );
880
881 if (recordDeleted)
882 err = FlushExtentFile( vcb );
883
884 CLEAN_CATALOGDATA(&catalogData);
885 return (errDF ? errDF : (errRF ? errRF : err));
886 Exit:
887
888 CLEAN_CATALOGDATA(&catalogData);
889 return( err );
890 }
891
892 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
893 // Routine: CompareExtentKeys
894 //
895 // Function: Compares two extent file keys (a search key and a trial key) for
896 // an HFS volume.
897 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
898
899 SInt32 CompareExtentKeys( const HFSExtentKey *searchKey, const HFSExtentKey *trialKey )
900 {
901 SInt32 result; // ± 1
902
903 #if DEBUG_BUILD
904 if (searchKey->keyLength != kHFSExtentKeyMaximumLength)
905 DebugStr("\pHFS: search Key is wrong length");
906 if (trialKey->keyLength != kHFSExtentKeyMaximumLength)
907 DebugStr("\pHFS: trial Key is wrong length");
908 #endif
909
910 result = -1; // assume searchKey < trialKey
911
912 if (searchKey->fileID == trialKey->fileID) {
913 //
914 // FileNum's are equal; compare fork types
915 //
916 if (searchKey->forkType == trialKey->forkType) {
917 //
918 // Fork types are equal; compare allocation block number
919 //
920 if (searchKey->startBlock == trialKey->startBlock) {
921 //
922 // Everything is equal
923 //
924 result = 0;
925 }
926 else {
927 //
928 // Allocation block numbers differ; determine sign
929 //
930 if (searchKey->startBlock > trialKey->startBlock)
931 result = 1;
932 }
933 }
934 else {
935 //
936 // Fork types differ; determine sign
937 //
938 if (searchKey->forkType > trialKey->forkType)
939 result = 1;
940 }
941 }
942 else {
943 //
944 // FileNums differ; determine sign
945 //
946 if (searchKey->fileID > trialKey->fileID)
947 result = 1;
948 }
949
950 return( result );
951 }
952
953
954
955 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
956 // Routine: CompareExtentKeysPlus
957 //
958 // Function: Compares two extent file keys (a search key and a trial key) for
959 // an HFS volume.
960 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
961
962 SInt32 CompareExtentKeysPlus( const HFSPlusExtentKey *searchKey, const HFSPlusExtentKey *trialKey )
963 {
964 SInt32 result; // ± 1
965
966 #if DEBUG_BUILD
967 if (searchKey->keyLength != kHFSPlusExtentKeyMaximumLength)
968 DebugStr("\pHFS: search Key is wrong length");
969 if (trialKey->keyLength != kHFSPlusExtentKeyMaximumLength)
970 DebugStr("\pHFS: trial Key is wrong length");
971 #endif
972
973 result = -1; // assume searchKey < trialKey
974
975 if (searchKey->fileID == trialKey->fileID) {
976 //
977 // FileNum's are equal; compare fork types
978 //
979 if (searchKey->forkType == trialKey->forkType) {
980 //
981 // Fork types are equal; compare allocation block number
982 //
983 if (searchKey->startBlock == trialKey->startBlock) {
984 //
985 // Everything is equal
986 //
987 result = 0;
988 }
989 else {
990 //
991 // Allocation block numbers differ; determine sign
992 //
993 if (searchKey->startBlock > trialKey->startBlock)
994 result = 1;
995 }
996 }
997 else {
998 //
999 // Fork types differ; determine sign
1000 //
1001 if (searchKey->forkType > trialKey->forkType)
1002 result = 1;
1003 }
1004 }
1005 else {
1006 //
1007 // FileNums differ; determine sign
1008 //
1009 if (searchKey->fileID > trialKey->fileID)
1010 result = 1;
1011 }
1012
1013 return( result );
1014 }
1015
1016
1017
1018 //_________________________________________________________________________________
1019 //
1020 // Routine: Extendfile
1021 //
1022 // Function: Extends the disk space allocated to a file.
1023 //
1024 // Input: A2.L - VCB pointer
1025 // A1.L - pointer to FCB array
1026 // D1.W - file refnum
1027 // D3.B - option flags
1028 // kEFContigMask - force contiguous allocation
1029 // kEFAllMask - allocate all requested bytes or none
1030 // NOTE: You may not set both options.
1031 // D4.L - number of additional bytes to allocate
1032 //
1033 // Output: D0.W - result code
1034 // 0 = ok
1035 // -n = IO error
1036 // D6.L - number of bytes allocated
1037 //
1038 // Called by: FileAloc,FileWrite,SetEof
1039 //
1040 // Note: ExtendFile updates the PEOF in the FCB.
1041 //_________________________________________________________________________________
1042
1043 OSErr ExtendFileC (
1044 ExtendedVCB *vcb, // volume that file resides on
1045 FCB *fcb, // FCB of file to truncate
1046 SInt64 bytesToAdd, // number of bytes to allocate
1047 UInt32 blockHint, // desired starting allocation block
1048 UInt32 flags, // EFContig and/or EFAll
1049 SInt64 *actualBytesAdded) // number of bytes actually allocated
1050 {
1051 OSErr err;
1052 UInt32 volumeBlockSize;
1053 SInt64 blocksToAdd;
1054 SInt64 bytesThisExtent;
1055 HFSPlusExtentKey foundKey;
1056 HFSPlusExtentRecord foundData;
1057 UInt32 foundIndex;
1058 UInt32 hint;
1059 UInt32 nextBlock;
1060 UInt32 startBlock;
1061 Boolean allOrNothing;
1062 Boolean forceContig;
1063 Boolean wantContig;
1064 Boolean needsFlush;
1065 UInt32 actualStartBlock;
1066 UInt32 actualNumBlocks;
1067 UInt32 numExtentsPerRecord;
1068 SInt64 maximumBytes;
1069 SInt64 peof;
1070 SInt64 previousPEOF;
1071
1072
1073 needsFlush = false;
1074 *actualBytesAdded = 0;
1075 volumeBlockSize = vcb->blockSize;
1076 allOrNothing = ((flags & kEFAllMask) != 0);
1077 forceContig = ((flags & kEFContigMask) != 0);
1078 previousPEOF = fcb->fcbPLen;
1079
1080 if (vcb->vcbSigWord == kHFSPlusSigWord)
1081 numExtentsPerRecord = kHFSPlusExtentDensity;
1082 else
1083 numExtentsPerRecord = kHFSExtentDensity;
1084
1085 //
1086 // Make sure the request and new PEOF are less than 2GB if HFS.
1087 //
1088 if (vcb->vcbSigWord == kHFSSigWord) {
1089 if (bytesToAdd >= kTwoGigabytes)
1090 goto Overflow;
1091 if ((fcb->fcbPLen + bytesToAdd) >= kTwoGigabytes)
1092 goto Overflow;
1093 }
1094 //
1095 // Determine how many blocks need to be allocated.
1096 // Round up the number of desired bytes to add.
1097 //
1098 blocksToAdd = FileBytesToBlocks(bytesToAdd, volumeBlockSize);
1099 bytesToAdd = (SInt64)((SInt64)blocksToAdd * (SInt64)volumeBlockSize);
1100
1101 //
1102 // If the file's clump size is larger than the allocation block size,
1103 // then set the maximum number of bytes to the requested number of bytes
1104 // rounded up to a multiple of the clump size.
1105 //
1106 if (fcb->fcbClmpSize > volumeBlockSize) {
1107 maximumBytes = (SInt64)FileBytesToBlocks(bytesToAdd, fcb->fcbClmpSize);
1108 maximumBytes *= fcb->fcbClmpSize;
1109 }
1110 else {
1111 maximumBytes = bytesToAdd;
1112 }
1113
1114 //
1115 // Compute new physical EOF, rounded up to a multiple of a block.
1116 //
1117 if ((vcb->vcbSigWord == kHFSSigWord) && ((fcb->fcbPLen + bytesToAdd) >= (SInt64) kTwoGigabytes)) // Too big?
1118 if (allOrNothing) // Yes, must they have it all?
1119 goto Overflow; // Yes, can't have it
1120 else {
1121 --blocksToAdd; // No, give give 'em one block less
1122 bytesToAdd -= volumeBlockSize;
1123 }
1124
1125 //
1126 // If allocation is all-or-nothing, make sure there are
1127 // enough free blocks on the volume (quick test).
1128 //
1129 if (allOrNothing && (blocksToAdd > (SInt64)vcb->freeBlocks)) {
1130 err = dskFulErr;
1131 goto ErrorExit;
1132 }
1133
1134 //
1135 // See if there are already enough blocks allocated to the file.
1136 //
1137 peof = fcb->fcbPLen + bytesToAdd; // potential new PEOF
1138 err = SearchExtentFile(vcb, fcb, peof-1, &foundKey, foundData, &foundIndex, &hint, &nextBlock);
1139 if (err == noErr) {
1140 // Enough blocks are already allocated. Just update the FCB to reflect the new length.
1141 fcb->fcbPLen = peof;
1142 H_EXTENDSIZE(fcb, bytesToAdd);
1143 fcb->fcbFlags |= fcbModifiedMask;
1144 goto Exit;
1145 }
1146 if (err != fxRangeErr) // Any real error?
1147 goto ErrorExit; // Yes, so exit immediately
1148
1149 //
1150 // Adjust the PEOF to the end of the last extent.
1151 //
1152 peof = (SInt64)((SInt64)nextBlock * (SInt64)volumeBlockSize); // currently allocated PEOF
1153 bytesThisExtent = peof - fcb->fcbPLen;
1154 if (bytesThisExtent != 0) {
1155 fcb->fcbPLen = peof;
1156 H_EXTENDSIZE(fcb, bytesThisExtent);
1157 fcb->fcbFlags |= fcbModifiedMask;
1158 bytesToAdd -= bytesThisExtent;
1159 }
1160
1161 //
1162 // Allocate some more space.
1163 //
1164 // First try a contiguous allocation (of the whole amount).
1165 // If that fails, get whatever we can.
1166 // If forceContig, then take whatever we got
1167 // else, keep getting bits and pieces (non-contig)
1168 err = noErr;
1169 wantContig = true;
1170 vcb->vcbFreeExtCnt = 0; /* For now, force rebuild of free extent list */
1171 do {
1172 if (blockHint != 0)
1173 startBlock = blockHint;
1174 else
1175 startBlock = foundData[foundIndex].startBlock + foundData[foundIndex].blockCount;
1176 err = BlockAllocate(vcb, startBlock, bytesToAdd, maximumBytes, wantContig, &actualStartBlock, &actualNumBlocks);
1177 if (err == dskFulErr) {
1178 if (forceContig)
1179 break; // AllocContig failed because not enough contiguous space
1180 if (wantContig) {
1181 // Couldn't get one big chunk, so get whatever we can.
1182 err = noErr;
1183 wantContig = false;
1184 continue;
1185 }
1186 if (actualNumBlocks != 0)
1187 err = noErr;
1188 }
1189 if (err == noErr) {
1190 // Add the new extent to the existing extent record, or create a new one.
1191 if ((actualStartBlock == startBlock) && (blockHint == 0)) {
1192 // We grew the file's last extent, so just adjust the number of blocks.
1193 foundData[foundIndex].blockCount += actualNumBlocks;
1194 err = UpdateExtentRecord(vcb, fcb, &foundKey, foundData, hint);
1195 if (err != noErr) break;
1196 }
1197 else {
1198 UInt16 i;
1199
1200 // Need to add a new extent. See if there is room in the current record.
1201 if (foundData[foundIndex].blockCount != 0) // Is current extent free to use?
1202 ++foundIndex; // No, so use the next one.
1203 if (foundIndex == numExtentsPerRecord) {
1204 // This record is full. Need to create a new one.
1205 if (H_FILEID(fcb) == kHFSExtentsFileID) {
1206 (void) BlockDeallocate(vcb, actualStartBlock, actualNumBlocks);
1207 err = dskFulErr; // Oops. Can't extend extents file past first record.
1208 break;
1209 }
1210
1211 foundKey.keyLength = kHFSPlusExtentKeyMaximumLength;
1212 if (fcb->fcbFlags & fcbResourceMask)
1213 foundKey.forkType = kResourceForkType;
1214 else
1215 foundKey.forkType = kDataForkType;
1216 foundKey.pad = 0;
1217 foundKey.fileID = H_FILEID(fcb);
1218 foundKey.startBlock = nextBlock;
1219
1220 foundData[0].startBlock = actualStartBlock;
1221 foundData[0].blockCount = actualNumBlocks;
1222
1223 // zero out remaining extents...
1224 for (i = 1; i < kHFSPlusExtentDensity; ++i)
1225 {
1226 foundData[i].startBlock = 0;
1227 foundData[i].blockCount = 0;
1228 }
1229
1230 foundIndex = 0;
1231
1232 err = CreateExtentRecord(vcb, &foundKey, foundData, &hint);
1233 if (err == fxOvFlErr) {
1234 // We couldn't create an extent record because extents B-tree
1235 // couldn't grow. Dellocate the extent just allocated and
1236 // return a disk full error.
1237 (void) BlockDeallocate(vcb, actualStartBlock, actualNumBlocks);
1238 err = dskFulErr;
1239 }
1240 if (err != noErr) break;
1241
1242 needsFlush = true; // We need to update the B-tree header
1243 }
1244 else {
1245 // Add a new extent into this record and update.
1246 foundData[foundIndex].startBlock = actualStartBlock;
1247 foundData[foundIndex].blockCount = actualNumBlocks;
1248 err = UpdateExtentRecord(vcb, fcb, &foundKey, foundData, hint);
1249 if (err != noErr) break;
1250 }
1251 }
1252
1253 // Figure out how many bytes were actually allocated.
1254 // NOTE: BlockAllocate could have allocated more than we asked for.
1255 // Don't set the PEOF beyond what our client asked for.
1256 nextBlock += actualNumBlocks;
1257 bytesThisExtent = (SInt64)((SInt64)actualNumBlocks * (SInt64)volumeBlockSize);
1258 if (bytesThisExtent > bytesToAdd) {
1259 bytesToAdd = 0;
1260 }
1261 else {
1262 bytesToAdd -= bytesThisExtent;
1263 maximumBytes -= bytesThisExtent;
1264 }
1265 fcb->fcbPLen += bytesThisExtent;
1266 H_EXTENDSIZE(fcb, bytesThisExtent);
1267 fcb->fcbFlags |= fcbModifiedMask;
1268
1269 // If contiguous allocation was requested, then we've already got one contiguous
1270 // chunk. If we didn't get all we wanted, then adjust the error to disk full.
1271 if (forceContig) {
1272 if (bytesToAdd != 0)
1273 err = dskFulErr;
1274 break; // We've already got everything that's contiguous
1275 }
1276 }
1277 } while (err == noErr && bytesToAdd);
1278
1279 ErrorExit:
1280 Exit:
1281 *actualBytesAdded = fcb->fcbPLen - previousPEOF;
1282
1283 if (needsFlush)
1284 (void) FlushExtentFile(vcb);
1285
1286 return err;
1287
1288 Overflow:
1289 err = fileBoundsErr;
1290 goto ErrorExit;
1291 }
1292
1293
1294
1295 //_________________________________________________________________________________
1296 //
1297 // Routine: TruncateFileC
1298 //
1299 // Function: Truncates the disk space allocated to a file. The file space is
1300 // truncated to a specified new PEOF rounded up to the next allocation
1301 // block boundry. If the 'TFTrunExt' option is specified, the file is
1302 // truncated to the end of the extent containing the new PEOF.
1303 //
1304 // Input: A2.L - VCB pointer
1305 // A1.L - pointer to FCB array
1306 // D1.W - file refnum
1307 // D2.B - option flags
1308 // TFTrunExt - truncate to the extent containing new PEOF
1309 // D3.L - new PEOF
1310 //
1311 // Output: D0.W - result code
1312 // 0 = ok
1313 // -n = IO error
1314 //
1315 // Note: TruncateFile updates the PEOF in the FCB.
1316 //_________________________________________________________________________________
1317
1318 OSErr TruncateFileC (
1319 ExtendedVCB *vcb, // volume that file resides on
1320 FCB *fcb, // FCB of file to truncate
1321 SInt64 peof, // new physical size for file
1322 Boolean truncateToExtent) // if true, truncate to end of extent containing newPEOF
1323 {
1324 OSErr err;
1325 UInt32 nextBlock; // next file allocation block to consider
1326 UInt32 startBlock; // Physical (volume) allocation block number of start of a range
1327 UInt32 physNumBlocks; // Number of allocation blocks in file (according to PEOF)
1328 UInt32 numBlocks;
1329 HFSPlusExtentKey key; // key for current extent record; key->keyLength == 0 if FCB's extent record
1330 UInt32 hint; // BTree hint corresponding to key
1331 HFSPlusExtentRecord extentRecord;
1332 UInt32 extentIndex;
1333 UInt32 extentNextBlock;
1334 UInt32 numExtentsPerRecord;
1335 SInt64 temp64;
1336 UInt8 forkType;
1337 Boolean extentChanged; // true if we actually changed an extent
1338 Boolean recordDeleted; // true if an extent record got deleted
1339
1340
1341 recordDeleted = false;
1342
1343 if (vcb->vcbSigWord == kHFSPlusSigWord)
1344 numExtentsPerRecord = kHFSPlusExtentDensity;
1345 else
1346 numExtentsPerRecord = kHFSExtentDensity;
1347
1348 if (fcb->fcbFlags & fcbResourceMask)
1349 forkType = kResourceForkType;
1350 else
1351 forkType = kDataForkType;
1352
1353 temp64 = fcb->fcbPLen / (SInt64)vcb->blockSize; // number of allocation blocks currently in file
1354 physNumBlocks = (UInt32)temp64;
1355
1356 //
1357 // Round newPEOF up to a multiple of the allocation block size. If new size is
1358 // two gigabytes or more, then round down by one allocation block (??? really?
1359 // shouldn't that be an error?).
1360 //
1361 nextBlock = FileBytesToBlocks(peof, vcb->blockSize); // number of allocation blocks to remain in file
1362 peof = (SInt64)((SInt64)nextBlock * (SInt64)vcb->blockSize); // number of bytes in those blocks
1363 if ((vcb->vcbSigWord == kHFSSigWord) && (peof >= (UInt32) kTwoGigabytes)) {
1364 #if DEBUG_BUILD
1365 DebugStr("\pHFS: Trying to truncate a file to 2GB or more");
1366 #endif
1367 err = fileBoundsErr;
1368 goto ErrorExit;
1369 }
1370
1371 //
1372 // Update FCB's length
1373 //
1374 H_TRUNCSIZE(fcb, fcb->fcbPLen - peof);
1375 fcb->fcbPLen = peof;
1376 fcb->fcbFlags |= fcbModifiedMask;
1377
1378 //
1379 // If the new PEOF is 0, then truncateToExtent has no meaning (we should always deallocate
1380 // all storage).
1381 //
1382 if (peof == 0) {
1383 int i;
1384
1385 // Deallocate all the extents for this fork
1386 err = DeallocateFork(vcb, H_FILEID(fcb), forkType, fcb->fcbExtents, &recordDeleted);
1387 if (err != noErr) goto ErrorExit; // got some error, so return it
1388
1389 // Update the catalog extent record (making sure it's zeroed out)
1390 if (err == noErr) {
1391 for (i=0; i < kHFSPlusExtentDensity; i++) {
1392 fcb->fcbExtents[i].startBlock = 0;
1393 fcb->fcbExtents[i].blockCount = 0;
1394 }
1395 }
1396 goto Done;
1397 }
1398
1399 //
1400 // Find the extent containing byte (peof-1). This is the last extent we'll keep.
1401 // (If truncateToExtent is true, we'll keep the whole extent; otherwise, we'll only
1402 // keep up through peof). The search will tell us how many allocation blocks exist
1403 // in the found extent plus all previous extents.
1404 //
1405 err = SearchExtentFile(vcb, fcb, peof-1, &key, extentRecord, &extentIndex, &hint, &extentNextBlock);
1406 if (err != noErr) goto ErrorExit;
1407
1408 extentChanged = false; // haven't changed the extent yet
1409
1410 if (!truncateToExtent) {
1411 //
1412 // Shorten this extent. It may be the case that the entire extent gets
1413 // freed here.
1414 //
1415 numBlocks = extentNextBlock - nextBlock; // How many blocks in this extent to free up
1416 if (numBlocks != 0) {
1417 // Compute first volume allocation block to free
1418 startBlock = extentRecord[extentIndex].startBlock + extentRecord[extentIndex].blockCount - numBlocks;
1419 // Free the blocks in bitmap
1420 err = BlockDeallocate(vcb, startBlock, numBlocks);
1421 if (err != noErr) goto ErrorExit;
1422 // Adjust length of this extent
1423 extentRecord[extentIndex].blockCount -= numBlocks;
1424 // If extent is empty, set start block to 0
1425 if (extentRecord[extentIndex].blockCount == 0)
1426 extentRecord[extentIndex].startBlock = 0;
1427 // Remember that we changed the extent record
1428 extentChanged = true;
1429 }
1430 }
1431
1432 //
1433 // Now move to the next extent in the record, and set up the file allocation block number
1434 //
1435 nextBlock = extentNextBlock; // Next file allocation block to free
1436 ++extentIndex; // Its index within the extent record
1437
1438 //
1439 // Release all following extents in this extent record. Update the record.
1440 //
1441 while (extentIndex < numExtentsPerRecord && extentRecord[extentIndex].blockCount != 0) {
1442 numBlocks = extentRecord[extentIndex].blockCount;
1443 // Deallocate this extent
1444 err = BlockDeallocate(vcb, extentRecord[extentIndex].startBlock, numBlocks);
1445 if (err != noErr) goto ErrorExit;
1446 // Update next file allocation block number
1447 nextBlock += numBlocks;
1448 // Zero out start and length of this extent to delete it from record
1449 extentRecord[extentIndex].startBlock = 0;
1450 extentRecord[extentIndex].blockCount = 0;
1451 // Remember that we changed an extent
1452 extentChanged = true;
1453 // Move to next extent in record
1454 ++extentIndex;
1455 }
1456
1457 //
1458 // If any of the extents in the current record were changed, then update that
1459 // record (in the FCB, or extents file).
1460 //
1461 if (extentChanged) {
1462 err = UpdateExtentRecord(vcb, fcb, &key, extentRecord, hint);
1463 if (err != noErr) goto ErrorExit;
1464 }
1465
1466 //
1467 // If there are any following allocation blocks, then we need
1468 // to seach for their extent records and delete those allocation
1469 // blocks.
1470 //
1471 if (nextBlock < physNumBlocks)
1472 err = TruncateExtents(vcb, forkType, H_FILEID(fcb), nextBlock, &recordDeleted);
1473
1474 Done:
1475 ErrorExit:
1476
1477 if (recordDeleted)
1478 (void) FlushExtentFile(vcb);
1479
1480 return err;
1481 }
1482
1483
1484
1485 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1486 // Routine: SearchExtentRecord (was XRSearch)
1487 //
1488 // Function: Searches extent record for the extent mapping a given file
1489 // allocation block number (FABN).
1490 //
1491 // Input: searchFABN - desired FABN
1492 // extentData - pointer to extent data record (xdr)
1493 // extentDataStartFABN - beginning FABN for extent record
1494 //
1495 // Output: foundExtentDataOffset - offset to extent entry within xdr
1496 // result = noErr, offset to extent mapping desired FABN
1497 // result = FXRangeErr, offset to last extent in record
1498 // endingFABNPlusOne - ending FABN +1
1499 // noMoreExtents - True if the extent was not found, and the
1500 // extent record was not full (so don't bother
1501 // looking in subsequent records); false otherwise.
1502 //
1503 // Result: noErr = ok
1504 // FXRangeErr = desired FABN > last mapped FABN in record
1505 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1506
1507 static OSErr SearchExtentRecord(
1508 const ExtendedVCB *vcb,
1509 UInt32 searchFABN,
1510 const HFSPlusExtentRecord extentData,
1511 UInt32 extentDataStartFABN,
1512 UInt32 *foundExtentIndex,
1513 UInt32 *endingFABNPlusOne,
1514 Boolean *noMoreExtents)
1515 {
1516 OSErr err = noErr;
1517 UInt32 extentIndex;
1518 UInt32 numberOfExtents;
1519 UInt32 numAllocationBlocks;
1520 Boolean foundExtent;
1521
1522 *endingFABNPlusOne = extentDataStartFABN;
1523 *noMoreExtents = false;
1524 foundExtent = false;
1525
1526 if (vcb->vcbSigWord == kHFSPlusSigWord)
1527 numberOfExtents = kHFSPlusExtentDensity;
1528 else
1529 numberOfExtents = kHFSExtentDensity;
1530
1531 for( extentIndex = 0; extentIndex < numberOfExtents; ++extentIndex )
1532 {
1533
1534 // Loop over the extent record and find the search FABN.
1535
1536 numAllocationBlocks = extentData[extentIndex].blockCount;
1537 if ( numAllocationBlocks == 0 )
1538 {
1539 break;
1540 }
1541
1542 *endingFABNPlusOne += numAllocationBlocks;
1543
1544 if( searchFABN < *endingFABNPlusOne )
1545 {
1546 // Found the extent.
1547 foundExtent = true;
1548 break;
1549 }
1550 }
1551
1552 if( foundExtent )
1553 {
1554 // Found the extent. Note the extent offset
1555 *foundExtentIndex = extentIndex;
1556 }
1557 else
1558 {
1559 // Did not find the extent. Set foundExtentDataOffset accordingly
1560 if( extentIndex > 0 )
1561 {
1562 *foundExtentIndex = extentIndex - 1;
1563 }
1564 else
1565 {
1566 *foundExtentIndex = 0;
1567 }
1568
1569 // If we found an empty extent, then set noMoreExtents.
1570 if (extentIndex < numberOfExtents)
1571 *noMoreExtents = true;
1572
1573 // Finally, return an error to the caller
1574 err = fxRangeErr;
1575 }
1576
1577 return( err );
1578 }
1579
1580 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1581 // Routine: SearchExtentFile (was XFSearch)
1582 //
1583 // Function: Searches extent file (including the FCB resident extent record)
1584 // for the extent mapping a given file position.
1585 //
1586 // Input: vcb - VCB pointer
1587 // fcb - FCB pointer
1588 // filePosition - file position (byte address)
1589 //
1590 // Output: foundExtentKey - extent key record (xkr)
1591 // If extent was found in the FCB's resident extent record,
1592 // then foundExtentKey->keyLength will be set to 0.
1593 // foundExtentData - extent data record(xdr)
1594 // foundExtentIndex - index to extent entry in xdr
1595 // result = 0, offset to extent mapping desired FABN
1596 // result = FXRangeErr, offset to last extent in record
1597 // (i.e., kNumExtentsPerRecord-1)
1598 // extentBTreeHint - BTree hint for extent record
1599 // kNoHint = Resident extent record
1600 // endingFABNPlusOne - ending FABN +1
1601 //
1602 // Result:
1603 // noErr Found an extent that contains the given file position
1604 // FXRangeErr Given position is beyond the last allocated extent
1605 // (other) (some other internal I/O error)
1606 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1607
1608 static OSErr SearchExtentFile(
1609 const ExtendedVCB *vcb,
1610 const FCB *fcb,
1611 SInt64 filePosition,
1612 HFSPlusExtentKey *foundExtentKey,
1613 HFSPlusExtentRecord foundExtentData,
1614 UInt32 *foundExtentIndex,
1615 UInt32 *extentBTreeHint,
1616 UInt32 *endingFABNPlusOne )
1617 {
1618 OSErr err;
1619 UInt32 filePositionBlock;
1620 SInt64 temp64;
1621 Boolean noMoreExtents;
1622
1623 temp64 = filePosition / (SInt64)vcb->blockSize;
1624 filePositionBlock = (UInt32)temp64;
1625
1626 bcopy ( fcb->fcbExtents, foundExtentData, sizeof(HFSPlusExtentRecord));
1627
1628 // Search the resident FCB first.
1629 err = SearchExtentRecord( vcb, filePositionBlock, foundExtentData, 0,
1630 foundExtentIndex, endingFABNPlusOne, &noMoreExtents );
1631
1632 if( err == noErr ) {
1633 // Found the extent. Set results accordingly
1634 *extentBTreeHint = kNoHint; // no hint, because not in the BTree
1635 foundExtentKey->keyLength = 0; // 0 = the FCB itself
1636
1637 goto Exit;
1638 }
1639
1640 // Didn't find extent in FCB. If FCB's extent record wasn't full, there's no point
1641 // in searching the extents file. Note that SearchExtentRecord left us pointing at
1642 // the last valid extent (or the first one, if none were valid). This means we need
1643 // to fill in the hint and key outputs, just like the "if" statement above.
1644 if ( noMoreExtents ) {
1645 *extentBTreeHint = kNoHint; // no hint, because not in the BTree
1646 foundExtentKey->keyLength = 0; // 0 = the FCB itself
1647 err = fxRangeErr; // There are no more extents, so must be beyond PEOF
1648 goto Exit;
1649 }
1650
1651 //
1652 // Find the desired record, or the previous record if it is the same fork
1653 //
1654 err = FindExtentRecord(vcb, (fcb->fcbFlags & fcbResourceMask) ? kResourceForkType : kDataForkType,
1655 H_FILEID(fcb), filePositionBlock, true, foundExtentKey, foundExtentData, extentBTreeHint);
1656
1657 if (err == btNotFound) {
1658 //
1659 // If we get here, the desired position is beyond the extents in the FCB, and there are no extents
1660 // in the extents file. Return the FCB's extents and a range error.
1661 //
1662 *extentBTreeHint = kNoHint;
1663 foundExtentKey->keyLength = 0;
1664 err = GetFCBExtentRecord(fcb, foundExtentData);
1665 // Note: foundExtentIndex and endingFABNPlusOne have already been set as a result of the very
1666 // first SearchExtentRecord call in this function (when searching in the FCB's extents, and
1667 // we got a range error).
1668
1669 return fxRangeErr;
1670 }
1671
1672 //
1673 // If we get here, there was either a BTree error, or we found an appropriate record.
1674 // If we found a record, then search it for the correct index into the extents.
1675 //
1676 if (err == noErr) {
1677 // Find appropriate index into extent record
1678 err = SearchExtentRecord(vcb, filePositionBlock, foundExtentData, foundExtentKey->startBlock,
1679 foundExtentIndex, endingFABNPlusOne, &noMoreExtents);
1680 }
1681
1682 Exit:
1683 return err;
1684 }
1685
1686
1687
1688 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1689 // Routine: UpdateExtentRecord
1690 //
1691 // Function: Write new extent data to an existing extent record with a given key.
1692 // If all of the extents are empty, and the extent record is in the
1693 // extents file, then the record is deleted.
1694 //
1695 // Input: vcb - the volume containing the extents
1696 // fcb - the file that owns the extents
1697 // extentFileKey - pointer to extent key record (xkr)
1698 // If the key length is 0, then the extents are actually part
1699 // of the catalog record, stored in the FCB.
1700 // extentData - pointer to extent data record (xdr)
1701 // extentBTreeHint - hint for given key, or kNoHint
1702 //
1703 // Result: noErr = ok
1704 // (other) = error from BTree
1705 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1706
1707 static OSErr UpdateExtentRecord (
1708 const ExtendedVCB *vcb,
1709 FCB *fcb,
1710 const HFSPlusExtentKey *extentFileKey,
1711 const HFSPlusExtentRecord extentData,
1712 UInt32 extentBTreeHint)
1713 {
1714 BTreeIterator btIterator;
1715 FSBufferDescriptor btRecord;
1716 UInt16 btRecordSize;
1717 FCB * btFCB;
1718 OSErr err = noErr;
1719
1720 if (extentFileKey->keyLength == 0) { // keyLength == 0 means the FCB's extent record
1721 BlockMoveData(extentData, fcb->fcbExtents, sizeof(HFSPlusExtentRecord));
1722 fcb->fcbFlags |= fcbModifiedMask;
1723 }
1724 else {
1725 //
1726 // Need to find and change a record in Extents BTree
1727 //
1728 btFCB = GetFileControlBlock(vcb->extentsRefNum);
1729
1730 if (vcb->vcbSigWord == kHFSSigWord) {
1731 HFSExtentKey * key; // Actual extent key used on disk in HFS
1732 HFSExtentRecord foundData; // The extent data actually found
1733
1734 key = (HFSExtentKey*) &btIterator.key;
1735 key->keyLength = kHFSExtentKeyMaximumLength;
1736 key->forkType = extentFileKey->forkType;
1737 key->fileID = extentFileKey->fileID;
1738 key->startBlock = extentFileKey->startBlock;
1739
1740 btIterator.hint.index = 0;
1741 btIterator.hint.nodeNum = extentBTreeHint;
1742
1743 btRecord.bufferAddress = &foundData;
1744 btRecord.itemSize = sizeof(HFSExtentRecord);
1745 btRecord.itemCount = 1;
1746
1747 err = BTSearchRecord(btFCB, &btIterator, kInvalidMRUCacheKey, &btRecord,
1748 &btRecordSize, &btIterator);
1749
1750 if (err == noErr)
1751 err = HFSPlusToHFSExtents(extentData, (HFSExtentDescriptor *)&foundData);
1752
1753 if (err == noErr)
1754 err = BTReplaceRecord(btFCB, &btIterator, &btRecord, btRecordSize);
1755 }
1756 else { // HFS Plus volume
1757 HFSPlusExtentRecord foundData; // The extent data actually found
1758
1759 BlockMoveData(extentFileKey, &btIterator.key, sizeof(HFSPlusExtentKey));
1760
1761 btIterator.hint.index = 0;
1762 btIterator.hint.nodeNum = extentBTreeHint;
1763
1764 btRecord.bufferAddress = &foundData;
1765 btRecord.itemSize = sizeof(HFSPlusExtentRecord);
1766 btRecord.itemCount = 1;
1767
1768 err = BTSearchRecord(btFCB, &btIterator, kInvalidMRUCacheKey, &btRecord,
1769 &btRecordSize, &btIterator);
1770
1771 if (err == noErr) {
1772 BlockMoveData(extentData, &foundData, sizeof(HFSPlusExtentRecord));
1773 err = BTReplaceRecord(btFCB, &btIterator, &btRecord, btRecordSize);
1774 }
1775 }
1776 }
1777
1778 return err;
1779 }
1780
1781
1782
1783 void HFSToHFSPlusExtents(
1784 const HFSExtentRecord oldExtents,
1785 HFSPlusExtentRecord newExtents)
1786 {
1787 UInt32 i;
1788
1789 // copy the first 3 extents
1790 newExtents[0].startBlock = oldExtents[0].startBlock;
1791 newExtents[0].blockCount = oldExtents[0].blockCount;
1792 newExtents[1].startBlock = oldExtents[1].startBlock;
1793 newExtents[1].blockCount = oldExtents[1].blockCount;
1794 newExtents[2].startBlock = oldExtents[2].startBlock;
1795 newExtents[2].blockCount = oldExtents[2].blockCount;
1796
1797 // zero out the remaining ones
1798 for (i = 3; i < kHFSPlusExtentDensity; ++i)
1799 {
1800 newExtents[i].startBlock = 0;
1801 newExtents[i].blockCount = 0;
1802 }
1803 }
1804
1805
1806
1807 OSErr HFSPlusToHFSExtents(
1808 const HFSPlusExtentRecord oldExtents,
1809 HFSExtentRecord newExtents)
1810 {
1811 OSErr err;
1812
1813 err = noErr;
1814
1815 // copy the first 3 extents
1816 newExtents[0].startBlock = oldExtents[0].startBlock;
1817 newExtents[0].blockCount = oldExtents[0].blockCount;
1818 newExtents[1].startBlock = oldExtents[1].startBlock;
1819 newExtents[1].blockCount = oldExtents[1].blockCount;
1820 newExtents[2].startBlock = oldExtents[2].startBlock;
1821 newExtents[2].blockCount = oldExtents[2].blockCount;
1822
1823 #if DEBUG_BUILD
1824 if (oldExtents[3].startBlock || oldExtents[3].blockCount) {
1825 DebugStr("\pExtentRecord with > 3 extents is invalid for HFS");
1826 err = fsDSIntErr;
1827 }
1828 #endif
1829
1830 return err;
1831 }
1832
1833
1834
1835
1836 OSErr GetFCBExtentRecord(
1837 const FCB *fcb,
1838 HFSPlusExtentRecord extents)
1839 {
1840
1841 BlockMoveData(fcb->fcbExtents, extents, sizeof(HFSPlusExtentRecord));
1842
1843 return noErr;
1844 }
1845
1846
1847 //_________________________________________________________________________________
1848 //
1849 // Routine: ExtentsAreIntegral
1850 //
1851 // Purpose: Ensure that each extent can hold an integral number of nodes
1852 // Called by the NodesAreContiguous function
1853 //_________________________________________________________________________________
1854
1855 static Boolean ExtentsAreIntegral(
1856 const HFSPlusExtentRecord extentRecord,
1857 UInt32 mask,
1858 UInt32 *blocksChecked,
1859 Boolean *checkedLastExtent)
1860 {
1861 UInt32 blocks;
1862 UInt32 extentIndex;
1863
1864 *blocksChecked = 0;
1865 *checkedLastExtent = false;
1866
1867 for(extentIndex = 0; extentIndex < kHFSPlusExtentDensity; extentIndex++)
1868 {
1869 blocks = extentRecord[extentIndex].blockCount;
1870
1871 if ( blocks == 0 )
1872 {
1873 *checkedLastExtent = true;
1874 break;
1875 }
1876
1877 *blocksChecked += blocks;
1878
1879 if (blocks & mask)
1880 return false;
1881 }
1882
1883 return true;
1884 }
1885
1886
1887 //_________________________________________________________________________________
1888 //
1889 // Routine: NodesAreContiguous
1890 //
1891 // Purpose: Ensure that all b-tree nodes are contiguous on disk
1892 // Called by BTOpenPath during volume mount
1893 //_________________________________________________________________________________
1894
1895 Boolean NodesAreContiguous(
1896 ExtendedVCB *vcb,
1897 FCB *fcb,
1898 UInt32 nodeSize)
1899 {
1900 UInt32 mask;
1901 UInt32 startBlock;
1902 UInt32 blocksChecked;
1903 UInt32 hint;
1904 HFSPlusExtentKey key;
1905 HFSPlusExtentRecord extents;
1906 OSErr result;
1907 Boolean lastExtentReached;
1908
1909
1910 if (vcb->blockSize >= nodeSize)
1911 return TRUE;
1912
1913 mask = (nodeSize / vcb->blockSize) - 1;
1914
1915 // check the local extents
1916 (void) GetFCBExtentRecord(fcb, extents);
1917 if ( !ExtentsAreIntegral(extents, mask, &blocksChecked, &lastExtentReached) )
1918 return FALSE;
1919
1920 if (lastExtentReached || (SInt64)((SInt64)blocksChecked * (SInt64)vcb->blockSize) >= fcb->fcbPLen)
1921 return TRUE;
1922
1923 startBlock = blocksChecked;
1924
1925 // check the overflow extents (if any)
1926 while ( !lastExtentReached )
1927 {
1928 result = FindExtentRecord(vcb, kDataForkType, H_FILEID(fcb), startBlock, FALSE, &key, extents, &hint);
1929 if (result) break;
1930
1931 if ( !ExtentsAreIntegral(extents, mask, &blocksChecked, &lastExtentReached) )
1932 return FALSE;
1933
1934 startBlock += blocksChecked;
1935 }
1936
1937 return TRUE;
1938 }
1939