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