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#include "../../hfs_macos_defs.h"
30#include "../../hfs_format.h"
31
32#include	"../headers/FileMgrInternal.h"
33#include	"../headers/HFSUnicodeWrappers.h"
34#include	"../headers/CatalogPrivate.h"
35#include <sys/kernel.h>
36#include <sys/malloc.h>
37#include <libkern/libkern.h>
38
39
40struct ExtentsRecBuffer {
41	ExtentKey	extentKey;
42	ExtentRecord	extentData;
43};
44typedef struct ExtentsRecBuffer ExtentsRecBuffer;
45
46
47static u_int32_t CheckExtents( void *extents, u_int32_t blocks, Boolean isHFSPlus );
48static OSErr  DeleteExtents( ExtendedVCB *vcb, u_int32_t fileNumber, int quitEarly, u_int8_t forkType, Boolean isHFSPlus );
49static OSErr  MoveExtents( ExtendedVCB *vcb, u_int32_t srcFileID, u_int32_t destFileID, int quitEarly, u_int8_t forkType, Boolean isHFSPlus );
50static void  CopyCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest );
51static void  CopyBigCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest );
52static void  CopyExtentInfo( ExtentKey *key, ExtentRecord *data, ExtentsRecBuffer *buffer, u_int16_t bufferCount );
53
54/*
55 * This function moves the overflow extents associated with srcID into the file associated with dstID.
56 * We should have already verified that 'srcID' has overflow extents. So now we move all of the overflow
57 * extent records.
58 */
59OSErr MoveData( ExtendedVCB *vcb, HFSCatalogNodeID srcID, HFSCatalogNodeID destID, int rsrc) {
60
61	OSErr		err;
62
63	/*
64	 * Only the source file should have extents, so we just track those.
65	 * We operate on the fork represented by the open FD that was used to call into this
66	 * function
67	 */
68	if (rsrc) {
69		/* Copy the extent overflow blocks. */
70		err = MoveExtents( vcb, srcID, destID, 1, (u_int8_t)0xff, 1);
71		if ( err != noErr ) {
72			if ( err != dskFulErr ) {
73				return( err );
74			}
75			/*
76			 * In case of error, we would have probably run into problems
77			 * growing the extents b-tree.  Since the move is actually a copy + delete
78			 * just delete the new entries. Same for below.
79			 */
80			err = DeleteExtents( vcb, destID, 1, (u_int8_t)0xff, 1);
81			ReturnIfError( err ); //	we are doomed. Just QUIT!
82			goto FlushAndReturn;
83		}
84	}
85	else {
86		/* Copy the extent overflow blocks. */
87		err = MoveExtents( vcb, srcID, destID, 1, 0, 1);
88		if ( err != noErr ) {
89			if ( err != dskFulErr ) {
90				return( err );
91			}
92			err = DeleteExtents( vcb, destID, 1, 0, 1);
93			ReturnIfError( err ); //	we are doomed. Just QUIT!
94			goto FlushAndReturn;
95		}
96	}
97
98FlushAndReturn:
99	/* Write out the catalog and extent overflow B-Tree changes */
100	err = FlushCatalog( vcb );
101	err = FlushExtentFile( vcb );
102
103	return( err );
104}
105
106
107OSErr ExchangeFileIDs( ExtendedVCB *vcb, ConstUTF8Param srcName, ConstUTF8Param destName, HFSCatalogNodeID srcID, HFSCatalogNodeID destID, u_int32_t srcHint, u_int32_t destHint )
108{
109	CatalogKey	srcKey;		// 518 bytes
110	CatalogKey	destKey;	// 518 bytes
111	CatalogRecord	srcData;	// 520 bytes
112	CatalogRecord	destData;	// 520 bytes
113	CatalogRecord	swapData;	// 520 bytes
114	int16_t		numSrcExtentBlocks;
115	int16_t		numDestExtentBlocks;
116	OSErr		err;
117	Boolean		isHFSPlus = ( vcb->vcbSigWord == kHFSPlusSigWord );
118
119	err = BuildCatalogKeyUTF8(vcb, srcID, srcName, kUndefinedStrLen, &srcKey, NULL);
120	ReturnIfError(err);
121
122	err = BuildCatalogKeyUTF8(vcb, destID, destName, kUndefinedStrLen, &destKey, NULL);
123	ReturnIfError(err);
124
125	if ( isHFSPlus )
126	{
127		//--	Step 1: Check the catalog nodes for extents
128
129		//--	locate the source file, test for extents in extent file, and copy the cat record for later
130		err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint );
131		ReturnIfError( err );
132
133		if ( srcData.recordType != kHFSPlusFileRecord )
134			return( cmFThdDirErr );					//	Error "cmFThdDirErr = it is a directory"
135
136		//--	Check if there are any extents in the source file
137		//��	I am only checling the extents in the low 32 bits, routine will fail if files extents after 2 gig are in overflow
138		numSrcExtentBlocks = CheckExtents( srcData.hfsPlusFile.dataFork.extents, srcData.hfsPlusFile.dataFork.totalBlocks, isHFSPlus );
139		if ( numSrcExtentBlocks == 0 )					//	then check the resource fork extents
140			numSrcExtentBlocks = CheckExtents( srcData.hfsPlusFile.resourceFork.extents, srcData.hfsPlusFile.resourceFork.totalBlocks, isHFSPlus );
141
142		//--	Check if there are any extents in the destination file
143		err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
144		ReturnIfError( err );
145
146		if ( destData.recordType != kHFSPlusFileRecord )
147			return( cmFThdDirErr );					//	Error "cmFThdDirErr = it is a directory"
148
149		numDestExtentBlocks = CheckExtents( destData.hfsPlusFile.dataFork.extents, destData.hfsPlusFile.dataFork.totalBlocks, isHFSPlus );
150		if ( numDestExtentBlocks == 0 )					//	then check the resource fork extents
151			numDestExtentBlocks = CheckExtents( destData.hfsPlusFile.resourceFork.extents, destData.hfsPlusFile.resourceFork.totalBlocks, isHFSPlus );
152
153		//--	Step 2: Exchange the Extent key in the extent file
154
155		//--	Exchange the extents key in the extent file
156		err = DeleteExtents( vcb, kHFSBogusExtentFileID, 0, 0, isHFSPlus );
157		ReturnIfError( err );
158
159		if ( numSrcExtentBlocks && numDestExtentBlocks )	//	if both files have extents
160		{
161			//--	Change the source extents file ids to our known bogus value
162			err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, kHFSBogusExtentFileID, 0,0, isHFSPlus );
163			if ( err != noErr )
164			{
165				if ( err != dskFulErr )
166					return( err );
167				else
168					goto ExUndo1a;
169			}
170
171			//--	Change the destination extents file id's to the source id's
172			err = MoveExtents( vcb, destData.hfsPlusFile.fileID, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
173			if ( err != noErr )
174			{
175				if ( err != dskFulErr )
176					return( err );
177
178ExUndo2aPlus:	err = DeleteExtents( vcb, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
179				ReturnIfError( err );					//	we are doomed. Just QUIT!
180
181                err = MoveExtents( vcb, kHFSBogusExtentFileID, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus );	//	Move the extents back
182				ReturnIfError( err );					//	we are doomed. Just QUIT!
183
184				goto ExUndo1a;
185			}
186
187			//--	Change the bogus extents file id's to the dest id's
188            err = MoveExtents( vcb, kHFSBogusExtentFileID, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
189			if ( err != noErr )
190			{
191				if ( err != dskFulErr )
192					return( err );
193
194				err = DeleteExtents( vcb, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
195				ReturnIfError( err );					//	we are doomed. Just QUIT!
196
197				err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus );	//	Move the extents back
198				ReturnIfError( err );					//	we are doomed. Just QUIT!
199
200				goto ExUndo2aPlus;
201			}
202
203		}
204		else if ( numSrcExtentBlocks )	//	just the source file has extents
205		{
206			err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
207			if ( err != noErr )
208			{
209				if ( err != dskFulErr )
210					return( err );
211
212				err = DeleteExtents( vcb, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
213				ReturnIfError( err );					//	we are doomed. Just QUIT!
214
215				goto FlushAndReturn;
216			}
217		}
218		else if ( numDestExtentBlocks )	//	just the destination file has extents
219		{
220			err = MoveExtents( vcb, destData.hfsPlusFile.fileID, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
221			if ( err != noErr )
222			{
223				if ( err != dskFulErr )
224					return( err );
225
226				err = DeleteExtents( vcb, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
227				ReturnIfError( err );					//	we are doomed. Just QUIT!
228
229				goto FlushAndReturn;
230			}
231		}
232
233		//--	Step 3: Change the data in the catalog nodes
234
235		//--	find the source cnode and put dest info in it
236		err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint );
237		if ( err != noErr )
238			return( cmBadNews );
239
240		BlockMoveData( &srcData, &swapData, sizeof(CatalogRecord) );
241		CopyBigCatalogNodeInfo( &destData, &srcData );
242
243		err = ReplaceBTreeRecord( vcb->catalogRefNum, &srcKey, srcHint, &srcData, sizeof(HFSPlusCatalogFile), &srcHint );
244		ReturnIfError( err );
245
246		//	find the destination cnode and put source info in it
247		err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
248		if ( err != noErr )
249			return( cmBadNews );
250
251		CopyBigCatalogNodeInfo( &swapData, &destData );
252		err = ReplaceBTreeRecord( vcb->catalogRefNum, &destKey, destHint, &destData, sizeof(HFSPlusCatalogFile), &destHint );
253		ReturnIfError( err );
254	}
255	else		//	HFS	//
256	{
257		//--	Step 1: Check the catalog nodes for extents
258
259		//--	locate the source file, test for extents in extent file, and copy the cat record for later
260		err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint );
261		ReturnIfError( err );
262
263		if ( srcData.recordType != kHFSFileRecord )
264			return( cmFThdDirErr );					//	Error "cmFThdDirErr = it is a directory"
265
266		//--	Check if there are any extents in the source file
267		numSrcExtentBlocks = CheckExtents( srcData.hfsFile.dataExtents, srcData.hfsFile.dataPhysicalSize / vcb->blockSize, isHFSPlus );
268		if ( numSrcExtentBlocks == 0 )					//	then check the resource fork extents
269			numSrcExtentBlocks = CheckExtents( srcData.hfsFile.rsrcExtents, srcData.hfsFile.rsrcPhysicalSize / vcb->blockSize, isHFSPlus );
270
271
272		//��	Do we save the found source node for later use?
273
274
275		//--	Check if there are any extents in the destination file
276		err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
277		ReturnIfError( err );
278
279		if ( destData.recordType != kHFSFileRecord )
280			return( cmFThdDirErr );					//	Error "cmFThdDirErr = it is a directory"
281
282		numDestExtentBlocks = CheckExtents( destData.hfsFile.dataExtents, destData.hfsFile.dataPhysicalSize / vcb->blockSize, isHFSPlus );
283		if ( numDestExtentBlocks == 0 )					//	then check the resource fork extents
284			numDestExtentBlocks = CheckExtents( destData.hfsFile.rsrcExtents, destData.hfsFile.rsrcPhysicalSize / vcb->blockSize, isHFSPlus );
285
286		//��	Do we save the found destination node for later use?
287
288
289		//--	Step 2: Exchange the Extent key in the extent file
290
291		//--	Exchange the extents key in the extent file
292        err = DeleteExtents( vcb, kHFSBogusExtentFileID, 0, 0, isHFSPlus );
293		ReturnIfError( err );
294
295		if ( numSrcExtentBlocks && numDestExtentBlocks )	//	if both files have extents
296		{
297			//--	Change the source extents file ids to our known bogus value
298        err = MoveExtents( vcb, srcData.hfsFile.fileID, kHFSBogusExtentFileID, 0, 0, isHFSPlus );
299			if ( err != noErr )
300			{
301				if ( err != dskFulErr )
302					return( err );
303
304ExUndo1a:		err = DeleteExtents( vcb, kHFSBogusExtentFileID, 0, 0, isHFSPlus );
305				ReturnIfError( err );					//	we are doomed. Just QUIT!
306
307				err = FlushCatalog( vcb );   			//	flush the catalog
308				err = FlushExtentFile( vcb );			//	flush the extent file (unneeded for common case, but it's cheap)
309				return( dskFulErr );
310			}
311
312			//--	Change the destination extents file id's to the source id's
313			err = MoveExtents( vcb, destData.hfsFile.fileID, srcData.hfsFile.fileID, 0, 0, isHFSPlus );
314			if ( err != noErr )
315			{
316				if ( err != dskFulErr )
317					return( err );
318
319ExUndo2a:		err = DeleteExtents( vcb, srcData.hfsFile.fileID, 0, 0, isHFSPlus );
320				ReturnIfError( err );					//	we are doomed. Just QUIT!
321
322                err = MoveExtents( vcb, kHFSBogusExtentFileID, srcData.hfsFile.fileID, 0, 0, isHFSPlus );	//	Move the extents back
323				ReturnIfError( err );					//	we are doomed. Just QUIT!
324
325				goto ExUndo1a;
326			}
327
328			//--	Change the bogus extents file id's to the dest id's
329            err = MoveExtents( vcb, kHFSBogusExtentFileID, destData.hfsFile.fileID, 0, 0, isHFSPlus );
330			if ( err != noErr )
331			{
332				if ( err != dskFulErr )
333					return( err );
334
335				err = DeleteExtents( vcb, destData.hfsFile.fileID, 0, 0, isHFSPlus );
336				ReturnIfError( err );					//	we are doomed. Just QUIT!
337
338				err = MoveExtents( vcb, srcData.hfsFile.fileID, destData.hfsFile.fileID, 0, 0, isHFSPlus );	//	Move the extents back
339				ReturnIfError( err );					//	we are doomed. Just QUIT!
340
341				goto ExUndo2a;
342			}
343
344		}
345		else if ( numSrcExtentBlocks )	//	just the source file has extents
346		{
347			err = MoveExtents( vcb, srcData.hfsFile.fileID, destData.hfsFile.fileID, 0, 0, isHFSPlus );
348			if ( err != noErr )
349			{
350				if ( err != dskFulErr )
351					return( err );
352
353				err = DeleteExtents( vcb, srcData.hfsFile.fileID, 0, 0, isHFSPlus );
354				ReturnIfError( err );					//	we are doomed. Just QUIT!
355
356				goto FlushAndReturn;
357			}
358		}
359		else if ( numDestExtentBlocks )	//	just the destination file has extents
360		{
361			err = MoveExtents( vcb, destData.hfsFile.fileID, srcData.hfsFile.fileID, 0, 0, isHFSPlus );
362			if ( err != noErr )
363			{
364				if ( err != dskFulErr )
365					return( err );
366
367				err = DeleteExtents( vcb, destData.hfsFile.fileID, 0, 0, isHFSPlus );
368				ReturnIfError( err );					//	we are doomed. Just QUIT!
369
370				goto FlushAndReturn;
371			}
372		}
373
374		//--	Step 3: Change the data in the catalog nodes
375
376		//--	find the source cnode and put dest info in it
377		err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint );
378		if ( err != noErr )
379			return( cmBadNews );
380
381		BlockMoveData( &srcData, &swapData, sizeof(CatalogRecord) );
382		//��	Asm source copies from the saved dest catalog node
383		CopyCatalogNodeInfo( &destData, &srcData );
384
385		err = ReplaceBTreeRecord( vcb->catalogRefNum, &srcKey, srcHint, &srcData, sizeof(HFSCatalogFile), &srcHint );
386		ReturnIfError( err );
387
388
389		//	find the destination cnode and put source info in it
390		err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
391		if ( err != noErr )
392			return( cmBadNews );
393
394		CopyCatalogNodeInfo( &swapData, &destData );
395		err = ReplaceBTreeRecord( vcb->catalogRefNum, &destKey, destHint, &destData, sizeof(HFSCatalogFile), &destHint );
396		ReturnIfError( err );
397	}
398
399	err = noErr;
400
401	//--	Step 4: Error Handling section
402
403
404FlushAndReturn:
405	err = FlushCatalog( vcb );   			//	flush the catalog
406	err = FlushExtentFile( vcb );			//	flush the extent file (unneeded for common case, but it's cheap)
407	return( err );
408}
409
410
411static void  CopyCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest )
412{
413	dest->hfsFile.dataLogicalSize	= src->hfsFile.dataLogicalSize;
414	dest->hfsFile.dataPhysicalSize = src->hfsFile.dataPhysicalSize;
415	dest->hfsFile.rsrcLogicalSize	= src->hfsFile.rsrcLogicalSize;
416	dest->hfsFile.rsrcPhysicalSize = src->hfsFile.rsrcPhysicalSize;
417	dest->hfsFile.modifyDate = src->hfsFile.modifyDate;
418	BlockMoveData( src->hfsFile.dataExtents, dest->hfsFile.dataExtents, sizeof(HFSExtentRecord) );
419	BlockMoveData( src->hfsFile.rsrcExtents, dest->hfsFile.rsrcExtents, sizeof(HFSExtentRecord) );
420}
421
422static void  CopyBigCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest )
423{
424	BlockMoveData( &src->hfsPlusFile.dataFork, &dest->hfsPlusFile.dataFork, sizeof(HFSPlusForkData) );
425	BlockMoveData( &src->hfsPlusFile.resourceFork, &dest->hfsPlusFile.resourceFork, sizeof(HFSPlusForkData) );
426	dest->hfsPlusFile.contentModDate = src->hfsPlusFile.contentModDate;
427}
428
429
430static OSErr  MoveExtents( ExtendedVCB *vcb, u_int32_t srcFileID, u_int32_t destFileID, int quitEarly, u_int8_t forkType, Boolean isHFSPlus )
431{
432	FCB *				fcb;
433	ExtentsRecBuffer	extentsBuffer[kNumExtentsToCache];
434	ExtentKey *			extentKeyPtr;
435	ExtentRecord		extentData;
436	struct BTreeIterator *btIterator = NULL;
437	struct BTreeIterator *tmpIterator = NULL;
438	FSBufferDescriptor	btRecord;
439	u_int16_t			btKeySize;
440	u_int16_t			btRecordSize;
441	int16_t				i, j;
442	OSErr				err;
443
444	MALLOC (btIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK);
445	if (btIterator == NULL) {
446		return memFullErr;  // translates to ENOMEM
447	}
448
449
450	MALLOC (tmpIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK);
451	if (tmpIterator == NULL) {
452		FREE (btIterator, M_TEMP);
453		return memFullErr;  // translates to ENOMEM
454	}
455
456	bzero(btIterator, sizeof(*btIterator));
457	bzero (tmpIterator, sizeof(*tmpIterator));
458
459
460	fcb = GetFileControlBlock(vcb->extentsRefNum);
461
462	(void) BTInvalidateHint(btIterator);
463	extentKeyPtr = (ExtentKey*) &btIterator->key;
464	btRecord.bufferAddress = &extentData;
465	btRecord.itemCount = 1;
466
467	//--	Collect the extent records
468
469	//
470	//	A search on the following key will cause the BTree to be positioned immediately
471	//	before the first extent record for file #srcFileID, but not actually positioned
472	//	on any record.  This is because there cannot be an extent record with FABN = 0
473	//	(the first extent of the fork, which would be in the catalog entry, not an extent
474	//	record).
475	//
476	//	Using BTIterateRecord with kBTreeNextRecord will then get that first extent record.
477	//
478	if (isHFSPlus) {
479		btRecord.itemSize = sizeof(HFSPlusExtentRecord);
480		btKeySize = sizeof(HFSPlusExtentKey);
481
482		extentKeyPtr->hfsPlus.keyLength	 = kHFSPlusExtentKeyMaximumLength;
483		extentKeyPtr->hfsPlus.forkType	 = forkType;
484		extentKeyPtr->hfsPlus.pad		 = 0;
485		extentKeyPtr->hfsPlus.fileID	 = srcFileID;
486		extentKeyPtr->hfsPlus.startBlock = 0;
487	}
488	else {
489		btRecord.itemSize = sizeof(HFSExtentRecord);
490		btKeySize = sizeof(HFSExtentKey);
491
492		extentKeyPtr->hfs.keyLength	 = kHFSExtentKeyMaximumLength;
493		extentKeyPtr->hfs.forkType	 = 0;
494		extentKeyPtr->hfs.fileID	 = srcFileID;
495		extentKeyPtr->hfs.startBlock = 0;
496	}
497
498	//
499	//	We do an initial BTSearchRecord to position the BTree's iterator just before any extent
500	//	records for srcFileID.  We then do a few BTIterateRecord and BTInsertRecord of those found
501	//	records, but with destFileID as the file number in the key.  Keep doing this sequence of
502	//	BTIterateRecord and BTInsertRecord until we find an extent for another file, or there are
503	//	no more extent records in the tree.
504	//
505	//	Basically, we're copying records kNumExtentsToCache at a time.  The copies have their file ID
506	//	set to destFileID.
507	//
508	//	This depends on BTInsertRecord not effecting the iterator used by BTIterateRecord.  If it
509	//	_did_ effect the iterator, then we would need to do a BTSearchRecord before each series
510	//	of BTIterateRecord.  We'd need to set up the key for BTSearchRecord to find the last record
511	//	we found, so that BTIterateRecord would get the next one (the first we haven't processed).
512	//
513
514	err = BTSearchRecord(fcb, btIterator, &btRecord, &btRecordSize, btIterator);
515
516	//	We expect a btNotFound here, since there shouldn't be an extent record with FABN = 0.
517	if (err != btNotFound)
518	{
519		if ( DEBUG_BUILD )
520			DebugStr("Unexpected error from SearchBTreeRecord");
521
522		if (err == noErr)			//	If we found such a bogus extent record, then the tree is really messed up
523			err = cmBadNews;		//	so return an error that conveys the disk is hosed.
524
525		FREE (tmpIterator, M_TEMP);
526		FREE (btIterator, M_TEMP);
527		return err;
528	}
529
530	do
531	{
532		btRecord.bufferAddress = &extentData;
533		btRecord.itemCount = 1;
534
535		for ( i=0 ; i<kNumExtentsToCache ; i++ )
536		{
537			HFSCatalogNodeID	foundFileID;
538
539			err = BTIterateRecord(fcb, kBTreeNextRecord, btIterator, &btRecord, &btRecordSize);
540			if ( err == btNotFound )		//	Did we run out of extent records in the extents tree?
541				break;						//	if xkrFNum(A0) is cleared on this error, then this test is bogus!
542			else if ( err != noErr ) {
543				FREE (btIterator, M_TEMP);
544				FREE (tmpIterator, M_TEMP);
545				return( err );				//	must be ioError
546			}
547			foundFileID = isHFSPlus ? extentKeyPtr->hfsPlus.fileID : extentKeyPtr->hfs.fileID;
548			if ( foundFileID == srcFileID ) {
549				/* Check if we need to quit early. */
550				if (quitEarly && isHFSPlus) {
551					if (extentKeyPtr->hfsPlus.forkType != forkType) {
552						break;
553					}
554				}
555				CopyExtentInfo(extentKeyPtr, &extentData, extentsBuffer, i);
556			}
557			else{
558				/* The fileID's are of a different file.  We're done here. */
559				break;
560			}
561		}
562
563
564
565		//--	edit each extent key, and reinsert each extent record in the extent file
566		if (isHFSPlus)
567			btRecordSize = sizeof(HFSPlusExtentRecord);
568		else
569			btRecordSize = sizeof(HFSExtentRecord);
570
571		for ( j=0 ; j<i ; j++ )
572		{
573
574			if (isHFSPlus)
575				extentsBuffer[j].extentKey.hfsPlus.fileID = destFileID;	//	change only the id in the key to dest ID
576			else
577				extentsBuffer[j].extentKey.hfs.fileID = destFileID;	//	change only the id in the key to dest ID
578
579			// get iterator and buffer descriptor ready...
580			(void) BTInvalidateHint(tmpIterator);
581			BlockMoveData(&(extentsBuffer[j].extentKey), &tmpIterator->key, btKeySize);
582			btRecord.bufferAddress = &(extentsBuffer[j].extentData);
583
584			err = BTInsertRecord(fcb, tmpIterator, &btRecord, btRecordSize);
585			if ( err != noErr ) {
586				/* Parse the error and free iterators */
587				FREE (btIterator, M_TEMP);
588				FREE (tmpIterator, M_TEMP);
589				if ( err == btExists )
590				{
591					if ( DEBUG_BUILD ) {
592						DebugStr("Can't insert record -- already exists");
593					}
594					return( cmBadNews );
595				}
596				else {
597					return( err );
598				}
599			}
600		}
601
602		//--	okay, done with this buffered batch, go get the next set of extent records
603		//	If our buffer is not full, we must be done, or recieved an error
604
605		if ( i != kNumExtentsToCache )			//	if the buffer is not full, we must be done
606		{
607			err = DeleteExtents( vcb, srcFileID, forkType, quitEarly, isHFSPlus );	//	Now delete all the extent entries with the sourceID
608			if ( DEBUG_BUILD && err != noErr )
609				DebugStr("Error from DeleteExtents");
610			break;									//	we're done!
611		}
612	} while ( true );
613
614	FREE (tmpIterator, M_TEMP);
615	FREE (btIterator, M_TEMP);
616
617	return( err );
618}
619
620
621static void  CopyExtentInfo( ExtentKey *key, ExtentRecord *data, ExtentsRecBuffer *buffer, u_int16_t bufferCount )
622{
623	BlockMoveData( key, &(buffer[bufferCount].extentKey), sizeof( ExtentKey ) );
624	BlockMoveData( data, &(buffer[bufferCount].extentData), sizeof( ExtentRecord ) );
625}
626
627
628//--	Delete all extents in extent file that have the ID given.
629static OSErr  DeleteExtents( ExtendedVCB *vcb, u_int32_t fileID, int quitEarly,  u_int8_t forkType, Boolean isHFSPlus )
630{
631	FCB *				fcb;
632	ExtentKey *			extentKeyPtr;
633	ExtentRecord		extentData;
634	struct BTreeIterator *btIterator = NULL;
635	struct BTreeIterator *tmpIterator = NULL;
636	FSBufferDescriptor	btRecord;
637	u_int16_t			btRecordSize;
638	OSErr				err;
639
640
641	MALLOC (btIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK);
642	if (btIterator == NULL) {
643		return memFullErr;  // translates to ENOMEM
644	}
645
646	MALLOC (tmpIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK);
647	if (tmpIterator == NULL) {
648		FREE (btIterator, M_TEMP);
649		return memFullErr;  // translates to ENOMEM
650	}
651
652	bzero(btIterator, sizeof(*btIterator));
653	bzero (tmpIterator, sizeof(*tmpIterator));
654
655	fcb = GetFileControlBlock(vcb->extentsRefNum);
656
657	(void) BTInvalidateHint(btIterator);
658	extentKeyPtr = (ExtentKey*) &btIterator->key;
659	btRecord.bufferAddress = &extentData;
660	btRecord.itemCount = 1;
661
662	//	The algorithm is to position the BTree just before any extent records for fileID.
663	//	Then just keep getting successive records.  If the record is still for fileID,
664	//	then delete it.
665
666	if (isHFSPlus) {
667		btRecord.itemSize = sizeof(HFSPlusExtentRecord);
668
669		extentKeyPtr->hfsPlus.keyLength	 = kHFSPlusExtentKeyMaximumLength;
670		extentKeyPtr->hfsPlus.forkType	 = forkType;
671		extentKeyPtr->hfsPlus.pad		 = 0;
672		extentKeyPtr->hfsPlus.fileID	 = fileID;
673		extentKeyPtr->hfsPlus.startBlock = 0;
674	}
675	else {
676		btRecord.itemSize = sizeof(HFSExtentRecord);
677
678		extentKeyPtr->hfs.keyLength	 = kHFSExtentKeyMaximumLength;
679		extentKeyPtr->hfs.forkType	 = forkType;
680		extentKeyPtr->hfs.fileID	 = fileID;
681		extentKeyPtr->hfs.startBlock = 0;
682	}
683
684	err = BTSearchRecord(fcb, btIterator, &btRecord, &btRecordSize, btIterator);
685	if ( err != btNotFound )
686	{
687		if (err == noErr) {		//	Did we find a bogus extent record?
688			err = cmBadNews;	//	Yes, so indicate things are messed up.
689		}
690
691		return err;				//	Got some unexpected error, so return it
692	}
693
694	do
695	{
696		HFSCatalogNodeID	foundFileID;
697
698		err = BTIterateRecord(fcb, kBTreeNextRecord, btIterator, &btRecord, &btRecordSize);
699		if ( err != noErr )
700		{
701			if (err == btNotFound)	//	If we hit the end of the BTree
702				err = noErr;		//		then it's OK
703
704			break;					//	We're done now.
705		}
706
707		foundFileID = isHFSPlus ? extentKeyPtr->hfsPlus.fileID : extentKeyPtr->hfs.fileID;
708		if ( foundFileID != fileID ) {
709			break;					//	numbers don't match, we must be done
710		}
711		if (quitEarly && isHFSPlus) {
712			/* If we're only deleting one type of fork, then quit early if it doesn't match */
713			if (extentKeyPtr->hfsPlus.forkType != forkType) {
714				break;
715			}
716		}
717
718		*tmpIterator = *btIterator;
719		err = BTDeleteRecord( fcb, tmpIterator );
720		if (err != noErr)
721			break;
722	}	while ( true );
723
724	FREE (tmpIterator, M_TEMP);
725	FREE (btIterator, M_TEMP);
726
727	return( err );
728}
729
730
731//	Check if there are extents represented in the extents overflow file.
732static u_int32_t  CheckExtents( void *extents, u_int32_t totalBlocks, Boolean isHFSPlus )
733{
734	u_int32_t		extentAllocationBlocks;
735	u_int16_t		i;
736
737
738	if ( totalBlocks == 0 )
739		return( 0 );
740
741	extentAllocationBlocks = 0;
742
743	if ( isHFSPlus )
744	{
745		for ( i = 0 ; i < kHFSPlusExtentDensity ; i++ )
746		{
747			extentAllocationBlocks += ((HFSPlusExtentDescriptor *)extents)[i].blockCount;
748			if ( extentAllocationBlocks >= totalBlocks )		//	greater than or equal (extents can add past eof if 'Close" crashes w/o truncating new clump)
749				return( 0 );
750		}
751	}
752	else
753	{
754		for ( i = 0 ; i < kHFSExtentDensity ; i++ )
755		{
756			extentAllocationBlocks += ((HFSExtentDescriptor *)extents)[i].blockCount;
757			if ( extentAllocationBlocks >= totalBlocks )		//	greater than or equal (extents can add past eof if 'Close" crashes w/o truncating new clump)
758				return( 0 );
759		}
760	}
761
762	return( extentAllocationBlocks );
763}
764