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