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