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