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