]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfscommon/Misc/FileExtentMapping.c
3f17d760eacdf4b58f4817a928bef0dceaa55a47
[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 kSectorSize = 512 // Size of a physical sector
250 };
251
252 void HFSToHFSPlusExtents(
253 const HFSExtentRecord oldExtents,
254 HFSPlusExtentRecord newExtents);
255
256 OSErr HFSPlusToHFSExtents(
257 const HFSPlusExtentRecord oldExtents,
258 HFSExtentRecord newExtents);
259
260 OSErr FindExtentRecord(
261 const ExtendedVCB *vcb,
262 UInt8 forkType,
263 UInt32 fileID,
264 UInt32 startBlock,
265 Boolean allowPrevious,
266 HFSPlusExtentKey *foundKey,
267 HFSPlusExtentRecord foundData,
268 UInt32 *foundHint);
269
270 OSErr DeleteExtentRecord(
271 const ExtendedVCB *vcb,
272 UInt8 forkType,
273 UInt32 fileID,
274 UInt32 startBlock);
275
276 static OSErr CreateExtentRecord(
277 const ExtendedVCB *vcb,
278 HFSPlusExtentKey *key,
279 HFSPlusExtentRecord extents,
280 UInt32 *hint);
281
282
283 OSErr GetFCBExtentRecord(
284 const FCB *fcb,
285 HFSPlusExtentRecord extents);
286
287 static OSErr SearchExtentFile(
288 const ExtendedVCB *vcb,
289 const FCB *fcb,
290 SInt64 filePosition,
291 HFSPlusExtentKey *foundExtentKey,
292 HFSPlusExtentRecord foundExtentData,
293 UInt32 *foundExtentDataIndex,
294 UInt32 *extentBTreeHint,
295 UInt32 *endingFABNPlusOne );
296
297 static OSErr SearchExtentRecord(
298 const ExtendedVCB *vcb,
299 UInt32 searchFABN,
300 const HFSPlusExtentRecord extentData,
301 UInt32 extentDataStartFABN,
302 UInt32 *foundExtentDataOffset,
303 UInt32 *endingFABNPlusOne,
304 Boolean *noMoreExtents);
305
306 static OSErr ReleaseExtents(
307 ExtendedVCB *vcb,
308 const HFSPlusExtentRecord extentRecord,
309 UInt32 *numReleasedAllocationBlocks,
310 Boolean *releasedLastExtent);
311
312 static OSErr DeallocateFork(
313 ExtendedVCB *vcb,
314 HFSCatalogNodeID fileID,
315 UInt8 forkType,
316 HFSPlusExtentRecord catalogExtents,
317 Boolean * recordDeleted);
318
319 static OSErr TruncateExtents(
320 ExtendedVCB *vcb,
321 UInt8 forkType,
322 UInt32 fileID,
323 UInt32 startBlock,
324 Boolean * recordDeleted);
325
326 static OSErr UpdateExtentRecord (
327 const ExtendedVCB *vcb,
328 FCB *fcb,
329 const HFSPlusExtentKey *extentFileKey,
330 const HFSPlusExtentRecord extentData,
331 UInt32 extentBTreeHint);
332
333 static Boolean ExtentsAreIntegral(
334 const HFSPlusExtentRecord extentRecord,
335 UInt32 mask,
336 UInt32 *blocksChecked,
337 Boolean *checkedLastExtent);
338
339 //_________________________________________________________________________________
340 //
341 // Routine: FindExtentRecord
342 //
343 // Purpose: Search the extents BTree for an extent record matching the given
344 // FileID, fork, and starting file allocation block number.
345 //
346 // Inputs:
347 // vcb Volume to search
348 // forkType 0 = data fork, -1 = resource fork
349 // fileID File's FileID (CatalogNodeID)
350 // startBlock Starting file allocation block number
351 // allowPrevious If the desired record isn't found and this flag is set,
352 // then see if the previous record belongs to the same fork.
353 // If so, then return it.
354 //
355 // Outputs:
356 // foundKey The key data for the record actually found
357 // foundData The extent record actually found (NOTE: on an HFS volume, the
358 // fourth entry will be zeroes.
359 // foundHint The BTree hint to find the node again
360 //_________________________________________________________________________________
361 OSErr FindExtentRecord(
362 const ExtendedVCB *vcb,
363 UInt8 forkType,
364 UInt32 fileID,
365 UInt32 startBlock,
366 Boolean allowPrevious,
367 HFSPlusExtentKey *foundKey,
368 HFSPlusExtentRecord foundData,
369 UInt32 *foundHint)
370 {
371 FCB * fcb;
372 BTreeIterator btIterator;
373 FSBufferDescriptor btRecord;
374 OSErr err;
375 UInt16 btRecordSize;
376
377 err = noErr;
378 *foundHint = 0;
379 fcb = GetFileControlBlock(vcb->extentsRefNum);
380
381 (void) BTInvalidateHint(&btIterator);
382
383 if (vcb->vcbSigWord == kHFSSigWord) {
384 HFSExtentKey * extentKeyPtr;
385 HFSExtentRecord extentData;
386
387 extentKeyPtr = (HFSExtentKey*) &btIterator.key;
388 extentKeyPtr->keyLength = kHFSExtentKeyMaximumLength;
389 extentKeyPtr->forkType = forkType;
390 extentKeyPtr->fileID = fileID;
391 extentKeyPtr->startBlock = startBlock;
392
393 btRecord.bufferAddress = &extentData;
394 btRecord.itemSize = sizeof(HFSExtentRecord);
395 btRecord.itemCount = 1;
396
397 err = BTSearchRecord(fcb, &btIterator, kInvalidMRUCacheKey, &btRecord, &btRecordSize, &btIterator);
398
399 if (err == btNotFound && allowPrevious) {
400 err = BTIterateRecord(fcb, kBTreePrevRecord, &btIterator, &btRecord, &btRecordSize);
401
402 // A previous record may not exist, so just return btNotFound (like we would if
403 // it was for the wrong file/fork).
404 if (err == (OSErr) fsBTStartOfIterationErr) //¥¥ fsBTStartOfIterationErr is type unsigned long
405 err = btNotFound;
406
407 if (err == noErr) {
408 // Found a previous record. Does it belong to the same fork of the same file?
409 if (extentKeyPtr->fileID != fileID || extentKeyPtr->forkType != forkType)
410 err = btNotFound;
411 }
412 }
413
414 if (err == noErr) {
415 UInt16 i;
416
417 // Copy the found key back for the caller
418 foundKey->keyLength = kHFSPlusExtentKeyMaximumLength;
419 foundKey->forkType = extentKeyPtr->forkType;
420 foundKey->pad = 0;
421 foundKey->fileID = extentKeyPtr->fileID;
422 foundKey->startBlock = extentKeyPtr->startBlock;
423
424 // Copy the found data back for the caller
425 foundData[0].startBlock = extentData[0].startBlock;
426 foundData[0].blockCount = extentData[0].blockCount;
427 foundData[1].startBlock = extentData[1].startBlock;
428 foundData[1].blockCount = extentData[1].blockCount;
429 foundData[2].startBlock = extentData[2].startBlock;
430 foundData[2].blockCount = extentData[2].blockCount;
431
432 for (i = 3; i < kHFSPlusExtentDensity; ++i)
433 {
434 foundData[i].startBlock = 0;
435 foundData[i].blockCount = 0;
436 }
437 }
438 }
439 else { // HFS Plus volume
440 HFSPlusExtentKey * extentKeyPtr;
441 HFSPlusExtentRecord extentData;
442
443 extentKeyPtr = (HFSPlusExtentKey*) &btIterator.key;
444 extentKeyPtr->keyLength = kHFSPlusExtentKeyMaximumLength;
445 extentKeyPtr->forkType = forkType;
446 extentKeyPtr->pad = 0;
447 extentKeyPtr->fileID = fileID;
448 extentKeyPtr->startBlock = startBlock;
449
450 btRecord.bufferAddress = &extentData;
451 btRecord.itemSize = sizeof(HFSPlusExtentRecord);
452 btRecord.itemCount = 1;
453
454 err = BTSearchRecord(fcb, &btIterator, kInvalidMRUCacheKey, &btRecord, &btRecordSize, &btIterator);
455
456 if (err == btNotFound && allowPrevious) {
457 err = BTIterateRecord(fcb, kBTreePrevRecord, &btIterator, &btRecord, &btRecordSize);
458
459 // A previous record may not exist, so just return btNotFound (like we would if
460 // it was for the wrong file/fork).
461 if (err == (OSErr) fsBTStartOfIterationErr) //¥¥ fsBTStartOfIterationErr is type unsigned long
462 err = btNotFound;
463
464 if (err == noErr) {
465 // Found a previous record. Does it belong to the same fork of the same file?
466 if (extentKeyPtr->fileID != fileID || extentKeyPtr->forkType != forkType)
467 err = btNotFound;
468 }
469 }
470
471 if (err == noErr) {
472 // Copy the found key back for the caller
473 BlockMoveData(extentKeyPtr, foundKey, sizeof(HFSPlusExtentKey));
474 // Copy the found data back for the caller
475 BlockMoveData(&extentData, foundData, sizeof(HFSPlusExtentRecord));
476 }
477 }
478
479 *foundHint = btIterator.hint.nodeNum;
480 return err;
481 }
482
483
484
485 static OSErr CreateExtentRecord(
486 const ExtendedVCB *vcb,
487 HFSPlusExtentKey *key,
488 HFSPlusExtentRecord extents,
489 UInt32 *hint)
490 {
491 BTreeIterator btIterator;
492 FSBufferDescriptor btRecord;
493 UInt16 btRecordSize;
494 OSErr err;
495
496 err = noErr;
497 *hint = 0;
498 (void) BTInvalidateHint(&btIterator);
499
500 if (vcb->vcbSigWord == kHFSSigWord) {
501 HFSExtentKey * keyPtr;
502 HFSExtentRecord data;
503
504 btRecordSize = sizeof(HFSExtentRecord);
505 btRecord.bufferAddress = &data;
506 btRecord.itemSize = btRecordSize;
507 btRecord.itemCount = 1;
508
509 keyPtr = (HFSExtentKey*) &btIterator.key;
510 keyPtr->keyLength = kHFSExtentKeyMaximumLength;
511 keyPtr->forkType = key->forkType;
512 keyPtr->fileID = key->fileID;
513 keyPtr->startBlock = key->startBlock;
514
515 err = HFSPlusToHFSExtents(extents, data);
516 }
517 else { // HFS Plus volume
518 btRecordSize = sizeof(HFSPlusExtentRecord);
519 btRecord.bufferAddress = extents;
520 btRecord.itemSize = btRecordSize;
521 btRecord.itemCount = 1;
522
523 BlockMoveData(key, &btIterator.key, sizeof(HFSPlusExtentKey));
524 }
525
526 if (err == noErr)
527 err = BTInsertRecord(GetFileControlBlock(vcb->extentsRefNum), &btIterator, &btRecord, btRecordSize);
528
529 if (err == noErr)
530 *hint = btIterator.hint.nodeNum;
531
532 return err;
533 }
534
535
536 OSErr DeleteExtentRecord(
537 const ExtendedVCB *vcb,
538 UInt8 forkType,
539 UInt32 fileID,
540 UInt32 startBlock)
541 {
542 BTreeIterator btIterator;
543 OSErr err;
544
545 err = noErr;
546 (void) BTInvalidateHint(&btIterator);
547
548 if (vcb->vcbSigWord == kHFSSigWord) {
549 HFSExtentKey * keyPtr;
550
551 keyPtr = (HFSExtentKey*) &btIterator.key;
552 keyPtr->keyLength = kHFSExtentKeyMaximumLength;
553 keyPtr->forkType = forkType;
554 keyPtr->fileID = fileID;
555 keyPtr->startBlock = startBlock;
556 }
557 else { // HFS Plus volume
558 HFSPlusExtentKey * keyPtr;
559
560 keyPtr = (HFSPlusExtentKey*) &btIterator.key;
561 keyPtr->keyLength = kHFSPlusExtentKeyMaximumLength;
562 keyPtr->forkType = forkType;
563 keyPtr->pad = 0;
564 keyPtr->fileID = fileID;
565 keyPtr->startBlock = startBlock;
566 }
567
568 err = BTDeleteRecord(GetFileControlBlock(vcb->extentsRefNum), &btIterator);
569
570 return err;
571 }
572
573
574
575 //_________________________________________________________________________________
576 //
577 // Routine: MapFileBlock
578 //
579 // Function: Maps a file position into a physical disk address.
580 //
581 // Input: A2.L - VCB pointer
582 // (A1,D1.W) - FCB pointer
583 // D4.L - number of bytes desired
584 // D5.L - file position (byte address)
585 //
586 // Output: D3.L - physical start block
587 // D6.L - number of contiguous bytes available (up to D4 bytes)
588 // D0.L - result code <01Oct85>
589 // 0 = ok
590 // FXRangeErr = file position beyond mapped range <17Oct85>
591 // FXOvFlErr = extents file overflow <17Oct85>
592 // other = error <17Oct85>
593 //
594 // Called By: Log2Phys (read/write in place), Cache (map a file block).
595 //_________________________________________________________________________________
596
597 OSErr MapFileBlockC (
598 ExtendedVCB *vcb, // volume that file resides on
599 FCB *fcb, // FCB of file
600 size_t numberOfBytes, // number of contiguous bytes desired
601 off_t offset, // starting offset within file (in bytes)
602 daddr_t *startSector, // first 512-byte sector (NOT an allocation block)
603 size_t *availableBytes) // number of contiguous bytes (up to numberOfBytes)
604 {
605 OSErr err;
606 UInt32 allocBlockSize; // Size of the volume's allocation block
607 HFSPlusExtentKey foundKey;
608 HFSPlusExtentRecord foundData;
609 UInt32 foundIndex;
610 UInt32 hint;
611 UInt32 firstFABN; // file allocation block of first block in found extent
612 UInt32 nextFABN; // file allocation block of block after end of found extent
613 off_t dataEnd; // (offset) end of range that is contiguous
614 UInt32 sectorsPerBlock; // Number of sectors per allocation block
615 UInt32 startBlock; // volume allocation block corresponding to firstFABN
616 daddr_t temp;
617 off_t tmpOff;
618
619 allocBlockSize = vcb->blockSize;
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 / kSectorSize; // sectors per allocation block
642
643 //
644 // Compute the absolute sector number that contains the offset of the given file
645 //
646 temp = (daddr_t)((offset - (off_t)((off_t)(firstFABN) * (off_t)(allocBlockSize)))/kSectorSize); // offset in sectors from start of the extent
647 temp += startBlock * sectorsPerBlock; // offset in sectors from start of allocation block space
648 if (vcb->vcbSigWord == kHFSPlusSigWord)
649 temp += vcb->hfsPlusIOPosOffset/512; /* offset inside wrapper */
650 else
651 temp += vcb->vcbAlBlSt; /* offset in sectors from start of volume */
652
653 // Return the desired sector for file position "offset"
654 *startSector = temp;
655
656 //
657 // Determine the number of contiguous bytes until the end of the extent
658 // (or the amount they asked for, whichever comes first).
659 //
660 tmpOff = dataEnd - offset;
661 if (tmpOff > (off_t)(numberOfBytes))
662 *availableBytes = numberOfBytes; // more there than they asked for, so pin the output
663 else
664 *availableBytes = tmpOff;
665
666 return noErr;
667 }
668
669
670 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
671 // Routine: ReleaseExtents
672 //
673 // Function: Release the extents of a single extent data record.
674 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
675
676 static OSErr ReleaseExtents(
677 ExtendedVCB *vcb,
678 const HFSPlusExtentRecord extentRecord,
679 UInt32 *numReleasedAllocationBlocks,
680 Boolean *releasedLastExtent)
681 {
682 UInt32 extentIndex;
683 UInt32 numberOfExtents;
684 OSErr err = noErr;
685
686 *numReleasedAllocationBlocks = 0;
687 *releasedLastExtent = false;
688
689 if (vcb->vcbSigWord == kHFSPlusSigWord)
690 numberOfExtents = kHFSPlusExtentDensity;
691 else
692 numberOfExtents = kHFSExtentDensity;
693
694 for( extentIndex = 0; extentIndex < numberOfExtents; extentIndex++)
695 {
696 UInt32 numAllocationBlocks;
697
698 // Loop over the extent record and release the blocks associated with each extent.
699
700 numAllocationBlocks = extentRecord[extentIndex].blockCount;
701 if ( numAllocationBlocks == 0 )
702 {
703 *releasedLastExtent = true;
704 break;
705 }
706
707 err = BlockDeallocate( vcb, extentRecord[extentIndex].startBlock, numAllocationBlocks );
708 if ( err != noErr )
709 break;
710
711 *numReleasedAllocationBlocks += numAllocationBlocks; // bump FABN to beg of next extent
712 }
713
714 return( err );
715 }
716
717
718
719 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
720 // Routine: TruncateExtents
721 //
722 // Purpose: Delete extent records whose starting file allocation block number
723 // is greater than or equal to a given starting block number. The
724 // allocation blocks represented by the extents are deallocated.
725 //
726 // Inputs:
727 // vcb Volume to operate on
728 // fileID Which file to operate on
729 // startBlock Starting file allocation block number for first extent
730 // record to delete.
731 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
732
733 static OSErr TruncateExtents(
734 ExtendedVCB *vcb,
735 UInt8 forkType,
736 UInt32 fileID,
737 UInt32 startBlock,
738 Boolean * recordDeleted)
739 {
740 OSErr err;
741 UInt32 numberExtentsReleased;
742 Boolean releasedLastExtent;
743 UInt32 hint;
744 HFSPlusExtentKey key;
745 HFSPlusExtentRecord extents;
746
747 while (true) {
748 err = FindExtentRecord(vcb, forkType, fileID, startBlock, false, &key, extents, &hint);
749 if (err != noErr) {
750 if (err == btNotFound)
751 err = noErr;
752 break;
753 }
754
755 err = ReleaseExtents( vcb, extents, &numberExtentsReleased, &releasedLastExtent );
756 if (err != noErr) break;
757
758 err = DeleteExtentRecord(vcb, forkType, fileID, startBlock);
759 if (err != noErr) break;
760
761 *recordDeleted = true;
762 startBlock += numberExtentsReleased;
763 }
764
765 return err;
766 }
767
768
769
770 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
771 // Routine: DeallocateFork
772 //
773 // Function: De-allocates all disk space allocated to a specified fork.
774 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
775
776 static OSErr DeallocateFork(
777 ExtendedVCB *vcb,
778 HFSCatalogNodeID fileID,
779 UInt8 forkType,
780 HFSPlusExtentRecord catalogExtents,
781 Boolean * recordDeleted) /* true if a record was deleted */
782 {
783 OSErr err;
784 UInt32 numReleasedAllocationBlocks;
785 Boolean releasedLastExtent;
786
787 // Release the catalog extents
788 err = ReleaseExtents( vcb, catalogExtents, &numReleasedAllocationBlocks, &releasedLastExtent );
789 // Release the extra extents, if present
790 if (err == noErr && !releasedLastExtent)
791 err = TruncateExtents(vcb, forkType, fileID, numReleasedAllocationBlocks, recordDeleted);
792
793 return( err );
794 }
795
796 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
797 // Routine: FlushExtentFile
798 //
799 // Function: Flushes the extent file for a specified volume
800 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
801
802 OSErr FlushExtentFile( ExtendedVCB *vcb )
803 {
804 FCB * fcb;
805 OSErr err;
806
807 fcb = GetFileControlBlock(vcb->extentsRefNum);
808 err = BTFlushPath(fcb);
809 if ( err == noErr )
810 {
811 // If the FCB for the extent "file" is dirty, mark the VCB as dirty.
812
813 if ((fcb->fcbFlags & fcbModifiedMask) != 0)
814 {
815 MarkVCBDirty( vcb );
816 err = FlushVolumeControlBlock( vcb );
817 }
818 }
819
820 return( err );
821 }
822
823 //-------------------------------------------------------------------------------
824 // Routine: DeleteFile
825 //
826 // Function: De-allocates all disk space allocated to a specified file
827 // including the space used by the catalog (ie the catalog record).
828 // The space occupied by both forks is also deallocated.
829 //
830 //-------------------------------------------------------------------------------
831
832 OSErr DeleteFile( ExtendedVCB *vcb, HFSCatalogNodeID parDirID, ConstUTF8Param catalogName, UInt32 catalogHint )
833 {
834 OSErr err;
835 OSErr errDF, errRF;
836 CatalogNodeData catalogData;
837 Boolean recordDeleted;
838
839 recordDeleted = false;
840
841 INIT_CATALOGDATA(&catalogData, kCatNameNoCopyName);
842
843 // Find catalog data in catalog
844 err = GetCatalogNode( vcb, parDirID, catalogName, kUndefinedStrLen, catalogHint, &catalogData, &catalogHint);
845 if( err != noErr )
846 goto Exit;
847
848
849 // Check to make sure record is for a file
850 if ( catalogData.cnd_type != kCatalogFileNode )
851 {
852 err = notAFileErr;
853 goto Exit;
854 }
855
856 //
857 // Always delete the Catalog record first (to minimize disk corruption)
858 //
859 err = DeleteCatalogNode(vcb, parDirID, catalogName, catalogHint);
860 if( err != noErr )
861 goto Exit;
862
863 //
864 // Note: we don't report errors from DeallocateFork since the
865 // file no longer exists (since DeleteCatalogNode succeeded).
866 // Any errors mean that there are possibly some orphaned disk
867 // blocks but from the clients perspective the file was deleted.
868 //
869
870 // Deallocate data fork extents
871 errDF = DeallocateFork( vcb, catalogData.cnd_nodeID, kDataForkType,
872 catalogData.cnd_datafork.extents, &recordDeleted );
873
874 // Deallocate resource fork extents
875 errRF = DeallocateFork( vcb, catalogData.cnd_nodeID, kResourceForkType,
876 catalogData.cnd_rsrcfork.extents, &recordDeleted );
877
878 if (recordDeleted)
879 err = FlushExtentFile( vcb );
880
881 CLEAN_CATALOGDATA(&catalogData);
882 return (errDF ? errDF : (errRF ? errRF : err));
883 Exit:
884
885 CLEAN_CATALOGDATA(&catalogData);
886 return( err );
887 }
888
889 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
890 // Routine: CompareExtentKeys
891 //
892 // Function: Compares two extent file keys (a search key and a trial key) for
893 // an HFS volume.
894 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
895
896 SInt32 CompareExtentKeys( const HFSExtentKey *searchKey, const HFSExtentKey *trialKey )
897 {
898 SInt32 result; // ± 1
899
900 #if DEBUG_BUILD
901 if (searchKey->keyLength != kHFSExtentKeyMaximumLength)
902 DebugStr("\pHFS: search Key is wrong length");
903 if (trialKey->keyLength != kHFSExtentKeyMaximumLength)
904 DebugStr("\pHFS: trial Key is wrong length");
905 #endif
906
907 result = -1; // assume searchKey < trialKey
908
909 if (searchKey->fileID == trialKey->fileID) {
910 //
911 // FileNum's are equal; compare fork types
912 //
913 if (searchKey->forkType == trialKey->forkType) {
914 //
915 // Fork types are equal; compare allocation block number
916 //
917 if (searchKey->startBlock == trialKey->startBlock) {
918 //
919 // Everything is equal
920 //
921 result = 0;
922 }
923 else {
924 //
925 // Allocation block numbers differ; determine sign
926 //
927 if (searchKey->startBlock > trialKey->startBlock)
928 result = 1;
929 }
930 }
931 else {
932 //
933 // Fork types differ; determine sign
934 //
935 if (searchKey->forkType > trialKey->forkType)
936 result = 1;
937 }
938 }
939 else {
940 //
941 // FileNums differ; determine sign
942 //
943 if (searchKey->fileID > trialKey->fileID)
944 result = 1;
945 }
946
947 return( result );
948 }
949
950
951
952 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
953 // Routine: CompareExtentKeysPlus
954 //
955 // Function: Compares two extent file keys (a search key and a trial key) for
956 // an HFS volume.
957 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
958
959 SInt32 CompareExtentKeysPlus( const HFSPlusExtentKey *searchKey, const HFSPlusExtentKey *trialKey )
960 {
961 SInt32 result; // ± 1
962
963 #if DEBUG_BUILD
964 if (searchKey->keyLength != kHFSPlusExtentKeyMaximumLength)
965 DebugStr("\pHFS: search Key is wrong length");
966 if (trialKey->keyLength != kHFSPlusExtentKeyMaximumLength)
967 DebugStr("\pHFS: trial Key is wrong length");
968 #endif
969
970 result = -1; // assume searchKey < trialKey
971
972 if (searchKey->fileID == trialKey->fileID) {
973 //
974 // FileNum's are equal; compare fork types
975 //
976 if (searchKey->forkType == trialKey->forkType) {
977 //
978 // Fork types are equal; compare allocation block number
979 //
980 if (searchKey->startBlock == trialKey->startBlock) {
981 //
982 // Everything is equal
983 //
984 result = 0;
985 }
986 else {
987 //
988 // Allocation block numbers differ; determine sign
989 //
990 if (searchKey->startBlock > trialKey->startBlock)
991 result = 1;
992 }
993 }
994 else {
995 //
996 // Fork types differ; determine sign
997 //
998 if (searchKey->forkType > trialKey->forkType)
999 result = 1;
1000 }
1001 }
1002 else {
1003 //
1004 // FileNums differ; determine sign
1005 //
1006 if (searchKey->fileID > trialKey->fileID)
1007 result = 1;
1008 }
1009
1010 return( result );
1011 }
1012
1013
1014
1015 //_________________________________________________________________________________
1016 //
1017 // Routine: Extendfile
1018 //
1019 // Function: Extends the disk space allocated to a file.
1020 //
1021 // Input: A2.L - VCB pointer
1022 // A1.L - pointer to FCB array
1023 // D1.W - file refnum
1024 // D3.B - option flags
1025 // kEFContigMask - force contiguous allocation
1026 // kEFAllMask - allocate all requested bytes or none
1027 // NOTE: You may not set both options.
1028 // D4.L - number of additional bytes to allocate
1029 //
1030 // Output: D0.W - result code
1031 // 0 = ok
1032 // -n = IO error
1033 // D6.L - number of bytes allocated
1034 //
1035 // Called by: FileAloc,FileWrite,SetEof
1036 //
1037 // Note: ExtendFile updates the PEOF in the FCB.
1038 //_________________________________________________________________________________
1039
1040 OSErr ExtendFileC (
1041 ExtendedVCB *vcb, // volume that file resides on
1042 FCB *fcb, // FCB of file to truncate
1043 SInt64 bytesToAdd, // number of bytes to allocate
1044 UInt32 blockHint, // desired starting allocation block
1045 UInt32 flags, // EFContig and/or EFAll
1046 SInt64 *actualBytesAdded) // number of bytes actually allocated
1047 {
1048 OSErr err;
1049 UInt32 volumeBlockSize;
1050 SInt64 blocksToAdd;
1051 SInt64 bytesThisExtent;
1052 HFSPlusExtentKey foundKey;
1053 HFSPlusExtentRecord foundData;
1054 UInt32 foundIndex;
1055 UInt32 hint;
1056 UInt32 nextBlock;
1057 UInt32 startBlock;
1058 Boolean allOrNothing;
1059 Boolean forceContig;
1060 Boolean wantContig;
1061 Boolean needsFlush;
1062 UInt32 actualStartBlock;
1063 UInt32 actualNumBlocks;
1064 UInt32 numExtentsPerRecord;
1065 SInt64 maximumBytes;
1066 SInt64 peof;
1067 SInt64 previousPEOF;
1068
1069
1070 needsFlush = false;
1071 *actualBytesAdded = 0;
1072 volumeBlockSize = vcb->blockSize;
1073 allOrNothing = ((flags & kEFAllMask) != 0);
1074 forceContig = ((flags & kEFContigMask) != 0);
1075 previousPEOF = fcb->fcbPLen;
1076
1077 if (vcb->vcbSigWord == kHFSPlusSigWord)
1078 numExtentsPerRecord = kHFSPlusExtentDensity;
1079 else
1080 numExtentsPerRecord = kHFSExtentDensity;
1081
1082 //
1083 // Make sure the request and new PEOF are less than 2GB if HFS.
1084 //
1085 if (vcb->vcbSigWord == kHFSSigWord) {
1086 if (bytesToAdd >= kTwoGigabytes)
1087 goto Overflow;
1088 if ((fcb->fcbPLen + bytesToAdd) >= kTwoGigabytes)
1089 goto Overflow;
1090 }
1091 //
1092 // Determine how many blocks need to be allocated.
1093 // Round up the number of desired bytes to add.
1094 //
1095 blocksToAdd = FileBytesToBlocks(bytesToAdd, volumeBlockSize);
1096 bytesToAdd = (SInt64)((SInt64)blocksToAdd * (SInt64)volumeBlockSize);
1097
1098 //
1099 // If the file's clump size is larger than the allocation block size,
1100 // then set the maximum number of bytes to the requested number of bytes
1101 // rounded up to a multiple of the clump size.
1102 //
1103 if (fcb->fcbClmpSize > volumeBlockSize) {
1104 maximumBytes = (SInt64)FileBytesToBlocks(bytesToAdd, fcb->fcbClmpSize);
1105 maximumBytes *= fcb->fcbClmpSize;
1106 }
1107 else {
1108 maximumBytes = bytesToAdd;
1109 }
1110
1111 //
1112 // Compute new physical EOF, rounded up to a multiple of a block.
1113 //
1114 if ((vcb->vcbSigWord == kHFSSigWord) && ((fcb->fcbPLen + bytesToAdd) >= (SInt64) kTwoGigabytes)) // Too big?
1115 if (allOrNothing) // Yes, must they have it all?
1116 goto Overflow; // Yes, can't have it
1117 else {
1118 --blocksToAdd; // No, give give 'em one block less
1119 bytesToAdd -= volumeBlockSize;
1120 }
1121
1122 //
1123 // If allocation is all-or-nothing, make sure there are
1124 // enough free blocks on the volume (quick test).
1125 //
1126 if (allOrNothing && (blocksToAdd > (SInt64)vcb->freeBlocks)) {
1127 err = dskFulErr;
1128 goto ErrorExit;
1129 }
1130
1131 //
1132 // See if there are already enough blocks allocated to the file.
1133 //
1134 peof = fcb->fcbPLen + bytesToAdd; // potential new PEOF
1135 err = SearchExtentFile(vcb, fcb, peof-1, &foundKey, foundData, &foundIndex, &hint, &nextBlock);
1136 if (err == noErr) {
1137 // Enough blocks are already allocated. Just update the FCB to reflect the new length.
1138 fcb->fcbPLen = peof;
1139 H_EXTENDSIZE(fcb, bytesToAdd);
1140 fcb->fcbFlags |= fcbModifiedMask;
1141 goto Exit;
1142 }
1143 if (err != fxRangeErr) // Any real error?
1144 goto ErrorExit; // Yes, so exit immediately
1145
1146 //
1147 // Adjust the PEOF to the end of the last extent.
1148 //
1149 peof = (SInt64)((SInt64)nextBlock * (SInt64)volumeBlockSize); // currently allocated PEOF
1150 bytesThisExtent = peof - fcb->fcbPLen;
1151 if (bytesThisExtent != 0) {
1152 fcb->fcbPLen = peof;
1153 H_EXTENDSIZE(fcb, bytesThisExtent);
1154 fcb->fcbFlags |= fcbModifiedMask;
1155 bytesToAdd -= bytesThisExtent;
1156 }
1157
1158 //
1159 // Allocate some more space.
1160 //
1161 // First try a contiguous allocation (of the whole amount).
1162 // If that fails, get whatever we can.
1163 // If forceContig, then take whatever we got
1164 // else, keep getting bits and pieces (non-contig)
1165 err = noErr;
1166 wantContig = true;
1167 vcb->vcbFreeExtCnt = 0; /* For now, force rebuild of free extent list */
1168 do {
1169 if (blockHint != 0)
1170 startBlock = blockHint;
1171 else
1172 startBlock = foundData[foundIndex].startBlock + foundData[foundIndex].blockCount;
1173 err = BlockAllocate(vcb, startBlock, bytesToAdd, maximumBytes, wantContig, &actualStartBlock, &actualNumBlocks);
1174 if (err == dskFulErr) {
1175 if (forceContig)
1176 break; // AllocContig failed because not enough contiguous space
1177 if (wantContig) {
1178 // Couldn't get one big chunk, so get whatever we can.
1179 err = noErr;
1180 wantContig = false;
1181 continue;
1182 }
1183 if (actualNumBlocks != 0)
1184 err = noErr;
1185 }
1186 if (err == noErr) {
1187 // Add the new extent to the existing extent record, or create a new one.
1188 if ((actualStartBlock == startBlock) && (blockHint == 0)) {
1189 // We grew the file's last extent, so just adjust the number of blocks.
1190 foundData[foundIndex].blockCount += actualNumBlocks;
1191 err = UpdateExtentRecord(vcb, fcb, &foundKey, foundData, hint);
1192 if (err != noErr) break;
1193 }
1194 else {
1195 UInt16 i;
1196
1197 // Need to add a new extent. See if there is room in the current record.
1198 if (foundData[foundIndex].blockCount != 0) // Is current extent free to use?
1199 ++foundIndex; // No, so use the next one.
1200 if (foundIndex == numExtentsPerRecord) {
1201 // This record is full. Need to create a new one.
1202 if (H_FILEID(fcb) == kHFSExtentsFileID) {
1203 (void) BlockDeallocate(vcb, actualStartBlock, actualNumBlocks);
1204 err = dskFulErr; // Oops. Can't extend extents file past first record.
1205 break;
1206 }
1207
1208 foundKey.keyLength = kHFSPlusExtentKeyMaximumLength;
1209 if (fcb->fcbFlags & fcbResourceMask)
1210 foundKey.forkType = kResourceForkType;
1211 else
1212 foundKey.forkType = kDataForkType;
1213 foundKey.pad = 0;
1214 foundKey.fileID = H_FILEID(fcb);
1215 foundKey.startBlock = nextBlock;
1216
1217 foundData[0].startBlock = actualStartBlock;
1218 foundData[0].blockCount = actualNumBlocks;
1219
1220 // zero out remaining extents...
1221 for (i = 1; i < kHFSPlusExtentDensity; ++i)
1222 {
1223 foundData[i].startBlock = 0;
1224 foundData[i].blockCount = 0;
1225 }
1226
1227 foundIndex = 0;
1228
1229 err = CreateExtentRecord(vcb, &foundKey, foundData, &hint);
1230 if (err == fxOvFlErr) {
1231 // We couldn't create an extent record because extents B-tree
1232 // couldn't grow. Dellocate the extent just allocated and
1233 // return a disk full error.
1234 (void) BlockDeallocate(vcb, actualStartBlock, actualNumBlocks);
1235 err = dskFulErr;
1236 }
1237 if (err != noErr) break;
1238
1239 needsFlush = true; // We need to update the B-tree header
1240 }
1241 else {
1242 // Add a new extent into this record and update.
1243 foundData[foundIndex].startBlock = actualStartBlock;
1244 foundData[foundIndex].blockCount = actualNumBlocks;
1245 err = UpdateExtentRecord(vcb, fcb, &foundKey, foundData, hint);
1246 if (err != noErr) break;
1247 }
1248 }
1249
1250 // Figure out how many bytes were actually allocated.
1251 // NOTE: BlockAllocate could have allocated more than we asked for.
1252 // Don't set the PEOF beyond what our client asked for.
1253 nextBlock += actualNumBlocks;
1254 bytesThisExtent = (SInt64)((SInt64)actualNumBlocks * (SInt64)volumeBlockSize);
1255 if (bytesThisExtent > bytesToAdd) {
1256 bytesToAdd = 0;
1257 }
1258 else {
1259 bytesToAdd -= bytesThisExtent;
1260 maximumBytes -= bytesThisExtent;
1261 }
1262 fcb->fcbPLen += bytesThisExtent;
1263 H_EXTENDSIZE(fcb, bytesThisExtent);
1264 fcb->fcbFlags |= fcbModifiedMask;
1265
1266 // If contiguous allocation was requested, then we've already got one contiguous
1267 // chunk. If we didn't get all we wanted, then adjust the error to disk full.
1268 if (forceContig) {
1269 if (bytesToAdd != 0)
1270 err = dskFulErr;
1271 break; // We've already got everything that's contiguous
1272 }
1273 }
1274 } while (err == noErr && bytesToAdd);
1275
1276 ErrorExit:
1277 Exit:
1278 *actualBytesAdded = fcb->fcbPLen - previousPEOF;
1279
1280 if (needsFlush)
1281 (void) FlushExtentFile(vcb);
1282
1283 return err;
1284
1285 Overflow:
1286 err = fileBoundsErr;
1287 goto ErrorExit;
1288 }
1289
1290
1291
1292 //_________________________________________________________________________________
1293 //
1294 // Routine: TruncateFileC
1295 //
1296 // Function: Truncates the disk space allocated to a file. The file space is
1297 // truncated to a specified new PEOF rounded up to the next allocation
1298 // block boundry. If the 'TFTrunExt' option is specified, the file is
1299 // truncated to the end of the extent containing the new PEOF.
1300 //
1301 // Input: A2.L - VCB pointer
1302 // A1.L - pointer to FCB array
1303 // D1.W - file refnum
1304 // D2.B - option flags
1305 // TFTrunExt - truncate to the extent containing new PEOF
1306 // D3.L - new PEOF
1307 //
1308 // Output: D0.W - result code
1309 // 0 = ok
1310 // -n = IO error
1311 //
1312 // Note: TruncateFile updates the PEOF in the FCB.
1313 //_________________________________________________________________________________
1314
1315 OSErr TruncateFileC (
1316 ExtendedVCB *vcb, // volume that file resides on
1317 FCB *fcb, // FCB of file to truncate
1318 SInt64 peof, // new physical size for file
1319 Boolean truncateToExtent) // if true, truncate to end of extent containing newPEOF
1320 {
1321 OSErr err;
1322 UInt32 nextBlock; // next file allocation block to consider
1323 UInt32 startBlock; // Physical (volume) allocation block number of start of a range
1324 UInt32 physNumBlocks; // Number of allocation blocks in file (according to PEOF)
1325 UInt32 numBlocks;
1326 HFSPlusExtentKey key; // key for current extent record; key->keyLength == 0 if FCB's extent record
1327 UInt32 hint; // BTree hint corresponding to key
1328 HFSPlusExtentRecord extentRecord;
1329 UInt32 extentIndex;
1330 UInt32 extentNextBlock;
1331 UInt32 numExtentsPerRecord;
1332 SInt64 temp64;
1333 UInt8 forkType;
1334 Boolean extentChanged; // true if we actually changed an extent
1335 Boolean recordDeleted; // true if an extent record got deleted
1336
1337
1338 recordDeleted = false;
1339
1340 if (vcb->vcbSigWord == kHFSPlusSigWord)
1341 numExtentsPerRecord = kHFSPlusExtentDensity;
1342 else
1343 numExtentsPerRecord = kHFSExtentDensity;
1344
1345 if (fcb->fcbFlags & fcbResourceMask)
1346 forkType = kResourceForkType;
1347 else
1348 forkType = kDataForkType;
1349
1350 temp64 = fcb->fcbPLen / (SInt64)vcb->blockSize; // number of allocation blocks currently in file
1351 physNumBlocks = (UInt32)temp64;
1352
1353 //
1354 // Round newPEOF up to a multiple of the allocation block size. If new size is
1355 // two gigabytes or more, then round down by one allocation block (??? really?
1356 // shouldn't that be an error?).
1357 //
1358 nextBlock = FileBytesToBlocks(peof, vcb->blockSize); // number of allocation blocks to remain in file
1359 peof = (SInt64)((SInt64)nextBlock * (SInt64)vcb->blockSize); // number of bytes in those blocks
1360 if ((vcb->vcbSigWord == kHFSSigWord) && (peof >= (UInt32) kTwoGigabytes)) {
1361 #if DEBUG_BUILD
1362 DebugStr("\pHFS: Trying to truncate a file to 2GB or more");
1363 #endif
1364 err = fileBoundsErr;
1365 goto ErrorExit;
1366 }
1367
1368 //
1369 // Update FCB's length
1370 //
1371 H_TRUNCSIZE(fcb, fcb->fcbPLen - peof);
1372 fcb->fcbPLen = peof;
1373 fcb->fcbFlags |= fcbModifiedMask;
1374
1375 //
1376 // If the new PEOF is 0, then truncateToExtent has no meaning (we should always deallocate
1377 // all storage).
1378 //
1379 if (peof == 0) {
1380 int i;
1381
1382 // Deallocate all the extents for this fork
1383 err = DeallocateFork(vcb, H_FILEID(fcb), forkType, fcb->fcbExtents, &recordDeleted);
1384 if (err != noErr) goto ErrorExit; // got some error, so return it
1385
1386 // Update the catalog extent record (making sure it's zeroed out)
1387 if (err == noErr) {
1388 for (i=0; i < kHFSPlusExtentDensity; i++) {
1389 fcb->fcbExtents[i].startBlock = 0;
1390 fcb->fcbExtents[i].blockCount = 0;
1391 }
1392 }
1393 goto Done;
1394 }
1395
1396 //
1397 // Find the extent containing byte (peof-1). This is the last extent we'll keep.
1398 // (If truncateToExtent is true, we'll keep the whole extent; otherwise, we'll only
1399 // keep up through peof). The search will tell us how many allocation blocks exist
1400 // in the found extent plus all previous extents.
1401 //
1402 err = SearchExtentFile(vcb, fcb, peof-1, &key, extentRecord, &extentIndex, &hint, &extentNextBlock);
1403 if (err != noErr) goto ErrorExit;
1404
1405 extentChanged = false; // haven't changed the extent yet
1406
1407 if (!truncateToExtent) {
1408 //
1409 // Shorten this extent. It may be the case that the entire extent gets
1410 // freed here.
1411 //
1412 numBlocks = extentNextBlock - nextBlock; // How many blocks in this extent to free up
1413 if (numBlocks != 0) {
1414 // Compute first volume allocation block to free
1415 startBlock = extentRecord[extentIndex].startBlock + extentRecord[extentIndex].blockCount - numBlocks;
1416 // Free the blocks in bitmap
1417 err = BlockDeallocate(vcb, startBlock, numBlocks);
1418 if (err != noErr) goto ErrorExit;
1419 // Adjust length of this extent
1420 extentRecord[extentIndex].blockCount -= numBlocks;
1421 // If extent is empty, set start block to 0
1422 if (extentRecord[extentIndex].blockCount == 0)
1423 extentRecord[extentIndex].startBlock = 0;
1424 // Remember that we changed the extent record
1425 extentChanged = true;
1426 }
1427 }
1428
1429 //
1430 // Now move to the next extent in the record, and set up the file allocation block number
1431 //
1432 nextBlock = extentNextBlock; // Next file allocation block to free
1433 ++extentIndex; // Its index within the extent record
1434
1435 //
1436 // Release all following extents in this extent record. Update the record.
1437 //
1438 while (extentIndex < numExtentsPerRecord && extentRecord[extentIndex].blockCount != 0) {
1439 numBlocks = extentRecord[extentIndex].blockCount;
1440 // Deallocate this extent
1441 err = BlockDeallocate(vcb, extentRecord[extentIndex].startBlock, numBlocks);
1442 if (err != noErr) goto ErrorExit;
1443 // Update next file allocation block number
1444 nextBlock += numBlocks;
1445 // Zero out start and length of this extent to delete it from record
1446 extentRecord[extentIndex].startBlock = 0;
1447 extentRecord[extentIndex].blockCount = 0;
1448 // Remember that we changed an extent
1449 extentChanged = true;
1450 // Move to next extent in record
1451 ++extentIndex;
1452 }
1453
1454 //
1455 // If any of the extents in the current record were changed, then update that
1456 // record (in the FCB, or extents file).
1457 //
1458 if (extentChanged) {
1459 err = UpdateExtentRecord(vcb, fcb, &key, extentRecord, hint);
1460 if (err != noErr) goto ErrorExit;
1461 }
1462
1463 //
1464 // If there are any following allocation blocks, then we need
1465 // to seach for their extent records and delete those allocation
1466 // blocks.
1467 //
1468 if (nextBlock < physNumBlocks)
1469 err = TruncateExtents(vcb, forkType, H_FILEID(fcb), nextBlock, &recordDeleted);
1470
1471 Done:
1472 ErrorExit:
1473
1474 if (recordDeleted)
1475 (void) FlushExtentFile(vcb);
1476
1477 return err;
1478 }
1479
1480
1481
1482 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1483 // Routine: SearchExtentRecord (was XRSearch)
1484 //
1485 // Function: Searches extent record for the extent mapping a given file
1486 // allocation block number (FABN).
1487 //
1488 // Input: searchFABN - desired FABN
1489 // extentData - pointer to extent data record (xdr)
1490 // extentDataStartFABN - beginning FABN for extent record
1491 //
1492 // Output: foundExtentDataOffset - offset to extent entry within xdr
1493 // result = noErr, offset to extent mapping desired FABN
1494 // result = FXRangeErr, offset to last extent in record
1495 // endingFABNPlusOne - ending FABN +1
1496 // noMoreExtents - True if the extent was not found, and the
1497 // extent record was not full (so don't bother
1498 // looking in subsequent records); false otherwise.
1499 //
1500 // Result: noErr = ok
1501 // FXRangeErr = desired FABN > last mapped FABN in record
1502 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1503
1504 static OSErr SearchExtentRecord(
1505 const ExtendedVCB *vcb,
1506 UInt32 searchFABN,
1507 const HFSPlusExtentRecord extentData,
1508 UInt32 extentDataStartFABN,
1509 UInt32 *foundExtentIndex,
1510 UInt32 *endingFABNPlusOne,
1511 Boolean *noMoreExtents)
1512 {
1513 OSErr err = noErr;
1514 UInt32 extentIndex;
1515 UInt32 numberOfExtents;
1516 UInt32 numAllocationBlocks;
1517 Boolean foundExtent;
1518
1519 *endingFABNPlusOne = extentDataStartFABN;
1520 *noMoreExtents = false;
1521 foundExtent = false;
1522
1523 if (vcb->vcbSigWord == kHFSPlusSigWord)
1524 numberOfExtents = kHFSPlusExtentDensity;
1525 else
1526 numberOfExtents = kHFSExtentDensity;
1527
1528 for( extentIndex = 0; extentIndex < numberOfExtents; ++extentIndex )
1529 {
1530
1531 // Loop over the extent record and find the search FABN.
1532
1533 numAllocationBlocks = extentData[extentIndex].blockCount;
1534 if ( numAllocationBlocks == 0 )
1535 {
1536 break;
1537 }
1538
1539 *endingFABNPlusOne += numAllocationBlocks;
1540
1541 if( searchFABN < *endingFABNPlusOne )
1542 {
1543 // Found the extent.
1544 foundExtent = true;
1545 break;
1546 }
1547 }
1548
1549 if( foundExtent )
1550 {
1551 // Found the extent. Note the extent offset
1552 *foundExtentIndex = extentIndex;
1553 }
1554 else
1555 {
1556 // Did not find the extent. Set foundExtentDataOffset accordingly
1557 if( extentIndex > 0 )
1558 {
1559 *foundExtentIndex = extentIndex - 1;
1560 }
1561 else
1562 {
1563 *foundExtentIndex = 0;
1564 }
1565
1566 // If we found an empty extent, then set noMoreExtents.
1567 if (extentIndex < numberOfExtents)
1568 *noMoreExtents = true;
1569
1570 // Finally, return an error to the caller
1571 err = fxRangeErr;
1572 }
1573
1574 return( err );
1575 }
1576
1577 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1578 // Routine: SearchExtentFile (was XFSearch)
1579 //
1580 // Function: Searches extent file (including the FCB resident extent record)
1581 // for the extent mapping a given file position.
1582 //
1583 // Input: vcb - VCB pointer
1584 // fcb - FCB pointer
1585 // filePosition - file position (byte address)
1586 //
1587 // Output: foundExtentKey - extent key record (xkr)
1588 // If extent was found in the FCB's resident extent record,
1589 // then foundExtentKey->keyLength will be set to 0.
1590 // foundExtentData - extent data record(xdr)
1591 // foundExtentIndex - index to extent entry in xdr
1592 // result = 0, offset to extent mapping desired FABN
1593 // result = FXRangeErr, offset to last extent in record
1594 // (i.e., kNumExtentsPerRecord-1)
1595 // extentBTreeHint - BTree hint for extent record
1596 // kNoHint = Resident extent record
1597 // endingFABNPlusOne - ending FABN +1
1598 //
1599 // Result:
1600 // noErr Found an extent that contains the given file position
1601 // FXRangeErr Given position is beyond the last allocated extent
1602 // (other) (some other internal I/O error)
1603 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1604
1605 static OSErr SearchExtentFile(
1606 const ExtendedVCB *vcb,
1607 const FCB *fcb,
1608 SInt64 filePosition,
1609 HFSPlusExtentKey *foundExtentKey,
1610 HFSPlusExtentRecord foundExtentData,
1611 UInt32 *foundExtentIndex,
1612 UInt32 *extentBTreeHint,
1613 UInt32 *endingFABNPlusOne )
1614 {
1615 OSErr err;
1616 UInt32 filePositionBlock;
1617 SInt64 temp64;
1618 Boolean noMoreExtents;
1619
1620 temp64 = filePosition / (SInt64)vcb->blockSize;
1621 filePositionBlock = (UInt32)temp64;
1622
1623 bcopy ( fcb->fcbExtents, foundExtentData, sizeof(HFSPlusExtentRecord));
1624
1625 // Search the resident FCB first.
1626 err = SearchExtentRecord( vcb, filePositionBlock, foundExtentData, 0,
1627 foundExtentIndex, endingFABNPlusOne, &noMoreExtents );
1628
1629 if( err == noErr ) {
1630 // Found the extent. Set results accordingly
1631 *extentBTreeHint = kNoHint; // no hint, because not in the BTree
1632 foundExtentKey->keyLength = 0; // 0 = the FCB itself
1633
1634 goto Exit;
1635 }
1636
1637 // Didn't find extent in FCB. If FCB's extent record wasn't full, there's no point
1638 // in searching the extents file. Note that SearchExtentRecord left us pointing at
1639 // the last valid extent (or the first one, if none were valid). This means we need
1640 // to fill in the hint and key outputs, just like the "if" statement above.
1641 if ( noMoreExtents ) {
1642 *extentBTreeHint = kNoHint; // no hint, because not in the BTree
1643 foundExtentKey->keyLength = 0; // 0 = the FCB itself
1644 err = fxRangeErr; // There are no more extents, so must be beyond PEOF
1645 goto Exit;
1646 }
1647
1648 //
1649 // Find the desired record, or the previous record if it is the same fork
1650 //
1651 err = FindExtentRecord(vcb, (fcb->fcbFlags & fcbResourceMask) ? kResourceForkType : kDataForkType,
1652 H_FILEID(fcb), filePositionBlock, true, foundExtentKey, foundExtentData, extentBTreeHint);
1653
1654 if (err == btNotFound) {
1655 //
1656 // If we get here, the desired position is beyond the extents in the FCB, and there are no extents
1657 // in the extents file. Return the FCB's extents and a range error.
1658 //
1659 *extentBTreeHint = kNoHint;
1660 foundExtentKey->keyLength = 0;
1661 err = GetFCBExtentRecord(fcb, foundExtentData);
1662 // Note: foundExtentIndex and endingFABNPlusOne have already been set as a result of the very
1663 // first SearchExtentRecord call in this function (when searching in the FCB's extents, and
1664 // we got a range error).
1665
1666 return fxRangeErr;
1667 }
1668
1669 //
1670 // If we get here, there was either a BTree error, or we found an appropriate record.
1671 // If we found a record, then search it for the correct index into the extents.
1672 //
1673 if (err == noErr) {
1674 // Find appropriate index into extent record
1675 err = SearchExtentRecord(vcb, filePositionBlock, foundExtentData, foundExtentKey->startBlock,
1676 foundExtentIndex, endingFABNPlusOne, &noMoreExtents);
1677 }
1678
1679 Exit:
1680 return err;
1681 }
1682
1683
1684
1685 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1686 // Routine: UpdateExtentRecord
1687 //
1688 // Function: Write new extent data to an existing extent record with a given key.
1689 // If all of the extents are empty, and the extent record is in the
1690 // extents file, then the record is deleted.
1691 //
1692 // Input: vcb - the volume containing the extents
1693 // fcb - the file that owns the extents
1694 // extentFileKey - pointer to extent key record (xkr)
1695 // If the key length is 0, then the extents are actually part
1696 // of the catalog record, stored in the FCB.
1697 // extentData - pointer to extent data record (xdr)
1698 // extentBTreeHint - hint for given key, or kNoHint
1699 //
1700 // Result: noErr = ok
1701 // (other) = error from BTree
1702 //\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
1703
1704 static OSErr UpdateExtentRecord (
1705 const ExtendedVCB *vcb,
1706 FCB *fcb,
1707 const HFSPlusExtentKey *extentFileKey,
1708 const HFSPlusExtentRecord extentData,
1709 UInt32 extentBTreeHint)
1710 {
1711 BTreeIterator btIterator;
1712 FSBufferDescriptor btRecord;
1713 UInt16 btRecordSize;
1714 FCB * btFCB;
1715 OSErr err = noErr;
1716
1717 if (extentFileKey->keyLength == 0) { // keyLength == 0 means the FCB's extent record
1718 BlockMoveData(extentData, fcb->fcbExtents, sizeof(HFSPlusExtentRecord));
1719 fcb->fcbFlags |= fcbModifiedMask;
1720 }
1721 else {
1722 //
1723 // Need to find and change a record in Extents BTree
1724 //
1725 btFCB = GetFileControlBlock(vcb->extentsRefNum);
1726
1727 if (vcb->vcbSigWord == kHFSSigWord) {
1728 HFSExtentKey * key; // Actual extent key used on disk in HFS
1729 HFSExtentRecord foundData; // The extent data actually found
1730
1731 key = (HFSExtentKey*) &btIterator.key;
1732 key->keyLength = kHFSExtentKeyMaximumLength;
1733 key->forkType = extentFileKey->forkType;
1734 key->fileID = extentFileKey->fileID;
1735 key->startBlock = extentFileKey->startBlock;
1736
1737 btIterator.hint.index = 0;
1738 btIterator.hint.nodeNum = extentBTreeHint;
1739
1740 btRecord.bufferAddress = &foundData;
1741 btRecord.itemSize = sizeof(HFSExtentRecord);
1742 btRecord.itemCount = 1;
1743
1744 err = BTSearchRecord(btFCB, &btIterator, kInvalidMRUCacheKey, &btRecord,
1745 &btRecordSize, &btIterator);
1746
1747 if (err == noErr)
1748 err = HFSPlusToHFSExtents(extentData, (HFSExtentDescriptor *)&foundData);
1749
1750 if (err == noErr)
1751 err = BTReplaceRecord(btFCB, &btIterator, &btRecord, btRecordSize);
1752 }
1753 else { // HFS Plus volume
1754 HFSPlusExtentRecord foundData; // The extent data actually found
1755
1756 BlockMoveData(extentFileKey, &btIterator.key, sizeof(HFSPlusExtentKey));
1757
1758 btIterator.hint.index = 0;
1759 btIterator.hint.nodeNum = extentBTreeHint;
1760
1761 btRecord.bufferAddress = &foundData;
1762 btRecord.itemSize = sizeof(HFSPlusExtentRecord);
1763 btRecord.itemCount = 1;
1764
1765 err = BTSearchRecord(btFCB, &btIterator, kInvalidMRUCacheKey, &btRecord,
1766 &btRecordSize, &btIterator);
1767
1768 if (err == noErr) {
1769 BlockMoveData(extentData, &foundData, sizeof(HFSPlusExtentRecord));
1770 err = BTReplaceRecord(btFCB, &btIterator, &btRecord, btRecordSize);
1771 }
1772 }
1773 }
1774
1775 return err;
1776 }
1777
1778
1779
1780 void HFSToHFSPlusExtents(
1781 const HFSExtentRecord oldExtents,
1782 HFSPlusExtentRecord newExtents)
1783 {
1784 UInt32 i;
1785
1786 // copy the first 3 extents
1787 newExtents[0].startBlock = oldExtents[0].startBlock;
1788 newExtents[0].blockCount = oldExtents[0].blockCount;
1789 newExtents[1].startBlock = oldExtents[1].startBlock;
1790 newExtents[1].blockCount = oldExtents[1].blockCount;
1791 newExtents[2].startBlock = oldExtents[2].startBlock;
1792 newExtents[2].blockCount = oldExtents[2].blockCount;
1793
1794 // zero out the remaining ones
1795 for (i = 3; i < kHFSPlusExtentDensity; ++i)
1796 {
1797 newExtents[i].startBlock = 0;
1798 newExtents[i].blockCount = 0;
1799 }
1800 }
1801
1802
1803
1804 OSErr HFSPlusToHFSExtents(
1805 const HFSPlusExtentRecord oldExtents,
1806 HFSExtentRecord newExtents)
1807 {
1808 OSErr err;
1809
1810 err = noErr;
1811
1812 // copy the first 3 extents
1813 newExtents[0].startBlock = oldExtents[0].startBlock;
1814 newExtents[0].blockCount = oldExtents[0].blockCount;
1815 newExtents[1].startBlock = oldExtents[1].startBlock;
1816 newExtents[1].blockCount = oldExtents[1].blockCount;
1817 newExtents[2].startBlock = oldExtents[2].startBlock;
1818 newExtents[2].blockCount = oldExtents[2].blockCount;
1819
1820 #if DEBUG_BUILD
1821 if (oldExtents[3].startBlock || oldExtents[3].blockCount) {
1822 DebugStr("\pExtentRecord with > 3 extents is invalid for HFS");
1823 err = fsDSIntErr;
1824 }
1825 #endif
1826
1827 return err;
1828 }
1829
1830
1831
1832
1833 OSErr GetFCBExtentRecord(
1834 const FCB *fcb,
1835 HFSPlusExtentRecord extents)
1836 {
1837
1838 BlockMoveData(fcb->fcbExtents, extents, sizeof(HFSPlusExtentRecord));
1839
1840 return noErr;
1841 }
1842
1843
1844 //_________________________________________________________________________________
1845 //
1846 // Routine: ExtentsAreIntegral
1847 //
1848 // Purpose: Ensure that each extent can hold an integral number of nodes
1849 // Called by the NodesAreContiguous function
1850 //_________________________________________________________________________________
1851
1852 static Boolean ExtentsAreIntegral(
1853 const HFSPlusExtentRecord extentRecord,
1854 UInt32 mask,
1855 UInt32 *blocksChecked,
1856 Boolean *checkedLastExtent)
1857 {
1858 UInt32 blocks;
1859 UInt32 extentIndex;
1860
1861 *blocksChecked = 0;
1862 *checkedLastExtent = false;
1863
1864 for(extentIndex = 0; extentIndex < kHFSPlusExtentDensity; extentIndex++)
1865 {
1866 blocks = extentRecord[extentIndex].blockCount;
1867
1868 if ( blocks == 0 )
1869 {
1870 *checkedLastExtent = true;
1871 break;
1872 }
1873
1874 *blocksChecked += blocks;
1875
1876 if (blocks & mask)
1877 return false;
1878 }
1879
1880 return true;
1881 }
1882
1883
1884 //_________________________________________________________________________________
1885 //
1886 // Routine: NodesAreContiguous
1887 //
1888 // Purpose: Ensure that all b-tree nodes are contiguous on disk
1889 // Called by BTOpenPath during volume mount
1890 //_________________________________________________________________________________
1891
1892 Boolean NodesAreContiguous(
1893 ExtendedVCB *vcb,
1894 FCB *fcb,
1895 UInt32 nodeSize)
1896 {
1897 UInt32 mask;
1898 UInt32 startBlock;
1899 UInt32 blocksChecked;
1900 UInt32 hint;
1901 HFSPlusExtentKey key;
1902 HFSPlusExtentRecord extents;
1903 OSErr result;
1904 Boolean lastExtentReached;
1905
1906
1907 if (vcb->blockSize >= nodeSize)
1908 return TRUE;
1909
1910 mask = (nodeSize / vcb->blockSize) - 1;
1911
1912 // check the local extents
1913 (void) GetFCBExtentRecord(fcb, extents);
1914 if ( !ExtentsAreIntegral(extents, mask, &blocksChecked, &lastExtentReached) )
1915 return FALSE;
1916
1917 if (lastExtentReached || (SInt64)((SInt64)blocksChecked * (SInt64)vcb->blockSize) >= fcb->fcbPLen)
1918 return TRUE;
1919
1920 startBlock = blocksChecked;
1921
1922 // check the overflow extents (if any)
1923 while ( !lastExtentReached )
1924 {
1925 result = FindExtentRecord(vcb, kDataForkType, H_FILEID(fcb), startBlock, FALSE, &key, extents, &hint);
1926 if (result) break;
1927
1928 if ( !ExtentsAreIntegral(extents, mask, &blocksChecked, &lastExtentReached) )
1929 return FALSE;
1930
1931 startBlock += blocksChecked;
1932 }
1933
1934 return TRUE;
1935 }
1936