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