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============================================================
41Public (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============================================================
73Internal 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
100static const int64_t kTwoGigabytes = 0x80000000LL;
101
102enum
103{
104	kDataForkType			= 0,
105	kResourceForkType		= 0xFF,
106
107	kPreviousRecord			= -1
108};
109
110
111static OSErr HFSPlusToHFSExtents(
112	const HFSPlusExtentRecord	oldExtents,
113	HFSExtentRecord				newExtents);
114
115static OSErr FindExtentRecord(
116	const ExtendedVCB		*vcb,
117	u_int8_t				forkType,
118	u_int32_t				fileID,
119	u_int32_t				startBlock,
120	Boolean					allowPrevious,
121	HFSPlusExtentKey		*foundKey,
122	HFSPlusExtentRecord		foundData,
123	u_int32_t				*foundHint);
124
125static OSErr DeleteExtentRecord(
126	const ExtendedVCB		*vcb,
127	u_int8_t				forkType,
128	u_int32_t				fileID,
129	u_int32_t				startBlock);
130
131static OSErr CreateExtentRecord(
132	ExtendedVCB		*vcb,
133	HFSPlusExtentKey		*key,
134	HFSPlusExtentRecord		extents,
135	u_int32_t				*hint);
136
137
138static OSErr GetFCBExtentRecord(
139	const FCB				*fcb,
140	HFSPlusExtentRecord		extents);
141
142static OSErr SearchExtentFile(
143	ExtendedVCB		*vcb,
144	const FCB	 			*fcb,
145	int64_t 				filePosition,
146	HFSPlusExtentKey		*foundExtentKey,
147	HFSPlusExtentRecord		foundExtentData,
148	u_int32_t				*foundExtentDataIndex,
149	u_int32_t				*extentBTreeHint,
150	u_int32_t				*endingFABNPlusOne );
151
152static OSErr SearchExtentRecord(
153	ExtendedVCB		*vcb,
154	u_int32_t				searchFABN,
155	const HFSPlusExtentRecord	extentData,
156	u_int32_t				extentDataStartFABN,
157	u_int32_t				*foundExtentDataOffset,
158	u_int32_t				*endingFABNPlusOne,
159	Boolean					*noMoreExtents);
160
161static OSErr ReleaseExtents(
162	ExtendedVCB				*vcb,
163	const HFSPlusExtentRecord	extentRecord,
164	u_int32_t				*numReleasedAllocationBlocks,
165	Boolean 				*releasedLastExtent);
166
167static OSErr DeallocateFork(
168	ExtendedVCB 		*vcb,
169	HFSCatalogNodeID	fileID,
170	u_int8_t			forkType,
171	HFSPlusExtentRecord	catalogExtents,
172	Boolean *		recordDeleted);
173
174static OSErr TruncateExtents(
175	ExtendedVCB			*vcb,
176	u_int8_t			forkType,
177	u_int32_t			fileID,
178	u_int32_t			startBlock,
179	Boolean *			recordDeleted);
180
181static OSErr UpdateExtentRecord (
182	ExtendedVCB		*vcb,
183	FCB						*fcb,
184	const HFSPlusExtentKey	*extentFileKey,
185	const HFSPlusExtentRecord	extentData,
186	u_int32_t					extentBTreeHint);
187
188static Boolean ExtentsAreIntegral(
189	const HFSPlusExtentRecord extentRecord,
190	u_int32_t	mask,
191	u_int32_t	*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//_________________________________________________________________________________
216static OSErr FindExtentRecord(
217	const ExtendedVCB	*vcb,
218	u_int8_t			forkType,
219	u_int32_t			fileID,
220	u_int32_t			startBlock,
221	Boolean				allowPrevious,
222	HFSPlusExtentKey	*foundKey,
223	HFSPlusExtentRecord	foundData,
224	u_int32_t			*foundHint)
225{
226	FCB *				fcb;
227	BTreeIterator		*btIterator;
228	FSBufferDescriptor	btRecord;
229	OSErr				err;
230	u_int16_t			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			u_int16_t	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
346static OSErr CreateExtentRecord(
347	ExtendedVCB	*vcb,
348	HFSPlusExtentKey	*key,
349	HFSPlusExtentRecord	extents,
350	u_int32_t			*hint)
351{
352	BTreeIterator * btIterator;
353	FSBufferDescriptor	btRecord;
354	u_int16_t  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
414static OSErr DeleteExtentRecord(
415	const ExtendedVCB	*vcb,
416	u_int8_t			forkType,
417	u_int32_t			fileID,
418	u_int32_t			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__
466OSErr 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	u_int32_t			allocBlockSize;			//	Size of the volume's allocation block
476	u_int32_t			sectorSize;
477	HFSPlusExtentKey	foundKey;
478	HFSPlusExtentRecord	foundData;
479	u_int32_t			foundIndex;
480	u_int32_t			hint;
481	u_int32_t			firstFABN;				// file allocation block of first block in found extent
482	u_int32_t			nextFABN;				// file allocation block of block after end of found extent
483	off_t				dataEnd;				// (offset) end of range that is contiguous
484	u_int32_t			sectorsPerBlock;		// Number of sectors per allocation block
485	u_int32_t			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_logical_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 += (daddr64_t)startBlock * (daddr64_t)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//�������������������������������������������������������������������������������
549//	Routine:	ReleaseExtents
550//
551//	Function: 	Release the extents of a single extent data record.
552//�������������������������������������������������������������������������������
553
554static OSErr ReleaseExtents(
555	ExtendedVCB 			*vcb,
556	const HFSPlusExtentRecord	extentRecord,
557	u_int32_t				*numReleasedAllocationBlocks,
558	Boolean 				*releasedLastExtent)
559{
560	u_int32_t	extentIndex;
561	u_int32_t	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		u_int32_t	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//�������������������������������������������������������������������������������
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//�������������������������������������������������������������������������������
610
611static OSErr TruncateExtents(
612	ExtendedVCB		*vcb,
613	u_int8_t		forkType,
614	u_int32_t		fileID,
615	u_int32_t		startBlock,
616	Boolean *		recordDeleted)
617{
618	OSErr				err;
619	u_int32_t			numberExtentsReleased;
620	Boolean				releasedLastExtent;
621	u_int32_t			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//�������������������������������������������������������������������������������
660//	Routine:	DeallocateFork
661//
662//	Function: 	De-allocates all disk space allocated to a specified fork.
663//�������������������������������������������������������������������������������
664
665static OSErr DeallocateFork(
666	ExtendedVCB 		*vcb,
667	HFSCatalogNodeID	fileID,
668	u_int8_t			forkType,
669	HFSPlusExtentRecord	catalogExtents,
670	Boolean *		recordDeleted) /* true if a record was deleted */
671{
672	OSErr				err;
673	u_int32_t			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//�������������������������������������������������������������������������������
686//	Routine:	FlushExtentFile
687//
688//	Function: 	Flushes the extent file for a specified volume
689//�������������������������������������������������������������������������������
690
691__private_extern__
692OSErr 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//�������������������������������������������������������������������������������
720//	Routine:	CompareExtentKeys
721//
722//	Function: 	Compares two extent file keys (a search key and a trial key) for
723//				an HFS volume.
724//�������������������������������������������������������������������������������
725
726__private_extern__
727int32_t CompareExtentKeys( const HFSExtentKey *searchKey, const HFSExtentKey *trialKey )
728{
729	int32_t	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//�������������������������������������������������������������������������������
784//	Routine:	CompareExtentKeysPlus
785//
786//	Function: 	Compares two extent file keys (a search key and a trial key) for
787//				an HFS volume.
788//�������������������������������������������������������������������������������
789
790__private_extern__
791int32_t CompareExtentKeysPlus( const HFSPlusExtentKey *searchKey, const HFSPlusExtentKey *trialKey )
792{
793	int32_t	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__
852int
853AddFileExtent(ExtendedVCB *vcb, FCB *fcb, u_int32_t startBlock, u_int32_t blockCount)
854{
855	HFSPlusExtentKey foundKey;
856	HFSPlusExtentRecord foundData;
857	u_int32_t foundIndex;
858	u_int32_t hint;
859	u_int32_t nextBlock;
860	int64_t peof;
861	int i;
862	int error;
863
864	peof = (int64_t)(fcb->ff_blocks + blockCount) * (int64_t)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__
922OSErr ExtendFileC (
923	ExtendedVCB		*vcb,				// volume that file resides on
924	FCB				*fcb,				// FCB of file to truncate
925	int64_t			bytesToAdd,			// number of bytes to allocate
926	u_int32_t		blockHint,			// desired starting allocation block
927	u_int32_t		flags,				// EFContig and/or EFAll
928	int64_t			*actualBytesAdded)	// number of bytes actually allocated
929{
930	OSErr				err;
931	u_int32_t			volumeBlockSize;
932	int64_t				blocksToAdd;
933	int64_t				bytesThisExtent;
934	HFSPlusExtentKey	foundKey;
935	HFSPlusExtentRecord	foundData;
936	u_int32_t			foundIndex;
937	u_int32_t			hint;
938	u_int32_t			nextBlock;
939	u_int32_t			startBlock;
940	Boolean				allOrNothing;
941	Boolean				forceContig;
942	Boolean				wantContig;
943	Boolean				useMetaZone;
944	Boolean				needsFlush;
945	u_int32_t			actualStartBlock;
946	u_int32_t			actualNumBlocks;
947	u_int32_t			numExtentsPerRecord;
948	int64_t				maximumBytes;
949	int64_t 			availbytes;
950	int64_t				peof;
951	u_int32_t			prevblocks;
952
953
954	needsFlush = false;
955	*actualBytesAdded = 0;
956	volumeBlockSize = vcb->blockSize;
957	allOrNothing = ((flags & kEFAllMask) != 0);
958	forceContig = ((flags & kEFContigMask) != 0);
959	prevblocks = fcb->ff_blocks;
960
961	if (vcb->vcbSigWord == kHFSPlusSigWord)
962		numExtentsPerRecord = kHFSPlusExtentDensity;
963	else
964		numExtentsPerRecord = kHFSExtentDensity;
965
966	//
967	//	Make sure the request and new PEOF are less than 2GB if HFS.
968	//
969	if (vcb->vcbSigWord == kHFSSigWord) {
970		if (bytesToAdd >=  kTwoGigabytes)
971			goto Overflow;
972		if ((((int64_t)fcb->ff_blocks * (int64_t)volumeBlockSize) + bytesToAdd) >= kTwoGigabytes)
973			goto Overflow;
974	}
975	//
976	//	Determine how many blocks need to be allocated.
977	//	Round up the number of desired bytes to add.
978	//
979	blocksToAdd = howmany(bytesToAdd, volumeBlockSize);
980	bytesToAdd = (int64_t)((int64_t)blocksToAdd * (int64_t)volumeBlockSize);
981
982	/*
983	 * For deferred allocations just reserve the blocks.
984	 */
985	if ((flags & kEFDeferMask)
986	&&  (vcb->vcbSigWord == kHFSPlusSigWord)
987	&&  (bytesToAdd < (int64_t)HFS_MAX_DEFERED_ALLOC)
988	&&  (blocksToAdd < hfs_freeblks(VCBTOHFS(vcb), 1))) {
989		HFS_MOUNT_LOCK(vcb, TRUE);
990		vcb->loanedBlocks += blocksToAdd;
991		HFS_MOUNT_UNLOCK(vcb, TRUE);
992
993		fcb->ff_unallocblocks += blocksToAdd;
994		FTOC(fcb)->c_blocks   += blocksToAdd;
995		fcb->ff_blocks        += blocksToAdd;
996
997		FTOC(fcb)->c_flag |= C_MODIFIED | C_FORCEUPDATE;
998		*actualBytesAdded = bytesToAdd;
999		return (0);
1000	}
1001	/*
1002	 * Give back any unallocated blocks before doing real allocations.
1003	 */
1004	if (fcb->ff_unallocblocks > 0) {
1005		u_int32_t loanedBlocks;
1006
1007		loanedBlocks = fcb->ff_unallocblocks;
1008		blocksToAdd += loanedBlocks;
1009		bytesToAdd = (int64_t)blocksToAdd * (int64_t)volumeBlockSize;
1010		FTOC(fcb)->c_blocks -= loanedBlocks;
1011		fcb->ff_blocks -= loanedBlocks;
1012		fcb->ff_unallocblocks  = 0;
1013
1014		HFS_MOUNT_LOCK(vcb, TRUE);
1015		vcb->loanedBlocks -= loanedBlocks;
1016		HFS_MOUNT_UNLOCK(vcb, TRUE);
1017	}
1018
1019	//
1020	//	If the file's clump size is larger than the allocation block size,
1021	//	then set the maximum number of bytes to the requested number of bytes
1022	//	rounded up to a multiple of the clump size.
1023	//
1024	if ((vcb->vcbClpSiz > (int32_t)volumeBlockSize)
1025	&&  (bytesToAdd < (int64_t)HFS_MAX_DEFERED_ALLOC)
1026	&&  (flags & kEFNoClumpMask) == 0) {
1027		maximumBytes = (int64_t)howmany(bytesToAdd, vcb->vcbClpSiz);
1028		maximumBytes *= vcb->vcbClpSiz;
1029	} else {
1030		maximumBytes = bytesToAdd;
1031	}
1032
1033	//
1034	//	Compute new physical EOF, rounded up to a multiple of a block.
1035	//
1036	if ( (vcb->vcbSigWord == kHFSSigWord) &&		//	Too big?
1037		 ((((int64_t)fcb->ff_blocks * (int64_t)volumeBlockSize) + bytesToAdd) >= kTwoGigabytes) ) {
1038		if (allOrNothing)					// Yes, must they have it all?
1039			goto Overflow;						// Yes, can't have it
1040		else {
1041			--blocksToAdd;						// No, give give 'em one block less
1042			bytesToAdd -= volumeBlockSize;
1043		}
1044	}
1045
1046	//
1047	//	If allocation is all-or-nothing, make sure there are
1048	//	enough free blocks on the volume (quick test).
1049	//
1050	if (allOrNothing &&
1051	    (blocksToAdd > hfs_freeblks(VCBTOHFS(vcb), flags & kEFReserveMask))) {
1052		err = dskFulErr;
1053		goto ErrorExit;
1054	}
1055
1056	//
1057	//	See if there are already enough blocks allocated to the file.
1058	//
1059	peof = ((int64_t)fcb->ff_blocks * (int64_t)volumeBlockSize) + bytesToAdd;  // potential new PEOF
1060	err = SearchExtentFile(vcb, fcb, peof-1, &foundKey, foundData, &foundIndex, &hint, &nextBlock);
1061	if (err == noErr) {
1062		//	Enough blocks are already allocated.  Just update the FCB to reflect the new length.
1063		fcb->ff_blocks = peof / volumeBlockSize;
1064		FTOC(fcb)->c_blocks += (bytesToAdd / volumeBlockSize);
1065		FTOC(fcb)->c_flag |= C_MODIFIED | C_FORCEUPDATE;
1066		goto Exit;
1067	}
1068	if (err != fxRangeErr)		// Any real error?
1069		goto ErrorExit;				// Yes, so exit immediately
1070
1071	//
1072	//	Adjust the PEOF to the end of the last extent.
1073	//
1074	peof = (int64_t)((int64_t)nextBlock * (int64_t)volumeBlockSize);			// currently allocated PEOF
1075	bytesThisExtent = (int64_t)(nextBlock - fcb->ff_blocks) * (int64_t)volumeBlockSize;
1076	if (bytesThisExtent != 0) {
1077		fcb->ff_blocks = nextBlock;
1078		FTOC(fcb)->c_blocks += (bytesThisExtent / volumeBlockSize);
1079		FTOC(fcb)->c_flag |= C_MODIFIED;
1080		bytesToAdd -= bytesThisExtent;
1081	}
1082
1083	//
1084	//	Allocate some more space.
1085	//
1086	//	First try a contiguous allocation (of the whole amount).
1087	//	If that fails, get whatever we can.
1088	//		If forceContig, then take whatever we got
1089	//		else, keep getting bits and pieces (non-contig)
1090	err = noErr;
1091	wantContig = true;
1092	useMetaZone = flags & kEFMetadataMask;
1093	vcb->vcbFreeExtCnt = 0;	/* For now, force rebuild of free extent list */
1094	do {
1095		if (blockHint != 0)
1096			startBlock = blockHint;
1097		else
1098			startBlock = foundData[foundIndex].startBlock + foundData[foundIndex].blockCount;
1099
1100		actualNumBlocks = 0;
1101		actualStartBlock = 0;
1102
1103		/* Find number of free blocks based on reserved block flag option */
1104		availbytes = (int64_t)hfs_freeblks(VCBTOHFS(vcb), flags & kEFReserveMask) *
1105		             (int64_t)volumeBlockSize;
1106		if (availbytes <= 0) {
1107			err = dskFulErr;
1108		} else {
1109			if (wantContig && (availbytes < bytesToAdd))
1110				err = dskFulErr;
1111			else {
1112				err = BlockAllocate(
1113						  vcb,
1114						  startBlock,
1115						  howmany(MIN(bytesToAdd, availbytes), volumeBlockSize),
1116						  howmany(MIN(maximumBytes, availbytes), volumeBlockSize),
1117						  wantContig,
1118						  useMetaZone,
1119						  &actualStartBlock,
1120						  &actualNumBlocks);
1121			}
1122		}
1123		if (err == dskFulErr) {
1124			if (forceContig)
1125				break;			// AllocContig failed because not enough contiguous space
1126			if (wantContig) {
1127				//	Couldn't get one big chunk, so get whatever we can.
1128				err = noErr;
1129				wantContig = false;
1130				continue;
1131			}
1132			if (actualNumBlocks != 0)
1133				err = noErr;
1134			if (useMetaZone == 0) {
1135				/* Couldn't get anything so dip into metadat zone */
1136				err = noErr;
1137				useMetaZone = 1;
1138				continue;
1139			}
1140		}
1141		if (err == noErr) {
1142		    if (actualNumBlocks != 0) {
1143				// this catalog entry *must* get forced to disk when
1144				// hfs_update() is called
1145				FTOC(fcb)->c_flag |= C_FORCEUPDATE;
1146			}
1147
1148			//	Add the new extent to the existing extent record, or create a new one.
1149			if ((actualStartBlock == startBlock) && (blockHint == 0)) {
1150				//	We grew the file's last extent, so just adjust the number of blocks.
1151				foundData[foundIndex].blockCount += actualNumBlocks;
1152				err = UpdateExtentRecord(vcb, fcb, &foundKey, foundData, hint);
1153				if (err != noErr) break;
1154			}
1155			else {
1156				u_int16_t	i;
1157
1158				//	Need to add a new extent.  See if there is room in the current record.
1159				if (foundData[foundIndex].blockCount != 0)	//	Is current extent free to use?
1160					++foundIndex;							// 	No, so use the next one.
1161				if (foundIndex == numExtentsPerRecord) {
1162					//	This record is full.  Need to create a new one.
1163					if (FTOC(fcb)->c_fileid == kHFSExtentsFileID) {
1164						(void) BlockDeallocate(vcb, actualStartBlock, actualNumBlocks);
1165						err = dskFulErr;		// Oops.  Can't extend extents file past first record.
1166						break;
1167					}
1168
1169					foundKey.keyLength = kHFSPlusExtentKeyMaximumLength;
1170					if (FORK_IS_RSRC(fcb))
1171						foundKey.forkType = kResourceForkType;
1172					else
1173						foundKey.forkType = kDataForkType;
1174					foundKey.pad = 0;
1175					foundKey.fileID = FTOC(fcb)->c_fileid;
1176					foundKey.startBlock = nextBlock;
1177
1178					foundData[0].startBlock = actualStartBlock;
1179					foundData[0].blockCount = actualNumBlocks;
1180
1181					// zero out remaining extents...
1182					for (i = 1; i < kHFSPlusExtentDensity; ++i)
1183					{
1184						foundData[i].startBlock = 0;
1185						foundData[i].blockCount = 0;
1186					}
1187
1188					foundIndex = 0;
1189
1190					err = CreateExtentRecord(vcb, &foundKey, foundData, &hint);
1191					if (err == fxOvFlErr) {
1192						//	We couldn't create an extent record because extents B-tree
1193						//	couldn't grow.  Dellocate the extent just allocated and
1194						//	return a disk full error.
1195						(void) BlockDeallocate(vcb, actualStartBlock, actualNumBlocks);
1196						err = dskFulErr;
1197					}
1198					if (err != noErr) break;
1199
1200					needsFlush = true;		//	We need to update the B-tree header
1201				}
1202				else {
1203					//	Add a new extent into this record and update.
1204					foundData[foundIndex].startBlock = actualStartBlock;
1205					foundData[foundIndex].blockCount = actualNumBlocks;
1206					err = UpdateExtentRecord(vcb, fcb, &foundKey, foundData, hint);
1207					if (err != noErr) break;
1208				}
1209			}
1210
1211			// Figure out how many bytes were actually allocated.
1212			// NOTE: BlockAllocate could have allocated more than we asked for.
1213			// Don't set the PEOF beyond what our client asked for.
1214			nextBlock += actualNumBlocks;
1215			bytesThisExtent = (int64_t)((int64_t)actualNumBlocks * (int64_t)volumeBlockSize);
1216			if (bytesThisExtent > bytesToAdd) {
1217				bytesToAdd = 0;
1218			}
1219			else {
1220				bytesToAdd -= bytesThisExtent;
1221				maximumBytes -= bytesThisExtent;
1222			}
1223			fcb->ff_blocks += (bytesThisExtent / volumeBlockSize);
1224			FTOC(fcb)->c_blocks += (bytesThisExtent / volumeBlockSize);
1225			FTOC(fcb)->c_flag |= C_MODIFIED | C_FORCEUPDATE;
1226
1227			//	If contiguous allocation was requested, then we've already got one contiguous
1228			//	chunk.  If we didn't get all we wanted, then adjust the error to disk full.
1229			if (forceContig) {
1230				if (bytesToAdd != 0)
1231					err = dskFulErr;
1232				break;			//	We've already got everything that's contiguous
1233			}
1234		}
1235	} while (err == noErr && bytesToAdd);
1236
1237ErrorExit:
1238Exit:
1239	if (VCBTOHFS(vcb)->hfs_flags & HFS_METADATA_ZONE) {
1240		/* Keep the roving allocator out of the metadata zone. */
1241		if (vcb->nextAllocation >= VCBTOHFS(vcb)->hfs_metazone_start &&
1242		    vcb->nextAllocation <= VCBTOHFS(vcb)->hfs_metazone_end) {
1243			HFS_MOUNT_LOCK(vcb, TRUE);
1244			HFS_UPDATE_NEXT_ALLOCATION(vcb, VCBTOHFS(vcb)->hfs_metazone_end + 1);
1245			MarkVCBDirty(vcb);
1246			HFS_MOUNT_UNLOCK(vcb, TRUE);
1247		}
1248	}
1249	if (prevblocks < fcb->ff_blocks) {
1250		*actualBytesAdded = (int64_t)(fcb->ff_blocks - prevblocks) * (int64_t)volumeBlockSize;
1251	} else {
1252		*actualBytesAdded = 0;
1253	}
1254
1255	if (needsFlush)
1256		(void) FlushExtentFile(vcb);
1257
1258	return err;
1259
1260Overflow:
1261	err = fileBoundsErr;
1262	goto ErrorExit;
1263}
1264
1265
1266
1267//_________________________________________________________________________________
1268//
1269// Routine:		TruncateFileC
1270//
1271// Function: 	Truncates the disk space allocated to a file.  The file space is
1272//				truncated to a specified new PEOF rounded up to the next allocation
1273//				block boundry.  If the 'TFTrunExt' option is specified, the file is
1274//				truncated to the end of the extent containing the new PEOF.
1275//
1276//_________________________________________________________________________________
1277
1278__private_extern__
1279OSErr TruncateFileC (
1280	ExtendedVCB		*vcb,				// volume that file resides on
1281	FCB				*fcb,				// FCB of file to truncate
1282	int64_t			peof,				// new physical size for file
1283	Boolean			truncateToExtent)	// if true, truncate to end of extent containing newPEOF
1284{
1285	OSErr				err;
1286	u_int32_t			nextBlock;		//	next file allocation block to consider
1287	u_int32_t			startBlock;		//	Physical (volume) allocation block number of start of a range
1288	u_int32_t			physNumBlocks;	//	Number of allocation blocks in file (according to PEOF)
1289	u_int32_t			numBlocks;
1290	HFSPlusExtentKey	key;			//	key for current extent record; key->keyLength == 0 if FCB's extent record
1291	u_int32_t			hint;			//	BTree hint corresponding to key
1292	HFSPlusExtentRecord	extentRecord;
1293	u_int32_t			extentIndex;
1294	u_int32_t			extentNextBlock;
1295	u_int32_t			numExtentsPerRecord;
1296	int64_t             temp64;
1297	u_int8_t			forkType;
1298	Boolean				extentChanged;	// true if we actually changed an extent
1299	Boolean				recordDeleted;	// true if an extent record got deleted
1300
1301	recordDeleted = false;
1302
1303	if (vcb->vcbSigWord == kHFSPlusSigWord)
1304		numExtentsPerRecord = kHFSPlusExtentDensity;
1305	else
1306		numExtentsPerRecord = kHFSExtentDensity;
1307
1308	if (FORK_IS_RSRC(fcb))
1309		forkType = kResourceForkType;
1310	else
1311		forkType = kDataForkType;
1312
1313	temp64 = fcb->ff_blocks;
1314	physNumBlocks = (u_int32_t)temp64;
1315
1316	//
1317	//	Round newPEOF up to a multiple of the allocation block size.  If new size is
1318	//	two gigabytes or more, then round down by one allocation block (??? really?
1319	//	shouldn't that be an error?).
1320	//
1321	nextBlock = howmany(peof, vcb->blockSize);	// number of allocation blocks to remain in file
1322	peof = (int64_t)((int64_t)nextBlock * (int64_t)vcb->blockSize);					// number of bytes in those blocks
1323	if ((vcb->vcbSigWord == kHFSSigWord) && (peof >= kTwoGigabytes)) {
1324		#if DEBUG_BUILD
1325			DebugStr("\pHFS: Trying to truncate a file to 2GB or more");
1326		#endif
1327		err = fileBoundsErr;
1328		goto ErrorExit;
1329	}
1330
1331	//
1332	//	Update FCB's length
1333	//
1334	/*
1335	 * XXX Any errors could cause ff_blocks and c_blocks to get out of sync...
1336	 */
1337	numBlocks = peof / vcb->blockSize;
1338	FTOC(fcb)->c_blocks -= (fcb->ff_blocks - numBlocks);
1339	fcb->ff_blocks = numBlocks;
1340
1341	// this catalog entry is modified and *must* get forced
1342	// to disk when hfs_update() is called
1343	FTOC(fcb)->c_flag |= C_MODIFIED | C_FORCEUPDATE;
1344
1345	//
1346	//	If the new PEOF is 0, then truncateToExtent has no meaning (we should always deallocate
1347	//	all storage).
1348	//
1349	if (peof == 0) {
1350		int i;
1351
1352		//	Deallocate all the extents for this fork
1353		err = DeallocateFork(vcb, FTOC(fcb)->c_fileid, forkType, fcb->fcbExtents, &recordDeleted);
1354		if (err != noErr) goto ErrorExit;	//	got some error, so return it
1355
1356		//	Update the catalog extent record (making sure it's zeroed out)
1357		if (err == noErr) {
1358			for (i=0; i < kHFSPlusExtentDensity; i++) {
1359				fcb->fcbExtents[i].startBlock = 0;
1360				fcb->fcbExtents[i].blockCount = 0;
1361			}
1362		}
1363		goto Done;
1364	}
1365
1366	//
1367	//	Find the extent containing byte (peof-1).  This is the last extent we'll keep.
1368	//	(If truncateToExtent is true, we'll keep the whole extent; otherwise, we'll only
1369	//	keep up through peof).  The search will tell us how many allocation blocks exist
1370	//	in the found extent plus all previous extents.
1371	//
1372	err = SearchExtentFile(vcb, fcb, peof-1, &key, extentRecord, &extentIndex, &hint, &extentNextBlock);
1373	if (err != noErr) goto ErrorExit;
1374
1375	extentChanged = false;		//	haven't changed the extent yet
1376
1377	if (!truncateToExtent) {
1378		//
1379		//	Shorten this extent.  It may be the case that the entire extent gets
1380		//	freed here.
1381		//
1382		numBlocks = extentNextBlock - nextBlock;	//	How many blocks in this extent to free up
1383		if (numBlocks != 0) {
1384			//	Compute first volume allocation block to free
1385			startBlock = extentRecord[extentIndex].startBlock + extentRecord[extentIndex].blockCount - numBlocks;
1386			//	Free the blocks in bitmap
1387			err = BlockDeallocate(vcb, startBlock, numBlocks);
1388			if (err != noErr) goto ErrorExit;
1389			//	Adjust length of this extent
1390			extentRecord[extentIndex].blockCount -= numBlocks;
1391			//	If extent is empty, set start block to 0
1392			if (extentRecord[extentIndex].blockCount == 0)
1393				extentRecord[extentIndex].startBlock = 0;
1394			//	Remember that we changed the extent record
1395			extentChanged = true;
1396		}
1397	}
1398
1399	//
1400	//	Now move to the next extent in the record, and set up the file allocation block number
1401	//
1402	nextBlock = extentNextBlock;		//	Next file allocation block to free
1403	++extentIndex;						//	Its index within the extent record
1404
1405	//
1406	//	Release all following extents in this extent record.  Update the record.
1407	//
1408	while (extentIndex < numExtentsPerRecord && extentRecord[extentIndex].blockCount != 0) {
1409		numBlocks = extentRecord[extentIndex].blockCount;
1410		//	Deallocate this extent
1411		err = BlockDeallocate(vcb, extentRecord[extentIndex].startBlock, numBlocks);
1412		if (err != noErr) goto ErrorExit;
1413		//	Update next file allocation block number
1414		nextBlock += numBlocks;
1415		//	Zero out start and length of this extent to delete it from record
1416		extentRecord[extentIndex].startBlock = 0;
1417		extentRecord[extentIndex].blockCount = 0;
1418		//	Remember that we changed an extent
1419		extentChanged = true;
1420		//	Move to next extent in record
1421		++extentIndex;
1422	}
1423
1424	//
1425	//	If any of the extents in the current record were changed, then update that
1426	//	record (in the FCB, or extents file).
1427	//
1428	if (extentChanged) {
1429		err = UpdateExtentRecord(vcb, fcb, &key, extentRecord, hint);
1430		if (err != noErr) goto ErrorExit;
1431	}
1432
1433	//
1434	//	If there are any following allocation blocks, then we need
1435	//	to seach for their extent records and delete those allocation
1436	//	blocks.
1437	//
1438	if (nextBlock < physNumBlocks)
1439		err = TruncateExtents(vcb, forkType, FTOC(fcb)->c_fileid, nextBlock, &recordDeleted);
1440
1441Done:
1442ErrorExit:
1443	if (recordDeleted)
1444		(void) FlushExtentFile(vcb);
1445
1446	return err;
1447}
1448
1449
1450/*
1451 * HFS Plus only
1452 *
1453 */
1454__private_extern__
1455OSErr HeadTruncateFile (
1456	ExtendedVCB  *vcb,
1457	FCB  *fcb,
1458	u_int32_t  headblks)
1459{
1460	HFSPlusExtentRecord  extents;
1461	HFSPlusExtentRecord  tailExtents;
1462	HFSCatalogNodeID  fileID;
1463	u_int8_t  forkType;
1464	u_int32_t  blkcnt;
1465	u_int32_t  startblk;
1466	u_int32_t  blksfreed;
1467	int  i, j;
1468	int  error = 0;
1469	int  lockflags;
1470
1471
1472	if (vcb->vcbSigWord != kHFSPlusSigWord)
1473		return (-1);
1474
1475	forkType = FORK_IS_RSRC(fcb) ? kResourceForkType : kDataForkType;
1476	fileID = FTOC(fcb)->c_fileid;
1477	bzero(tailExtents, sizeof(tailExtents));
1478
1479	blksfreed = 0;
1480	startblk = 0;
1481
1482	/*
1483	 * Process catalog resident extents
1484	 */
1485	for (i = 0, j = 0; i < kHFSPlusExtentDensity; ++i) {
1486		blkcnt = fcb->fcbExtents[i].blockCount;
1487		if (blkcnt == 0)
1488			break;  /* end of extents */
1489
1490		if (blksfreed < headblks) {
1491			error = BlockDeallocate(vcb, fcb->fcbExtents[i].startBlock, blkcnt);
1492			/*
1493			 * Any errors after the first BlockDeallocate
1494			 * must be ignored so we can put the file in
1495			 * a known state.
1496			 */
1497			if (error ) {
1498				if (i == 0)
1499					goto ErrorExit;  /* uh oh */
1500				else {
1501					error = 0;
1502					printf("HeadTruncateFile: problems deallocating %s (%d)\n",
1503					       FTOC(fcb)->c_desc.cd_nameptr ? (const char *)FTOC(fcb)->c_desc.cd_nameptr : "", error);
1504				}
1505			}
1506
1507			blksfreed += blkcnt;
1508			fcb->fcbExtents[i].startBlock = 0;
1509			fcb->fcbExtents[i].blockCount = 0;
1510		} else {
1511			tailExtents[j].startBlock = fcb->fcbExtents[i].startBlock;
1512			tailExtents[j].blockCount = blkcnt;
1513			++j;
1514		}
1515		startblk += blkcnt;
1516	}
1517
1518	if (blkcnt == 0)
1519		goto CopyExtents;
1520
1521	lockflags = hfs_systemfile_lock(vcb, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
1522
1523	/*
1524	 * Process overflow extents
1525	 */
1526	for (;;) {
1527		u_int32_t  extblks;
1528
1529		error = FindExtentRecord(vcb, forkType, fileID, startblk, false, NULL, extents, NULL);
1530		if (error) {
1531			/*
1532			 * Any errors after the first BlockDeallocate
1533			 * must be ignored so we can put the file in
1534			 * a known state.
1535			 */
1536			if (error != btNotFound)
1537				printf("HeadTruncateFile: problems finding extents %s (%d)\n",
1538				       FTOC(fcb)->c_desc.cd_nameptr ? (const char *)FTOC(fcb)->c_desc.cd_nameptr : "", error);
1539			error = 0;
1540			break;
1541		}
1542
1543		for(i = 0, extblks = 0; i < kHFSPlusExtentDensity; ++i) {
1544			blkcnt = extents[i].blockCount;
1545			if (blkcnt == 0)
1546				break;  /* end of extents */
1547
1548			if (blksfreed < headblks) {
1549				error = BlockDeallocate(vcb, extents[i].startBlock, blkcnt);
1550				if (error) {
1551					printf("HeadTruncateFile: problems deallocating %s (%d)\n",
1552					       FTOC(fcb)->c_desc.cd_nameptr ? (const char *)FTOC(fcb)->c_desc.cd_nameptr : "", error);
1553					error = 0;
1554				}
1555				blksfreed += blkcnt;
1556			} else {
1557				tailExtents[j].startBlock = extents[i].startBlock;
1558				tailExtents[j].blockCount = blkcnt;
1559				++j;
1560			}
1561			extblks += blkcnt;
1562		}
1563
1564		error = DeleteExtentRecord(vcb, forkType, fileID, startblk);
1565		if (error) {
1566			printf("HeadTruncateFile: problems deallocating %s (%d)\n",
1567				FTOC(fcb)->c_desc.cd_nameptr ? (const char *)FTOC(fcb)->c_desc.cd_nameptr : "", error);
1568			error = 0;
1569		}
1570
1571		if (blkcnt == 0)
1572			break;  /* all done */
1573
1574		startblk += extblks;
1575	}
1576	hfs_systemfile_unlock(vcb, lockflags);
1577
1578CopyExtents:
1579	if (blksfreed) {
1580		bcopy(tailExtents, fcb->fcbExtents, sizeof(tailExtents));
1581		blkcnt = fcb->ff_blocks - headblks;
1582		FTOC(fcb)->c_blocks -= headblks;
1583		fcb->ff_blocks = blkcnt;
1584
1585		FTOC(fcb)->c_flag |= C_FORCEUPDATE;
1586		FTOC(fcb)->c_touch_chgtime = TRUE;
1587
1588		(void) FlushExtentFile(vcb);
1589	}
1590
1591ErrorExit:
1592	return MacToVFSError(error);
1593}
1594
1595
1596
1597//�������������������������������������������������������������������������������
1598//	Routine:	SearchExtentRecord (was XRSearch)
1599//
1600//	Function: 	Searches extent record for the extent mapping a given file
1601//				allocation block number (FABN).
1602//
1603//	Input:		searchFABN  			-  desired FABN
1604//				extentData  			-  pointer to extent data record (xdr)
1605//				extentDataStartFABN  	-  beginning FABN for extent record
1606//
1607//	Output:		foundExtentDataOffset  -  offset to extent entry within xdr
1608//							result = noErr, offset to extent mapping desired FABN
1609//							result = FXRangeErr, offset to last extent in record
1610//				endingFABNPlusOne	-  ending FABN +1
1611//				noMoreExtents		- True if the extent was not found, and the
1612//									  extent record was not full (so don't bother
1613//									  looking in subsequent records); false otherwise.
1614//
1615//	Result:		noErr = ok
1616//				FXRangeErr = desired FABN > last mapped FABN in record
1617//�������������������������������������������������������������������������������
1618
1619static OSErr SearchExtentRecord(
1620	ExtendedVCB		*vcb,
1621	u_int32_t				searchFABN,
1622	const HFSPlusExtentRecord	extentData,
1623	u_int32_t				extentDataStartFABN,
1624	u_int32_t				*foundExtentIndex,
1625	u_int32_t				*endingFABNPlusOne,
1626	Boolean					*noMoreExtents)
1627{
1628	OSErr	err = noErr;
1629	u_int32_t	extentIndex;
1630	u_int32_t	numberOfExtents;
1631	u_int32_t	numAllocationBlocks;
1632	Boolean	foundExtent;
1633
1634	*endingFABNPlusOne 	= extentDataStartFABN;
1635	*noMoreExtents		= false;
1636	foundExtent			= false;
1637
1638	if (vcb->vcbSigWord == kHFSPlusSigWord)
1639		numberOfExtents = kHFSPlusExtentDensity;
1640	else
1641		numberOfExtents = kHFSExtentDensity;
1642
1643	for( extentIndex = 0; extentIndex < numberOfExtents; ++extentIndex )
1644	{
1645
1646		// Loop over the extent record and find the search FABN.
1647
1648		numAllocationBlocks = extentData[extentIndex].blockCount;
1649		if ( numAllocationBlocks == 0 )
1650		{
1651			break;
1652		}
1653
1654		*endingFABNPlusOne += numAllocationBlocks;
1655
1656		if( searchFABN < *endingFABNPlusOne )
1657		{
1658			// Found the extent.
1659			foundExtent = true;
1660			break;
1661		}
1662	}
1663
1664	if( foundExtent )
1665	{
1666		// Found the extent. Note the extent offset
1667		*foundExtentIndex = extentIndex;
1668	}
1669	else
1670	{
1671		// Did not find the extent. Set foundExtentDataOffset accordingly
1672		if( extentIndex > 0 )
1673		{
1674			*foundExtentIndex = extentIndex - 1;
1675		}
1676		else
1677		{
1678			*foundExtentIndex = 0;
1679		}
1680
1681		// If we found an empty extent, then set noMoreExtents.
1682		if (extentIndex < numberOfExtents)
1683			*noMoreExtents = true;
1684
1685		// Finally, return an error to the caller
1686		err = fxRangeErr;
1687	}
1688
1689	return( err );
1690}
1691
1692//�������������������������������������������������������������������������������
1693//	Routine:	SearchExtentFile (was XFSearch)
1694//
1695//	Function: 	Searches extent file (including the FCB resident extent record)
1696//				for the extent mapping a given file position.
1697//
1698//	Input:		vcb  			-  VCB pointer
1699//				fcb  			-  FCB pointer
1700//				filePosition  	-  file position (byte address)
1701//
1702// Output:		foundExtentKey  		-  extent key record (xkr)
1703//							If extent was found in the FCB's resident extent record,
1704//							then foundExtentKey->keyLength will be set to 0.
1705//				foundExtentData			-  extent data record(xdr)
1706//				foundExtentIndex  	-  index to extent entry in xdr
1707//							result =  0, offset to extent mapping desired FABN
1708//							result = FXRangeErr, offset to last extent in record
1709//									 (i.e., kNumExtentsPerRecord-1)
1710//				extentBTreeHint  		-  BTree hint for extent record
1711//							kNoHint = Resident extent record
1712//				endingFABNPlusOne  		-  ending FABN +1
1713//
1714//	Result:
1715//		noErr			Found an extent that contains the given file position
1716//		FXRangeErr		Given position is beyond the last allocated extent
1717//		(other)			(some other internal I/O error)
1718//�������������������������������������������������������������������������������
1719
1720static OSErr SearchExtentFile(
1721	ExtendedVCB 	*vcb,
1722	const FCB	 		*fcb,
1723	int64_t 			filePosition,
1724	HFSPlusExtentKey	*foundExtentKey,
1725	HFSPlusExtentRecord	foundExtentData,
1726	u_int32_t			*foundExtentIndex,
1727	u_int32_t			*extentBTreeHint,
1728	u_int32_t			*endingFABNPlusOne )
1729{
1730	OSErr				err;
1731	u_int32_t			filePositionBlock;
1732	int64_t                         temp64;
1733	Boolean				noMoreExtents;
1734	int  lockflags;
1735
1736	temp64 = filePosition / (int64_t)vcb->blockSize;
1737	filePositionBlock = (u_int32_t)temp64;
1738
1739    bcopy ( fcb->fcbExtents, foundExtentData, sizeof(HFSPlusExtentRecord));
1740
1741	//	Search the resident FCB first.
1742    err = SearchExtentRecord( vcb, filePositionBlock, foundExtentData, 0,
1743									foundExtentIndex, endingFABNPlusOne, &noMoreExtents );
1744
1745	if( err == noErr ) {
1746		// Found the extent. Set results accordingly
1747		*extentBTreeHint = kNoHint;			// no hint, because not in the BTree
1748		foundExtentKey->keyLength = 0;		// 0 = the FCB itself
1749
1750		goto Exit;
1751	}
1752
1753	//	Didn't find extent in FCB.  If FCB's extent record wasn't full, there's no point
1754	//	in searching the extents file.  Note that SearchExtentRecord left us pointing at
1755	//	the last valid extent (or the first one, if none were valid).  This means we need
1756	//	to fill in the hint and key outputs, just like the "if" statement above.
1757	if ( noMoreExtents ) {
1758		*extentBTreeHint = kNoHint;			// no hint, because not in the BTree
1759		foundExtentKey->keyLength = 0;		// 0 = the FCB itself
1760		err = fxRangeErr;		// There are no more extents, so must be beyond PEOF
1761		goto Exit;
1762	}
1763
1764	//
1765	//	Find the desired record, or the previous record if it is the same fork
1766	//
1767	lockflags = hfs_systemfile_lock(vcb, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
1768
1769	err = FindExtentRecord(vcb, FORK_IS_RSRC(fcb) ? kResourceForkType : kDataForkType,
1770						   FTOC(fcb)->c_fileid, filePositionBlock, true, foundExtentKey, foundExtentData, extentBTreeHint);
1771	hfs_systemfile_unlock(vcb, lockflags);
1772
1773	if (err == btNotFound) {
1774		//
1775		//	If we get here, the desired position is beyond the extents in the FCB, and there are no extents
1776		//	in the extents file.  Return the FCB's extents and a range error.
1777		//
1778		*extentBTreeHint = kNoHint;
1779		foundExtentKey->keyLength = 0;
1780		err = GetFCBExtentRecord(fcb, foundExtentData);
1781		//	Note: foundExtentIndex and endingFABNPlusOne have already been set as a result of the very
1782		//	first SearchExtentRecord call in this function (when searching in the FCB's extents, and
1783		//	we got a range error).
1784
1785		return fxRangeErr;
1786	}
1787
1788	//
1789	//	If we get here, there was either a BTree error, or we found an appropriate record.
1790	//	If we found a record, then search it for the correct index into the extents.
1791	//
1792	if (err == noErr) {
1793		//	Find appropriate index into extent record
1794		err = SearchExtentRecord(vcb, filePositionBlock, foundExtentData, foundExtentKey->startBlock,
1795								 foundExtentIndex, endingFABNPlusOne, &noMoreExtents);
1796	}
1797
1798Exit:
1799	return err;
1800}
1801
1802
1803
1804//============================================================================
1805//	Routine:	UpdateExtentRecord
1806//
1807//	Function: 	Write new extent data to an existing extent record with a given key.
1808//				If all of the extents are empty, and the extent record is in the
1809//				extents file, then the record is deleted.
1810//
1811//	Input:		vcb			  			-	the volume containing the extents
1812//				fcb						-	the file that owns the extents
1813//				extentFileKey  			-	pointer to extent key record (xkr)
1814//						If the key length is 0, then the extents are actually part
1815//						of the catalog record, stored in the FCB.
1816//				extentData  			-	pointer to extent data record (xdr)
1817//				extentBTreeHint			-	hint for given key, or kNoHint
1818//
1819//	Result:		noErr = ok
1820//				(other) = error from BTree
1821//============================================================================
1822
1823static OSErr UpdateExtentRecord (
1824	ExtendedVCB  *vcb,
1825	FCB  *fcb,
1826	const HFSPlusExtentKey  *extentFileKey,
1827	const HFSPlusExtentRecord  extentData,
1828	u_int32_t  extentBTreeHint)
1829{
1830    OSErr err = noErr;
1831
1832	if (extentFileKey->keyLength == 0) {	// keyLength == 0 means the FCB's extent record
1833		BlockMoveData(extentData, fcb->fcbExtents, sizeof(HFSPlusExtentRecord));
1834		FTOC(fcb)->c_flag |= C_MODIFIED;
1835	}
1836	else {
1837		BTreeIterator * btIterator;
1838		FSBufferDescriptor btRecord;
1839		u_int16_t btRecordSize;
1840		FCB * btFCB;
1841		int lockflags;
1842
1843		//
1844		//	Need to find and change a record in Extents BTree
1845		//
1846		btFCB = GetFileControlBlock(vcb->extentsRefNum);
1847
1848		MALLOC(btIterator, BTreeIterator *, sizeof(*btIterator), M_TEMP, M_WAITOK);
1849		bzero(btIterator, sizeof(*btIterator));
1850
1851		/*
1852		 * The lock taken by callers of ExtendFileC/TruncateFileC is
1853		 * speculative and only occurs when the file already has
1854		 * overflow extents. So we need to make sure we have the lock
1855		 * here.  The extents btree lock can be nested (its recursive)
1856		 * so we always take it here.
1857		 */
1858		lockflags = hfs_systemfile_lock(vcb, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
1859
1860		if (vcb->vcbSigWord == kHFSSigWord) {
1861			HFSExtentKey *	key;				// Actual extent key used on disk in HFS
1862			HFSExtentRecord	foundData;			// The extent data actually found
1863
1864			key = (HFSExtentKey*) &btIterator->key;
1865			key->keyLength	= kHFSExtentKeyMaximumLength;
1866			key->forkType	= extentFileKey->forkType;
1867			key->fileID		= extentFileKey->fileID;
1868			key->startBlock	= extentFileKey->startBlock;
1869
1870			btIterator->hint.index = 0;
1871			btIterator->hint.nodeNum = extentBTreeHint;
1872
1873			btRecord.bufferAddress = &foundData;
1874			btRecord.itemSize = sizeof(HFSExtentRecord);
1875			btRecord.itemCount = 1;
1876
1877			err = BTSearchRecord(btFCB, btIterator, &btRecord, &btRecordSize, btIterator);
1878
1879			if (err == noErr)
1880				err = HFSPlusToHFSExtents(extentData, (HFSExtentDescriptor *)&foundData);
1881
1882			if (err == noErr)
1883				err = BTReplaceRecord(btFCB, btIterator, &btRecord, btRecordSize);
1884			(void) BTFlushPath(btFCB);
1885		}
1886		else {		//	HFS Plus volume
1887			HFSPlusExtentRecord	foundData;		// The extent data actually found
1888
1889			BlockMoveData(extentFileKey, &btIterator->key, sizeof(HFSPlusExtentKey));
1890
1891			btIterator->hint.index = 0;
1892			btIterator->hint.nodeNum = extentBTreeHint;
1893
1894			btRecord.bufferAddress = &foundData;
1895			btRecord.itemSize = sizeof(HFSPlusExtentRecord);
1896			btRecord.itemCount = 1;
1897
1898			err = BTSearchRecord(btFCB, btIterator, &btRecord, &btRecordSize, btIterator);
1899
1900			if (err == noErr) {
1901				BlockMoveData(extentData, &foundData, sizeof(HFSPlusExtentRecord));
1902				err = BTReplaceRecord(btFCB, btIterator, &btRecord, btRecordSize);
1903			}
1904			(void) BTFlushPath(btFCB);
1905		}
1906		hfs_systemfile_unlock(vcb, lockflags);
1907		FREE(btIterator, M_TEMP);
1908	}
1909
1910	return err;
1911}
1912
1913
1914
1915
1916static OSErr HFSPlusToHFSExtents(
1917	const HFSPlusExtentRecord	oldExtents,
1918	HFSExtentRecord		newExtents)
1919{
1920	OSErr	err;
1921
1922	err = noErr;
1923
1924	// copy the first 3 extents
1925	newExtents[0].startBlock = oldExtents[0].startBlock;
1926	newExtents[0].blockCount = oldExtents[0].blockCount;
1927	newExtents[1].startBlock = oldExtents[1].startBlock;
1928	newExtents[1].blockCount = oldExtents[1].blockCount;
1929	newExtents[2].startBlock = oldExtents[2].startBlock;
1930	newExtents[2].blockCount = oldExtents[2].blockCount;
1931
1932	#if DEBUG_BUILD
1933		if (oldExtents[3].startBlock || oldExtents[3].blockCount) {
1934			DebugStr("\pExtentRecord with > 3 extents is invalid for HFS");
1935			err = fsDSIntErr;
1936		}
1937	#endif
1938
1939	return err;
1940}
1941
1942
1943
1944
1945static OSErr GetFCBExtentRecord(
1946	const FCB			*fcb,
1947	HFSPlusExtentRecord	extents)
1948{
1949
1950	BlockMoveData(fcb->fcbExtents, extents, sizeof(HFSPlusExtentRecord));
1951
1952	return noErr;
1953}
1954
1955
1956//_________________________________________________________________________________
1957//
1958// Routine:		ExtentsAreIntegral
1959//
1960// Purpose:		Ensure that each extent can hold an integral number of nodes
1961//				Called by the NodesAreContiguous function
1962//_________________________________________________________________________________
1963
1964static Boolean ExtentsAreIntegral(
1965	const HFSPlusExtentRecord extentRecord,
1966	u_int32_t	mask,
1967	u_int32_t	*blocksChecked,
1968	Boolean		*checkedLastExtent)
1969{
1970	u_int32_t	blocks;
1971	u_int32_t	extentIndex;
1972
1973	*blocksChecked = 0;
1974	*checkedLastExtent = false;
1975
1976	for(extentIndex = 0; extentIndex < kHFSPlusExtentDensity; extentIndex++)
1977	{
1978		blocks = extentRecord[extentIndex].blockCount;
1979
1980		if ( blocks == 0 )
1981		{
1982			*checkedLastExtent = true;
1983			break;
1984		}
1985
1986		*blocksChecked += blocks;
1987
1988		if (blocks & mask)
1989			return false;
1990	}
1991
1992	return true;
1993}
1994
1995
1996//_________________________________________________________________________________
1997//
1998// Routine:		NodesAreContiguous
1999//
2000// Purpose:		Ensure that all b-tree nodes are contiguous on disk
2001//				Called by BTOpenPath during volume mount
2002//_________________________________________________________________________________
2003
2004__private_extern__
2005Boolean NodesAreContiguous(
2006	ExtendedVCB	*vcb,
2007	FCB			*fcb,
2008	u_int32_t	nodeSize)
2009{
2010	u_int32_t			mask;
2011	u_int32_t			startBlock;
2012	u_int32_t			blocksChecked;
2013	u_int32_t			hint;
2014	HFSPlusExtentKey	key;
2015	HFSPlusExtentRecord	extents;
2016	OSErr				result;
2017	Boolean				lastExtentReached;
2018	int  lockflags;
2019
2020
2021	if (vcb->blockSize >= nodeSize)
2022		return TRUE;
2023
2024	mask = (nodeSize / vcb->blockSize) - 1;
2025
2026	// check the local extents
2027	(void) GetFCBExtentRecord(fcb, extents);
2028	if ( !ExtentsAreIntegral(extents, mask, &blocksChecked, &lastExtentReached) )
2029		return FALSE;
2030
2031	if ( lastExtentReached ||
2032		 (int64_t)((int64_t)blocksChecked * (int64_t)vcb->blockSize) >= (int64_t)fcb->ff_size)
2033		return TRUE;
2034
2035	startBlock = blocksChecked;
2036
2037	lockflags = hfs_systemfile_lock(vcb, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
2038
2039	// check the overflow extents (if any)
2040	while ( !lastExtentReached )
2041	{
2042		result = FindExtentRecord(vcb, kDataForkType, fcb->ff_cp->c_fileid, startBlock, FALSE, &key, extents, &hint);
2043		if (result) break;
2044
2045		if ( !ExtentsAreIntegral(extents, mask, &blocksChecked, &lastExtentReached) ) {
2046			hfs_systemfile_unlock(vcb, lockflags);
2047			return FALSE;
2048		}
2049		startBlock += blocksChecked;
2050	}
2051	hfs_systemfile_unlock(vcb, lockflags);
2052	return TRUE;
2053}
2054
2055