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