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