1/*
2 * Copyright (c) 1999-2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24	File:		SRepair.c
25
26	Contains:	This file contains the Scavenger repair routines.
27
28	Written by:	Bill Bruffey
29
30	Copyright:	� 1986, 1990, 1992-1999 by Apple Computer, Inc., all rights reserved.
31
32*/
33
34#include "Scavenger.h"
35#include <unistd.h>
36#include <sys/stat.h>
37#include <stdlib.h>
38#include <stddef.h>
39#include "../cache.h"
40
41enum {
42	clearBlocks,
43	addBitmapBit,
44	deleteExtents
45};
46
47/* internal routine prototypes */
48
49static 	int MRepair( SGlobPtr GPtr );
50void	SetOffset (void *buffer, UInt16 btNodeSize, SInt16 recOffset, SInt16 vecOffset);
51#define SetOffset(buffer,nodesize,offset,record)		(*(SInt16 *) ((Byte *) (buffer) + (nodesize) + (-2 * (record))) = (offset))
52static	OSErr	UpdateBTreeHeader( SFCB * fcbPtr );
53static	OSErr	FixBTreeHeaderReservedFields( SGlobPtr GPtr, short refNum );
54static	OSErr	UpdBTM( SGlobPtr GPtr, short refNum);
55static	OSErr	UpdateVolumeBitMap( SGlobPtr GPtr, Boolean preAllocateOverlappedExtents );
56static	OSErr	DoMinorOrders( SGlobPtr GPtr );
57static	OSErr	UpdVal( SGlobPtr GPtr, RepairOrderPtr rP );
58static	int		DelFThd( SGlobPtr GPtr, UInt32 fid );
59static	OSErr	FixDirThread( SGlobPtr GPtr, UInt32 did );
60static	OSErr	FixOrphanedFiles ( SGlobPtr GPtr );
61static	OSErr	RepairReservedBTreeFields ( SGlobPtr GPtr );
62static 	OSErr   GetCatalogRecord(SGlobPtr GPtr, UInt32 fileID, Boolean isHFSPlus, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize);
63static 	OSErr   RepairAttributesCheckABT(SGlobPtr GPtr, Boolean isHFSPlus);
64static 	OSErr   RepairAttributesCheckCBT(SGlobPtr GPtr, Boolean isHFSPlus);
65static	OSErr	RepairAttributes( SGlobPtr GPtr );
66static	OSErr	FixFinderFlags( SGlobPtr GPtr, RepairOrderPtr p );
67static	OSErr	FixLinkCount( SGlobPtr GPtr, RepairOrderPtr p );
68static	OSErr	FixLinkChainNext( SGlobPtr GPtr, RepairOrderPtr p );
69static	OSErr	FixLinkChainPrev( SGlobPtr GPtr, RepairOrderPtr p );
70static	OSErr	FixBSDInfo( SGlobPtr GPtr, RepairOrderPtr p );
71static	OSErr	DeleteUnlinkedFile( SGlobPtr GPtr, RepairOrderPtr p );
72static	OSErr	FixOrphanedExtent( SGlobPtr GPtr );
73static 	OSErr	FixFileSize(SGlobPtr GPtr, RepairOrderPtr p);
74static  OSErr 	VolumeObjectFixVHBorMDB( Boolean * fixedIt );
75static 	OSErr 	VolumeObjectRestoreWrapper( void );
76static	OSErr	FixBloatedThreadRecords( SGlob *GPtr );
77static	OSErr	FixMissingThreadRecords( SGlob *GPtr );
78static	OSErr	FixEmbededVolDescription( SGlobPtr GPtr, RepairOrderPtr p );
79static	OSErr	FixWrapperExtents( SGlobPtr GPtr, RepairOrderPtr p );
80static  OSErr	FixIllegalNames( SGlobPtr GPtr, RepairOrderPtr roPtr );
81static HFSCatalogNodeID GetObjectID( CatalogRecord * theRecPtr );
82static 	OSErr	FixMissingDirectory( SGlob *GPtr, UInt32 theObjID, UInt32 theParID );
83static 	OSErr 	FixAttrSize(SGlobPtr GPtr, RepairOrderPtr p);
84static 	OSErr 	FixOrphanAttrRecord(SGlobPtr GPtr);
85static 	OSErr 	FixBadExtent(SGlobPtr GPtr, RepairOrderPtr p);
86static	OSErr	FixHardLinkFinderInfo(SGlobPtr, RepairOrderPtr);
87static	OSErr	FixOrphanLink(SGlobPtr GPtr, RepairOrderPtr p);
88static	OSErr	FixOrphanInode(SGlobPtr GPtr, RepairOrderPtr p);
89static 	OSErr	FixDirLinkOwnerFlags(SGlobPtr GPtr, RepairOrderPtr p);
90static  int 	DeleteCatalogRecordByID(SGlobPtr GPtr, uint32_t id, Boolean for_rename);
91static  int 	MoveCatalogRecordByID(SGlobPtr GPtr, uint32_t id, uint32_t new_parentid);
92static 	int 	DeleteAllAttrsByID(SGlobPtr GPtr, uint32_t id);
93static  int     delete_attr_record(SGlobPtr GPtr, HFSPlusAttrKey *attr_key, HFSPlusAttrRecord *attr_record);
94static	int		ZeroFillUnusedNodes(SGlobPtr GPtr, short fileRefNum);
95
96/* Functions to fix overlapping extents */
97static	OSErr	FixOverlappingExtents(SGlobPtr GPtr);
98static 	int 	CompareExtentBlockCount(const void *first, const void *second);
99static 	OSErr 	MoveExtent(SGlobPtr GPtr, ExtentInfo *extentInfo);
100static 	OSErr 	CreateCorruptFileSymlink(SGlobPtr GPtr, UInt32 fileID);
101static 	OSErr 	SearchExtentInAttributeBT(SGlobPtr GPtr, ExtentInfo *extentInfo, HFSPlusAttrKey *attrKey, HFSPlusAttrRecord *attrRecord, UInt16 *recordSize, UInt32 *foundExtentIndex);
102static 	OSErr 	UpdateExtentInAttributeBT (SGlobPtr GPtr, ExtentInfo *extentInfo, HFSPlusAttrKey *attrKey, HFSPlusAttrRecord *attrRecord, UInt16 *recordSize, UInt32 foundInExtentIndex);
103static 	OSErr 	SearchExtentInVH(SGlobPtr GPtr, ExtentInfo *extentInfo, UInt32 *foundExtentIndex, Boolean *noMoreExtents);
104static 	OSErr 	UpdateExtentInVH (SGlobPtr GPtr, ExtentInfo *extentInfo, UInt32 foundExtentIndex);
105static 	OSErr 	SearchExtentInCatalogBT(SGlobPtr GPtr, ExtentInfo *extentInfo, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize, UInt32 *foundExtentIndex, Boolean *noMoreExtents);
106static 	OSErr 	UpdateExtentInCatalogBT (SGlobPtr GPtr, ExtentInfo *extentInfo, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize, UInt32 foundExtentIndex);
107static 	OSErr 	SearchExtentInExtentBT(SGlobPtr GPtr, ExtentInfo *extentInfo, HFSPlusExtentKey *extentKey, HFSPlusExtentRecord *extentRecord, UInt16 *recordSize, UInt32 *foundExtentIndex);
108static 	OSErr 	FindExtentInExtentRec (Boolean isHFSPlus, UInt32 startBlock, UInt32 blockCount, const HFSPlusExtentRecord extentData, UInt32 *foundExtentIndex, Boolean *noMoreExtents);
109
110/* Functions to copy disk blocks or data buffer to disk */
111static 	OSErr 	CopyDiskBlocks(SGlobPtr GPtr, const UInt32 startAllocationBlock, const UInt32 blockCount, const UInt32 newStartAllocationBlock );
112static 	OSErr 	WriteBufferToDisk(SGlobPtr GPtr, UInt32 startBlock, UInt32 blockCount, u_char *buffer, int buflen);
113
114/* Functions to create file and directory by name */
115static 	OSErr 	CreateFileByName(SGlobPtr GPtr, UInt32 parentID, UInt16 fileType, u_char *fileName, unsigned int filenameLen, u_char *data, unsigned int dataLen);
116static 	UInt32 	CreateDirByName(SGlob *GPtr , const u_char *dirName, const UInt32 parentID);
117
118static 	int		BuildFolderRec( SGlob*, u_int16_t theMode, UInt32 theObjID, Boolean isHFSPlus, CatalogRecord * theRecPtr );
119static 	int		BuildThreadRec( CatalogKey * theKeyPtr, CatalogRecord * theRecPtr, Boolean isHFSPlus, Boolean isDirectory );
120static 	int 	BuildFileRec(UInt16 fileType, UInt16 fileMode, UInt32 fileID, Boolean isHFSPlus, CatalogRecord *catRecord);
121static 	void 	BuildAttributeKey(u_int32_t fileID, u_int32_t startBlock, unsigned char *attrName, u_int16_t attrNameLen, HFSPlusAttrKey *key);
122
123
124OSErr	RepairVolume( SGlobPtr GPtr )
125{
126	OSErr			err;
127
128	SetDFAStage( kAboutToRepairStage );											//	Notify callers repair is starting...
129 	err = CheckForStop( GPtr ); ReturnIfError( err );							//	Permit the user to interrupt
130
131	//
132	//	Do the repair
133	//
134	SetDFAStage( kRepairStage );									//	Stops GNE from being called, and changes behavior of MountCheck
135
136	err = MRepair( GPtr );
137
138	return( err );
139}
140
141
142/*------------------------------------------------------------------------------
143Routine:	MRepair		- (Minor Repair)
144Function:	Performs minor repair operations.
145Input:		GPtr		-	pointer to scavenger global area
146Output:		MRepair		-	function result:
147------------------------------------------------------------------------------*/
148
149static int MRepair( SGlobPtr GPtr )
150{
151	OSErr			err;
152	SVCB			*calculatedVCB	= GPtr->calculatedVCB;
153	Boolean			isHFSPlus;
154	Boolean			didRebuild = false;
155
156	isHFSPlus = VolumeObjectIsHFSPlus( );
157
158	if ( GPtr->EBTStat & S_RebuildBTree )
159	{
160		fsckPrint(GPtr->context, hfsRebuildExtentBTree);
161		err = RebuildBTree( GPtr, kHFSExtentsFileID );
162		if (err)
163			return (err);
164		didRebuild = true;
165	}
166
167	if ( GPtr->CBTStat & S_RebuildBTree )
168	{
169		/* once we do the rebuild we will force another verify since the */
170		/* first verify was aborted when we determined a rebuild was necessary */
171		fsckPrint(GPtr->context, hfsRebuildCatalogBTree);
172		err = RebuildBTree( GPtr, kHFSCatalogFileID );
173		if (err)
174			return (err);
175		didRebuild = true;
176	}
177
178	if ( GPtr->ABTStat & S_RebuildBTree )
179	{
180		fsckPrint(GPtr->context, hfsRebuildAttrBTree);
181		err = RebuildBTree( GPtr, kHFSAttributesFileID );
182		if (err)
183			return (err);
184		didRebuild = true;
185	}
186
187	if (didRebuild)
188		return noErr;	// Need to restart the verification
189
190 	/*
191 	 * If there were unused nodes in the B-trees which were non-zero-filled,
192 	 * then zero fill them.
193 	 */
194	if (GPtr->ABTStat & S_UnusedNodesNotZero)
195	{
196		err = ZeroFillUnusedNodes(GPtr, kCalculatedAttributesRefNum);
197		ReturnIfError(err);
198	}
199	if (GPtr->EBTStat & S_UnusedNodesNotZero)
200	{
201		err = ZeroFillUnusedNodes(GPtr, kCalculatedExtentRefNum);
202		ReturnIfError(err);
203	}
204	if (GPtr->CBTStat & S_UnusedNodesNotZero)
205	{
206		err = ZeroFillUnusedNodes(GPtr, kCalculatedCatalogRefNum);
207		ReturnIfError(err);
208	}
209	if ((calculatedVCB->vcbAttributes & kHFSUnusedNodeFixMask) == 0)
210	{
211		calculatedVCB->vcbAttributes |= kHFSUnusedNodeFixMask;
212		MarkVCBDirty(calculatedVCB);
213	}
214
215	/*
216	 * We do this check here because it may make set up some minor repair orders;
217	 * however, because determining the repairs to be done is expensive, we have only
218	 * checked to see if there is any sort of problem so far.
219	 *
220	 * After it's done, DoMinorOrders() will take care of any requests that have been
221	 * set up.
222	 */
223	if (GPtr->CatStat & S_FileHardLinkChain) {
224		err = RepairHardLinkChains(GPtr, false);
225		ReturnIfError(err);
226	}
227
228 	err = CheckForStop( GPtr ); ReturnIfError( err );				//	Permit the user to interrupt
229
230	if (GPtr->CatStat & S_DirHardLinkChain) {
231		err = RepairHardLinkChains(GPtr, true);
232		ReturnIfError(err);
233	}
234
235 	err = CheckForStop( GPtr ); ReturnIfError( err );				//	Permit the user to interrupt
236	//  Handle repair orders.  Note that these must be done *BEFORE* the MDB is updated.
237	err = DoMinorOrders( GPtr );
238	ReturnIfError( err );
239  	err = CheckForStop( GPtr ); ReturnIfError( err );
240
241	/* Clear Catalog status for things repaired by DoMinorOrders */
242	GPtr->CatStat &= ~(S_FileAllocation | S_Permissions | S_UnlinkedFile | S_LinkCount | S_IllName | S_BadExtent | S_LinkErrRepair | S_FileHardLinkChain | S_DirHardLinkChain);
243
244	/*
245	 * Fix missing thread records
246	 */
247	if (GPtr->CatStat & S_MissingThread) {
248		err = FixMissingThreadRecords(GPtr);
249		ReturnIfError(err);
250
251		GPtr->CatStat &= ~S_MissingThread;
252		GPtr->CBTStat |= S_BTH;  /* leaf record count changed */
253	}
254
255	//	2210409, in System 8.1, moving file or folder would cause HFS+ thread records to be
256	//	520 bytes in size.  We only shrink the threads if other repairs are needed.
257	if ( GPtr->VeryMinorErrorsStat & S_BloatedThreadRecordFound )
258	{
259		(void) FixBloatedThreadRecords( GPtr );
260		GPtr->VeryMinorErrorsStat &= ~S_BloatedThreadRecordFound;
261	}
262
263	//
264	//	we will update the following data structures regardless of whether we have done
265	//	major or minor repairs, so we might end up doing this multiple times. Look into this.
266	//
267
268	//
269	//	Isolate and fix Overlapping Extents
270	//
271 	err = CheckForStop( GPtr ); ReturnIfError( err );				//	Permit the user to interrupt
272
273	if ( (GPtr->VIStat & S_OverlappingExtents) != 0 )
274	{
275		if (embedded == 1 && debug == 0)
276			return R_RFail;
277
278		err = FixOverlappingExtents( GPtr );						//	Isolate and fix Overlapping Extents
279		ReturnIfError( err );
280
281		GPtr->VIStat &= ~S_OverlappingExtents;
282		GPtr->VIStat |= S_VBM;										//	Now that we changed the extents, we need to rebuild the bitmap
283		InvalidateCalculatedVolumeBitMap( GPtr );					//	Invalidate our BitMap
284	}
285
286	//
287	//	FixOrphanedFiles
288	//
289 	err = CheckForStop( GPtr ); ReturnIfError( err );				//	Permit the user to interrupt
290
291	if ( (GPtr->CBTStat & S_Orphan) != 0 )
292	{
293		err = FixOrphanedFiles ( GPtr );							//	Orphaned file were found
294		ReturnIfError( err );
295		GPtr->CBTStat |= S_BTH;  									// leaf record count may change - 2913311
296	}
297
298	/* Some minor repairs would have failed at the first
299	 * attempt because of missing thread record or missing
300	 * file/folder record because of ordering of repairs
301	 * (example, deletion of file/folder before setting
302	 * the flag).  If any minor repairs orders are left,
303	 * try to repair them again after fixing incorrect
304	 * number of thread records.
305	 */
306	if (GPtr->MinorRepairsP) {
307		err = DoMinorOrders(GPtr);
308		ReturnIfError( err );
309	}
310
311	//
312	//	FixOrphanedExtent records
313	//
314	if ( (GPtr->EBTStat & S_OrphanedExtent) != 0 )					//	Orphaned extents were found
315	{
316		err = FixOrphanedExtent( GPtr );
317		GPtr->EBTStat &= ~S_OrphanedExtent;
318	//	if ( err == errRebuildBtree )
319	//		goto RebuildBtrees;
320		ReturnIfError( err );
321	}
322
323 	err = CheckForStop( GPtr ); ReturnIfError( err );				//	Permit the user to interrupt
324
325	//
326	//	Update the extent BTree header and bit map
327	//
328 	err = CheckForStop( GPtr ); ReturnIfError( err );				//	Permit the user to interrupt
329
330	if ( (GPtr->EBTStat & S_BTH) || (GPtr->EBTStat & S_ReservedBTH) )
331	{
332		err = UpdateBTreeHeader( GPtr->calculatedExtentsFCB );	//	update extent file BTH
333
334		if ( (err == noErr) && (GPtr->EBTStat & S_ReservedBTH) )
335		{
336			err = FixBTreeHeaderReservedFields( GPtr, kCalculatedExtentRefNum );
337		}
338
339		ReturnIfError( err );
340	}
341
342
343	if ( (GPtr->EBTStat & S_BTM) != 0 )
344	{
345		err = UpdBTM( GPtr, kCalculatedExtentRefNum );				//	update extent file BTM
346		ReturnIfError( err );
347	}
348
349	//
350	//	Update the catalog BTree header and bit map
351	//
352
353 	err = CheckForStop( GPtr ); ReturnIfError( err );				//	Permit the user to interrupt
354
355	if ( (GPtr->CBTStat & S_BTH) || (GPtr->CBTStat & S_ReservedBTH) )
356	{
357		err = UpdateBTreeHeader( GPtr->calculatedCatalogFCB );	//	update catalog BTH
358
359		if ( (err == noErr) && (GPtr->CBTStat & S_ReservedBTH) )
360		{
361			err = FixBTreeHeaderReservedFields( GPtr, kCalculatedCatalogRefNum );
362		}
363
364		ReturnIfError( err );
365	}
366
367	if ( GPtr->CBTStat & S_BTM )
368	{
369		err = UpdBTM( GPtr, kCalculatedCatalogRefNum );				//	update catalog BTM
370		ReturnIfError( err );
371	}
372
373	if ( (GPtr->CBTStat & S_ReservedNotZero) != 0 )
374	{
375		err = RepairReservedBTreeFields( GPtr );					//	update catalog fields
376		ReturnIfError( err );
377	}
378
379	// Repair orphaned/invalid attribute records
380	if (  (GPtr->ABTStat & S_AttrRec) )
381	{
382		err = FixOrphanAttrRecord( GPtr );
383		ReturnIfError( err );
384	}
385
386	// Repair inconsistency of attribute btree and corresponding bits in
387	// catalog btree
388	if ( (GPtr->ABTStat & S_AttributeCount) ||
389	     (GPtr->ABTStat & S_SecurityCount))
390	{
391		err = RepairAttributes( GPtr );
392		ReturnIfError( err );
393	}
394
395	// Update the attribute BTree header and bit map
396	if ( (GPtr->ABTStat & S_BTH) )
397	{
398		err = UpdateBTreeHeader( GPtr->calculatedAttributesFCB );	//	update attribute BTH
399		ReturnIfError( err );
400	}
401
402	if ( GPtr->ABTStat & S_BTM )
403	{
404		err = UpdBTM( GPtr, kCalculatedAttributesRefNum );		//	update attribute BTM
405		ReturnIfError( err );
406	}
407
408	/* Extended attribute repair can also detect incorrect number
409	 * of thread records, so trigger thread records repair now and
410	 * come back again in next pass for any fallouts and/or repairing
411	 * extended attribute inconsistency.
412	 * Note:  This should be removed when Chinese Remainder Theorem
413	 * is used for detecting incorrect number of thread records
414	 * (rdar://3968148).
415	 */
416	if ( (GPtr->CBTStat & S_Orphan) != 0 )
417	{
418		err = FixOrphanedFiles ( GPtr );
419		ReturnIfError( err );
420	}
421
422	//
423	//	Update the volume bit map
424	//
425	// Note, moved volume bit map update to end after other repairs
426	// (except the MDB / VolumeHeader) have been completed
427	//
428 	err = CheckForStop( GPtr ); ReturnIfError( err );				//	Permit the user to interrupt
429
430	if ( (GPtr->VIStat & S_VBM) != 0 )
431	{
432		err = UpdateVolumeBitMap( GPtr, false );					//	update VolumeBitMap
433		ReturnIfError( err );
434		InvalidateCalculatedVolumeBitMap( GPtr );					//	Invalidate our BitMap
435	}
436
437	//
438	//	Fix missing Primary or Alternate VHB or MDB
439	//
440
441 	err = CheckForStop( GPtr ); ReturnIfError( err );				//	Permit the user to interrupt
442
443	if ( (GPtr->VIStat & S_MDB) != 0 )		//	fix MDB / VolumeHeader
444	{
445		Boolean		fixedIt = false;
446		err = VolumeObjectFixVHBorMDB( &fixedIt );
447		ReturnIfError( err );
448		// don't call FlushAlternateVolumeControlBlock if we fixed it since that would
449		// mean our calculated VCB has not been completely set up.
450		if ( fixedIt ) {
451			GPtr->VIStat &= ~S_MDB;
452			MarkVCBClean( calculatedVCB );
453		}
454	}
455
456 	err = CheckForStop( GPtr ); ReturnIfError( err );				//	Permit the user to interrupt
457
458	if ( (GPtr->VIStat & S_WMDB) != 0  )		//	fix wrapper MDB
459	{
460		err = VolumeObjectRestoreWrapper();
461		ReturnIfError( err );
462	}
463
464	//
465	//	Update the MDB / VolumeHeader
466	//
467	// Note, moved MDB / VolumeHeader update to end
468	// after all other repairs have been completed.
469	//
470
471 	err = CheckForStop( GPtr ); ReturnIfError( err );				//	Permit the user to interrupt
472
473	if ( (GPtr->VIStat & S_MDB) != 0 || IsVCBDirty(calculatedVCB) ) //	update MDB / VolumeHeader
474	{
475		MarkVCBDirty(calculatedVCB);								// make sure its dirty
476		calculatedVCB->vcbAttributes |= kHFSVolumeUnmountedMask;
477		err = FlushAlternateVolumeControlBlock( calculatedVCB, isHFSPlus );	//	Writes real & alt blocks
478		ReturnIfError( err );
479	}
480
481 	err = CheckForStop( GPtr ); ReturnIfError( err );				//	Permit the user to interrupt
482
483 	// if we had minor repairs that failed we still want to fix as much as possible
484 	// so we wait until now to indicate the volume still has problems
485 	if ( GPtr->minorRepairErrors )
486 		err = R_RFail;
487
488	return( err );													//	all done
489}
490
491
492
493//
494//	Internal Routines
495//
496
497//�������������������������������������������������������������������������������
498//	Routine:	VolumeObjectFixVHBorMDB
499//
500//	Function:	When the primary or alternate Volume Header Block or Master
501//				Directory Block is damaged or missing use the undamaged one to
502//				restore the other.
503//�������������������������������������������������������������������������������
504
505static OSErr VolumeObjectFixVHBorMDB( Boolean* fixedItPtr )
506{
507	OSErr				err;
508	OSErr				err2;
509	VolumeObjectPtr		myVOPtr;
510	BlockDescriptor  	myPrimary;
511	BlockDescriptor  	myAlternate;
512
513	myVOPtr = GetVolumeObjectPtr( );
514	myPrimary.buffer = NULL;
515	myAlternate.buffer = NULL;
516	err = noErr;
517
518	// bail if both are OK
519	if ( VolumeObjectIsHFS() ) {
520		if ( (myVOPtr->flags & kVO_PriMDBOK) != 0 &&
521			 (myVOPtr->flags & kVO_AltMDBOK) != 0 )
522			goto ExitThisRoutine;
523	}
524	else {
525		if ( (myVOPtr->flags & kVO_PriVHBOK) != 0 &&
526			 (myVOPtr->flags & kVO_AltVHBOK) != 0 )
527			goto ExitThisRoutine;
528	}
529
530	// it's OK if one of the primary or alternate is invalid
531	err = GetVolumeObjectPrimaryBlock( &myPrimary );
532	if ( !(err == noErr || err == badMDBErr || err == noMacDskErr) )
533		goto ExitThisRoutine;
534
535	// invalidate if we have not marked the primary as OK
536	if ( VolumeObjectIsHFS( ) ) {
537		if ( (myVOPtr->flags & kVO_PriMDBOK) == 0 )
538			err = badMDBErr;
539	}
540	else if ( (myVOPtr->flags & kVO_PriVHBOK) == 0 ) {
541		err = badMDBErr;
542	}
543
544	err2 = GetVolumeObjectAlternateBlock( &myAlternate );
545	if ( !(err2 == noErr || err2 == badMDBErr || err2 == noMacDskErr) )
546		goto ExitThisRoutine;
547
548	// invalidate if we have not marked the alternate as OK
549	if ( VolumeObjectIsHFS( ) ) {
550		if ( (myVOPtr->flags & kVO_AltMDBOK) == 0 )
551			err2 = badMDBErr;
552	}
553	else if ( (myVOPtr->flags & kVO_AltVHBOK) == 0 ) {
554		err2 = badMDBErr;
555	}
556
557	// primary is OK so use it to restore alternate
558	if ( err == noErr ) {
559		CopyMemory( myPrimary.buffer, myAlternate.buffer, Blk_Size );
560		(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myAlternate, kForceWriteBlock );
561		myAlternate.buffer = NULL;
562		*fixedItPtr = true;
563		if ( VolumeObjectIsHFS( ) )
564			myVOPtr->flags |= kVO_AltMDBOK;
565		else
566			myVOPtr->flags |= kVO_AltVHBOK;
567	}
568	// alternate is OK so use it to restore the primary
569	else if ( err2 == noErr ) {
570		CopyMemory( myAlternate.buffer, myPrimary.buffer, Blk_Size );
571		(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myPrimary, kForceWriteBlock );
572		myPrimary.buffer = NULL;
573		*fixedItPtr = true;
574		if ( VolumeObjectIsHFS( ) )
575			myVOPtr->flags |= kVO_PriMDBOK;
576		else
577			myVOPtr->flags |= kVO_PriVHBOK;
578		err = noErr;
579	}
580	else
581		err = noMacDskErr;
582
583ExitThisRoutine:
584	if ( myPrimary.buffer != NULL )
585		(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myPrimary, kReleaseBlock );
586	if ( myAlternate.buffer != NULL )
587		(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myAlternate, kReleaseBlock );
588
589	return( err );
590
591} /* VolumeObjectFixVHBorMDB */
592
593
594//�������������������������������������������������������������������������������
595//	Routine:	VolumeObjectRestoreWrapper
596//
597//	Function:	When the primary or alternate Master Directory Block is damaged
598//				or missing use the undamaged one to restore the other.
599//�������������������������������������������������������������������������������
600
601static OSErr VolumeObjectRestoreWrapper( void )
602{
603	OSErr				err;
604	OSErr				err2;
605	VolumeObjectPtr		myVOPtr;
606	BlockDescriptor  	myPrimary;
607	BlockDescriptor  	myAlternate;
608
609	myVOPtr = GetVolumeObjectPtr( );
610	myPrimary.buffer = NULL;
611	myAlternate.buffer = NULL;
612
613	// it's OK if one of the MDB is invalid
614	err = GetVolumeObjectPrimaryMDB( &myPrimary );
615	if ( !(err == noErr || err == badMDBErr || err == noMacDskErr) )
616		goto ExitThisRoutine;
617	err2 = GetVolumeObjectAlternateMDB( &myAlternate );
618	if ( !(err2 == noErr || err2 == badMDBErr || err2 == noMacDskErr) )
619		goto ExitThisRoutine;
620
621	// primary is OK so use it to restore alternate
622	if ( err == noErr && (myVOPtr->flags & kVO_PriMDBOK) != 0 ) {
623		CopyMemory( myPrimary.buffer, myAlternate.buffer, Blk_Size );
624		(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myAlternate, kForceWriteBlock );
625		myAlternate.buffer = NULL;
626		myVOPtr->flags |= kVO_AltMDBOK;
627	}
628	// alternate is OK so use it to restore the primary
629	else if ( err2 == noErr && (myVOPtr->flags & kVO_AltMDBOK) != 0 ) {
630		CopyMemory( myAlternate.buffer, myPrimary.buffer, Blk_Size );
631		(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myPrimary, kForceWriteBlock );
632		myPrimary.buffer = NULL;
633		myVOPtr->flags |= kVO_PriMDBOK;
634		err = noErr;
635	}
636	else
637		err = noMacDskErr;
638
639ExitThisRoutine:
640	if ( myPrimary.buffer != NULL )
641		(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myPrimary, kReleaseBlock );
642	if ( myAlternate.buffer != NULL )
643		(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myAlternate, kReleaseBlock );
644
645	return( err );
646
647} /* VolumeObjectRestoreWrapper */
648
649
650/*------------------------------------------------------------------------------
651Routine:	UpdateBTreeHeader - (Update BTree Header)
652
653Function:	Replaces a BTH on disk with info from a scavenger BTCB.
654
655Input:		GPtr		-	pointer to scavenger global area
656			refNum		-	file refnum
657
658Output:		UpdateBTreeHeader	-	function result:
659								0	= no error
660								n 	= error
661------------------------------------------------------------------------------*/
662
663static	OSErr	UpdateBTreeHeader( SFCB * fcbPtr )
664{
665	OSErr err;
666
667	M_BTreeHeaderDirty( ((BTreeControlBlockPtr) fcbPtr->fcbBtree) );
668	err = BTFlushPath( fcbPtr );
669
670	return( err );
671
672} /* End UpdateBTreeHeader */
673
674
675
676/*------------------------------------------------------------------------------
677Routine:	FixBTreeHeaderReservedFields
678
679Function:	Fix reserved fields in BTree Header
680
681Input:		GPtr		-	pointer to scavenger global area
682			refNum		-	file refnum
683
684Output:		0	= no error
685			n 	= error
686------------------------------------------------------------------------------*/
687
688static	OSErr	FixBTreeHeaderReservedFields( SGlobPtr GPtr, short refNum )
689{
690	OSErr        err;
691	BTHeaderRec  header;
692
693	err = GetBTreeHeader(GPtr, ResolveFCB(refNum), &header);
694	ReturnIfError( err );
695
696	if ( (header.clumpSize % GPtr->calculatedVCB->vcbBlockSize) != 0 )
697		header.clumpSize = GPtr->calculatedVCB->vcbBlockSize;
698
699	header.reserved1	= 0;
700	header.btreeType	= kHFSBTreeType;  //	control file
701/*
702 * TBD - we'll need to repair an invlid keyCompareType field.
703 */
704#if 0
705	if (-->TBD<--)
706		header.keyCompareType = kHFSBinaryCompare;
707#endif
708	ClearMemory( header.reserved3, sizeof(header.reserved3) );
709
710	return( err );
711}
712
713
714
715
716/*------------------------------------------------------------------------------
717
718Routine:	UpdBTM - (Update BTree Map)
719
720Function:	Replaces a BTM on disk with a scavenger BTM.
721
722Input:		GPtr		-	pointer to scavenger global area
723			refNum		-	file refnum
724
725Output:		UpdBTM	-	function result:
726								0	= no error
727								n 	= error
728------------------------------------------------------------------------------*/
729
730static	OSErr	UpdBTM( SGlobPtr GPtr, short refNum )
731{
732	OSErr				err;
733	UInt16				recSize;
734	SInt32				mapSize;
735	SInt16				size;
736	SInt16				recIndx;
737	Ptr					p;
738	Ptr					btmP;
739	Ptr					sbtmP;
740	UInt32				nodeNum;
741	NodeRec				node;
742	UInt32				fLink;
743	BTreeControlBlock	*calculatedBTCB	= GetBTreeControlBlock( refNum );
744
745	//	Set up
746	mapSize			= ((BTreeExtensionsRec*)calculatedBTCB->refCon)->BTCBMSize;
747
748	//
749	//	update the map records
750	//
751	if ( mapSize > 0 )
752	{
753		nodeNum	= 0;
754		recIndx	= 2;
755		sbtmP	= ((BTreeExtensionsRec*)calculatedBTCB->refCon)->BTCBMPtr;
756
757		do
758		{
759			GPtr->TarBlock = nodeNum;								//	set target node number
760
761			err = GetNode( calculatedBTCB, nodeNum, &node );
762			ReturnIfError( err );									//	could't get map node
763
764			//	Locate the map record
765			recSize = GetRecordSize( calculatedBTCB, (BTNodeDescriptor *)node.buffer, recIndx );
766			btmP = (Ptr)GetRecordAddress( calculatedBTCB, (BTNodeDescriptor *)node.buffer, recIndx );
767			fLink	= ((NodeDescPtr)node.buffer)->fLink;
768			size	= ( recSize  > mapSize ) ? mapSize : recSize;
769
770			CopyMemory( sbtmP, btmP, size );						//	update it
771
772			err = UpdateNode( calculatedBTCB, &node );				//	write it, and unlock buffer
773
774			mapSize	-= size;										//	move to next map record
775			if ( mapSize == 0 )										//	more to go?
776				break;												//	no, zero remainder of record
777			if ( fLink == 0 )										//	out of bitmap blocks in file?
778			{
779				RcdError( GPtr, E_ShortBTM );
780				(void) ReleaseNode(calculatedBTCB, &node);
781				return( E_ShortBTM );
782			}
783
784			nodeNum	= fLink;
785			sbtmP	+= size;
786			recIndx	= 0;
787
788		} while ( mapSize > 0 );
789
790		//	clear the unused portion of the map record
791		for ( p = btmP + size ; p < btmP + recSize ; p++ )
792			*p = 0;
793
794		err = UpdateNode( calculatedBTCB, &node );					//	Write it, and unlock buffer
795	}
796
797	return( noErr );												//	All done
798}	//	end UpdBTM
799
800
801
802
803/*------------------------------------------------------------------------------
804
805Routine:	UpdateVolumeBitMap - (Update Volume Bit Map)
806
807Function:	Replaces the VBM on disk with the scavenger VBM.
808
809Input:		GPtr			-	pointer to scavenger global area
810
811Output:		UpdateVolumeBitMap			- 	function result:
812									0 = no error
813									n = error
814			GPtr->VIStat	-	S_VBM flag set if VBM is damaged.
815------------------------------------------------------------------------------*/
816
817static	OSErr	UpdateVolumeBitMap( SGlobPtr GPtr, Boolean preAllocateOverlappedExtents )
818{
819	GPtr->TarID = VBM_FNum;
820
821	return ( CheckVolumeBitMap(GPtr, true) );
822}
823
824/*
825Routine:	FixBadLinkChainFirst - fix the first link in a hard link chain
826
827Input:		GPtr		-- pointer to scavenger global data
828			p			-- pointer to a minor repair order
829
830Output:		funciton result:
831				0	-- no error
832				n	-- error
833*/
834
835OSErr FixBadLinkChainFirst(SGlobPtr GPtr, RepairOrderPtr p)
836{
837	CatalogRecord rec;
838	uint16_t recsize;
839	OSErr retval = 0;
840	HFSPlusAttrData *attrRec;
841	HFSPlusAttrKey *attrKey;
842	BTreeIterator iterator;
843	FSBufferDescriptor bt_data;
844	u_int8_t	attrdata[FIRST_LINK_XATTR_REC_SIZE];
845	size_t unicode_bytes = 0;
846
847	ClearMemory(&iterator, sizeof(iterator));
848	retval = GetCatalogRecordByID(GPtr, (UInt32)p->parid, true, (CatalogKey*)&iterator.key, &rec, &recsize);
849	if (retval != 0) {
850		if (retval == btNotFound) {
851			/* If the record was not found because either the thread
852			 * record is missing or the file/folder record was deleted by
853			 * another repair order, return false success to retry again
854			 * after thread repair code.
855			 */
856		 	GPtr->minorRepairFalseSuccess = true;
857			retval = 0;
858		}
859		goto done;
860	}
861
862	switch (rec.recordType) {
863	case kHFSPlusFolderRecord:	// directory hard link
864		attrKey = (HFSPlusAttrKey*)&iterator.key;
865		utf_decodestr((unsigned char *)FIRST_LINK_XATTR_NAME,
866			strlen(FIRST_LINK_XATTR_NAME), attrKey->attrName,
867			&unicode_bytes, sizeof(attrKey->attrName));
868		attrKey->attrNameLen = unicode_bytes / sizeof(UniChar);
869		attrKey->keyLength = kHFSPlusAttrKeyMinimumLength + unicode_bytes;
870		attrKey->pad = 0;
871		attrKey->fileID = p->parid;
872		attrKey->startBlock = 0;
873		attrRec = (HFSPlusAttrData*)&attrdata[0];
874		attrRec->recordType = kHFSPlusAttrInlineData;
875		attrRec->reserved[0] = 0;
876		attrRec->reserved[1] = 0;
877		(void)snprintf((char*)&attrRec->attrData[0],
878			sizeof(attrdata) - offsetof(HFSPlusAttrData, attrData),
879			"%lu", (unsigned long)(p->correct));
880		attrRec->attrSize = 1 + strlen((char*)&attrRec->attrData[0]);
881		bt_data.bufferAddress = attrRec;
882		recsize = sizeof(HFSPlusAttrData) - 2 + attrRec->attrSize + ((attrRec->attrSize & 1) ? 1 : 0);
883		bt_data.itemSize = recsize;
884		bt_data.itemCount = 1;
885
886		retval = BTInsertRecord(GPtr->calculatedAttributesFCB, &iterator, &bt_data, recsize);
887		if (retval == btExists) {
888			retval = BTReplaceRecord(GPtr->calculatedAttributesFCB, &iterator, &bt_data, recsize);
889		}
890
891		if (retval) {
892			/* If there is error on inserting a new attribute record
893			 * because attribute btree does not exists, print message.
894			 */
895			if ((GPtr->calculatedAttributesFCB->fcbPhysicalSize == 0) &&
896			    (GPtr->calculatedAttributesFCB->fcbLogicalSize == 0) &&
897			    (GPtr->calculatedAttributesFCB->fcbClumpSize == 0) &&
898			    (fsckGetVerbosity(GPtr->context) >= kDebugLog)) {
899					plog ("\tFixBadLinkChainFirst: Attribute btree does not exists.\n");
900			}
901		}
902		break;
903	case kHFSPlusFileRecord:	// file hard link
904		rec.hfsPlusFile.hl_firstLinkID = (UInt32)p->correct;
905		bt_data.bufferAddress = &rec;
906		bt_data.itemSize = recsize;
907		bt_data.itemCount = 1;
908		retval = BTReplaceRecord(GPtr->calculatedCatalogFCB, &iterator, &bt_data, recsize);
909		break;
910	default:
911		retval = IntError(GPtr, R_IntErr);
912		break;
913	}
914done:
915	return retval;
916}
917
918
919/*
920Routine:	FixHardLinkBadDate - fix the date of an indirect-node
921
922Input:		GPtr		-- pointer to scavenger global data
923			p			-- pointer to a minor repair order
924
925Output:		function result:
926				0 -- no error
927				n -- error
928*/
929
930OSErr FixHardLinkBadDate(SGlobPtr GPtr, RepairOrderPtr p)
931{
932	CatalogKey key;
933	CatalogRecord rec;
934	uint16_t recsize;
935	OSErr retval = 0;
936	UInt32 hint;
937
938	retval = GetCatalogRecordByID(GPtr, (UInt32)p->parid, true, &key, &rec, &recsize);
939
940	if (retval == 0) {
941		if (rec.recordType != kHFSPlusFileRecord) {
942			retval = IntError(GPtr, R_IntErr);
943		} else {
944			rec.hfsPlusFile.createDate = p->correct;
945			retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key, kNoHint, &rec, recsize, &hint);
946		}
947	}
948
949	return retval;
950
951}
952
953/*
954Routine:	FixFileHardLinkFlag - clear the HardLinkChain flag in a file record
955
956Input:		GPtr		-- pointer to scavenger global data
957			p			-- pointer to minor repair order
958
959Output:		function result:
960				0	-- no error
961				n	-- error
962*/
963
964OSErr FixFileHardLinkFlag(SGlobPtr GPtr, RepairOrderPtr p)
965{
966	CatalogKey key;
967	CatalogRecord rec;
968	uint16_t recsize;
969	OSErr retval = 0;
970	UInt32 hint;
971
972	retval = GetCatalogRecordByID(GPtr, (UInt32)p->parid, true, &key, &rec, &recsize);
973
974	if (retval == 0) {
975		if (rec.recordType != kHFSPlusFileRecord) {
976			retval = IntError(GPtr, R_IntErr);
977		} else {
978			rec.hfsPlusFile.flags &= ~kHFSHasLinkChainMask;
979			retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key, kNoHint, &rec, recsize, &hint);
980		}
981	}
982	return retval;
983}
984
985/*
986Routine:	FixPrivDirBadPerms - fix the permissions of the directory hard-link private dir
987
988Input:		GPtr		-- pointer to scavenger global data
989			p			-- poitner to a minor repair order
990
991Output:		function result:
992				0 -- no error
993				n -- error
994*/
995
996static OSErr FixPrivDirBadPerms(SGlobPtr GPtr, RepairOrderPtr p)
997{
998	CatalogKey key;
999	CatalogRecord rec;
1000	uint16_t recsize;
1001	OSErr retval = 0;
1002	UInt32 hint;
1003
1004	retval = GetCatalogRecordByID(GPtr, (UInt32)p->parid, true, &key, &rec, &recsize);
1005
1006	if (retval != 0) {
1007		if (retval == btNotFound) {
1008			/* If the record was not found because either the thread
1009			 * record is missing or the file/folder record was deleted by
1010			 * another repair order, return false success to retry again
1011			 * after thread repair code.
1012			 */
1013		 	GPtr->minorRepairFalseSuccess = true;
1014			retval = 0;
1015		}
1016		goto done;
1017	}
1018	if (rec.recordType != kHFSPlusFolderRecord) {
1019		retval = IntError(GPtr, R_IntErr);
1020		goto done;
1021	}
1022
1023	rec.hfsPlusFolder.bsdInfo.ownerFlags |= UF_IMMUTABLE;
1024	rec.hfsPlusFolder.bsdInfo.fileMode |= S_ISVTX;
1025
1026	retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key, kNoHint, &rec, recsize, &hint);
1027
1028done:
1029	return retval;
1030}
1031
1032/*------------------------------------------------------------------------------
1033Routine:	FixOrphanLink
1034
1035Function:	Delete the orphan directory/file hard link as no corresponding
1036		directory/file inode was found.
1037
1038Input:		GPtr - ptr to scavenger global data
1039		p    - pointer to a minor repair order
1040
1041Output:		function returns -
1042		0 - no error, success
1043		n - error
1044-------------------------------------------------------------------------------*/
1045static OSErr FixOrphanLink(SGlobPtr GPtr, RepairOrderPtr p)
1046{
1047	int retval;
1048
1049	retval = DeleteCatalogRecordByID(GPtr, p->parid, false);
1050	if (retval == btNotFound) {
1051		/* If the record was not found because either the thread
1052		 * record is missing or the file/folder record was deleted by
1053		 * another repair order, return false success to retry again
1054		 * after thread repair code.
1055		 */
1056		GPtr->minorRepairFalseSuccess = true;
1057		retval = 0;
1058	}
1059
1060	return retval;
1061}
1062
1063/*------------------------------------------------------------------------------
1064Routine:	FixOrphanInode
1065
1066Function:	Repair orphan file/directory inode, i.e. no hard links point
1067		to this file/directory inode by moving them to lost+found.
1068
1069Input:		GPtr - ptr to scavenger global data
1070		p    - pointer to a minor repair order
1071
1072Output:		function returns -
1073		0 - no error, success
1074		n - error
1075-------------------------------------------------------------------------------*/
1076static OSErr FixOrphanInode(SGlobPtr GPtr, RepairOrderPtr p)
1077{
1078	int retval;
1079	uint32_t lost_found_id;
1080	static int msg_display = 0;
1081
1082	if (embedded == 1 && debug == 0) {
1083		retval = EPERM;
1084		goto out;
1085	}
1086
1087	/* Make sure that lost+found exists */
1088	lost_found_id = CreateDirByName(GPtr, (u_char *)"lost+found",
1089				kHFSRootFolderID);
1090	if (lost_found_id == 0) {
1091		retval = ENOENT;
1092		goto out;
1093	}
1094
1095	retval = MoveCatalogRecordByID(GPtr, p->parid, lost_found_id);
1096	if (retval == btNotFound) {
1097		/* If the record was not found because either the thread
1098		 * record is missing or the file/folder record was deleted by
1099		 * another repair order, return false success to retry again
1100		 * after thread repair code.
1101		 */
1102		GPtr->minorRepairFalseSuccess = true;
1103		retval = 0;
1104	}
1105	if (msg_display == 0) {
1106		fsckPrint(GPtr->context, fsckLostFoundDirectory, "lost+found");
1107		msg_display = 1;
1108	}
1109
1110out:
1111	return retval;
1112}
1113
1114/*------------------------------------------------------------------------------
1115Routine:	FixDirLinkOwnerFlags
1116
1117Function:	Fix the owner flags for directory hard link.
1118
1119Input:		GPtr - ptr to scavenger global data
1120		p    - pointer to a minor repair order
1121
1122Output:		function returns -
1123		0 - no error, success
1124		n - error
1125-------------------------------------------------------------------------------*/
1126static OSErr FixDirLinkOwnerFlags(SGlobPtr GPtr, RepairOrderPtr p)
1127{
1128	CatalogKey key;
1129	CatalogRecord rec;
1130	uint16_t recsize;
1131	OSErr retval = 0;
1132	UInt32 hint;
1133
1134	retval = GetCatalogRecordByID(GPtr, p->parid, true, &key, &rec, &recsize);
1135	if (retval != 0) {
1136		if (retval == btNotFound) {
1137			/* If the record was not found because either the thread
1138			 * record is missing or the file/folder record was deleted by
1139			 * another repair order, return false success to retry again
1140			 * after thread repair code.
1141			 */
1142		 	GPtr->minorRepairFalseSuccess = true;
1143			retval = 0;
1144		}
1145		goto done;
1146	}
1147
1148	rec.hfsPlusFile.bsdInfo.ownerFlags = p->correct;
1149
1150	retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key, kNoHint,
1151		&rec, recsize, &hint);
1152
1153done:
1154	return retval;
1155}
1156
1157/*------------------------------------------------------------------------------
1158Routine:	FixBadFlags
1159
1160Function:	Update the flags field of a directory or file node
1161
1162Input:		GPtr		-- ptr to scavenger global data
1163			p			-- pointer to a minor repair order
1164
1165Output:		function result:
1166				0 - no error
1167				n - error
1168*/
1169static OSErr FixBadFlags(SGlobPtr GPtr, RepairOrderPtr p)
1170{
1171	CatalogKey key;
1172	CatalogRecord rec;
1173	uint16_t recsize;
1174	OSErr retval = 0;
1175	UInt32 hint;
1176
1177	retval = GetCatalogRecordByID(GPtr, p->parid, true, &key, &rec, &recsize);
1178	if (retval != 0) {
1179		if (retval == btNotFound) {
1180			/* If the record was not found because either the thread
1181			 * record is missing or the file/folder record was deleted by
1182			 * another repair order, return false success to retry again
1183			 * after thread repair code.
1184			 */
1185		 	GPtr->minorRepairFalseSuccess = true;
1186			retval = 0;
1187		}
1188		goto done;
1189	}
1190
1191	if (p->type == E_DirInodeBadFlags) {
1192		if ((rec.hfsPlusFolder.flags != p->incorrect) && (fsckGetVerbosity(GPtr->context) >= kDebugLog)) {
1193			fplog(stderr, "\tFixBadFlags (folder):  old = %#x, incorrect = %#x, correct = %#x\n", rec.hfsPlusFolder.flags, (int)p->incorrect, (int)p->correct);
1194		}
1195		rec.hfsPlusFolder.flags = p->correct;
1196	} else if (p->type == E_DirLinkAncestorFlags) {
1197		if ((rec.hfsPlusFolder.flags != p->incorrect) && (fsckGetVerbosity(GPtr->context) >= kDebugLog)) {
1198			fplog(stderr, "\tFixBadFlags (parent folder):  old = %#x, incorrect = %#x, correct = %#x\n", rec.hfsPlusFolder.flags, (int)p->incorrect, (int)p->correct);
1199		}
1200		rec.hfsPlusFolder.flags = p->correct;
1201	} else {
1202		if ((rec.hfsPlusFolder.flags != p->incorrect) && (fsckGetVerbosity(GPtr->context) >= kDebugLog)) {
1203			fplog(stderr, "\tFixBadFlags (file):  old = %#x, incorrect = %#x, correct = %#x\n", rec.hfsPlusFolder.flags, (int)p->incorrect, (int)p->correct);
1204		}
1205		rec.hfsPlusFile.flags = p->correct;
1206	}
1207
1208	retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key, kNoHint,
1209		&rec, recsize, &hint);
1210
1211done:
1212
1213	return retval;
1214
1215}
1216
1217/*------------------------------------------------------------------------------
1218Routine:	UpdFolderCount
1219
1220Function:	Update the folder count in an HFS+ folder record
1221
1222Input:		GPtr		-- ptr to scavenger global data
1223			p			-- pointer to minor repair order
1224
1225Output:		function result:
1226				0 - no error
1227				n - error
1228
1229------------------------------------------------------------------------------*/
1230OSErr UpdFolderCount( SGlobPtr GPtr, RepairOrderPtr p)
1231{
1232	OSErr result = -1;
1233	CatalogRecord record;
1234	CatalogKey key, foundKey;
1235	UInt16 recSize = 0;
1236	UInt32 hint = 0;
1237
1238#define DPRINT(where, fmt, ...) \
1239	if (fsckGetVerbosity(GPtr->context) >= kDebugLog) \
1240		fplog(where, fmt, ## __VA_ARGS__);
1241
1242	/*
1243	 * We do the search in two stages.  First, we look for just the
1244	 * catalog ID we get from the repair order; this SHOULD give us
1245	 * a thread record, which we can then use to get the real record.
1246	 */
1247	BuildCatalogKey( p->parid, NULL, true, &key);
1248	result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint,
1249		&foundKey, &record, &recSize, &hint);
1250	if (result) {
1251		if (result == btNotFound) {
1252			/* If the record was not found because either the thread
1253			 * record is missing or the file/folder record was deleted by
1254			 * another repair order, return false success to retry again
1255			 * after thread repair code.
1256			 */
1257		 	GPtr->minorRepairFalseSuccess = true;
1258			return 0;
1259		} else {
1260			DPRINT(stderr, "\tUpdFolderCount: first SearchBTreeRecord failed, parid = %u, result = %d\n", p->parid, result);
1261			return IntError(GPtr, R_IntErr);
1262		}
1263	}
1264
1265	if (record.recordType != kHFSPlusFolderThreadRecord) {
1266		GPtr->CBTStat |= S_Orphan;
1267		GPtr->minorRepairFalseSuccess = true;
1268		return 0;
1269	}
1270
1271	BuildCatalogKey( record.hfsPlusThread.parentID, (const CatalogName *)&record.hfsPlusThread.nodeName, true, &key);
1272	result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint,
1273		&foundKey, &record, &recSize, &hint);
1274
1275	if (result) {
1276		DPRINT(stderr, "UpdFolderCount: second SearchBTreeRecord failed (thread.parentID = %u, result = %d), just returning without complaint\n", record.hfsPlusThread.parentID, result);
1277		return 0;
1278	}
1279
1280	if (record.recordType != kHFSPlusFolderRecord) {
1281		DPRINT(stderr, "UpdFolderCount:  actual record type (%d) != FolderRecord\n", record.recordType);
1282		return IntError(GPtr, R_IntErr);
1283	}
1284
1285#if 0
1286	/*
1287	 * If we've had to make a folder on an HFSX volume, we set the folderCount to what
1288	 * it should be -- which may not be what it found at a different part of the pass.
1289	 */
1290	if ((UInt32)p->incorrect != record.hfsPlusFolder.folderCount) {
1291		DPRINT(stderr, "UpdFolderCount:  incorrect (%u) != expected folderCount (%u)\n", (UInt32)p->incorrect, record.hfsPlusFolder.folderCount);
1292		return IntError( GPtr, R_IntErr);
1293	}
1294#else
1295	if (record.hfsPlusFolder.folderCount == p->correct) {
1296		/* We've gotten it already, no need to do anything */
1297		return noErr;
1298	}
1299#endif
1300
1301	record.hfsPlusFolder.folderCount = p->correct;
1302	result = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint,
1303		&record, recSize, &hint);
1304	if (result) {
1305		DPRINT(stderr, "UpdFolderCount:  ReplaceBTreeRecord failed (%d)\n", result);
1306		return IntError( GPtr, R_IntErr );
1307	}
1308	return noErr;
1309}
1310#undef DPRINT
1311
1312/*------------------------------------------------------------------------------
1313Routine:	UpdHasFolderCount
1314
1315Function:	Update the HasFolderCount flag on an HFS+ folder's flags
1316
1317Input:		GPtr		-- ptr to scavenger global data
1318			p			-- pointer to minor repair order
1319
1320Output:		function result:
1321				0 - no error
1322				n - error
1323
1324------------------------------------------------------------------------------*/
1325OSErr UpdHasFolderCount( SGlobPtr GPtr, RepairOrderPtr p)
1326{
1327	OSErr result = -1;
1328	CatalogRecord record;
1329	CatalogKey key, foundKey;
1330	UInt16 recSize = 0;
1331	UInt32 hint = 0;
1332
1333	/*
1334	 * As above, we do the search in two stages:  first to get the
1335	 * thread record (based solely on the CNID); second, to get the
1336	 * folder record based from the thread record.
1337	 */
1338	BuildCatalogKey( p->parid, NULL, true, &key);
1339	result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint,
1340		&foundKey, &record, &recSize, &hint);
1341
1342	if (result) {
1343		if (result == btNotFound) {
1344			/* If the record was not found because either the thread
1345			 * record is missing or the file/folder record was deleted by
1346			 * another repair order, return false success to retry again
1347			 * after thread repair code.
1348			 */
1349		 	GPtr->minorRepairFalseSuccess = true;
1350			return 0;
1351		} else {
1352			return IntError(GPtr, R_IntErr);
1353		}
1354	}
1355
1356	/* If it's not a folder thread record, we've got a problem */
1357	if (record.recordType != kHFSPlusFolderThreadRecord) {
1358		GPtr->CBTStat |= S_Orphan;
1359		GPtr->minorRepairFalseSuccess = true;
1360		return 0;
1361	}
1362
1363	BuildCatalogKey( record.hfsPlusThread.parentID, (const CatalogName *)&record.hfsPlusThread.nodeName, true, &key);
1364	result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint,
1365		&foundKey, &record, &recSize, &hint);
1366
1367	if (result) {
1368		return IntError(GPtr, R_IntErr);
1369	}
1370
1371	if (record.recordType != kHFSPlusFolderRecord) {
1372		return IntError(GPtr, R_IntErr);
1373	}
1374
1375	/* Verify that the kHFSHasFolderCountMask bit hasn't been set, and set if necessary */
1376	if ((record.hfsPlusFolder.flags & kHFSHasFolderCountMask) == 0) {
1377		record.hfsPlusFolder.flags |= kHFSHasFolderCountMask;
1378		result = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint,
1379			&record, recSize, &hint);
1380		if (result) {
1381			return IntError( GPtr, R_IntErr );
1382		}
1383	}
1384
1385	return noErr;
1386}
1387
1388/*------------------------------------------------------------------------------
1389
1390Routine:	DoMinorOrders
1391
1392Function:	Execute minor repair orders.
1393
1394Input:		GPtr	- ptr to scavenger global data
1395
1396Outut:		function result:
1397				0 - no error
1398				n - error
1399------------------------------------------------------------------------------*/
1400
1401static	OSErr	DoMinorOrders( SGlobPtr GPtr )				//	the globals
1402{
1403	RepairOrderPtr		p;
1404	RepairOrderPtr	cur;
1405	OSErr				err	= noErr;						//	initialize to "no error"
1406
1407	/* Manipulate the list for minor repairs separately from the global
1408	 * list head because global list head will be used to store repair
1409	 * orders which returned false success in anticipation of re-repair
1410	 * after other corruptioins on the disk.
1411	 */
1412	cur = GPtr->MinorRepairsP;
1413	GPtr->MinorRepairsP = NULL;
1414
1415	while( (p = cur) && (err == noErr) )	//	loop over each repair order
1416	{
1417		cur = p->link;
1418
1419		GPtr->minorRepairFalseSuccess = false;
1420
1421		switch( p->type )									//	branch on repair type
1422		{
1423			case E_FldCount:								// folderCount needs to be updated
1424				err = UpdFolderCount( GPtr, p );
1425				break;
1426
1427			case E_HsFldCount:								// HasFolderCount bit needs to be set
1428				err = UpdHasFolderCount( GPtr, p );
1429				break;
1430
1431			case E_RtDirCnt:								//	the valence errors
1432			case E_RtFilCnt:								//	(of which there are several)
1433			case E_DirCnt:
1434			case E_FilCnt:
1435			case E_DirVal:
1436				err = UpdVal( GPtr, p );					//	handle a valence error
1437				break;
1438
1439			case E_LockedDirName:
1440				err = FixFinderFlags( GPtr, p );
1441				break;
1442
1443			case E_UnlinkedFile:
1444				err = DeleteUnlinkedFile( GPtr, p );
1445				break;
1446
1447			case E_FileLinkCountError:
1448			case E_InvalidLinkCount:
1449				err = FixLinkCount( GPtr, p );
1450				break;
1451
1452			case E_InvalidLinkChainPrev:
1453				err = FixLinkChainPrev( GPtr, p );
1454				break;
1455
1456			case E_InvalidLinkChainNext:
1457				err = FixLinkChainNext( GPtr, p );
1458				break;
1459
1460			case E_DirHardLinkFinderInfo:
1461			case E_FileHardLinkFinderInfo:
1462				err = FixHardLinkFinderInfo( GPtr, p );
1463				break;
1464
1465			case E_InvalidPermissions:
1466				err = FixBSDInfo( GPtr, p );
1467				break;
1468
1469			case E_NoFile:									//	dangling file thread
1470				err = DelFThd( GPtr, p->parid );			//	delete the dangling thread
1471				break;
1472
1473			//��	E_NoFile case is never hit since VLockedChk() registers the error,
1474			//��	and returns the error causing the verification to quit.
1475			case E_EntryNotFound:
1476				GPtr->EBTStat |= S_OrphanedExtent;
1477				break;
1478
1479			//��	Same with E_NoDir
1480			case E_NoDir:									//	missing directory record
1481				err = FixDirThread( GPtr, p->parid );		//	fix the directory thread record
1482				break;
1483
1484			case E_InvalidMDBdrAlBlSt:
1485				err = FixEmbededVolDescription( GPtr, p );
1486				break;
1487
1488			case E_InvalidWrapperExtents:
1489				err = FixWrapperExtents(GPtr, p);
1490				break;
1491
1492			case E_IllegalName:
1493				err = FixIllegalNames( GPtr, p );
1494				break;
1495
1496			case E_PEOF:
1497			case E_LEOF:
1498				err = FixFileSize(GPtr, p);
1499				break;
1500
1501			case E_PEOAttr:
1502			case E_LEOAttr:
1503				err = FixAttrSize(GPtr, p);
1504				break;
1505
1506			case E_ExtEnt:
1507				err = FixBadExtent(GPtr, p);
1508				break;
1509
1510			case E_DirInodeBadFlags:
1511			case E_DirLinkAncestorFlags:
1512			case E_FileInodeBadFlags:
1513			case E_DirLinkBadFlags:
1514			case E_FileLinkBadFlags:
1515				err = FixBadFlags(GPtr, p);
1516				break;
1517
1518			case E_BadPermPrivDir:
1519				err = FixPrivDirBadPerms(GPtr, p);
1520				break;
1521
1522			case E_InvalidLinkChainFirst:
1523				err = FixBadLinkChainFirst(GPtr, p);
1524				break;
1525
1526			case E_OrphanFileLink:
1527			case E_OrphanDirLink:
1528				err = FixOrphanLink(GPtr, p);
1529				break;
1530
1531			case E_OrphanFileInode:
1532			case E_OrphanDirInode:
1533				err = FixOrphanInode(GPtr, p);
1534				break;
1535
1536			case E_DirHardLinkOwnerFlags:
1537				err = FixDirLinkOwnerFlags(GPtr, p);
1538				break;
1539
1540			case E_BadHardLinkDate:
1541				err = FixHardLinkBadDate(GPtr, p);
1542				break;
1543
1544			case E_LinkChainNonLink:
1545				err = FixFileHardLinkFlag(GPtr, p);
1546				break;
1547
1548			default:										//	unknown repair type
1549				if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
1550						plog ("\tUnknown repair order found (type = %d)\n", p->type);
1551				}
1552				err = IntError( GPtr, R_IntErr );			//	treat as an internal error
1553				break;
1554		}
1555
1556		if ((err != 0) && (fsckGetVerbosity(GPtr->context) >= kDebugLog)) {
1557			plog ("\tDoMinorRepair: Repair for type=%d failed (err=%d).\n", p->type, err);
1558		}
1559
1560		/* A repair order can return false success if lookup of a
1561		 * record failed --- which can happen if the corresponding
1562		 * thread record is missing or a file/folder record was
1563		 * deleted as part of another repair order.  If repair
1564		 * order returned false success, do not free it up, instead
1565		 * add it back to the global minor repair list to retry
1566		 * repair after repairing incorrect number of thread records.
1567		 * Note:  We do not return error when repair of minor
1568		 * repair orders fail second time due to missing record
1569		 * because if we did not find the catalog record second time,
1570		 * it is already deleted and the minor repair order is invalid.
1571		 * The minor repair order list is later freed up in clean up
1572		 * for the scavenger.
1573		 */
1574		if (GPtr->minorRepairFalseSuccess == true) {
1575			p->link = GPtr->MinorRepairsP;
1576			GPtr->MinorRepairsP = p;
1577		} else {
1578			DisposeMemory( p );								//	free the node
1579		}
1580	}
1581
1582	return( err );											//	return error code to our caller
1583}
1584
1585
1586
1587/*------------------------------------------------------------------------------
1588
1589Routine:	DelFThd - (delete file thread)
1590
1591Function:	Executes the delete dangling file thread repair orders.  These are typically
1592			threads left after system 6 deletes an aliased file, since system 6 is not
1593			aware of aliases and thus will not delete the thread along with the file.
1594
1595Input:		GPtr	- global data
1596			fid		- the thread record's key's parent-ID
1597
1598Output:		0 - no error
1599			n - deletion failed
1600Modification History:
1601	29Oct90		KST		CBTDelete was using "k" as key which points to cache buffer.
1602-------------------------------------------------------------------------------*/
1603
1604static	int	DelFThd( SGlobPtr GPtr, UInt32 fid )				//	the file ID
1605{
1606	CatalogRecord		record;
1607	CatalogKey			foundKey;
1608	CatalogKey			key;
1609	UInt32				hint;								//	as returned by CBTSearch
1610	OSErr				result;								//	status return
1611	UInt16				recSize;
1612	Boolean				isHFSPlus;
1613	ExtentRecord		zeroExtents;
1614
1615	isHFSPlus = VolumeObjectIsHFSPlus( );
1616
1617	BuildCatalogKey( fid, (const CatalogName*) nil, isHFSPlus, &key );
1618	result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, &foundKey, &record, &recSize, &hint );
1619
1620	if ( result )	return ( IntError( GPtr, result ) );
1621
1622	if ( (record.recordType != kHFSFileThreadRecord) && (record.recordType != kHFSPlusFileThreadRecord) )	//	quit if not a file thread
1623		return ( IntError( GPtr, R_IntErr ) );
1624
1625	//	Zero the record on disk
1626	ClearMemory( (Ptr)&zeroExtents, sizeof(ExtentRecord) );
1627	result	= ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &key, hint, &zeroExtents, recSize, &hint );
1628	if ( result )	return ( IntError( GPtr, result ) );
1629
1630	result	= DeleteBTreeRecord( GPtr->calculatedCatalogFCB, &key );
1631	if ( result )	return ( IntError( GPtr, result ) );
1632
1633	//	After deleting a record, we'll need to write back the BT header and map,
1634	//	to reflect the updated record count etc.
1635
1636	GPtr->CBTStat |= S_BTH + S_BTM;							//	set flags to write back hdr and map
1637
1638	return( noErr );										//	successful return
1639}
1640
1641
1642/*------------------------------------------------------------------------------
1643
1644Routine:	FixDirThread - (fix directory thread record's parent ID info)
1645
1646Function:	Executes the missing directory record repair orders most likely caused by
1647			disappearing folder bug.  This bug causes some folders to jump to Desktop
1648			from the root window.  The catalog directory record for such a folder has
1649			the Desktop folder as the parent but its thread record still the root
1650			directory as its parent.
1651
1652Input:		GPtr	- global data
1653			did		- the thread record's key's parent-ID
1654
1655Output:		0 - no error
1656			n - deletion failed
1657-------------------------------------------------------------------------------*/
1658
1659static	OSErr	FixDirThread( SGlobPtr GPtr, UInt32 did )	//	the dir ID
1660{
1661	UInt8				*dataPtr;
1662	UInt32				hint;							//	as returned by CBTSearch
1663	OSErr				result;							//	status return
1664	UInt16				recSize;
1665	CatalogName			catalogName;					//	temporary name record
1666	CatalogName			*keyName;						//	temporary name record
1667	register short 		index;							//	loop index for all records in the node
1668	UInt32  			curLeafNode;					//	current leaf node being checked
1669	CatalogRecord		record;
1670	CatalogKey			foundKey;
1671	CatalogKey			key;
1672	CatalogKey		 	*keyP;
1673	SInt16				recordType;
1674	UInt32				folderID;
1675	NodeRec				node;
1676	NodeDescPtr			nodeDescP;
1677	UInt32				newParDirID		= 0;			//	the parent ID where the dir record is really located
1678	Boolean				isHFSPlus;
1679	BTreeControlBlock	*calculatedBTCB	= GetBTreeControlBlock( kCalculatedCatalogRefNum );
1680
1681	isHFSPlus = VolumeObjectIsHFSPlus( );
1682
1683	BuildCatalogKey( did, (const CatalogName*) nil, isHFSPlus, &key );
1684	result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, &foundKey, &record, &recSize, &hint );
1685
1686	if ( result )
1687		return( IntError( GPtr, result ) );
1688	if ( (record.recordType != kHFSFolderThreadRecord) && (record.recordType != kHFSPlusFolderThreadRecord) )			//	quit if not a directory thread
1689		return ( IntError( GPtr, R_IntErr ) );
1690
1691	curLeafNode = calculatedBTCB->freeNodes;
1692
1693	while ( curLeafNode )
1694	{
1695		result = GetNode( calculatedBTCB, curLeafNode, &node );
1696		if ( result != noErr ) return( IntError( GPtr, result ) );
1697
1698		nodeDescP = node.buffer;
1699
1700		// loop on number of records in node
1701		for ( index = 0 ; index < nodeDescP->numRecords ; index++ )
1702		{
1703			GetRecordByIndex( calculatedBTCB, (NodeDescPtr)nodeDescP, index, (BTreeKey **)&keyP, &dataPtr, &recSize );
1704
1705			recordType	= ((CatalogRecord *)dataPtr)->recordType;
1706			folderID	= recordType == kHFSPlusFolderRecord ? ((HFSPlusCatalogFolder *)dataPtr)->folderID : ((HFSCatalogFolder *)dataPtr)->folderID;
1707
1708			// did we locate a directory record whode dirID match the the thread's key's parent dir ID?
1709			if ( (folderID == did) && ( recordType == kHFSPlusFolderRecord || recordType == kHFSFolderRecord ) )
1710			{
1711				newParDirID	= recordType == kHFSPlusFolderRecord ? keyP->hfsPlus.parentID : keyP->hfs.parentID;
1712				keyName		= recordType == kHFSPlusFolderRecord ? (CatalogName *)&keyP->hfsPlus.nodeName : (CatalogName *)&keyP->hfs.nodeName;
1713				CopyCatalogName( keyName, &catalogName, isHFSPlus );
1714				break;
1715			}
1716		}
1717
1718		if ( newParDirID ) {
1719			(void) ReleaseNode(calculatedBTCB, &node);
1720			break;
1721		}
1722
1723		curLeafNode = nodeDescP->fLink;	 // sibling of this leaf node
1724
1725		(void) ReleaseNode(calculatedBTCB, &node);
1726	}
1727
1728	if ( newParDirID == 0 )
1729	{
1730		return ( IntError( GPtr, R_IntErr ) ); // ��  Try fixing by creating a new directory record?
1731	}
1732	else
1733	{
1734		(void) SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, &foundKey, &record, &recSize, &hint );
1735
1736		if ( isHFSPlus )
1737		{
1738			HFSPlusCatalogThread	*largeCatalogThreadP	= (HFSPlusCatalogThread *) &record;
1739
1740			largeCatalogThreadP->parentID = newParDirID;
1741			CopyCatalogName( &catalogName, (CatalogName *) &largeCatalogThreadP->nodeName, isHFSPlus );
1742		}
1743		else
1744		{
1745			HFSCatalogThread	*smallCatalogThreadP	= (HFSCatalogThread *) &record;
1746
1747			smallCatalogThreadP->parentID = newParDirID;
1748			CopyCatalogName( &catalogName, (CatalogName *)&smallCatalogThreadP->nodeName, isHFSPlus );
1749		}
1750
1751		result = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recSize, &hint );
1752	}
1753
1754	return( noErr );										//	successful return
1755}
1756
1757
1758/*------------------------------------------------------------------------------
1759
1760Routine:	UpdVal - (Update Valence)
1761
1762Function:	Replaces out of date valences with correct vals computed during scavenge.
1763
1764Input:		GPtr			-	pointer to scavenger global area
1765			p				- 	pointer to the repair order
1766
1767Output:		UpdVal			- 	function result:
1768									0 = no error
1769									n = error
1770------------------------------------------------------------------------------*/
1771
1772static	OSErr	UpdVal( SGlobPtr GPtr, RepairOrderPtr p )					//	the valence repair order
1773{
1774	OSErr				result;						//	status return
1775	Boolean				isHFSPlus;
1776	UInt32				hint;						//	as returned by CBTSearch
1777	UInt16				recSize;
1778	CatalogRecord		record;
1779	CatalogKey			foundKey;
1780	CatalogKey			key;
1781	SVCB				*calculatedVCB = GPtr->calculatedVCB;
1782
1783	isHFSPlus = VolumeObjectIsHFSPlus( );
1784
1785	switch( p->type )
1786	{
1787		case E_RtDirCnt: //	invalid count of Dirs in Root
1788			if ( (UInt16)p->incorrect != calculatedVCB->vcbNmRtDirs )
1789				return ( IntError( GPtr, R_IntErr ) );
1790			calculatedVCB->vcbNmRtDirs = (UInt16)p->correct;
1791			GPtr->VIStat |= S_MDB;
1792			break;
1793
1794		case E_RtFilCnt:
1795			if ( (UInt16)p->incorrect != calculatedVCB->vcbNmFls )
1796				return ( IntError( GPtr, R_IntErr ) );
1797			calculatedVCB->vcbNmFls = (UInt16)p->correct;
1798			GPtr->VIStat |= S_MDB;
1799			break;
1800
1801		case E_DirCnt:
1802			if ( (UInt32)p->incorrect != calculatedVCB->vcbFolderCount )
1803				return ( IntError( GPtr, R_IntErr ) );
1804			calculatedVCB->vcbFolderCount = (UInt32)p->correct;
1805			GPtr->VIStat |= S_MDB;
1806			break;
1807
1808		case E_FilCnt:
1809			if ( (UInt32)p->incorrect != calculatedVCB->vcbFileCount )
1810				return ( IntError( GPtr, R_IntErr ) );
1811			calculatedVCB->vcbFileCount = (UInt32)p->correct;
1812			GPtr->VIStat |= S_MDB;
1813			break;
1814
1815		case E_DirVal:
1816			BuildCatalogKey( p->parid, (CatalogName *)&p->name, isHFSPlus, &key );
1817			result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint,
1818					&foundKey, &record, &recSize, &hint );
1819			if ( result )
1820				return ( IntError( GPtr, result ) );
1821
1822			if ( record.recordType == kHFSPlusFolderRecord )
1823			{
1824				if ( (UInt32)p->incorrect != record.hfsPlusFolder.valence)
1825					return ( IntError( GPtr, R_IntErr ) );
1826				record.hfsPlusFolder.valence = (UInt32)p->correct;
1827			}
1828			else
1829			{
1830				if ( (UInt16)p->incorrect != record.hfsFolder.valence )
1831					return ( IntError( GPtr, R_IntErr ) );
1832				record.hfsFolder.valence = (UInt16)p->correct;
1833			}
1834
1835			result = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &key, hint,\
1836						&record, recSize, &hint );
1837			if ( result )
1838				return ( IntError( GPtr, result ) );
1839			break;
1840	}
1841
1842	return( noErr );														//	no error
1843}
1844
1845/*------------------------------------------------------------------------------
1846
1847Routine:	FixFinderFlags
1848
1849Function:	Changes some of the Finder flag bits for directories.
1850
1851Input:		GPtr			-	pointer to scavenger global area
1852			p				- 	pointer to the repair order
1853
1854Output:		FixFinderFlags	- 	function result:
1855									0 = no error
1856									n = error
1857------------------------------------------------------------------------------*/
1858
1859static	OSErr	FixFinderFlags( SGlobPtr GPtr, RepairOrderPtr p )				//	the repair order
1860{
1861	CatalogRecord		record;
1862	CatalogKey			foundKey;
1863	CatalogKey			key;
1864	UInt32				hint;												//	as returned by CBTSearch
1865	OSErr				result;												//	status return
1866	UInt16				recSize;
1867	Boolean				isHFSPlus;
1868
1869	isHFSPlus = VolumeObjectIsHFSPlus( );
1870
1871	BuildCatalogKey( p->parid, (CatalogName *)&p->name, isHFSPlus, &key );
1872
1873	result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, &foundKey, &record, &recSize, &hint );
1874	if ( result )
1875		return ( IntError( GPtr, result ) );
1876
1877	if ( record.recordType == kHFSPlusFolderRecord )
1878	{
1879		HFSPlusCatalogFolder	*largeCatalogFolderP	= (HFSPlusCatalogFolder *) &record;
1880		if ( (UInt16) p->incorrect != largeCatalogFolderP->userInfo.frFlags)
1881		{
1882			//	Another repair order may have affected the flags
1883			if ( p->correct < p->incorrect )
1884				largeCatalogFolderP->userInfo.frFlags &= ~((UInt16)p->maskBit);
1885			else
1886				largeCatalogFolderP->userInfo.frFlags |= (UInt16)p->maskBit;
1887		}
1888		else
1889		{
1890			largeCatalogFolderP->userInfo.frFlags = (UInt16)p->correct;
1891		}
1892	//	largeCatalogFolderP->contentModDate = timeStamp;
1893	}
1894	else
1895	{
1896		HFSCatalogFolder	*smallCatalogFolderP	= (HFSCatalogFolder *) &record;
1897		if ( p->incorrect != smallCatalogFolderP->userInfo.frFlags)		//	do we know what we're doing?
1898		{
1899			//	Another repair order may have affected the flags
1900			if ( p->correct < p->incorrect )
1901				smallCatalogFolderP->userInfo.frFlags &= ~((UInt16)p->maskBit);
1902			else
1903				smallCatalogFolderP->userInfo.frFlags |= (UInt16)p->maskBit;
1904		}
1905		else
1906		{
1907			smallCatalogFolderP->userInfo.frFlags = (UInt16)p->correct;
1908		}
1909
1910	//	smallCatalogFolderP->modifyDate = timeStamp;						// also update the modify date! -DJB
1911	}
1912
1913	result = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recSize, &hint );	//	write the node back to the file
1914	if ( result )
1915		return( IntError( GPtr, result ) );
1916
1917	return( noErr );														//	no error
1918}
1919
1920/*------------------------------------------------------------------------------
1921FixHardLinkFinderInfo:  Set the Finder Info contents to be correct for the type
1922	of hard link (directory or file).
1923	(HFS+ volumes only)
1924------------------------------------------------------------------------------*/
1925static OSErr FixHardLinkFinderInfo(SGlobPtr GPtr, RepairOrderPtr p)
1926{
1927	CatalogRecord rec;
1928	CatalogKey key;
1929	uint16_t recsize;
1930	OSErr retval = 0;
1931	UInt32 hint;
1932
1933	retval = GetCatalogRecordByID(GPtr, (UInt32)p->parid, true, &key, &rec, &recsize);
1934	if (retval != 0) {
1935		if (retval == btNotFound) {
1936			/* If the record was not found because either the thread
1937			 * record is missing or the file/folder record was deleted by
1938			 * another repair order, return false success to retry again
1939			 * after thread repair code.
1940			 */
1941		 	GPtr->minorRepairFalseSuccess = true;
1942			retval = 0;
1943		}
1944		goto done;
1945	}
1946
1947	if (rec.recordType != kHFSPlusFileRecord) {
1948		retval = IntError(GPtr, R_IntErr);
1949		goto done;
1950	}
1951
1952	if (p->type == E_DirHardLinkFinderInfo) {
1953		rec.hfsPlusFile.userInfo.fdType = kHFSAliasType;
1954		rec.hfsPlusFile.userInfo.fdCreator = kHFSAliasCreator;
1955		rec.hfsPlusFile.userInfo.fdFlags |= kIsAlias;
1956	} else if (p->type == E_FileHardLinkFinderInfo) {
1957		rec.hfsPlusFile.userInfo.fdType = kHardLinkFileType;
1958		rec.hfsPlusFile.userInfo.fdCreator = kHFSPlusCreator;
1959	} else {
1960		retval = IntError(GPtr, R_IntErr);
1961		goto done;
1962	}
1963
1964	retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key, kNoHint, &rec, recsize, &hint);
1965
1966done:
1967	return retval;
1968}
1969
1970/*------------------------------------------------------------------------------
1971FixLinkChainNext:  Adjust the link chain in a hard link
1972               (HFS Plus volumes only)
1973------------------------------------------------------------------------------*/
1974static OSErr
1975FixLinkChainPrev(SGlobPtr GPtr, RepairOrderPtr p)
1976{
1977	SFCB *fcb;
1978	CatalogRecord rec;
1979	CatalogKey key, foundKey;
1980	UInt16 recSize;
1981	OSErr result;
1982	Boolean				isHFSPlus;
1983	UInt32 hint = 0;
1984
1985	isHFSPlus = VolumeObjectIsHFSPlus( );
1986	if (!isHFSPlus)
1987		return (0);
1988	fcb = GPtr->calculatedCatalogFCB;
1989
1990	BuildCatalogKey( p->parid, NULL, true, &key );
1991	result = SearchBTreeRecord( fcb, &key, kNoHint, &foundKey, &rec, &recSize, &hint );
1992
1993	if (result) {
1994		if (result == btNotFound) {
1995			/* If the record was not found because either the thread
1996			 * record is missing or the file/folder record was deleted by
1997			 * another repair order, return false success to retry again
1998			 * after thread repair code.
1999			 */
2000		 	GPtr->minorRepairFalseSuccess = true;
2001			return 0;
2002		} else {
2003			return IntError(GPtr, R_IntErr);
2004		}
2005	}
2006
2007	if (rec.recordType != kHFSPlusFileThreadRecord) {
2008		return IntError(GPtr, R_IntErr);
2009	}
2010
2011	BuildCatalogKey( rec.hfsPlusThread.parentID, (const CatalogName*)&rec.hfsPlusThread.nodeName, true, &key);
2012	result = SearchBTreeRecord( fcb, &key, kNoHint, &foundKey, &rec, &recSize, &hint);
2013
2014	if (result) {
2015		return IntError(GPtr, R_IntErr);
2016	}
2017
2018	if (rec.recordType != kHFSPlusFileRecord) {
2019		return IntError(GPtr, R_IntErr);
2020	}
2021
2022	if ((UInt32)p->incorrect != rec.hfsPlusFile.hl_prevLinkID) {
2023		return IntError(GPtr, R_IntErr);
2024	}
2025
2026	rec.hfsPlusFile.hl_prevLinkID = (UInt32)p->correct;
2027	result = ReplaceBTreeRecord(fcb, &foundKey, hint, &rec, recSize, &hint);
2028	if (result) {
2029		return IntError(GPtr, R_IntErr);
2030	}
2031
2032	return noErr;
2033}
2034
2035/*------------------------------------------------------------------------------
2036FixLinkChainNext:  Adjust the link chain in a hard link
2037               (HFS Plus volumes only)
2038------------------------------------------------------------------------------*/
2039static OSErr
2040FixLinkChainNext(SGlobPtr GPtr, RepairOrderPtr p)
2041{
2042	SFCB *fcb;
2043	CatalogRecord rec;
2044	CatalogKey key, foundKey;
2045	UInt16 recSize;
2046	OSErr result;
2047	Boolean				isHFSPlus;
2048	UInt32 hint = 0;
2049
2050	isHFSPlus = VolumeObjectIsHFSPlus( );
2051	if (!isHFSPlus)
2052		return (0);
2053	fcb = GPtr->calculatedCatalogFCB;
2054
2055	BuildCatalogKey( p->parid, NULL, true, &key );
2056	result = SearchBTreeRecord( fcb, &key, kNoHint, &foundKey, &rec, &recSize, &hint );
2057
2058	if (result) {
2059		if (result == btNotFound) {
2060			/* If the record was not found because either the thread
2061			 * record is missing or the file/folder record was deleted by
2062			 * another repair order, return false success to retry again
2063			 * after thread repair code.
2064			 */
2065		 	GPtr->minorRepairFalseSuccess = true;
2066			return 0;
2067		} else {
2068			return IntError(GPtr, R_IntErr);
2069		}
2070	}
2071
2072	if (rec.recordType != kHFSPlusFileThreadRecord) {
2073		return IntError(GPtr, R_IntErr);
2074	}
2075
2076	BuildCatalogKey( rec.hfsPlusThread.parentID, (const CatalogName*)&rec.hfsPlusThread.nodeName, true, &key);
2077	result = SearchBTreeRecord( fcb, &key, kNoHint, &foundKey, &rec, &recSize, &hint);
2078
2079	if (result) {
2080		return IntError(GPtr, R_IntErr);
2081	}
2082
2083	if (rec.recordType != kHFSPlusFileRecord) {
2084		return IntError(GPtr, R_IntErr);
2085	}
2086
2087	if ((UInt32)p->incorrect != rec.hfsPlusFile.hl_nextLinkID) {
2088		return IntError(GPtr, R_IntErr);
2089	}
2090
2091	rec.hfsPlusFile.hl_nextLinkID = (UInt32)p->correct;
2092	result = ReplaceBTreeRecord(fcb, &foundKey, hint, &rec, recSize, &hint);
2093	if (result) {
2094		return IntError(GPtr, R_IntErr);
2095	}
2096
2097	return noErr;
2098}
2099
2100/*------------------------------------------------------------------------------
2101FixLinkCount:  Adjust a data node link count (BSD hard link)
2102               (HFS Plus volumes only)
2103------------------------------------------------------------------------------*/
2104static OSErr
2105FixLinkCount(SGlobPtr GPtr, RepairOrderPtr p)
2106{
2107	CatalogRecord rec;
2108	CatalogKey key;
2109	OSErr result;
2110	UInt16 recSize;
2111	UInt32 hint;
2112	Boolean	isHFSPlus;
2113	Boolean	isdir = 0;
2114	int lc;	// linkcount
2115
2116	isHFSPlus = VolumeObjectIsHFSPlus( );
2117	if (!isHFSPlus)
2118		return (0);
2119
2120	result = GetCatalogRecordByID(GPtr, p->parid, isHFSPlus, &key, &rec, &recSize);
2121	if (result) {
2122		if (result == btNotFound) {
2123			/* If the record was not found because either the thread
2124			 * record is missing or the file/folder record was deleted by
2125			 * another repair order, return false success to retry again
2126			 * after thread repair code.
2127			 */
2128		 	GPtr->minorRepairFalseSuccess = true;
2129			result = 0;
2130		}
2131		return result;
2132	}
2133
2134	if (rec.recordType != kHFSPlusFileRecord && rec.recordType != kHFSPlusFolderRecord)
2135		return (noErr);
2136
2137	isdir = (rec.recordType == kHFSPlusFolderRecord);
2138
2139	lc = (isdir ? rec.hfsPlusFolder.bsdInfo.special.linkCount : rec.hfsPlusFile.bsdInfo.special.linkCount);
2140	if ((UInt32)p->correct != lc) {
2141		if (isdir)
2142			rec.hfsPlusFolder.bsdInfo.special.linkCount = (UInt32)p->correct;
2143		else
2144			rec.hfsPlusFile.bsdInfo.special.linkCount = (UInt32)p->correct;
2145
2146		result = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key,
2147				kNoHint, &rec, recSize, &hint);
2148		if (result)
2149			return (IntError(GPtr, result));
2150	}
2151
2152	return (noErr);
2153}
2154
2155
2156/*------------------------------------------------------------------------------
2157FixIllegalNames:  Fix catalog enties that have illegal names.
2158    RepairOrder.name[] holds the old (illegal) name followed by the new name.
2159    The new name has been checked to make sure it is unique within the target
2160    directory.  The names will look like this:
2161        2 byte length of old name (in unicode characters not bytes)
2162        unicode characters for old name
2163        2 byte length of new name (in unicode characters not bytes)
2164        unicode characters for new name
2165------------------------------------------------------------------------------*/
2166static OSErr
2167FixIllegalNames( SGlobPtr GPtr, RepairOrderPtr roPtr )
2168{
2169	OSErr 				result;
2170	Boolean				isHFSPlus;
2171	Boolean				isDirectory;
2172	UInt16				recSize;
2173	SFCB *				fcbPtr;
2174    CatalogName *		oldNamePtr;
2175    CatalogName *		newNamePtr;
2176	UInt32				hint;
2177	CatalogRecord		record;
2178	CatalogKey			key;
2179	CatalogKey			newKey;
2180
2181	isHFSPlus = VolumeObjectIsHFSPlus( );
2182 	fcbPtr = GPtr->calculatedCatalogFCB;
2183
2184	oldNamePtr = (CatalogName *) &roPtr->name;
2185    if ( isHFSPlus )
2186    {
2187        int					myLength;
2188        u_int16_t *			myPtr = (u_int16_t *) oldNamePtr;
2189        myLength = *myPtr; // get length of old name
2190        myPtr += (1 + myLength);  // bump past length of old name and old name
2191        newNamePtr = (CatalogName *) myPtr;
2192    }
2193    else
2194    {
2195        int					myLength;
2196        u_char *			myPtr = (u_char *) oldNamePtr;
2197        myLength = *myPtr; // get length of old name
2198        myPtr += (1 + myLength);  // bump past length of old name and old name
2199        newNamePtr = (CatalogName *) myPtr;
2200    }
2201
2202	// make sure new name isn't already there
2203	BuildCatalogKey( roPtr->parid, newNamePtr, isHFSPlus, &newKey );
2204	result = SearchBTreeRecord( fcbPtr, &newKey, kNoHint, NULL, &record, &recSize, NULL );
2205	if ( result == noErr ) {
2206		if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
2207        	plog( "\treplacement name already exists \n" );
2208			plog( "\tduplicate name is 0x" );
2209			PrintName( newNamePtr->ustr.length, (UInt8 *) &newNamePtr->ustr.unicode, true );
2210		}
2211        goto ErrorExit;
2212    }
2213
2214    // get catalog record for object with the illegal name.  We will restore this
2215    // info with our new (valid) name.
2216	BuildCatalogKey( roPtr->parid, oldNamePtr, isHFSPlus, &key );
2217	result = SearchBTreeRecord( fcbPtr, &key, kNoHint, NULL, &record, &recSize, &hint );
2218	if ( result != noErr ) {
2219        goto ErrorExit;
2220    }
2221
2222    result	= DeleteBTreeRecord( fcbPtr, &key );
2223	if ( result != noErr ) {
2224        goto ErrorExit;
2225    }
2226
2227    // insert record back into the catalog using the new name
2228    result	= InsertBTreeRecord( fcbPtr, &newKey, &record, recSize, &hint );
2229	if ( result != noErr ) {
2230        goto ErrorExit;
2231    }
2232
2233	isDirectory = false;
2234    switch( record.recordType )
2235    {
2236        case kHFSFolderRecord:
2237        case kHFSPlusFolderRecord:
2238			isDirectory = true;	 break;
2239    }
2240
2241    // now we need to remove the old thread record and create a new one with
2242    // our new name.
2243	BuildCatalogKey( GetObjectID( &record ), NULL, isHFSPlus, &key );
2244	result = SearchBTreeRecord( fcbPtr, &key, kNoHint, NULL, &record, &recSize, &hint );
2245	if ( result != noErr ) {
2246        goto ErrorExit;
2247    }
2248
2249    result	= DeleteBTreeRecord( fcbPtr, &key );
2250	if ( result != noErr ) {
2251        goto ErrorExit;
2252    }
2253
2254    // insert thread record with new name as thread data
2255	recSize = BuildThreadRec( &newKey, &record, isHFSPlus, isDirectory );
2256 	result = InsertBTreeRecord( fcbPtr, &key, &record, recSize, &hint );
2257	if ( result != noErr ) {
2258        goto ErrorExit;
2259	}
2260
2261    return( noErr );
2262
2263ErrorExit:
2264	GPtr->minorRepairErrors = true;
2265    if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
2266       	plog( "\t%s - repair failed for type 0x%02X %d \n", __FUNCTION__, roPtr->type, roPtr->type );
2267    return( noErr );  // errors in this routine should not be fatal
2268
2269} /* FixIllegalNames */
2270
2271
2272/*------------------------------------------------------------------------------
2273FixBSDInfo:  Reset or repair BSD info
2274                 (HFS Plus volumes only)
2275------------------------------------------------------------------------------*/
2276static OSErr
2277FixBSDInfo(SGlobPtr GPtr, RepairOrderPtr p)
2278{
2279	SFCB 						*fcb;
2280	CatalogRecord 				rec;
2281	FSBufferDescriptor 			btRecord;
2282	BTreeIterator 				btIterator;
2283	Boolean						isHFSPlus;
2284	OSErr 						result;
2285	UInt16 						recSize;
2286	size_t 						namelen;
2287	unsigned char 				filename[256];
2288
2289	isHFSPlus = VolumeObjectIsHFSPlus( );
2290	if (!isHFSPlus)
2291		return (0);
2292	fcb = GPtr->calculatedCatalogFCB;
2293
2294	ClearMemory(&btIterator, sizeof(btIterator));
2295	btIterator.hint.nodeNum = p->hint;
2296	BuildCatalogKey(p->parid, (CatalogName *)&p->name, true,
2297		(CatalogKey*)&btIterator.key);
2298	btRecord.bufferAddress = &rec;
2299	btRecord.itemCount = 1;
2300	btRecord.itemSize = sizeof(rec);
2301
2302	result = BTSearchRecord(fcb, &btIterator, kInvalidMRUCacheKey,
2303			&btRecord, &recSize, &btIterator);
2304	if (result) {
2305		return (IntError(GPtr, result));
2306	}
2307	if (rec.recordType != kHFSPlusFileRecord &&
2308	    rec.recordType != kHFSPlusFolderRecord)
2309		return (noErr);
2310
2311	utf_encodestr(((HFSUniStr255 *)&p->name)->unicode,
2312		((HFSUniStr255 *)&p->name)->length << 1, filename, &namelen, sizeof(filename));
2313	filename[namelen] = '\0';
2314
2315	if (p->type == E_InvalidPermissions &&
2316	    ((UInt16)p->incorrect == rec.hfsPlusFile.bsdInfo.fileMode)) {
2317		if (fsckGetVerbosity(GPtr->context) >= kDebugLog)
2318		   	plog("\t\"%s\": fixing mode from %07o to %07o\n",
2319			   filename, (int)p->incorrect, (int)p->correct);
2320
2321		rec.hfsPlusFile.bsdInfo.fileMode = (UInt16)p->correct;
2322		result = BTReplaceRecord(fcb, &btIterator, &btRecord, recSize);
2323	}
2324
2325	if (result)
2326		return (IntError(GPtr, result));
2327	else
2328		return (noErr);
2329}
2330
2331
2332/*------------------------------------------------------------------------------
2333DeleteUnlinkedFile:  Delete orphaned data node (BSD unlinked file)
2334		     Also used to delete empty "HFS+ Private Data" directories
2335                     (HFS Plus volumes only)
2336------------------------------------------------------------------------------*/
2337static OSErr
2338DeleteUnlinkedFile(SGlobPtr GPtr, RepairOrderPtr p)
2339{
2340	OSErr				result = -1;
2341	CatalogName 		name;
2342	CatalogName 		*cNameP;
2343	Boolean				isHFSPlus;
2344	size_t 				len;
2345	CatalogKey			key;
2346	CatalogRecord		record;
2347	uint32_t			id = 0;
2348	UInt16				recSize;
2349	UInt32				hint;
2350
2351	isHFSPlus = VolumeObjectIsHFSPlus( );
2352	if (!isHFSPlus)
2353		return (0);
2354
2355	if (p->name[0] > 0) {
2356		/* name was stored in UTF-8 */
2357		(void) utf_decodestr(&p->name[1], p->name[0], name.ustr.unicode, &len, sizeof(name.ustr.unicode));
2358		name.ustr.length = len / 2;
2359		cNameP = &name;
2360	} else {
2361		cNameP = NULL;
2362		goto out;
2363	}
2364
2365	/* Lookup the record to find out file/folder ID for attribute deletion */
2366	BuildCatalogKey(p->parid, cNameP, true, &key);
2367	result = SearchBTreeRecord (GPtr->calculatedCatalogFCB, &key, kNoHint,
2368				NULL, &record, &recSize, &hint);
2369	if (result) {
2370		if (result == btNotFound) {
2371			result = 0;
2372		}
2373		goto out;
2374	}
2375
2376	result = DeleteCatalogNode(GPtr->calculatedVCB, p->parid, cNameP, p->hint, false);
2377	if (result) {
2378		goto out;
2379	}
2380
2381	GPtr->VIStat |= S_MDB;
2382	GPtr->VIStat |= S_VBM;
2383
2384	if (record.recordType == kHFSPlusFileRecord) {
2385		id = record.hfsPlusFile.fileID;
2386	} else if (record.recordType == kHFSPlusFolderRecord) {
2387		id = record.hfsPlusFolder.folderID;
2388	}
2389
2390	/* Delete all extended attributes associated with this file/folder */
2391	result = DeleteAllAttrsByID(GPtr, id);
2392
2393out:
2394	return result;
2395}
2396
2397/*
2398 * Fix a file's PEOF or LEOF (truncate)
2399 * (HFS Plus volumes only)
2400 */
2401static OSErr
2402FixFileSize(SGlobPtr GPtr, RepairOrderPtr p)
2403{
2404	SFCB *fcb;
2405	CatalogRecord rec;
2406	HFSPlusCatalogKey * keyp;
2407	FSBufferDescriptor btRecord;
2408	BTreeIterator btIterator;
2409	size_t len;
2410	Boolean	isHFSPlus;
2411	Boolean replace;
2412	OSErr result;
2413	UInt16 recSize;
2414	UInt64 bytes;
2415
2416	isHFSPlus = VolumeObjectIsHFSPlus( );
2417	if (!isHFSPlus)
2418		return (0);
2419	fcb = GPtr->calculatedCatalogFCB;
2420	replace = false;
2421
2422	ClearMemory(&btIterator, sizeof(btIterator));
2423	btIterator.hint.nodeNum = p->hint;
2424	keyp = (HFSPlusCatalogKey*)&btIterator.key;
2425	keyp->parentID = p->parid;
2426
2427	/* name was stored in UTF-8 */
2428	(void) utf_decodestr(&p->name[1], p->name[0], keyp->nodeName.unicode, &len, sizeof(keyp->nodeName.unicode));
2429	keyp->nodeName.length = len / 2;
2430	keyp->keyLength = kHFSPlusCatalogKeyMinimumLength + len;
2431
2432	btRecord.bufferAddress = &rec;
2433	btRecord.itemCount = 1;
2434	btRecord.itemSize = sizeof(rec);
2435
2436	result = BTSearchRecord(fcb, &btIterator, kInvalidMRUCacheKey,
2437			&btRecord, &recSize, &btIterator);
2438	if (result)
2439		return (IntError(GPtr, result));
2440
2441	if (rec.recordType != kHFSPlusFileRecord)
2442		return (noErr);
2443
2444	if (p->type == E_PEOF) {
2445		bytes = p->correct * (UInt64)GPtr->calculatedVCB->vcbBlockSize;
2446		if ((p->forkType == kRsrcFork) &&
2447		    ((UInt32)p->incorrect == rec.hfsPlusFile.resourceFork.totalBlocks)) {
2448
2449			rec.hfsPlusFile.resourceFork.totalBlocks = (UInt32)p->correct;
2450			replace = true;
2451			/*
2452			 * Make sure our new block count is large
2453			 * enough to cover the current LEOF.  If
2454			 * its not we must truncate the fork.
2455			 */
2456			if (rec.hfsPlusFile.resourceFork.logicalSize > bytes) {
2457				rec.hfsPlusFile.resourceFork.logicalSize = bytes;
2458			}
2459		} else if ((p->forkType == kDataFork) &&
2460		           ((UInt32)p->incorrect == rec.hfsPlusFile.dataFork.totalBlocks)) {
2461
2462			rec.hfsPlusFile.dataFork.totalBlocks = (UInt32)p->correct;
2463			replace = true;
2464			/*
2465			 * Make sure our new block count is large
2466			 * enough to cover the current LEOF.  If
2467			 * its not we must truncate the fork.
2468			 */
2469			if (rec.hfsPlusFile.dataFork.logicalSize > bytes) {
2470				rec.hfsPlusFile.dataFork.logicalSize = bytes;
2471			}
2472		}
2473	} else /* E_LEOF */ {
2474		if ((p->forkType == kRsrcFork) &&
2475		    (p->incorrect == rec.hfsPlusFile.resourceFork.logicalSize)) {
2476
2477			rec.hfsPlusFile.resourceFork.logicalSize = p->correct;
2478			replace = true;
2479
2480		} else if ((p->forkType == kDataFork) &&
2481		           (p->incorrect == rec.hfsPlusFile.dataFork.logicalSize)) {
2482
2483			rec.hfsPlusFile.dataFork.logicalSize = p->correct;
2484			replace = true;
2485		}
2486	}
2487
2488	if (replace) {
2489		result = BTReplaceRecord(fcb, &btIterator, &btRecord, recSize);
2490		if (result)
2491			return (IntError(GPtr, result));
2492	}
2493
2494	return (noErr);
2495}
2496
2497/*------------------------------------------------------------------------------
2498
2499Routine:	FixEmbededVolDescription
2500
2501Function:	If the "mdb->drAlBlSt" field has been modified, i.e. Norton Disk Doctor
2502			3.5 tried to "Fix" an HFS+ volume, it reduces the value in the
2503			"mdb->drAlBlSt" field.  If this field is changed, the file system can
2504			no longer find the VolumeHeader or AltVolumeHeader.
2505
2506Input:		GPtr			-	pointer to scavenger global area
2507			p				- 	pointer to the repair order
2508
2509Output:		FixMDBdrAlBlSt	- 	function result:
2510									0 = no error
2511									n = error
2512------------------------------------------------------------------------------*/
2513
2514static	OSErr	FixEmbededVolDescription( SGlobPtr GPtr, RepairOrderPtr p )
2515{
2516	OSErr					err;
2517	HFSMasterDirectoryBlock	*mdb;
2518	EmbededVolDescription	*desc;
2519	SVCB					*vcb = GPtr->calculatedVCB;
2520	BlockDescriptor  		block;
2521
2522	desc = (EmbededVolDescription *) &(p->name);
2523	block.buffer = NULL;
2524
2525	/* Fix the Alternate MDB */
2526	err = GetVolumeObjectAlternateMDB( &block );
2527	if ( err != noErr )
2528		goto ExitThisRoutine;
2529	mdb = (HFSMasterDirectoryBlock *) block.buffer;
2530
2531	mdb->drAlBlSt			= desc->drAlBlSt;
2532	mdb->drEmbedSigWord		= desc->drEmbedSigWord;
2533	mdb->drEmbedExtent.startBlock	= desc->drEmbedExtent.startBlock;
2534	mdb->drEmbedExtent.blockCount	= desc->drEmbedExtent.blockCount;
2535
2536	err = ReleaseVolumeBlock( vcb, &block, kForceWriteBlock );
2537	block.buffer = NULL;
2538	if ( err != noErr )
2539		goto ExitThisRoutine;
2540
2541	/* Fix the MDB */
2542	err = GetVolumeObjectPrimaryMDB( &block );
2543	if ( err != noErr )
2544		goto ExitThisRoutine;
2545	mdb = (HFSMasterDirectoryBlock *) block.buffer;
2546
2547	mdb->drAlBlSt			= desc->drAlBlSt;
2548	mdb->drEmbedSigWord		= desc->drEmbedSigWord;
2549	mdb->drEmbedExtent.startBlock	= desc->drEmbedExtent.startBlock;
2550	mdb->drEmbedExtent.blockCount	= desc->drEmbedExtent.blockCount;
2551	err = ReleaseVolumeBlock( vcb, &block, kForceWriteBlock );
2552	block.buffer = NULL;
2553
2554ExitThisRoutine:
2555	if ( block.buffer != NULL )
2556		err = ReleaseVolumeBlock( vcb, &block, kReleaseBlock );
2557
2558	return( err );
2559}
2560
2561
2562
2563
2564/*------------------------------------------------------------------------------
2565
2566Routine:	FixWrapperExtents
2567
2568Function:	When Norton Disk Doctor 2.0 tries to repair an HFS Plus volume, it
2569			assumes that the first catalog extent must be a fixed number of
2570			allocation blocks after the start of the first extents extent (in the
2571			wrapper).  In reality, the first catalog extent should start immediately
2572			after the first extents extent.
2573
2574Input:		GPtr			-	pointer to scavenger global area
2575			p				- 	pointer to the repair order
2576
2577Output:
2578			0 = no error
2579			n = error
2580------------------------------------------------------------------------------*/
2581
2582static	OSErr	FixWrapperExtents( SGlobPtr GPtr, RepairOrderPtr p )
2583{
2584#pragma unused (p)
2585
2586	OSErr						err;
2587	HFSMasterDirectoryBlock		*mdb;
2588	SVCB						*vcb = GPtr->calculatedVCB;
2589	BlockDescriptor  			block;
2590
2591	/* Get the Alternate MDB */
2592	block.buffer = NULL;
2593	err = GetVolumeObjectAlternateMDB( &block );
2594	if ( err != noErr )
2595		goto ExitThisRoutine;
2596	mdb = (HFSMasterDirectoryBlock	*) block.buffer;
2597
2598	/* Fix the wrapper catalog's first (and only) extent */
2599	mdb->drCTExtRec[0].startBlock = mdb->drXTExtRec[0].startBlock +
2600	                                mdb->drXTExtRec[0].blockCount;
2601
2602	err = ReleaseVolumeBlock(vcb, &block, kForceWriteBlock);
2603	block.buffer = NULL;
2604	if ( err != noErr )
2605		goto ExitThisRoutine;
2606
2607	/* Fix the MDB */
2608	err = GetVolumeObjectPrimaryMDB( &block );
2609	if ( err != noErr )
2610		goto ExitThisRoutine;
2611	mdb = (HFSMasterDirectoryBlock	*) block.buffer;
2612
2613	mdb->drCTExtRec[0].startBlock = mdb->drXTExtRec[0].startBlock +
2614	                                mdb->drXTExtRec[0].blockCount;
2615
2616	err = ReleaseVolumeBlock(vcb, &block, kForceWriteBlock);
2617	block.buffer = NULL;
2618
2619ExitThisRoutine:
2620	if ( block.buffer != NULL )
2621		(void) ReleaseVolumeBlock( vcb, &block, kReleaseBlock );
2622
2623	return( err );
2624}
2625
2626
2627//
2628//	Entries in the extents BTree which do not have a corresponding catalog entry get fixed here
2629//	This routine will run slowly if the extents file is large because we require a Catalog BTree
2630//	search for each extent record.
2631//
2632static	OSErr	FixOrphanedExtent( SGlobPtr GPtr )
2633{
2634#if 0
2635	OSErr				err;
2636	UInt32				hint;
2637	UInt32				recordSize;
2638	UInt32				maxRecords;
2639	UInt32				numberOfFilesInList;
2640	ExtentKey			*extentKeyPtr;
2641	ExtentRecord		*extentDataPtr;
2642	ExtentRecord		extents;
2643	ExtentRecord		zeroExtents;
2644	CatalogKey			foundExtentKey;
2645	CatalogRecord		catalogData;
2646	CatalogRecord		threadData;
2647	HFSCatalogNodeID	fileID;
2648	BTScanState			scanState;
2649
2650	HFSCatalogNodeID	lastFileID			= -1;
2651	UInt32			recordsFound		= 0;
2652	Boolean			mustRebuildBTree	= false;
2653	Boolean			isHFSPlus;
2654	SVCB			*calculatedVCB		= GPtr->calculatedVCB;
2655	UInt32			**dataHandle		= GPtr->validFilesList;
2656	SFCB *			fcb = GPtr->calculatedExtentsFCB;
2657
2658	//	Set Up
2659	isHFSPlus = VolumeObjectIsHFSPlus( );
2660	//
2661	//	Use the BTree scanner since we use MountCheck to find orphaned extents, and MountCheck uses the scanner
2662	err = BTScanInitialize( fcb, 0, 0, 0, gFSBufferPtr, gFSBufferSize, &scanState );
2663	if ( err != noErr )	return( badMDBErr );
2664
2665	ClearMemory( (Ptr)&zeroExtents, sizeof(ExtentRecord) );
2666
2667	if ( isHFSPlus )
2668	{
2669		maxRecords = fcb->fcbLogicalSize / sizeof(HFSPlusExtentRecord);
2670	}
2671	else
2672	{
2673		maxRecords = fcb->fcbLogicalSize / sizeof(HFSExtentRecord);
2674		numberOfFilesInList = GetHandleSize((Handle) dataHandle) / sizeof(UInt32);
2675		qsort( *dataHandle, numberOfFilesInList, sizeof (UInt32), cmpLongs );	// Sort the list of found file IDs
2676	}
2677
2678
2679	while ( recordsFound < maxRecords )
2680	{
2681		err = BTScanNextRecord( &scanState, false, (void **) &extentKeyPtr, (void **) &extentDataPtr, &recordSize );
2682
2683		if ( err != noErr )
2684		{
2685			if ( err == btNotFound )
2686				err	= noErr;
2687			break;
2688		}
2689
2690		++recordsFound;
2691		fileID = (isHFSPlus == true) ? extentKeyPtr->hfsPlus.fileID : extentKeyPtr->hfs.fileID;
2692
2693		if ( (fileID > kHFSBadBlockFileID) && (lastFileID != fileID) )	// Keep us out of reserved file trouble
2694		{
2695			lastFileID	= fileID;
2696
2697			if ( isHFSPlus )
2698			{
2699				err = LocateCatalogThread( calculatedVCB, fileID, &threadData, (UInt16*)&recordSize, &hint );	//	This call returns nodeName as either Str31 or HFSUniStr255, no need to call PrepareInputName()
2700
2701				if ( err == noErr )									//	Thread is found, just verify actual record exists.
2702				{
2703					err = LocateCatalogNode( calculatedVCB, threadData.hfsPlusThread.parentID, (const CatalogName *) &(threadData.hfsPlusThread.nodeName), kNoHint, &foundExtentKey, &catalogData, &hint );
2704				}
2705				else if ( err == cmNotFound )
2706				{
2707					err = SearchBTreeRecord( GPtr->calculatedExtentsFCB, extentKeyPtr, kNoHint, &foundExtentKey, &extents, (UInt16*)&recordSize, &hint );
2708					if ( err == noErr )
2709					{	//�� can't we just delete btree record?
2710						err = ReplaceBTreeRecord( GPtr->calculatedExtentsFCB, &foundExtentKey, hint, &zeroExtents, recordSize, &hint );
2711						err	= DeleteBTreeRecord( GPtr->calculatedExtentsFCB, &foundExtentKey );	//	Delete the orphaned extent
2712					}
2713				}
2714
2715				if ( err != noErr )
2716					mustRebuildBTree	= true;						//	if we have errors here we should rebuild the extents btree
2717			}
2718			else
2719			{
2720				if ( ! bsearch( &fileID, *dataHandle, numberOfFilesInList, sizeof(UInt32), cmpLongs ) )
2721				{
2722					err = SearchBTreeRecord( GPtr->calculatedExtentsFCB, extentKeyPtr, kNoHint, &foundExtentKey, &extents, (UInt16*)&recordSize, &hint );
2723					if ( err == noErr )
2724					{	//�� can't we just delete btree record?
2725						err = ReplaceBTreeRecord( GPtr->calculatedExtentsFCB, &foundExtentKey, hint, &zeroExtents, recordSize, &hint );
2726						err = DeleteBTreeRecord( GPtr->calculatedExtentsFCB, &foundExtentKey );	//	Delete the orphaned extent
2727					}
2728
2729					if ( err != noErr )
2730						mustRebuildBTree	= true;						//	if we have errors here we should rebuild the extents btree
2731				}
2732			}
2733		}
2734	}
2735
2736	if ( mustRebuildBTree == true )
2737	{
2738		GPtr->EBTStat |= S_RebuildBTree;
2739		err	= errRebuildBtree;
2740	}
2741
2742	return( err );
2743#else
2744	return (0);
2745#endif
2746}
2747
2748/* Function: FixAttrSize
2749 *
2750 * Description:
2751 * Fixes the incorrect block count or attribute size for extended attribute
2752 * detected during verify stage.  This is a minor repair order function
2753 * i.e. it depends on previously created repair order to repair the disk.
2754 *
2755 * Input:
2756 *	GPtr - global scavenger structure pointer
2757 *	p - current repair order
2758 *
2759 * Output:
2760 *	result - zero indicates success, non-zero failure.
2761 */
2762static OSErr FixAttrSize(SGlobPtr GPtr, RepairOrderPtr p)
2763{
2764	OSErr result = noErr;
2765	HFSPlusAttrKey *key;
2766	HFSPlusAttrRecord record;
2767	BTreeIterator iterator;
2768	FSBufferDescriptor btRecord;
2769	u_int16_t recSize;
2770	u_int64_t bytes;
2771	Boolean doReplace = false;
2772
2773	/* Initialize the iterator, attribute key, and attribute record */
2774	ClearMemory(&iterator, sizeof(iterator));
2775	key = (HFSPlusAttrKey *)&iterator.key;
2776	BuildAttributeKey(p->parid, 0, &p->name[1], p->name[0], key);
2777
2778	btRecord.bufferAddress = &record;
2779	btRecord.itemCount = 1;
2780	btRecord.itemSize = sizeof(record);
2781
2782	/* Search for the attribute record.
2783	 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
2784	 * truncated on read! (4425232).  This function only uses recordType
2785	 * field from inline attribute record.
2786	 */
2787	result = BTSearchRecord(GPtr->calculatedAttributesFCB, &iterator,
2788				kInvalidMRUCacheKey, &btRecord, &recSize, &iterator);
2789	if (result) {
2790		DPRINTF (d_error|d_xattr, "%s: Cannot find attribute record (err = %d)\n", __FUNCTION__, result);
2791		goto out;
2792	}
2793
2794	/* We should only get record of type kHFSPlusAttrForkData */
2795	if (record.recordType != kHFSPlusAttrForkData) {
2796		DPRINTF (d_error|d_xattr, "%s: Record found is not attribute fork data\n", __FUNCTION__);
2797		result = btNotFound;
2798		goto out;
2799	}
2800
2801	/* Manipulate necessary fields in the attribute record */
2802	if (p->type == E_PEOAttr) {
2803		if ((u_int32_t)p->incorrect == record.forkData.theFork.totalBlocks) {
2804			record.forkData.theFork.totalBlocks = (u_int32_t)p->correct;
2805			doReplace = true;
2806
2807			/* Make sure that new block count is large enough to
2808			 * cover the current LEOAttr.  If not, truncate it.
2809			 */
2810			bytes = p->correct * (u_int64_t)GPtr->calculatedVCB->vcbBlockSize;
2811			if (record.forkData.theFork.logicalSize > bytes) {
2812				record.forkData.theFork.logicalSize = bytes;
2813			}
2814		}
2815	} else if (p->type == E_LEOAttr) {
2816		if (p->incorrect == record.forkData.theFork.logicalSize) {
2817			record.forkData.theFork.logicalSize = p->correct;
2818			doReplace = true;
2819		}
2820	}
2821
2822	/* Replace the attribute record, if required */
2823	if (doReplace == true) {
2824		result = BTReplaceRecord(GPtr->calculatedAttributesFCB, &iterator,
2825					&btRecord, recSize);
2826		if (result) {
2827			DPRINTF (d_error|d_xattr, "%s: Cannot replace attribute record (err=%d)\n", __FUNCTION__, result);
2828			goto out;
2829		}
2830	}
2831
2832out:
2833	return(result);
2834}
2835
2836/* Function: FixBadExtent
2837 *
2838 * Description:  The function repairs bad extent entry by zeroing out the
2839 * bad extent entry and truncating all extent information found after the
2840 * bad extent entry.
2841 *
2842 *	1. The information for repair is retrieved from the repair order passed
2843 *	   as parameter.
2844 *	2. If the start block of bad extent is zero, bad extent existed in
2845 *	   catalog record extent information.  Lookup the catalog record, zero
2846 *	   out bad extent entry and all entries after it and update the catalog
2847 *	   record.
2848 *	3. If the start block of bad extent is non-zero, bad extent existed
2849 *	   in overflow extent.  If the index of bad extent is zero, we want
2850 *	   to delete the record completely.  If the index is non-zero, search
2851 *	   the extent record, zero out bad extent entry and all entries after it
2852 *	   and update the extent record.
2853 *	4. Search for any extent record in the overflow extent after the
2854 *	   the bad extent entry.  If found, delete the record.
2855 *	5. If the file was truncated, create symlink in DamagedFiles folder
2856 * 	   and display message to the user.
2857 *
2858 * Input:
2859 *	GPtr - global scavenger structure pointer
2860 *	p - current repair order
2861 *
2862 * Output:
2863 *	result - zero indicates success, non-zero failure.
2864 */
2865static OSErr FixBadExtent(SGlobPtr GPtr, RepairOrderPtr p)
2866{
2867	OSErr err = noErr;
2868	UInt32 badExtentIndex;
2869	UInt32 extentStartBlock, foundStartBlock;
2870	UInt32 fileID;
2871	int i;
2872	UInt8 forkType;
2873
2874	UInt16 recordSize;
2875	ExtentRecord extentRecord;
2876	ExtentKey extentKey;
2877	UInt32 hint;
2878	Boolean isHFSPlus;
2879	Boolean didRepair;
2880
2881	fileID = p->parid;
2882	badExtentIndex = p->correct;
2883	extentStartBlock = p->hint;
2884	forkType = p->forkType;
2885
2886	isHFSPlus = VolumeObjectIsHFSPlus();
2887	didRepair = false;
2888
2889	assert (forkType != kEAData);
2890
2891	/* extentStartBlock = 0, the bad extent exists in catalog record */
2892	if (extentStartBlock == 0) {
2893
2894		CatalogRecord catRecord;
2895		CatalogKey catKey;
2896		HFSPlusExtentDescriptor *hfsPlusExtent;
2897		HFSExtentDescriptor *hfsExtent;
2898
2899		/* Lookup record in catalog BTree */
2900		err = GetCatalogRecord(GPtr, fileID, isHFSPlus, &catKey, &catRecord, &recordSize);
2901		if (err) {
2902			plog("%s: Could not get catalog record for fileID %u\n", __FUNCTION__, fileID);
2903			goto out;
2904		}
2905
2906		/* Check record type */
2907		assert ((catRecord.recordType == kHFSPlusFileRecord) ||
2908				(catRecord.recordType == kHFSFileRecord));
2909
2910		/* Zero out the bad extent entry and all entries after it */
2911		if (isHFSPlus) {
2912			if (forkType == kDataFork) {
2913				hfsPlusExtent = catRecord.hfsPlusFile.dataFork.extents;
2914			} else {
2915				hfsPlusExtent = catRecord.hfsPlusFile.resourceFork.extents;
2916			}
2917
2918			for (i = badExtentIndex; i < GPtr->numExtents; i++) {
2919				hfsPlusExtent[i].startBlock = 0;
2920				hfsPlusExtent[i].blockCount = 0;
2921			}
2922		} else {
2923			if (forkType == kDataFork) {
2924				hfsExtent = catRecord.hfsFile.dataExtents;
2925			} else {
2926				hfsExtent = catRecord.hfsFile.rsrcExtents;
2927			}
2928			for (i = badExtentIndex; i < GPtr->numExtents; i++) {
2929				hfsExtent[i].startBlock = 0;
2930				hfsExtent[i].blockCount = 0;
2931			}
2932		}
2933
2934		/* Write the catalog record back */
2935		err = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &catKey, kNoHint,
2936								&catRecord, recordSize, &hint);
2937		if (err) {
2938			plog("%s: Could not replace catalog record for fileID %u\n", __FUNCTION__, fileID);
2939			goto out;
2940		}
2941		didRepair = true;
2942
2943	} else { /* bad extent exists in overflow extent record */
2944
2945		/* First entry in overflow extent record is bad, delete entire record */
2946		if (badExtentIndex == 0) {
2947			goto del_overflow_extents;
2948		}
2949
2950		/* Lookup record in extents overflow BTree */
2951		BuildExtentKey (isHFSPlus, forkType, fileID, extentStartBlock, &extentKey);
2952		err = SearchBTreeRecord (GPtr->calculatedExtentsFCB, &extentKey, kNoHint,
2953								 &extentKey, &extentRecord, &recordSize, &hint);
2954		if (err) {
2955			plog("%s: Could not get overflow extents record for fileID %u, fork %u, start block %u\n", __FUNCTION__, fileID, forkType, extentStartBlock);
2956			goto out;
2957		}
2958
2959		/* Zero out the bad extent entry and all entries after it */
2960		if (isHFSPlus) {
2961			for (i = badExtentIndex; i < GPtr->numExtents; i++) {
2962				extentRecord.hfsPlus[i].startBlock = 0;
2963				extentRecord.hfsPlus[i].blockCount = 0;
2964			}
2965		} else {
2966			for (i = badExtentIndex; i < GPtr->numExtents; i++) {
2967				extentRecord.hfs[i].startBlock = 0;
2968				extentRecord.hfs[i].blockCount = 0;
2969			}
2970		}
2971
2972		/* Write the extent record back */
2973		err = ReplaceBTreeRecord(GPtr->calculatedExtentsFCB, &extentKey, hint,
2974								&extentRecord, recordSize, &hint);
2975		if (err) {
2976			plog("%s: Could not replace overflow extents record for fileID %u, fork %u, start block %u\n", __FUNCTION__, fileID, forkType, extentStartBlock);
2977			goto out;
2978		}
2979		didRepair = true;
2980
2981		/* The startBlock for extent record with bad extent entry is updated
2982		 * because we use this startBlock later to lookup next extent record
2983		 * for this file and forktype in overflow extent btree which should
2984		 * be deleted.  By incrementing the startBlock by one, we ensure that
2985		 * we find the next record, if any, that should be deleted instead of
2986		 * finding the same record that was updated above.
2987		 */
2988		extentStartBlock++;
2989	}
2990
2991del_overflow_extents:
2992	/* Search for overflow extent records.  We should get a valid record only
2993	 * if the bad extent entry was the first entry in the extent overflow
2994	 * record.  For all other cases, the search record will return an error
2995	 */
2996	BuildExtentKey (isHFSPlus, forkType, fileID, extentStartBlock, &extentKey);
2997	err = SearchBTreeRecord (GPtr->calculatedExtentsFCB, &extentKey, kNoHint,
2998							&extentKey, &extentRecord, &recordSize, &hint);
2999	if ((err != noErr) && (err != btNotFound)) {
3000		goto create_symlink;
3001	}
3002
3003	/* If we got error, check the next record */
3004	if (err == btNotFound) {
3005		err = GetBTreeRecord(GPtr->calculatedExtentsFCB, 1, &extentKey, &extentRecord,
3006							&recordSize, &hint);
3007	}
3008
3009	while (err == noErr) {
3010		/* Check if the record has correct fileID, forkType */
3011		if (isHFSPlus) {
3012			if ((fileID != extentKey.hfsPlus.fileID) ||
3013				(forkType != extentKey.hfsPlus.forkType)) {
3014				break;
3015			}
3016			foundStartBlock = extentKey.hfsPlus.startBlock;
3017		} else {
3018			if ((fileID != extentKey.hfs.fileID) ||
3019				(forkType != extentKey.hfs.forkType)) {
3020				break;
3021			}
3022			foundStartBlock = extentKey.hfs.startBlock;
3023		}
3024
3025		/* Delete the extent record */
3026		err = DeleteBTreeRecord(GPtr->calculatedExtentsFCB, &extentKey);
3027		DPRINTF (d_info, "%s: Deleting extent overflow for fileID=%u, forkType=%u, startBlock=%u\n", __FUNCTION__, fileID, forkType, foundStartBlock);
3028		if (err) {
3029			goto create_symlink;
3030		}
3031		didRepair = true;
3032
3033		/* Get the next extent record */
3034		err = GetBTreeRecord(GPtr->calculatedExtentsFCB, 1, &extentKey, &extentRecord,
3035							&recordSize, &hint);
3036	}
3037
3038	if (err == btNotFound) {
3039		err = noErr;
3040	}
3041
3042	UpdateBTreeHeader(GPtr->calculatedExtentsFCB);
3043
3044create_symlink:
3045	/* Create symlink for repaired files in damaged files folder */
3046	if (didRepair == true) {
3047		/* Create symlink for damaged files */
3048		(void) CreateCorruptFileSymlink(GPtr, fileID);
3049	}
3050
3051out:
3052	return err;
3053}
3054
3055/* Function: FixOrphanedFiles
3056 *
3057 * Description:
3058 *	Incorrect number of thread records get fixed in this function.
3059 *
3060 *	The function traverses the entire catalog Btree.
3061 *
3062 *	For a file/folder record, it tries to lookup its corresponding thread
3063 *	record.  If the thread record does not exist, or is not correct, a new
3064 *	thread record is created.  The parent ID, record type, and the name of
3065 *	the file/folder are compared for correctness.
3066 *	For plain HFS, a thread record is only looked-up if kHFSThreadExistsMask is set.
3067 *
3068 *	For a thread record, it tries to lookup its corresponding file/folder
3069 *	record.  If its does not exist or is not correct, the thread record
3070 *	is deleted.  The file/folder ID is compared for correctness.
3071 *
3072 * Input:	1. GPtr - pointer to global scavenger area
3073 *
3074 * Return value:
3075 * 		      zero means success
3076 *		      non-zero means failure
3077 */
3078static	OSErr	FixOrphanedFiles ( SGlobPtr GPtr )
3079{
3080	CatalogKey			key;
3081	CatalogKey			foundKey;
3082	CatalogKey			tempKey;
3083	CatalogRecord		record;
3084	CatalogRecord		threadRecord;
3085	CatalogRecord		record2;
3086	HFSCatalogNodeID	parentID;
3087	HFSCatalogNodeID	cNodeID = 0;
3088	BTreeIterator		savedIterator;
3089	UInt32				hint;
3090	UInt32				hint2;
3091	UInt32				threadHint;
3092	OSErr				err;
3093	UInt16				recordSize;
3094	UInt16				threadRecordSize;
3095	SInt16				recordType;
3096	SInt16				foundRecType;
3097	SInt16				selCode				= 0x8001;	/* Get first record */
3098	Boolean				isHFSPlus;
3099	BTreeControlBlock	*btcb				= GetBTreeControlBlock( kCalculatedCatalogRefNum );
3100	Boolean				isDirectory;
3101
3102	isHFSPlus = VolumeObjectIsHFSPlus( );
3103	CopyMemory( &btcb->lastIterator, &savedIterator, sizeof(BTreeIterator) );
3104
3105	do
3106	{
3107		//	Save/Restore Iterator around calls to GetBTreeRecord
3108		CopyMemory( &savedIterator, &btcb->lastIterator, sizeof(BTreeIterator) );
3109		err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint );
3110		if ( err != noErr ) break;
3111		CopyMemory( &btcb->lastIterator, &savedIterator, sizeof(BTreeIterator) );
3112
3113		selCode		= 1;	//	 kNextRecord
3114		recordType	= record.recordType;
3115
3116		isDirectory = false;
3117
3118		switch( recordType )
3119		{
3120			case kHFSFileRecord:
3121				//	If the small file is not supposed to have a thread, just break
3122				if ( ( record.hfsFile.flags & kHFSThreadExistsMask ) == 0 )
3123					break;
3124
3125			case kHFSFolderRecord:
3126			case kHFSPlusFolderRecord:
3127			case kHFSPlusFileRecord:
3128
3129				//	Locate the thread associated with this record
3130
3131				(void) CheckForStop( GPtr );		//	rotate cursor
3132
3133				parentID	= isHFSPlus == true ? foundKey.hfsPlus.parentID : foundKey.hfs.parentID;
3134				threadHint	= hint;
3135
3136				switch( recordType )
3137				{
3138					case kHFSFolderRecord:
3139						cNodeID = record.hfsFolder.folderID;
3140						isDirectory = true;
3141						break;
3142					case kHFSFileRecord:
3143						cNodeID = record.hfsFile.fileID;
3144						break;
3145					case kHFSPlusFolderRecord:
3146						cNodeID = record.hfsPlusFolder.folderID;
3147						isDirectory = true;
3148						break;
3149					case kHFSPlusFileRecord:
3150						cNodeID	= record.hfsPlusFile.fileID;
3151						break;
3152				}
3153
3154				//-- Build the key for the file thread
3155				BuildCatalogKey( cNodeID, nil, isHFSPlus, &key );
3156
3157				err = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint,
3158										 &tempKey, &threadRecord, &threadRecordSize, &hint2 );
3159
3160				/* We found a thread record for this file/folder record. */
3161				if (err == noErr) {
3162					/* Check if the parent ID and nodeName are same, and recordType is as
3163					 * expected.  If not, we are missing a correct thread record.  Force
3164					 * btNotFound in such case.
3165					 */
3166					if (isHFSPlus) {
3167						/* Check thread's recordType */
3168						foundRecType = threadRecord.hfsPlusThread.recordType;
3169						if (isDirectory == true) {
3170							if (foundRecType != kHFSPlusFolderThreadRecord) {
3171								err = btNotFound;
3172								if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3173									plog ("\t%s: Folder thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
3174								}
3175							}
3176						} else {
3177							if (foundRecType != kHFSPlusFileThreadRecord) {
3178								err = btNotFound;
3179								if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3180									plog ("\t%s: File thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
3181								}
3182							}
3183						}
3184
3185						/* Compare parent ID */
3186						if (parentID != threadRecord.hfsPlusThread.parentID) {
3187							err = btNotFound;
3188							if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3189								plog ("\t%s: parentID for id=%u do not match (fileKey=%u threadRecord=%u)\n", __FUNCTION__, cNodeID, parentID, threadRecord.hfsPlusThread.parentID);
3190							}
3191						}
3192
3193						/* Compare nodeName from file/folder key and thread reocrd */
3194						if (!((foundKey.hfsPlus.nodeName.length == threadRecord.hfsPlusThread.nodeName.length)
3195						    && (!bcmp(foundKey.hfsPlus.nodeName.unicode,
3196								      threadRecord.hfsPlusThread.nodeName.unicode,
3197									  foundKey.hfsPlus.nodeName.length * 2)))) {
3198							err = btNotFound;
3199							if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3200								unsigned maxLength = foundKey.hfsPlus.nodeName.length;
3201								if (maxLength < threadRecord.hfsPlusThread.nodeName.length)
3202									maxLength = threadRecord.hfsPlusThread.nodeName.length;
3203
3204								plog ("\t%s: nodeName for id=%u do not match\n", __FUNCTION__, cNodeID);
3205								if (cur_debug_level & d_dump_record)
3206								{
3207									plog("\tFile/Folder record:\n");
3208									HexDump(&foundKey, foundKey.hfsPlus.keyLength + 2, FALSE);
3209									plog("--\n");
3210									HexDump(&record, recordSize, FALSE);
3211									plog("\n");
3212									plog("\tThread record:\n");
3213									HexDump(&tempKey, tempKey.hfsPlus.keyLength + 2, FALSE);
3214									plog("--\n");
3215									HexDump(&threadRecord, threadRecordSize, FALSE);
3216									plog("\n");
3217								}
3218							}
3219						}
3220
3221						/* If any of the above checks failed, delete the bad thread record */
3222						if (err == btNotFound) {
3223							(void) DeleteBTreeRecord(GPtr->calculatedCatalogFCB, &tempKey);
3224						}
3225					} else { /* plain HFS */
3226						/* Check thread's recordType */
3227						foundRecType = threadRecord.hfsThread.recordType;
3228						if (isDirectory == true) {
3229							if (foundRecType != kHFSFolderThreadRecord) {
3230								err = btNotFound;
3231								if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3232									plog ("\t%s: Folder thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
3233								}
3234							}
3235						} else {
3236							if (foundRecType != kHFSFileThreadRecord) {
3237								err = btNotFound;
3238								if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3239									plog ("\t%s: File thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
3240								}
3241							}
3242						}
3243
3244						/* Compare parent ID */
3245						if (parentID != threadRecord.hfsThread.parentID) {
3246							err = btNotFound;
3247							if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3248								plog ("\t%s: parentID for id=%u do not match (fileKey=%u threadRecord=%u)\n", __FUNCTION__, cNodeID, parentID, threadRecord.hfsThread.parentID);
3249							}
3250						}
3251
3252						/* Compare nodeName from file/folder key and thread reocrd */
3253						if (!((foundKey.hfs.nodeName[0] == threadRecord.hfsThread.nodeName[0])
3254						    && (!bcmp(&foundKey.hfs.nodeName[1],
3255								      &threadRecord.hfsThread.nodeName[1],
3256									  foundKey.hfs.nodeName[0])))) {
3257							err = btNotFound;
3258							if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3259								plog ("\t%s: nodeName for id=%u do not match\n", __FUNCTION__, cNodeID);
3260							}
3261						}
3262
3263						/* If any of the above checks failed, delete the bad thread record */
3264						if (err == btNotFound) {
3265							(void) DeleteBTreeRecord(GPtr->calculatedCatalogFCB, &tempKey);
3266						}
3267					}
3268				} /* err == noErr */
3269
3270				//	For missing thread records, just create the thread
3271				if ( err == btNotFound )
3272				{
3273					//	Create the missing thread record.
3274
3275					isDirectory = false;
3276					switch( recordType )
3277					{
3278						case kHFSFolderRecord:
3279						case kHFSPlusFolderRecord:
3280							isDirectory = true;
3281							break;
3282					}
3283
3284					//-- Fill out the data for the new file thread from the key
3285					// of catalog file/folder record
3286					recordSize = BuildThreadRec( &foundKey, &threadRecord, isHFSPlus,
3287												 isDirectory );
3288					err = InsertBTreeRecord( GPtr->calculatedCatalogFCB, &key,
3289											 &threadRecord, recordSize, &threadHint );
3290					if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3291						plog ("\t%s: Created thread record for id=%u (err=%u)\n", __FUNCTION__, cNodeID, err);
3292					}
3293				}
3294
3295				break;
3296
3297
3298			case kHFSFolderThreadRecord:
3299			case kHFSPlusFolderThreadRecord:
3300				isDirectory = true;
3301
3302			case kHFSFileThreadRecord:
3303			case kHFSPlusFileThreadRecord:
3304
3305				//	Find the catalog record, if it does not exist, delete the existing thread.
3306				if ( isHFSPlus )
3307					BuildCatalogKey( record.hfsPlusThread.parentID, (const CatalogName *)&record.hfsPlusThread.nodeName, isHFSPlus, &key );
3308				else
3309					BuildCatalogKey( record.hfsThread.parentID, (const CatalogName *)&record.hfsThread.nodeName, isHFSPlus, &key );
3310
3311				err = SearchBTreeRecord ( GPtr->calculatedCatalogFCB, &key, kNoHint, &tempKey, &record2, &recordSize, &hint2 );
3312
3313				/* We found a file/folder record for this thread record. */
3314				if (err == noErr) {
3315					/* Check if the file/folder ID are same and if the recordType is as
3316					 * expected.  If not, we are missing a correct file/folder record.
3317					 * Delete the extra thread record
3318					 */
3319					if (isHFSPlus) {
3320						/* Check recordType */
3321						foundRecType = record2.hfsPlusFile.recordType;
3322						if (isDirectory == true) {
3323							if (foundRecType != kHFSPlusFolderRecord) {
3324								err = btNotFound;
3325								if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3326									plog ("\t%s: Folder recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
3327								}
3328							}
3329						} else {
3330							if (foundRecType != kHFSPlusFileRecord) {
3331								err = btNotFound;
3332								if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3333									plog ("\t%s: File recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
3334								}
3335							}
3336						}
3337
3338						/* Compare file/folder ID */
3339						if (foundKey.hfsPlus.parentID != record2.hfsPlusFile.fileID) {
3340							err = btNotFound;
3341							if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3342								plog ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__, foundKey.hfsPlus.parentID, record2.hfsPlusFile.fileID, record.hfsPlusThread.parentID);
3343							}
3344						}
3345					} else { /* plain HFS */
3346						/* Check recordType */
3347						foundRecType = record2.hfsFile.recordType;
3348						if (isDirectory == true) {
3349							if (foundRecType != kHFSFolderRecord) {
3350								err = btNotFound;
3351								if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3352									plog ("\t%s: Folder recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
3353								}
3354							}
3355						} else {
3356							if (foundRecType != kHFSFileRecord) {
3357								err = btNotFound;
3358								if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3359									plog ("\t%s: File recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
3360								}
3361							}
3362						}
3363
3364						/* Compare file/folder ID */
3365						if (foundKey.hfs.parentID != record2.hfsFile.fileID) {
3366							err = btNotFound;
3367							if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3368								if (recordType == kHFSFolderThreadRecord) {
3369									plog ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__, foundKey.hfs.parentID, record2.hfsFolder.folderID, record.hfsThread.parentID);
3370								} else {
3371									plog ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__, foundKey.hfs.parentID, record2.hfsFile.fileID, record.hfsThread.parentID);
3372								}
3373							}
3374						}
3375					}
3376				} /* if (err == noErr) */
3377
3378				if ( err != noErr )
3379				{
3380					err = DeleteBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey );
3381					if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3382						if (isHFSPlus) {
3383							plog ("\t%s: Deleted thread record for id=%d (err=%d)\n", __FUNCTION__, foundKey.hfsPlus.parentID, err);
3384						} else {
3385							plog ("\t%s: Deleted thread record for id=%d (err=%d)\n", __FUNCTION__, foundKey.hfs.parentID, err);
3386						}
3387					}
3388				}
3389
3390				break;
3391
3392			default:
3393				if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3394					plog ("\t%s: Unknown record type.\n", __FUNCTION__);
3395				}
3396				break;
3397
3398		}
3399	} while ( err == noErr );
3400
3401	if ( err == btNotFound )
3402		err = noErr;				 						//	all done, no more catalog records
3403
3404//	if (err == noErr)
3405//		err = BTFlushPath( GPtr->calculatedCatalogFCB );
3406
3407	return( err );
3408}
3409
3410
3411static	OSErr	RepairReservedBTreeFields ( SGlobPtr GPtr )
3412{
3413	CatalogRecord		record;
3414	CatalogKey			foundKey;
3415	UInt16 				recordSize;
3416	SInt16				selCode;
3417	UInt32				hint;
3418	UInt32				*reserved;
3419	OSErr				err;
3420
3421	selCode = 0x8001;															//	 start with 1st record
3422
3423	err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint );
3424	if ( err != noErr ) goto EXIT;
3425
3426	selCode = 1;																//	 get next record from now on
3427
3428	do
3429	{
3430		switch( record.recordType )
3431		{
3432			case kHFSPlusFolderRecord:
3433				/* XXX -- this should not always be cleared out (but doesn't appear to being called) */
3434				if ( record.hfsPlusFolder.flags != 0 )
3435				{
3436					record.hfsPlusFolder.flags = 0;
3437					err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
3438				}
3439				break;
3440
3441			case kHFSPlusFileRecord:
3442				//	Note: bit 7 (mask 0x80) of flags is unused in HFS or HFS Plus.  However, Inside Macintosh: Files
3443				//	describes it as meaning the file record is in use.  Some non-Apple implementations end up setting
3444				//	this bit, so we just ignore it.
3445				if ( ( record.hfsPlusFile.flags  & (UInt16) ~(0X83) ) != 0 )
3446				{
3447					record.hfsPlusFile.flags &= 0X83;
3448					err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
3449				}
3450				break;
3451
3452			case kHFSFolderRecord:
3453				if ( record.hfsFolder.flags != 0 )
3454				{
3455					record.hfsFolder.flags = 0;
3456					err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
3457				}
3458				break;
3459
3460			case kHFSFolderThreadRecord:
3461			case kHFSFileThreadRecord:
3462				reserved = (UInt32*) &(record.hfsThread.reserved);
3463				if ( reserved[0] || reserved[1] )
3464				{
3465					reserved[0]	= 0;
3466					reserved[1]	= 0;
3467					err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
3468				}
3469				break;
3470
3471			case kHFSFileRecord:
3472				//	Note: bit 7 (mask 0x80) of flags is unused in HFS or HFS Plus.  However, Inside Macintosh: Files
3473				//	describes it as meaning the file record is in use.  Some non-Apple implementations end up setting
3474				//	this bit, so we just ignore it.
3475				if ( 	( ( record.hfsFile.flags  & (UInt8) ~(0X83) ) != 0 )
3476					||	( record.hfsFile.dataStartBlock != 0 )
3477					||	( record.hfsFile.rsrcStartBlock != 0 )
3478					||	( record.hfsFile.reserved != 0 )			)
3479				{
3480					record.hfsFile.flags			&= 0X83;
3481					record.hfsFile.dataStartBlock	= 0;
3482					record.hfsFile.rsrcStartBlock	= 0;
3483					record.hfsFile.reserved		= 0;
3484					err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
3485				}
3486				break;
3487
3488			default:
3489				break;
3490		}
3491
3492		if ( err != noErr ) goto EXIT;
3493
3494		err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint );
3495
3496	} while ( err == noErr );
3497
3498	if ( err == btNotFound )
3499		err = noErr;				 						//	all done, no more catalog records
3500
3501EXIT:
3502	return( err );
3503}
3504
3505
3506/* Function: FixOrphanAttrRecord
3507 *
3508 * Description:
3509 * The function traverses the attribute BTree completely and for every
3510 * leaf record, calls CheckAttributeRecord.  CheckAttributeRecord function
3511 * is common function for verify and repair stage.  CheckAttributeRecord
3512 * deletes invalid/orphaned extended attribute records under following
3513 * conditions -
3514 *	1. record is overflow extents with no valid fork data or overflow extents
3515 *	preceeding it.
3516 *	2. record type is unknown.
3517 *
3518 * Input:
3519 * 	GPtr - global scavenger structure pointer
3520 *
3521 * Output:
3522 *	error code - zero on success, non-zero on failure.
3523 */
3524static OSErr FixOrphanAttrRecord(SGlobPtr GPtr)
3525{
3526	OSErr err = noErr;
3527	UInt16 selCode;
3528	UInt32 hint;
3529
3530	HFSPlusAttrRecord record;
3531	HFSPlusAttrKey key;
3532	UInt16 recordSize;
3533
3534	/* Zero out last attribute information from global scavenger structure */
3535	bzero (&(GPtr->lastAttrInfo), sizeof(GPtr->lastAttrInfo));
3536
3537	/* Warning: Attribute record of type kHFSPlusAttrInlineData may be
3538	 * truncated on read! (4425232).  CheckAttributeRecord only uses recordType
3539	 * field from inline attribute record.
3540	 */
3541	selCode = 0x8001;
3542	err = GetBTreeRecord(GPtr->calculatedAttributesFCB, selCode, &key,
3543			&record, &recordSize, &hint);
3544	if (err != noErr) {
3545		goto out;
3546	}
3547
3548	selCode = 1;
3549	do {
3550		err = CheckAttributeRecord(GPtr, &key, &record, recordSize);
3551		if (err) {
3552			break;
3553		}
3554
3555		/* Access the next record.
3556		 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
3557	 	 * truncated on read! (4425232).  CheckAttributeRecord only uses recordType
3558		 * field from inline attribute record.
3559	 	 */
3560		err = GetBTreeRecord(GPtr->calculatedAttributesFCB, selCode, &key,
3561				&record, &recordSize, &hint);
3562	} while (err == noErr);
3563
3564	if (err == btNotFound) {
3565		err = noErr;
3566	}
3567
3568out:
3569	return(err);
3570}
3571
3572/* Function:	GetCatalogRecord
3573 *
3574 * Description:
3575 * This function returns a catalog file/folder record for a given
3576 * file/folder ID from the catalog BTree.
3577 *
3578 * Input:	1. GPtr - pointer to global scavenger area
3579 *        	2. fileID - file ID to search the file/folder record
3580 *      	3. isHFSPlus - boolean value to indicate if volume is HFSPlus
3581 *
3582 * Output:	1. catKey - catalog key
3583 *        	2. catRecord - catalog record for given ID
3584 *         	3. recordSize - size of catalog record return back
3585 *
3586 * Return value:
3587 * 		      zero means success
3588 *		      non-zero means failure
3589 */
3590static OSErr GetCatalogRecord(SGlobPtr GPtr, UInt32 fileID, Boolean isHFSPlus, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize)
3591{
3592	OSErr err = noErr;
3593	CatalogKey catThreadKey;
3594	CatalogName catalogName;
3595	UInt32 hint;
3596	uint32_t thread_key_parentID;
3597
3598	/* Look up for catalog thread record for the file that owns attribute */
3599	BuildCatalogKey(fileID, NULL, isHFSPlus, &catThreadKey);
3600	err = SearchBTreeRecord(GPtr->calculatedCatalogFCB, &catThreadKey, kNoHint, catKey, catRecord, recordSize, &hint);
3601	if (err) {
3602		plog ("%s: No matching catalog thread record found\n", __FUNCTION__);
3603		goto out;
3604	}
3605
3606#if DEBUG_XATTR
3607	plog ("%s(%s,%d):1 recordType=%x, flags=%x\n", __FUNCTION__, __FILE__, __LINE__,
3608				catRecord->hfsPlusFile.recordType,
3609				catRecord->hfsPlusFile.flags);
3610#endif
3611
3612	/* We were expecting a thread record.  The recordType says it is a file
3613	 * record or folder record.  Return error.
3614	 */
3615	if ((catRecord->hfsPlusFile.recordType == kHFSPlusFolderRecord) ||
3616	    (catRecord->hfsPlusFile.recordType == kHFSPlusFileRecord)) {
3617		err = fsBTRecordNotFoundErr;
3618		goto out;
3619	}
3620	thread_key_parentID = catKey->hfsPlus.parentID;
3621
3622	/* It is either a file thread record or folder thread record.
3623	 * Look up for catalog record for the file that owns attribute */
3624	CopyCatalogName((CatalogName *)&(catRecord->hfsPlusThread.nodeName), &catalogName, isHFSPlus);
3625	BuildCatalogKey(catRecord->hfsPlusThread.parentID, &catalogName, isHFSPlus, catKey);
3626	err = SearchBTreeRecord(GPtr->calculatedCatalogFCB, catKey, kNoHint, catKey, catRecord, recordSize, &hint);
3627	if (err) {
3628		plog ("%s: No matching catalog record found\n", __FUNCTION__);
3629		if (cur_debug_level & d_dump_record)
3630		{
3631			plog ("Searching for key:\n");
3632			HexDump(catKey, CalcKeySize(GPtr->calculatedCatalogBTCB, (BTreeKey *)catKey), FALSE);
3633		}
3634		goto out;
3635	}
3636
3637#if DEBUG_XATTR
3638	plog ("%s(%s,%d):2 recordType=%x, flags=%x\n", __FUNCTION__, __FILE__, __LINE__,
3639				catRecord->hfsPlusFile.recordType,
3640				catRecord->hfsPlusFile.flags);
3641#endif
3642
3643	/* For catalog file or folder record, the parentID in the thread
3644	 * record's key should be equal to the fileID in the file/folder
3645	 * record --- which is equal to the ID of the file/folder record
3646	 * that is being looked up.  If not, mark the volume for repair.
3647	 */
3648	if (thread_key_parentID != catRecord->hfsPlusFile.fileID) {
3649		RcdError(GPtr, E_IncorrectNumThdRcd);
3650		if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3651			plog("\t%s: fileID=%u, thread.key.parentID=%u, record.fileID=%u\n",
3652				__FUNCTION__, fileID, thread_key_parentID, catRecord->hfsPlusFile.fileID);
3653		}
3654		GPtr->CBTStat |= S_Orphan;
3655	}
3656out:
3657	return err;
3658}
3659
3660/* Function:	RepairAttributesCheckABT
3661 *
3662 * Description:
3663 * This function is called from RepairAttributes (to repair extended
3664 * attributes) during repair stage of fsck_hfs.
3665 *
3666 * 1. Make full pass through attribute BTree.
3667 * 2. For every unique fileID, lookup its catalog record in Catalog BTree.
3668 * 3. If found, check the attributes/security bit in catalog record.
3669 *              If not set correctly, set it and replace the catalog record.
3670 * 4. If not found, return error
3671 *
3672 * Input:	1. GPtr - pointer to global scavenger area
3673 *      	2. isHFSPlus - boolean value to indicate if volume is HFSPlus
3674 *
3675 * Output:	err - Function result
3676 * 		      zero means success
3677 *		      non-zero means failure
3678 */
3679static OSErr RepairAttributesCheckABT(SGlobPtr GPtr, Boolean isHFSPlus)
3680{
3681	OSErr err = noErr;
3682	UInt16 selCode;		/* select access pattern for BTree */
3683	UInt32 hint;
3684
3685	HFSPlusAttrRecord attrRecord;
3686	HFSPlusAttrKey attrKey;
3687	UInt16 attrRecordSize;
3688	CatalogRecord catRecord;
3689	CatalogKey catKey;
3690	UInt16 catRecordSize;
3691
3692	attributeInfo lastID;	/* fileID for the last attribute searched */
3693	Boolean didRecordChange = false; 	/* whether catalog record was changed after checks */
3694
3695#if DEBUG_XATTR
3696	char attrName[XATTR_MAXNAMELEN];
3697	size_t len;
3698#endif
3699
3700	lastID.fileID = 0;
3701	lastID.hasSecurity = false;
3702
3703	selCode = 0x8001;	/* Get first record from BTree */
3704	/* Warning: Attribute record of type kHFSPlusAttrInlineData may be
3705	 * truncated on read! (4425232).  This function only uses recordType
3706	 * field from inline attribute record.
3707	 */
3708	err = GetBTreeRecord(GPtr->calculatedAttributesFCB, selCode, &attrKey, &attrRecord, &attrRecordSize, &hint);
3709	if (err == btNotFound) {
3710		/* The attributes B-tree is empty, which is OK; nothing to do. */
3711		err = noErr;
3712		goto out;
3713	}
3714	if (err != noErr) goto out;
3715
3716	selCode = 1;	/* Get next record */
3717	do {
3718#if DEBUG_XATTR
3719		/* Convert unicode attribute name to char for ACL check */
3720		(void) utf_encodestr(attrKey.attrName, attrKey.attrNameLen * 2, attrName, &len, sizeof(attrName));
3721		attrName[len] = '\0';
3722		plog ("%s(%s,%d): Found attrName=%s for fileID=%d\n", __FUNCTION__, __FILE__, __LINE__, attrName, attrKey.fileID);
3723#endif
3724
3725		if (attrKey.fileID != lastID.fileID) {
3726			/* We found an attribute with new file ID */
3727
3728			/* Replace the previous catalog record only if we updated the flags */
3729			if (didRecordChange == true) {
3730				err = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &catKey , kNoHint, &catRecord, catRecordSize, &hint);
3731				if (err) {
3732					if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3733						plog ("\t%s: Error in replacing catalog record for id=%u\n", __FUNCTION__, lastID.fileID);
3734					}
3735					goto out;
3736				}
3737			}
3738
3739			didRecordChange = false; /* reset to indicate new record has not changed */
3740
3741			/* Get the catalog record for the new fileID */
3742			err = GetCatalogRecord(GPtr, attrKey.fileID, isHFSPlus, &catKey, &catRecord, &catRecordSize);
3743			if (err) {
3744				/* No catalog record was found for this fileID. */
3745				if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3746					plog ("\t%s: No matching catalog record found for id=%u\n", __FUNCTION__, attrKey.fileID);
3747				}
3748
3749				/* 3984119 - Do not delete extended attributes for file IDs less
3750	 			 * kHFSFirstUserCatalogNodeID but not equal to kHFSRootFolderID
3751	 			 * in prime modulus checksum.  These file IDs do not have
3752	 			 * any catalog record
3753	 			 */
3754				if ((attrKey.fileID < kHFSFirstUserCatalogNodeID) &&
3755	    			    (attrKey.fileID != kHFSRootFolderID)) {
3756#if DEBUG_XATTR
3757				plog ("%s: Ignore catalog check for fileID=%d for attribute=%s\n", __FUNCTION__, attrKey.fileID, attrName);
3758#endif
3759					goto getnext;
3760				}
3761
3762				/* Delete this orphan extended attribute */
3763				err = delete_attr_record(GPtr, &attrKey, &attrRecord);
3764				if (err) {
3765					if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3766						plog ("\t%s: Error in deleting attribute record for id=%u\n", __FUNCTION__, attrKey.fileID);
3767					}
3768					goto out;
3769				}
3770				goto getnext;
3771			}
3772
3773			lastID.fileID = attrKey.fileID;	/* set last fileID to the new ID */
3774			lastID.hasSecurity = false; /* reset to indicate new fileID does not have security */
3775
3776			/* Check the Attribute bit */
3777			if (!(catRecord.hfsPlusFile.flags & kHFSHasAttributesMask)) {
3778				/* kHFSHasAttributeBit should be set */
3779				catRecord.hfsPlusFile.flags |= kHFSHasAttributesMask;
3780				didRecordChange = true;
3781			}
3782
3783			/* Check if attribute is ACL */
3784			if (!bcmp(attrKey.attrName, GPtr->securityAttrName, GPtr->securityAttrLen)) {
3785				lastID.hasSecurity = true;
3786				/* Check the security bit */
3787				if (!(catRecord.hfsPlusFile.flags & kHFSHasSecurityMask)) {
3788					/* kHFSHasSecurityBit should be set */
3789					catRecord.hfsPlusFile.flags |= kHFSHasSecurityMask;
3790					didRecordChange = true;
3791				}
3792			}
3793		} else {
3794			/* We have seen attribute for fileID in past */
3795
3796			/* If last time we saw this fileID did not have an ACL and this
3797			 * extended attribute is an ACL, ONLY check consistency of
3798			 * security bit from Catalog record
3799			 */
3800			if ((lastID.hasSecurity == false) && !bcmp(attrKey.attrName, GPtr->securityAttrName, GPtr->securityAttrLen)) {
3801				lastID.hasSecurity = true;
3802				/* Check the security bit */
3803				if (!(catRecord.hfsPlusFile.flags & kHFSHasSecurityMask)) {
3804					/* kHFSHasSecurityBit should be set */
3805					catRecord.hfsPlusFile.flags |= kHFSHasSecurityMask;
3806					didRecordChange = true;
3807				}
3808			}
3809		}
3810
3811getnext:
3812		/* Access the next record
3813		 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
3814		 * truncated on read! (4425232).  This function only uses recordType
3815		 * field from inline attribute record.
3816	 	 */
3817		err = GetBTreeRecord(GPtr->calculatedAttributesFCB, selCode, &attrKey, &attrRecord, &attrRecordSize, &hint);
3818	} while (err == noErr);
3819
3820	err = noErr;
3821
3822	/* Replace the catalog record for last extended attribute in the attributes BTree
3823	 * only if we updated the flags
3824	 */
3825	if (didRecordChange == true) {
3826		err = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &catKey , kNoHint, &catRecord, catRecordSize, &hint);
3827		if (err) {
3828#if DEBUG_XATTR
3829			plog ("%s: Error in replacing Catalog Record\n", __FUNCTION__);
3830#endif
3831			goto out;
3832		}
3833	}
3834
3835out:
3836	return err;
3837}
3838
3839/* Function:	RepairAttributesCheckCBT
3840 *
3841 * Description:
3842 * This function is called from RepairAttributes (to repair extended
3843 * attributes) during repair stage of fsck_hfs.
3844 *
3845 * NOTE: The case where attribute exists and bit is not set is being taken care in
3846 * RepairAttributesCheckABT.  This function determines relationship from catalog
3847 * Btree to attribute Btree, and not vice-versa.
3848
3849 * 1. Make full pass through catalog BTree.
3850 * 2. For every fileID, if the attributes/security bit is set,
3851 *    lookup all the extended attributes in the attributes BTree.
3852 * 3. If found, check that if bits are set correctly.
3853 * 4. If not found, clear the bits.
3854 *
3855 * Input:	1. GPtr - pointer to global scavenger area
3856 *      	2. isHFSPlus - boolean value to indicate if volume is HFSPlus
3857 *
3858 * Output:	err - Function result
3859 * 		      zero means success
3860 *		      non-zero means failure
3861 */
3862static OSErr RepairAttributesCheckCBT(SGlobPtr GPtr, Boolean isHFSPlus)
3863{
3864	OSErr err = noErr;
3865	UInt16 selCode;		/* select access pattern for BTree */
3866	UInt16 recordSize;
3867	UInt32 hint;
3868
3869	HFSPlusAttrKey *attrKey;
3870	CatalogRecord catRecord;
3871	CatalogKey catKey;
3872
3873	Boolean didRecordChange = false; 	/* whether catalog record was changed after checks */
3874
3875	BTreeIterator iterator;
3876
3877	UInt32 curFileID;
3878	Boolean curRecordHasAttributes = false;
3879	Boolean curRecordHasSecurity = false;
3880
3881	selCode = 0x8001;	/* Get first record from BTree */
3882	err = GetBTreeRecord(GPtr->calculatedCatalogFCB, selCode, &catKey, &catRecord, &recordSize, &hint);
3883	if ( err != noErr ) goto out;
3884
3885	selCode = 1;	/* Get next record */
3886	do {
3887		/* Check only file record and folder record, else skip to next record */
3888		if ( (catRecord.hfsPlusFile.recordType != kHFSPlusFileRecord) &&
3889		     (catRecord.hfsPlusFile.recordType != kHFSPlusFolderRecord)) {
3890			goto getnext;
3891		}
3892
3893		/* Check if catalog record has attribute and/or security bit set, else
3894		 * skip to next record
3895		 */
3896		if ( ((catRecord.hfsPlusFile.flags & kHFSHasAttributesMask) == 0) &&
3897		   	 ((catRecord.hfsPlusFile.flags & kHFSHasSecurityMask) == 0) ) {
3898			 goto getnext;
3899		}
3900
3901		/* Initialize some flags */
3902		didRecordChange = false;
3903		curRecordHasSecurity = false;
3904		curRecordHasAttributes = false;
3905
3906		/* Access all extended attributes for this fileID */
3907		curFileID = catRecord.hfsPlusFile.fileID;
3908
3909		/* Initialize the iterator and attribute key */
3910		ClearMemory(&iterator, sizeof(BTreeIterator));
3911		attrKey = (HFSPlusAttrKey *)&iterator.key;
3912		attrKey->keyLength = kHFSPlusAttrKeyMinimumLength;
3913		attrKey->fileID = curFileID;
3914
3915		/* Search for attribute with NULL name.  This will place the iterator at correct fileID location in BTree */
3916		err = BTSearchRecord(GPtr->calculatedAttributesFCB, &iterator, kInvalidMRUCacheKey, NULL, NULL, &iterator);
3917		if (err && (err != btNotFound)) {
3918#if DEBUG_XATTR
3919			plog ("%s: No matching attribute record found\n", __FUNCTION__);
3920#endif
3921			goto out;
3922		}
3923
3924		/* Iterate over to all extended attributes for given fileID */
3925		err = BTIterateRecord(GPtr->calculatedAttributesFCB, kBTreeNextRecord, &iterator, NULL, NULL);
3926
3927		/* Check only if we did _find_ an attribute record for the current fileID */
3928		while ((err == noErr) && (attrKey->fileID == curFileID)) {
3929			/* Current record should have attribute bit set */
3930			curRecordHasAttributes = true;
3931
3932			/* Current record should have security bit set */
3933			if (!bcmp(attrKey->attrName, GPtr->securityAttrName, GPtr->securityAttrLen)) {
3934				curRecordHasSecurity = true;
3935			}
3936
3937			/* Get the next record */
3938			err = BTIterateRecord(GPtr->calculatedAttributesFCB, kBTreeNextRecord, &iterator, NULL, NULL);
3939		}
3940
3941		/* Determine if we need to update the catalog record */
3942		if ((curRecordHasAttributes == false) && (catRecord.hfsPlusFile.flags & kHFSHasAttributesMask)) {
3943			/* If no attribute exists and attributes bit is set, clear it */
3944			catRecord.hfsPlusFile.flags &= ~kHFSHasAttributesMask;
3945			didRecordChange = true;
3946		}
3947
3948		if ((curRecordHasSecurity == false) && (catRecord.hfsPlusFile.flags & kHFSHasSecurityMask)) {
3949			/* If no security attribute exists and security bit is set, clear it */
3950			catRecord.hfsPlusFile.flags &= ~kHFSHasSecurityMask;
3951			didRecordChange = true;
3952		}
3953
3954		/* If there was any change in catalog record, write it back to disk */
3955		if (didRecordChange == true) {
3956			err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &catKey , kNoHint, &catRecord, recordSize, &hint );
3957			if (err) {
3958#if DEBUG_XATTR
3959				plog ("%s: Error writing catalog record\n", __FUNCTION__);
3960#endif
3961				goto out;
3962			}
3963		}
3964
3965getnext:
3966		/* Access the next record */
3967		err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &catKey, &catRecord, &recordSize, &hint );
3968	} while (err == noErr);
3969
3970	err = noErr;
3971
3972out:
3973	return err;
3974}
3975
3976/* Function:	RepairAttributes
3977 *
3978 * Description:
3979 * This function fixes the extended attributes consistency by
3980 * calling two functions:
3981 * 1. RepairAttributesCheckABT:  Traverses attributes Btree and
3982 * checks if each attribute has correct bits set in its corresponding
3983 * catalog record.
3984 * 2. RepairAttributesCheckCBT:  Traverses catalog Btree and checks
3985 * if each catalog record that has attribute/security bit set have
3986 * corresponding extended attributes.
3987 *
3988 * Input:	1. GPtr - pointer to global scavenger area
3989 *
3990 * Output:	err - Function result
3991 * 		      zero means success
3992 *		      non-zero means failure
3993 */
3994static OSErr RepairAttributes(SGlobPtr GPtr)
3995{
3996	OSErr err = noErr;
3997	Boolean isHFSPlus;
3998
3999	/* Check if the volume is HFS Plus volume */
4000	isHFSPlus = VolumeObjectIsHFSPlus();
4001	if (!isHFSPlus) {
4002		goto out;
4003	}
4004
4005	/* Traverse Attributes BTree and access required records in Catalog BTree */
4006	err = RepairAttributesCheckABT(GPtr, isHFSPlus);
4007	if (err) {
4008		goto out;
4009	}
4010
4011	/* Traverse Catalog BTree and access required records in Attributes BTree */
4012	err = RepairAttributesCheckCBT(GPtr, isHFSPlus);
4013	if (err) {
4014		goto out;
4015	}
4016
4017out:
4018	return err;
4019}
4020
4021/*------------------------------------------------------------------------------
4022
4023Function:	cmpLongs
4024
4025Function:	compares two longs.
4026
4027Input:		*a:  pointer to first number
4028			*b:  pointer to second number
4029
4030Output:		<0 if *a < *b
4031			0 if a == b
4032			>0 if a > b
4033------------------------------------------------------------------------------*/
4034
4035int cmpLongs ( const void *a, const void *b )
4036{
4037	return( *(long*)a - *(long*)b );
4038}
4039
4040/* Function: FixOverlappingExtents
4041 *
4042 * Description: Fix overlapping extents problem.  The implementation copies all
4043 * the extents existing in overlapping extents to a new location and updates the
4044 * extent record to point to the new extent.  At the end of repair, symlinks are
4045 * created with name "fileID filename" to point to the file involved in
4046 * overlapping extents problem.  Note that currently only HFS Plus volumes are
4047 * repaired.
4048 *
4049 * PARTIAL SUCCESS: This function handles partial success in the following
4050 * two ways:
4051 *		a. The function pre-allocates space for the all the extents.  If the
4052 *		allocation fails, it proceeds to allocate space for other extents
4053 *		instead of returning error.
4054 *		b. If moving an extent succeeds and symlink creation fails, the function
4055 *		proceeds to another extent.
4056 * If the repair encounters either a or b condition, appropriate message is
4057 * printed at the end of the function.
4058 * If even a single repair operation succeeds (moving of extent), the function
4059 * returns zero.
4060 *
4061 * Current limitations:
4062 *	1. A regular file instead of symlink is created under following conditions:
4063 *		a. The volume is plain HFS volume.  HFS does not support symlink
4064 *		creation.
4065 *		b. The path the new symlink points to is greater than PATH_MAX bytes.
4066 *		c. The path the new symlink points has some intermediate component
4067 *		greater than NAME_MAX bytes.
4068 *	2. Contiguous disk space for every new extent is expected.  The extent is
4069 *	not broken into multiple extents if contiguous space is not available on the
4070 *	disk.
4071 *	3. The current fix for overlapping extent only works for HFS Plus volumes.
4072 *  Plain HFS volumes have problem in accessing the catalog record by fileID.
4073 *	4. Plain HFS volumes might have encoding issues with the newly created
4074 *	symlink and its data.
4075 *
4076 * Input:
4077 *	GPtr - global scavenger pointer
4078 *
4079 * Output:
4080 *	returns zero on success/partial success (moving of one extent succeeds),
4081 *			non-zero on failure.
4082 */
4083static OSErr FixOverlappingExtents(SGlobPtr GPtr)
4084{
4085	OSErr err = noErr;
4086	Boolean isHFSPlus;
4087	unsigned int i;
4088	unsigned int numOverlapExtents = 0;
4089	ExtentInfo *extentInfo;
4090	ExtentsTable **extentsTableH = GPtr->overlappedExtents;
4091
4092	unsigned int status = 0;
4093#define S_DISKFULL			0x01	/* error due to disk full */
4094#define	S_MOVEEXTENT		0x02	/* moving extent succeeded */
4095
4096	isHFSPlus = VolumeObjectIsHFSPlus();
4097	if (isHFSPlus == false) {
4098		/* Do not repair plain HFS volumes */
4099		err = R_RFail;
4100		goto out;
4101	}
4102
4103	if (extentsTableH == NULL) {
4104		/* No overlapping extents exist */
4105		goto out;
4106	}
4107
4108	numOverlapExtents = (**extentsTableH).count;
4109
4110	/* Optimization - sort the overlap extent list based on blockCount to
4111	 * start allocating contiguous space for largest number of blocks first
4112	 */
4113	qsort((**extentsTableH).extentInfo, numOverlapExtents, sizeof(ExtentInfo),
4114		  CompareExtentBlockCount);
4115
4116#if DEBUG_OVERLAP
4117	/* Print all overlapping extents structure */
4118	for (i=0; i<numOverlapExtents; i++) {
4119		extentInfo	= &((**extentsTableH).extentInfo[i]);
4120		plog ("%d: fileID = %d, startBlock = %d, blockCount = %d\n", i, extentInfo->fileID, extentInfo->startBlock, extentInfo->blockCount);
4121	}
4122#endif
4123
4124	/* Pre-allocate free space for all overlapping extents */
4125	for (i=0; i<numOverlapExtents; i++) {
4126		extentInfo	= &((**extentsTableH).extentInfo[i]);
4127		err = AllocateContigBitmapBits (GPtr->calculatedVCB, extentInfo->blockCount, &(extentInfo->newStartBlock));
4128		if ((err != noErr)) {
4129			/* Not enough disk space */
4130			status |= S_DISKFULL;
4131#if DEBUG_OVERLAP
4132			plog ("%s: Not enough disk space to allocate extent for fileID = %d (start=%d, count=%d)\n", __FUNCTION__, extentInfo->fileID, extentInfo->startBlock, extentInfo->blockCount);
4133#endif
4134		}
4135	}
4136
4137	/* For every extent info, copy the extent into new location and create symlink */
4138	for (i=0; i<numOverlapExtents; i++) {
4139		extentInfo	= &((**extentsTableH).extentInfo[i]);
4140
4141		/* Do not repair this extent as no new extent was allocated */
4142		if (extentInfo->newStartBlock == 0) {
4143			continue;
4144		}
4145
4146		/* Move extent data to new location */
4147		err	= MoveExtent(GPtr, extentInfo);
4148		if (err != noErr) {
4149			extentInfo->didRepair = false;
4150#if DEBUG_OVERLAP
4151			plog ("%s: Extent move failed for extent for fileID = %u (old=%u, new=%u, count=%u) (err=%d)\n", __FUNCTION__, extentInfo->fileID, extentInfo->startBlock, extentInfo->newStartBlock, extentInfo->blockCount, err);
4152#endif
4153		} else {
4154			/* Mark the overlapping extent as repaired */
4155			extentInfo->didRepair = true;
4156			status |= S_MOVEEXTENT;
4157#if DEBUG_OVERLAP
4158			plog ("%s: Extent move success for extent for fileID = %u (old=%u, new=%u, count=%u)\n", __FUNCTION__, extentInfo->fileID, extentInfo->startBlock, extentInfo->newStartBlock, extentInfo->blockCount);
4159#endif
4160		}
4161
4162		/* Create symlink for the corrupt file */
4163		err = CreateCorruptFileSymlink(GPtr, extentInfo->fileID);
4164		if (err != noErr) {
4165#if DEBUG_OVERLAP
4166			plog ("%s: Error in creating symlink for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
4167#endif
4168		} else {
4169#if DEBUG_OVERLAP
4170			plog ("%s: Created symlink for fileID = %u (old=%u, new=%u, count=%u)\n", __FUNCTION__, extentInfo->fileID, extentInfo->startBlock, extentInfo->newStartBlock, extentInfo->blockCount);
4171#endif
4172		}
4173	}
4174
4175out:
4176	/* Release all blocks used by overlap extents that are repaired */
4177	for (i=0; i<numOverlapExtents; i++) {
4178		extentInfo	= &((**extentsTableH).extentInfo[i]);
4179		if (extentInfo->didRepair == true) {
4180			ReleaseBitmapBits (extentInfo->startBlock, extentInfo->blockCount);
4181		}
4182	}
4183
4184	/* For all un-repaired extents,
4185	 *	1. Release all blocks allocated for new extent.
4186	 *	2. Mark all blocks used for the old extent (since the overlapping region
4187	 *	have been marked free in the for loop above.
4188	 */
4189	for (i=0; i<numOverlapExtents; i++) {
4190		extentInfo	= &((**extentsTableH).extentInfo[i]);
4191		if (extentInfo->didRepair == false) {
4192			CaptureBitmapBits (extentInfo->startBlock, extentInfo->blockCount);
4193
4194			if (extentInfo->newStartBlock != 0) {
4195				ReleaseBitmapBits (extentInfo->newStartBlock, extentInfo->blockCount);
4196			}
4197		}
4198	}
4199
4200	/* Update the volume free block count since the release and alloc above might
4201	 * have worked on same bit multiple times.
4202	 */
4203	UpdateFreeBlockCount (GPtr);
4204
4205	/* Print correct status messages */
4206	if (status & S_DISKFULL) {
4207		fsckPrint(GPtr->context, E_DiskFull);
4208	}
4209
4210	/* If moving of even one extent succeeds, return success */
4211	if (status & S_MOVEEXTENT) {
4212		err = noErr;
4213	}
4214
4215	return err;
4216} /* FixOverlappingExtents */
4217
4218/* Function: CompareExtentBlockCount
4219 *
4220 * Description: Compares the blockCount from two ExtentInfo and return the
4221 * comparison result. (since we have to arrange in descending order)
4222 *
4223 * Input:
4224 *	first and second - void pointers to ExtentInfo structure.
4225 *
4226 * Output:
4227 *	<0 if first > second
4228 * 	=0 if first == second
4229 *	>0 if first < second
4230 */
4231static int CompareExtentBlockCount(const void *first, const void *second)
4232{
4233	return (((ExtentInfo *)second)->blockCount -
4234			((ExtentInfo *)first)->blockCount);
4235} /* CompareExtentBlockCount */
4236
4237/* Function: MoveExtent
4238 *
4239 * Description: Move data from old extent to new extent and update corresponding
4240 * records.
4241 * 1. Search the extent record for the overlapping extent.
4242 *		If the fileID < kHFSFirstUserCatalogNodeID,
4243 *			Ignore repair for BadBlock, RepairCatalog, BogusExtent files.
4244 *			Search for extent record in volume header.
4245 *		Else,
4246 *			Search for extent record in catalog BTree.  If the extent list does
4247 *			not end in catalog record and extent record not found in catalog
4248 *			record, search in extents BTree.
4249 * 2. If found, copy disk blocks from old extent to new extent.
4250 * 3. If it succeeds, update extent record with new start block and write back
4251 *    to disk.
4252 * This function does not take care to deallocate blocks from old start block.
4253 *
4254 * Input:
4255 *	GPtr - Global Scavenger structure pointer
4256 *  extentInfo - Current overlapping extent details.
4257 *
4258 * Output:
4259 * 	err: zero on success, non-zero on failure
4260 *		paramErr - Invalid paramter, ex. file ID is less than
4261 *		kHFSFirstUserCatalogNodeID.
4262 */
4263static OSErr MoveExtent(SGlobPtr GPtr, ExtentInfo *extentInfo)
4264{
4265	OSErr err = noErr;
4266	Boolean isHFSPlus;
4267
4268	CatalogRecord catRecord;
4269	CatalogKey catKey;
4270	HFSPlusExtentKey extentKey;
4271	HFSPlusExtentRecord extentData;
4272	HFSPlusAttrKey attrKey;
4273	HFSPlusAttrRecord attrRecord;
4274	UInt16 recordSize;
4275
4276	enum locationTypes {volumeHeader, catalogBTree, extentsBTree, attributeBTree} foundLocation;
4277
4278	UInt32 foundExtentIndex = 0;
4279	Boolean noMoreExtents = true;
4280
4281	isHFSPlus = VolumeObjectIsHFSPlus();
4282
4283	/* Find correct location of this overlapping extent */
4284	if (extentInfo->forkType == kEAData) {
4285		assert(isHFSPlus == true);
4286
4287		/* Search extent in attribute btree */
4288		err = SearchExtentInAttributeBT (GPtr, extentInfo, &attrKey, &attrRecord,
4289										&recordSize, &foundExtentIndex);
4290		if (err != noErr) {
4291			goto out;
4292		}
4293		foundLocation = attributeBTree;
4294	} else { /* kDataFork or kRsrcFork */
4295		if (extentInfo->fileID < kHFSFirstUserCatalogNodeID) {
4296			/* Ignore these fileIDs in repair.  Bad block file blocks should
4297			 * never be moved.  kHFSRepairCatalogFileID and kHFSBogusExtentFileID
4298			 * are temporary runtime files.  We need to return error to the  caller
4299			 * to deallocate disk blocks preallocated during preflight
4300			 * to move the overlapping extents.  Any other extent that overlaps
4301			 * with these extents might have moved successfully, thus repairing
4302			 * the problem.
4303			 */
4304			if ((extentInfo->fileID == kHFSBadBlockFileID) ||
4305				(extentInfo->fileID == kHFSBogusExtentFileID) ||
4306				(extentInfo->fileID == kHFSRepairCatalogFileID)) {
4307				err = paramErr;
4308				goto out;
4309			}
4310
4311			/* Search for extent record in the volume header */
4312			err = SearchExtentInVH (GPtr, extentInfo, &foundExtentIndex, &noMoreExtents);
4313			foundLocation = volumeHeader;
4314		} else {
4315			/* Search the extent record from the catalog btree */
4316			err = SearchExtentInCatalogBT (GPtr, extentInfo, &catKey, &catRecord,
4317										  &recordSize, &foundExtentIndex, &noMoreExtents);
4318			foundLocation = catalogBTree;
4319		}
4320		if (err != noErr) {
4321			if (noMoreExtents == false) {
4322				/* search extent in extents overflow btree */
4323				err = SearchExtentInExtentBT (GPtr, extentInfo, &extentKey,
4324											  &extentData, &recordSize, &foundExtentIndex);
4325				foundLocation = extentsBTree;
4326				if (err != noErr) {
4327					DPRINTF (d_error|d_overlap, "%s: No matching extent record found in extents btree for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
4328					goto out;
4329				}
4330			} else {
4331				/* No more extents exist for this file */
4332				DPRINTF (d_error|d_overlap, "%s: No matching extent record found for fileID = %d\n", __FUNCTION__, extentInfo->fileID);
4333				goto out;
4334			}
4335		}
4336	}
4337	/* Copy disk blocks from old extent to new extent */
4338	err = CopyDiskBlocks(GPtr, extentInfo->startBlock, extentInfo->blockCount,
4339						 extentInfo->newStartBlock);
4340	if (err != noErr) {
4341		DPRINTF (d_error|d_overlap, "%s: Error in copying disk blocks for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
4342		goto out;
4343	}
4344
4345	/* Replace the old start block in extent record with new start block */
4346	if (foundLocation == catalogBTree) {
4347		err = UpdateExtentInCatalogBT(GPtr, extentInfo, &catKey, &catRecord,
4348									  &recordSize, foundExtentIndex);
4349	} else if (foundLocation == volumeHeader) {
4350		err = UpdateExtentInVH(GPtr, extentInfo, foundExtentIndex);
4351	} else if (foundLocation == extentsBTree) {
4352		extentData[foundExtentIndex].startBlock = extentInfo->newStartBlock;
4353		err = UpdateExtentRecord(GPtr->calculatedVCB, NULL, &extentKey, extentData, kNoHint);
4354	} else if (foundLocation == attributeBTree) {
4355		err = UpdateExtentInAttributeBT(GPtr, extentInfo, &attrKey, &attrRecord,
4356										&recordSize, foundExtentIndex);
4357
4358	}
4359	if (err != noErr) {
4360	        DPRINTF (d_error|d_overlap, "%s: Error in updating extent record for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
4361		goto out;
4362	}
4363
4364out:
4365	return err;
4366} /* MoveExtent */
4367
4368/* Function: CreateCorruptFileSymlink
4369 *
4370 * Description: Create symlink to point to the corrupt files that might
4371 * have data loss due to repair (overlapping extents, bad extents)
4372 *
4373 * The function looks up directory with name "DamagedFiles" in the
4374 * root of the file system being repaired.  If it does not exists, it
4375 * creates the directory.  The symlink to damaged file is created in this
4376 * directory.
4377 *
4378 * If fileID >= kHFSFirstUserCatalogNodeID,
4379 *	Lookup the filename and path to the file based on file ID.  Create the
4380 *	new file name as "fileID filename" and data as the relative path of the file
4381 *	from the root of the volume.
4382 *	If either
4383 *		the volume is plain HFS, or
4384 *		the length of the path pointed by data is greater than PATH_MAX, or
4385 *		the length of any intermediate path component is greater than NAME_MAX,
4386 *		Create a plain file with given data.
4387 *	Else
4388 *		Create a symlink.
4389 * Else
4390 *	Find the name of file based on ID (ie. Catalog BTree, etc), and create plain
4391 *	regular file with name "fileID filename" and data as "System File:
4392 *	filename".
4393 *
4394 * Input:
4395 *	1. GPtr - global scavenger structure pointer.
4396 * 	2. fileID - fileID of the source for creating symlink
4397 *
4398 * Output:
4399 * 	returns zero on success, non-zero on failure.
4400 *		memFullErr - Not enough memory
4401 */
4402static OSErr CreateCorruptFileSymlink(SGlobPtr GPtr, UInt32 fileID)
4403{
4404	OSErr err = noErr;
4405	Boolean isHFSPlus;
4406	char *filename = NULL;
4407	unsigned int filenamelen;
4408	char *data = NULL;
4409	unsigned int datalen;
4410	unsigned int filenameoffset;
4411	unsigned int dataoffset;
4412	UInt32 damagedDirID;
4413	UInt16 status;
4414	UInt16 fileType;
4415
4416	isHFSPlus = VolumeObjectIsHFSPlus();
4417
4418	/* Lookup and create, if required, the DamagedFiles folder */
4419	damagedDirID = CreateDirByName(GPtr, (u_char *)"DamagedFiles", kHFSRootFolderID);
4420	if (damagedDirID == 0) {
4421		goto out;
4422	}
4423
4424	/* Allocate (kHFSPlusMaxFileNameChars * 4) for unicode - utf8 conversion */
4425	filenamelen = kHFSPlusMaxFileNameChars * 4;
4426	filename = malloc(filenamelen);
4427	if (!filename) {
4428		err = memFullErr;
4429		goto out;
4430	}
4431
4432	/* Allocate (PATH_MAX * 4) instead of PATH_MAX for unicode - utf8 conversion */
4433	datalen = PATH_MAX * 4;
4434	data = malloc(datalen);
4435	if (!data) {
4436		err = memFullErr;
4437		goto out;
4438	}
4439
4440	/* Find filename, path for fileID >= 16 and determine new fileType */
4441	if (fileID >= kHFSFirstUserCatalogNodeID) {
4442		char *name;
4443		char *path;
4444
4445		/* construct symlink data with .. prefix */
4446		dataoffset = sprintf (data, "..");
4447		path = data + dataoffset;
4448		datalen -= dataoffset;
4449
4450		/* construct new filename prefix with fileID<space> */
4451		filenameoffset = sprintf (filename, "%08x ", fileID);
4452		name = filename + filenameoffset;
4453		filenamelen -= filenameoffset;
4454
4455		/* find file name and path (data for symlink) for fileID */
4456		err = GetFileNamePathByID(GPtr, fileID, path, &datalen,
4457								  name, &filenamelen, &status);
4458		if (err != noErr) {
4459#if DEBUG_OVERLAP
4460			plog ("%s: Error in getting name/path for fileID = %d (err=%d)\n", __FUNCTION__, fileID, err);
4461#endif
4462			goto out;
4463		}
4464
4465		/* update length of path and filename */
4466		filenamelen += filenameoffset;
4467		datalen += dataoffset;
4468
4469		/* If
4470		 * (1) the volume is plain HFS volume, or
4471		 * (2) any intermediate component in path was more than NAME_MAX bytes, or
4472		 * (3) the entire path was greater than PATH_MAX bytes
4473		 * then create regular file
4474		 * else create symlink.
4475		 */
4476		if (!isHFSPlus || (status & FPATH_BIGNAME) || (datalen > PATH_MAX)) {
4477			/* create file */
4478			fileType = S_IFREG;
4479		} else {
4480			/* create symlink */
4481			fileType = S_IFLNK;
4482		}
4483	} else {
4484		/* for fileID < 16, create regular file */
4485		fileType = S_IFREG;
4486
4487		/* construct the name of the file */
4488		filenameoffset = sprintf (filename, "%08x ", fileID);
4489		filenamelen -= filenameoffset;
4490		err = GetSystemFileName (fileID, (filename + filenameoffset), &filenamelen);
4491		filenamelen += filenameoffset;
4492
4493		/* construct the data of the file */
4494		dataoffset = sprintf (data, "System File: ");
4495		datalen -= dataoffset;
4496		err = GetSystemFileName (fileID, (data + dataoffset), &datalen);
4497		datalen += dataoffset;
4498	}
4499
4500	/* Create new file */
4501	err = CreateFileByName (GPtr, damagedDirID, fileType, (u_char *)filename,
4502							filenamelen, (u_char *)data, datalen);
4503	/* Mask error if file already exists */
4504	if (err == EEXIST) {
4505		err = noErr;
4506	}
4507	if (err != noErr) {
4508#if DEBUG_OVERLAP
4509		plog ("%s: Error in creating fileType = %d for fileID = %d (err=%d)\n", __FUNCTION__, fileType, fileID, err);
4510#endif
4511		goto out;
4512	}
4513
4514out:
4515	if (err) {
4516		if ((GPtr->PrintStat & S_SymlinkCreate) == 0) {
4517			fsckPrint(GPtr->context, E_SymlinkCreate);
4518			GPtr->PrintStat|= S_SymlinkCreate;
4519		}
4520	} else {
4521		if ((GPtr->PrintStat & S_DamagedDir) == 0) {
4522			fsckPrint(GPtr->context, fsckCorruptFilesDirectory, "DamagedFiles");
4523			GPtr->PrintStat|= S_DamagedDir;
4524		}
4525	}
4526
4527	if (data) {
4528		free (data);
4529	}
4530	if (filename) {
4531		free (filename);
4532	}
4533
4534	return err;
4535} /* CreateCorruptFileSymlink */
4536
4537/* Function: SearchExtentInAttributeBT
4538 *
4539 * Description: Search extent with given information (fileID, attribute name,
4540 * startBlock, blockCount) in the attribute BTree.
4541 *
4542 * Input:
4543 *	1. GPtr - global scavenger structure pointer.
4544 *	2. extentInfo - Information about extent to be searched.
4545 *
4546 * Output:
4547 *	Returns zero on success, fnfErr on failure.
4548 *	1. *attrKey - Attribute key for given fileID and attribute name, if found.
4549 *	2. *attrRecord - Attribute record for given fileID and attribute name, if found.
4550 *	3. *recordSize - Size of the record being returned.
4551 * 	4. *foundExtentIndex - Index in extent record which matches the input data.
4552 */
4553static OSErr SearchExtentInAttributeBT(SGlobPtr GPtr, ExtentInfo *extentInfo,
4554				HFSPlusAttrKey *attrKey, HFSPlusAttrRecord *attrRecord,
4555				UInt16 *recordSize, UInt32 *foundExtentIndex)
4556{
4557	OSErr result = fnfErr;
4558	BTreeIterator iterator;
4559	FSBufferDescriptor btRecord;
4560	HFSPlusAttrKey *key;
4561	Boolean noMoreExtents;
4562	unsigned char *attrname = NULL;
4563	size_t attrnamelen;
4564
4565	assert((extentInfo->attrname != NULL));
4566
4567	attrname = malloc (XATTR_MAXNAMELEN + 1);
4568	if (!attrname) {
4569		result = memFullErr;
4570		goto out;
4571	}
4572
4573	/* Initialize the iterator, attribute record buffer, and attribute key */
4574	ClearMemory(&iterator, sizeof(BTreeIterator));
4575	key = (HFSPlusAttrKey *)&iterator.key;
4576	attrnamelen = strlen(extentInfo->attrname);
4577	BuildAttributeKey(extentInfo->fileID, 0, (unsigned char *)extentInfo->attrname, attrnamelen, key);
4578
4579	btRecord.bufferAddress = attrRecord;
4580	btRecord.itemCount = 1;
4581	btRecord.itemSize = sizeof(HFSPlusAttrRecord);
4582
4583	/* Search for the attribute record
4584	 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
4585	 * truncated on read! (4425232).  This function only uses recordType
4586	 * field from inline attribute record.
4587	 */
4588	result = BTSearchRecord(GPtr->calculatedAttributesFCB, &iterator,
4589				kInvalidMRUCacheKey, &btRecord, recordSize, &iterator);
4590	if (result) {
4591		DPRINTF (d_error|d_overlap, "%s: Error finding attribute record (err=%d) for fileID = %d, attrname = %d\n", __FUNCTION__, result, extentInfo->fileID, extentInfo->attrname);
4592		goto out;
4593	}
4594
4595	/* Search the attribute record for overlapping extent.  If found, return
4596	 * success.  If not, iterate to the next record.  If it is a valid
4597	 * attribute extent record belonging to the same attribute, search
4598	 * for the desired extent.
4599	 */
4600	while (1) {
4601		if (attrRecord->recordType == kHFSPlusAttrForkData) {
4602			result = FindExtentInExtentRec(true, extentInfo->startBlock,
4603					extentInfo->blockCount, attrRecord->forkData.theFork.extents,
4604					foundExtentIndex, &noMoreExtents);
4605			if ((result == noErr) || (noMoreExtents == true)) {
4606				goto out;
4607			}
4608		} else if (attrRecord->recordType == kHFSPlusAttrExtents) {
4609			result = FindExtentInExtentRec(true, extentInfo->startBlock,
4610					extentInfo->blockCount, attrRecord->overflowExtents.extents,
4611					foundExtentIndex, &noMoreExtents);
4612			if ((result == noErr) || (noMoreExtents == true)) {
4613				goto out;
4614			}
4615		} else {
4616			/* Invalid attribute record.  This function should not find any
4617			 * attribute record except forkData and AttrExtents.
4618			 */
4619			result = fnfErr;
4620			goto out;
4621		}
4622
4623		/* Iterate to the next record
4624	 	 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
4625	 	 * truncated on read! (4425232).  This function only uses recordType
4626		 * field from inline attribute record.
4627	 	 */
4628		result = BTIterateRecord(GPtr->calculatedAttributesFCB, kBTreeNextRecord,
4629					&iterator, &btRecord, recordSize);
4630		if (result) {
4631			goto out;
4632		}
4633
4634		(void) utf_encodestr(key->attrName, key->attrNameLen * 2, attrname, &attrnamelen, XATTR_MAXNAMELEN + 1);
4635		attrname[attrnamelen] = '\0';
4636
4637		/* Check if the attribute record belongs to the same attribute */
4638		if ((key->fileID != extentInfo->fileID) ||
4639			(strcmp((char *)attrname, extentInfo->attrname))) {
4640			/* The attribute record belongs to another attribute */
4641			result = fnfErr;
4642			goto out;
4643		}
4644	}
4645
4646out:
4647	/* Copy the correct key to the caller */
4648	if (result == noErr) {
4649		CopyMemory(key, attrKey, sizeof(HFSPlusAttrKey));
4650	}
4651
4652	if (attrname != NULL) {
4653		free (attrname);
4654	}
4655
4656	return (result);
4657}
4658
4659/* Function: UpdateExtentInAttributeBT
4660 *
4661 * Description: Update extent record with given information (fileID, startBlock,
4662 * blockCount) in attribute BTree.
4663 *
4664 * Input:
4665 *	1. GPtr - global scavenger structure pointer.
4666 *	2. extentInfo - Information about extent to be searched.
4667 *	3. *attrKey - Attribute key for record to update.
4668 *	4. *attrRecord - Attribute record to update.
4669 *	5. *recordSize - Size of the record.
4670 *	6. foundExtentIndex - Index in extent record to update.
4671 *
4672 * Output:
4673 *	Returns zero on success, non-zero on failure.
4674 */
4675static OSErr UpdateExtentInAttributeBT (SGlobPtr GPtr, ExtentInfo *extentInfo,
4676				HFSPlusAttrKey *attrKey, HFSPlusAttrRecord *attrRecord,
4677				UInt16 *recordSize, UInt32 foundInExtentIndex)
4678{
4679	OSErr err;
4680	UInt32 foundHint;
4681
4682	assert ((attrRecord->recordType == kHFSPlusAttrForkData) ||
4683			(attrRecord->recordType == kHFSPlusAttrExtents));
4684
4685	/* Update the new start block count in the extent */
4686	if (attrRecord->recordType == kHFSPlusAttrForkData) {
4687		attrRecord->forkData.theFork.extents[foundInExtentIndex].startBlock =
4688			extentInfo->newStartBlock;
4689	} else if (attrRecord->recordType == kHFSPlusAttrExtents) {
4690		attrRecord->overflowExtents.extents[foundInExtentIndex].startBlock =
4691			extentInfo->newStartBlock;
4692	}
4693
4694	/* Replace the attribute record.
4695 	 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
4696 	 * truncated on read! (4425232).
4697	 */
4698	err = ReplaceBTreeRecord (GPtr->calculatedAttributesFCB, attrKey, kNoHint,
4699							  attrRecord, *recordSize, &foundHint);
4700
4701	return (err);
4702}
4703
4704/* Function: SearchExtentInVH
4705 *
4706 * Description: Search extent with given information (fileID, startBlock,
4707 * blockCount) in volume header.
4708 *
4709 * Input:
4710 *	1. GPtr - global scavenger structure pointer.
4711 *	2. extentInfo - Information about extent to be searched.
4712 *
4713 * Output:
4714 *	Returns zero on success, fnfErr on failure.
4715 *	1. *foundExtentIndex - Index in extent record which matches the input data.
4716 *	2. *noMoreExtents - Indicates that no more extents will exist for this
4717 *	fileID in extents BTree.
4718 */
4719static OSErr SearchExtentInVH(SGlobPtr GPtr, ExtentInfo *extentInfo, UInt32 *foundExtentIndex, Boolean *noMoreExtents)
4720{
4721	OSErr err = fnfErr;
4722	Boolean isHFSPlus;
4723	SFCB *fcb = NULL;
4724
4725	isHFSPlus = VolumeObjectIsHFSPlus();
4726	*noMoreExtents = true;
4727
4728	/* Find correct FCB structure */
4729	switch (extentInfo->fileID) {
4730		case kHFSExtentsFileID:
4731			fcb = GPtr->calculatedVCB->vcbExtentsFile;
4732			break;
4733
4734		case kHFSCatalogFileID:
4735			fcb = GPtr->calculatedVCB->vcbCatalogFile;
4736			break;
4737
4738		case kHFSAllocationFileID:
4739			fcb = GPtr->calculatedVCB->vcbAllocationFile;
4740			break;
4741
4742		case kHFSStartupFileID:
4743			fcb = GPtr->calculatedVCB->vcbStartupFile;
4744			break;
4745
4746		case kHFSAttributesFileID:
4747			fcb = GPtr->calculatedVCB->vcbAttributesFile;
4748			break;
4749	};
4750
4751	/* If extent found, find correct extent index */
4752	if (fcb != NULL) {
4753		if (isHFSPlus) {
4754			err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
4755										extentInfo->blockCount, fcb->fcbExtents32,
4756										foundExtentIndex, noMoreExtents);
4757		} else {
4758			err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
4759										extentInfo->blockCount,
4760										(*(HFSPlusExtentRecord *)fcb->fcbExtents16),
4761										foundExtentIndex, noMoreExtents);
4762		}
4763	}
4764	return err;
4765} /* SearchExtentInVH */
4766
4767/* Function: UpdateExtentInVH
4768 *
4769 * Description:	Update the extent record for given fileID and index in the
4770 * volume header with new start block.
4771 *
4772 * Input:
4773 *	1. GPtr - global scavenger structure pointer.
4774 *	2. extentInfo - Information about extent to be searched.
4775 *	3. foundExtentIndex - Index in extent record to update.
4776 *
4777 * Output:
4778 *	Returns zero on success, fnfErr on failure.  This function will fail an
4779 *	incorrect fileID is passed.
4780 */
4781static OSErr UpdateExtentInVH (SGlobPtr GPtr, ExtentInfo *extentInfo, UInt32 foundExtentIndex)
4782{
4783	OSErr err = fnfErr;
4784	Boolean isHFSPlus;
4785	SFCB *fcb = NULL;
4786
4787	isHFSPlus = VolumeObjectIsHFSPlus();
4788
4789	/* Find correct FCB structure */
4790	switch (extentInfo->fileID) {
4791		case kHFSExtentsFileID:
4792			fcb = GPtr->calculatedVCB->vcbExtentsFile;
4793			break;
4794
4795		case kHFSCatalogFileID:
4796			fcb = GPtr->calculatedVCB->vcbCatalogFile;
4797			break;
4798
4799		case kHFSAllocationFileID:
4800			fcb = GPtr->calculatedVCB->vcbAllocationFile;
4801			break;
4802
4803		case kHFSStartupFileID:
4804			fcb = GPtr->calculatedVCB->vcbStartupFile;
4805			break;
4806
4807		case kHFSAttributesFileID:
4808			fcb = GPtr->calculatedVCB->vcbAttributesFile;
4809			break;
4810	};
4811
4812	/* If extent found, find correct extent index */
4813	if (fcb != NULL) {
4814		if (isHFSPlus) {
4815			fcb->fcbExtents32[foundExtentIndex].startBlock = extentInfo->newStartBlock;
4816		} else {
4817			fcb->fcbExtents16[foundExtentIndex].startBlock = extentInfo->newStartBlock;
4818		}
4819		MarkVCBDirty(GPtr->calculatedVCB);
4820		err = noErr;
4821	}
4822	return err;
4823} /* UpdateExtentInVH */
4824
4825/* Function: SearchExtentInCatalogBT
4826 *
4827 * Description: Search extent with given information (fileID, startBlock,
4828 * blockCount) in catalog BTree.
4829 *
4830 * Input:
4831 *	1. GPtr - global scavenger structure pointer.
4832 *	2. extentInfo - Information about extent to be searched.
4833 *
4834 * Output:
4835 *	Returns zero on success, non-zero on failure.
4836 *	1. *catKey - Catalog key for given fileID, if found.
4837 *	2. *catRecord - Catalog record for given fileID, if found.
4838 *	3. *recordSize - Size of the record being returned.
4839 *	4. *foundExtentIndex - Index in extent record which matches the input data.
4840 *	5. *noMoreExtents - Indicates that no more extents will exist for this
4841 *	fileID in extents BTree.
4842 */
4843static OSErr SearchExtentInCatalogBT(SGlobPtr GPtr, ExtentInfo *extentInfo, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize, UInt32 *foundExtentIndex, Boolean *noMoreExtents)
4844{
4845	OSErr err;
4846	Boolean isHFSPlus;
4847
4848	isHFSPlus = VolumeObjectIsHFSPlus();
4849
4850	/* Search catalog btree for this file ID */
4851	err = GetCatalogRecord(GPtr, extentInfo->fileID, isHFSPlus, catKey, catRecord,
4852						   recordSize);
4853	if (err != noErr) {
4854#if DEBUG_OVERLAP
4855		plog ("%s: No matching catalog record found for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
4856#endif
4857		goto out;
4858	}
4859
4860	if (isHFSPlus) {
4861		/* HFS Plus */
4862		if (extentInfo->forkType == kDataFork) {
4863			/* data fork */
4864			err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
4865										extentInfo->blockCount,
4866										catRecord->hfsPlusFile.dataFork.extents,
4867										foundExtentIndex, noMoreExtents);
4868		} else {
4869			/* resource fork */
4870			err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
4871										extentInfo->blockCount,
4872										catRecord->hfsPlusFile.resourceFork.extents,
4873										foundExtentIndex, noMoreExtents);
4874		}
4875	} else {
4876		/* HFS */
4877		if (extentInfo->forkType == kDataFork) {
4878			/* data fork */
4879			err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
4880										extentInfo->blockCount,
4881										(*(HFSPlusExtentRecord *)catRecord->hfsFile.dataExtents),
4882										foundExtentIndex, noMoreExtents);
4883		} else {
4884			/* resource fork */
4885			err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
4886										extentInfo->blockCount,
4887										(*(HFSPlusExtentRecord *)catRecord->hfsFile.rsrcExtents),
4888										foundExtentIndex, noMoreExtents);
4889		}
4890	}
4891
4892out:
4893	return err;
4894} /* SearchExtentInCatalogBT */
4895
4896/* Function: UpdateExtentInCatalogBT
4897 *
4898 * Description: Update extent record with given information (fileID, startBlock,
4899 * blockCount) in catalog BTree.
4900 *
4901 * Input:
4902 *	1. GPtr - global scavenger structure pointer.
4903 *	2. extentInfo - Information about extent to be searched.
4904 *	3. *catKey - Catalog key for record to update.
4905 *	4. *catRecord - Catalog record to update.
4906 *	5. *recordSize - Size of the record.
4907 *	6. foundExtentIndex - Index in extent record to update.
4908 *
4909 * Output:
4910 *	Returns zero on success, non-zero on failure.
4911 */
4912static OSErr UpdateExtentInCatalogBT (SGlobPtr GPtr, ExtentInfo *extentInfo, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize, UInt32 foundInExtentIndex)
4913{
4914	OSErr err;
4915	Boolean isHFSPlus;
4916	UInt32 foundHint;
4917
4918	isHFSPlus = VolumeObjectIsHFSPlus();
4919
4920	/* Modify the catalog record */
4921	if (isHFSPlus) {
4922		if (extentInfo->forkType == kDataFork) {
4923			catRecord->hfsPlusFile.dataFork.extents[foundInExtentIndex].startBlock = extentInfo->newStartBlock;
4924		} else {
4925			catRecord->hfsPlusFile.resourceFork.extents[foundInExtentIndex].startBlock = extentInfo->newStartBlock;
4926		}
4927	} else {
4928		if (extentInfo->forkType == kDataFork) {
4929			catRecord->hfsFile.dataExtents[foundInExtentIndex].startBlock = extentInfo->newStartBlock;
4930		} else {
4931			catRecord->hfsFile.rsrcExtents[foundInExtentIndex].startBlock = extentInfo->newStartBlock;
4932		}
4933	}
4934
4935	/* Replace the catalog record */
4936	err = ReplaceBTreeRecord (GPtr->calculatedCatalogFCB, catKey, kNoHint,
4937							  catRecord, *recordSize, &foundHint);
4938	if (err != noErr) {
4939#if DEBUG_OVERLAP
4940		plog ("%s: Error in replacing catalog record for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
4941#endif
4942	}
4943	return err;
4944} /* UpdateExtentInCatalogBT */
4945
4946/* Function: SearchExtentInExtentBT
4947 *
4948 * Description: Search extent with given information (fileID, startBlock,
4949 * blockCount) in Extent BTree.
4950 *
4951 * Input:
4952 *	1. GPtr - global scavenger structure pointer.
4953 *	2. extentInfo - Information about extent to be searched.
4954 *
4955 * Output:
4956 *	Returns zero on success, non-zero on failure.
4957 *		fnfErr - desired extent record was not found.
4958 *	1. *extentKey - Extent key, if found.
4959 *	2. *extentRecord - Extent record, if found.
4960 *	3. *recordSize - Size of the record being returned.
4961 *	4. *foundExtentIndex - Index in extent record which matches the input data.
4962 */
4963static OSErr SearchExtentInExtentBT(SGlobPtr GPtr, ExtentInfo *extentInfo, HFSPlusExtentKey *extentKey, HFSPlusExtentRecord *extentRecord, UInt16 *recordSize, UInt32 *foundExtentIndex)
4964{
4965	OSErr err = noErr;
4966	Boolean isHFSPlus;
4967	Boolean noMoreExtents = true;
4968	UInt32 hint;
4969
4970	isHFSPlus = VolumeObjectIsHFSPlus();
4971
4972	/* set up extent key */
4973	BuildExtentKey (isHFSPlus, extentInfo->forkType, extentInfo->fileID, 0, extentKey);
4974	err = SearchBTreeRecord (GPtr->calculatedExtentsFCB, extentKey, kNoHint,
4975							 extentKey, extentRecord, recordSize, &hint);
4976	if ((err != noErr) && (err != btNotFound)) {
4977#if DEBUG_OVERLAP
4978		plog ("%s: Error on searching first record for fileID = %d in Extents Btree (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
4979#endif
4980		goto out;
4981	}
4982
4983	if (err == btNotFound)
4984	{
4985		/* Position to the first record for the given fileID */
4986		err = GetBTreeRecord (GPtr->calculatedExtentsFCB, 1, extentKey,
4987							  extentRecord, recordSize, &hint);
4988	}
4989
4990	while (err == noErr)
4991	{
4992		/* Break out if we see different fileID, forkType in the BTree */
4993		if (isHFSPlus) {
4994			if ((extentKey->fileID != extentInfo->fileID) ||
4995				(extentKey->forkType != extentInfo->forkType)) {
4996				err = fnfErr;
4997				break;
4998			}
4999		} else {
5000			if ((((HFSExtentKey *)extentKey)->fileID != extentInfo->fileID) ||
5001				(((HFSExtentKey *)extentKey)->forkType != extentInfo->forkType)) {
5002				err = fnfErr;
5003				break;
5004			}
5005		}
5006
5007		/* Check the extents record for corresponding startBlock, blockCount */
5008		err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
5009									extentInfo->blockCount, *extentRecord,
5010									foundExtentIndex, &noMoreExtents);
5011		if (err == noErr) {
5012			break;
5013		}
5014		if (noMoreExtents == true) {
5015			err = fnfErr;
5016			break;
5017		}
5018
5019		/* Try next record for this fileID and forkType */
5020		err = GetBTreeRecord (GPtr->calculatedExtentsFCB, 1, extentKey,
5021							  extentRecord, recordSize, &hint);
5022	}
5023
5024out:
5025	return err;
5026} /* SearchExtentInExtentBT */
5027
5028/* Function: FindExtentInExtentRec
5029 *
5030 * Description: Traverse the given extent record (size based on if the volume is
5031 * HFS or HFSPlus volume) and find the index location if the given startBlock
5032 * and blockCount match.
5033 *
5034 * Input:
5035 *	1. isHFSPlus - If the volume is plain HFS or HFS Plus.
5036 *	2. startBlock - startBlock to be searched in extent record.
5037 *	3. blockCount - blockCount to be searched in extent record.
5038 *	4. extentData - Extent Record to be searched.
5039 *
5040 * Output:
5041 *	Returns zero if the match is found, else fnfErr on failure.
5042 *	1. *foundExtentIndex - Index in extent record which matches the input data.
5043 *	2. *noMoreExtents - Indicates that no more extents exist after this extent
5044 *	record.
5045 */
5046static OSErr FindExtentInExtentRec (Boolean isHFSPlus, UInt32 startBlock, UInt32 blockCount, const HFSPlusExtentRecord extentData, UInt32 *foundExtentIndex, Boolean *noMoreExtents)
5047{
5048	OSErr err = noErr;
5049	UInt32 numOfExtents;
5050	Boolean foundExtent;
5051	int i;
5052
5053	foundExtent = false;
5054	*noMoreExtents = false;
5055	*foundExtentIndex = 0;
5056
5057	if (isHFSPlus) {
5058		numOfExtents = kHFSPlusExtentDensity;
5059	} else {
5060		numOfExtents = kHFSExtentDensity;
5061	}
5062
5063	for (i=0; i<numOfExtents; i++) {
5064		if (extentData[i].blockCount == 0) {
5065			/* no more extents left to check */
5066			*noMoreExtents = true;
5067			break;
5068		}
5069		if ((startBlock == extentData[i].startBlock) &&
5070			(blockCount == extentData[i].blockCount)) {
5071			foundExtent = true;
5072			*foundExtentIndex = i;
5073			break;
5074		}
5075	}
5076
5077	if (foundExtent == false) {
5078		err = fnfErr;
5079	}
5080
5081	return err;
5082} /* FindExtentInExtentRec */
5083
5084/* Function: GetSystemFileName
5085 *
5086 * Description: Return the name of the system file based on fileID
5087 *
5088 * Input:
5089 *	1. fileID - fileID whose name is to be returned.
5090 *	2. *filenamelen - length of filename buffer.
5091 *
5092 * Output:
5093 *	1. *filename - filename, is limited by the length of filename buffer passed
5094 *	in *filenamelen.
5095 *	2. *filenamelen - length of the filename
5096 *	Always returns zero.
5097 */
5098OSErr GetSystemFileName(UInt32 fileID, char *filename, unsigned int *filenamelen)
5099{
5100	OSErr err = noErr;
5101	unsigned int len;
5102
5103	if (filename) {
5104		len = *filenamelen - 1;
5105		switch (fileID) {
5106			case kHFSExtentsFileID:
5107				strncpy (filename, "Extents Overflow BTree", len);
5108				break;
5109
5110			case kHFSCatalogFileID:
5111				strncpy (filename, "Catalog BTree", len);
5112				break;
5113
5114			case kHFSAllocationFileID:
5115				strncpy (filename, "Allocation File", len);
5116				break;
5117
5118			case kHFSStartupFileID:
5119				strncpy (filename, "Startup File", len);
5120				break;
5121
5122			case kHFSAttributesFileID:
5123				strncpy (filename, "Attributes BTree", len);
5124				break;
5125
5126			case kHFSBadBlockFileID:
5127				strncpy (filename, "Bad Allocation File", len);
5128				break;
5129
5130			case kHFSRepairCatalogFileID:
5131				strncpy (filename, "Repair Catalog File", len);
5132				break;
5133
5134			case kHFSBogusExtentFileID:
5135				strncpy (filename, "Bogus Extents File", len);
5136				break;
5137
5138			default:
5139				strncpy (filename, "Unknown File", len);
5140				break;
5141		};
5142		filename[len] = '\0';
5143		*filenamelen = strlen (filename);
5144	}
5145	return err;
5146}
5147
5148/* structure to store the intermediate path components during BTree traversal.
5149 * This is used as a LIFO linked list
5150 */
5151struct fsPathString
5152{
5153	char *name;
5154	unsigned int namelen;
5155	struct fsPathString *childPtr;
5156};
5157
5158/* Function: GetFileNamePathByID
5159 *
5160 * Description: Return the file/directory name and/or full path by ID.  The
5161 * length of the strings returned is limited by string lengths passed as
5162 * parameters.
5163 * The function lookups catalog thread record for given fileID and its parents
5164 * until it reaches the Root Folder.
5165 *
5166 * Note:
5167 * 	1. The path returned currently does not return mangled names.
5168 *	2. Either one or both of fullPath and fileName can be NULL.
5169 *	3. fullPath and fileName are returned as NULL-terminated UTF8 strings.
5170 *	4. Returns error if fileID < kHFSFirstUserCatalogID.
5171 *
5172 * Input:
5173 *	1. GPtr - global scavenger structure pointer
5174 *	2. fileID - fileID for the target file/directory for finding the path
5175 *	3. fullPathLen - size of array to return full path
5176 *	4. fileNameLen - size of array to return file name
5177 *
5178 * Output:
5179 *	Return value: zero on success, non-zero on failure
5180 *		memFullErr - Not enough memory
5181 *		paramErr - Invalid paramter
5182 *
5183 *	The data in *fileNameLen and *fullPathLen is undefined on error.
5184 *
5185 *	1. fullPath - If fullPath is non-NULL, full path of file/directory is
5186 *	returned (size limited by fullPathLen)
5187 *	2. *fullPathLen - length of fullPath returned.
5188 *	3. fileName - If fileName is non-NULL, file name of fileID is returned (size
5189 *	limited by fileNameLen).
5190 *	4. *fileNameLen - length of fileName returned.
5191 *	5. *status - status of operation, any of the following bits can be set
5192 *	(defined in dfalib/Scavenger.h).
5193 *		FNAME_BUF2SMALL	- filename buffer is too small.
5194 *		FNAME_BIGNAME	- filename is more than NAME_MAX bytes.
5195 *		FPATH_BUF2SMALL	- path buffer is too small.
5196 *		FPATH_BIGNAME	- one or more intermediate path component is greater
5197 *						  than NAME_MAX bytes.
5198 *		F_RESERVE_FILEID- fileID is less than kHFSFirstUserCatalogNodeID.
5199 */
5200OSErr GetFileNamePathByID(SGlobPtr GPtr, UInt32 fileID, char *fullPath, unsigned int *fullPathLen, char *fileName, unsigned int *fileNameLen, u_int16_t *status)
5201{
5202	OSErr err = noErr;
5203	Boolean isHFSPlus;
5204	UInt16 recordSize;
5205	UInt16 curStatus = 0;
5206	UInt32 hint;
5207	CatalogKey catKey;
5208	CatalogRecord catRecord;
5209	struct fsPathString *listHeadPtr = NULL;
5210	struct fsPathString *listTailPtr = NULL;
5211	struct fsPathString *curPtr = NULL;
5212	u_char *filename = NULL;
5213	size_t namelen;
5214
5215	if (!fullPath && !fileName) {
5216		goto out;
5217	}
5218
5219	if (fileID < kHFSFirstUserCatalogNodeID) {
5220		curStatus = F_RESERVE_FILEID;
5221		err = paramErr;
5222		goto out;
5223	}
5224
5225	isHFSPlus = VolumeObjectIsHFSPlus();
5226
5227	if (isHFSPlus) {
5228		filename = malloc(kHFSPlusMaxFileNameChars * 3 + 1);
5229	} else {
5230		filename = malloc(kHFSMaxFileNameChars + 1);
5231	}
5232	if (!filename) {
5233		err = memFullErr;
5234#if DEBUG_OVERLAP
5235		plog ("%s: Not enough memory (err=%d)\n", __FUNCTION__, err);
5236#endif
5237		goto out;
5238	}
5239
5240	while (fileID != kHFSRootFolderID) {
5241		/* lookup for thread record for this fileID */
5242		BuildCatalogKey(fileID, NULL, isHFSPlus, &catKey);
5243		err = SearchBTreeRecord(GPtr->calculatedCatalogFCB, &catKey, kNoHint,
5244								&catKey, &catRecord, &recordSize, &hint);
5245		if (err) {
5246#if DEBUG_OVERLAP
5247			plog ("%s: Error finding thread record for fileID = %d (err=%d)\n", __FUNCTION__, fileID, err);
5248#endif
5249			goto out;
5250		}
5251
5252		/* Check if this is indeed a thread record */
5253		if ((catRecord.hfsPlusThread.recordType != kHFSPlusFileThreadRecord) &&
5254			(catRecord.hfsPlusThread.recordType != kHFSPlusFolderThreadRecord) &&
5255		    (catRecord.hfsThread.recordType != kHFSFileThreadRecord) &&
5256		    (catRecord.hfsThread.recordType != kHFSFolderThreadRecord)) {
5257			err = paramErr;
5258#if DEBUG_OVERLAP
5259			plog ("%s: Error finding valid thread record for fileID = %d\n", __FUNCTION__, fileID);
5260#endif
5261			goto out;
5262		}
5263
5264		/* Convert the name string to utf8 */
5265		if (isHFSPlus) {
5266			(void) utf_encodestr(catRecord.hfsPlusThread.nodeName.unicode,
5267								 catRecord.hfsPlusThread.nodeName.length * 2,
5268								 filename, &namelen, kHFSPlusMaxFileNameChars * 3 + 1);
5269		} else {
5270			namelen = catRecord.hfsThread.nodeName[0];
5271			memcpy (filename, catKey.hfs.nodeName, namelen);
5272		}
5273
5274		/* Store the path name in LIFO linked list */
5275		curPtr = malloc(sizeof(struct fsPathString));
5276		if (!curPtr) {
5277			err = memFullErr;
5278#if DEBUG_OVERLAP
5279			plog ("%s: Not enough memory (err=%d)\n", __FUNCTION__, err);
5280#endif
5281			goto out;
5282		}
5283
5284		/* Do not NULL terminate the string */
5285		curPtr->namelen = namelen;
5286		curPtr->name = malloc(namelen);
5287		if (!curPtr->name) {
5288			err = memFullErr;
5289#if DEBUG_OVERLAP
5290			plog ("%s: Not enough memory (err=%d)\n", __FUNCTION__, err);
5291#endif
5292		}
5293		memcpy (curPtr->name, filename, namelen);
5294		curPtr->childPtr = listHeadPtr;
5295		listHeadPtr = curPtr;
5296		if (listTailPtr == NULL) {
5297			listTailPtr = curPtr;
5298		}
5299
5300		/* lookup for parentID */
5301		if (isHFSPlus) {
5302			fileID = catRecord.hfsPlusThread.parentID;
5303		} else {
5304			fileID = catRecord.hfsThread.parentID;
5305		}
5306
5307		/* no need to find entire path, bail out */
5308		if (fullPath == NULL) {
5309			break;
5310		}
5311	}
5312
5313	/* return the name of the file/directory */
5314	if (fileName) {
5315		/* Do not overflow the buffer length passed */
5316		if (*fileNameLen < (listTailPtr->namelen + 1)) {
5317			*fileNameLen = *fileNameLen - 1;
5318			curStatus |= FNAME_BUF2SMALL;
5319		} else {
5320			*fileNameLen = listTailPtr->namelen;
5321		}
5322		if (*fileNameLen > NAME_MAX) {
5323			curStatus |= FNAME_BIGNAME;
5324		}
5325		memcpy (fileName, listTailPtr->name, *fileNameLen);
5326		fileName[*fileNameLen] = '\0';
5327	}
5328
5329	/* return the full path of the file/directory */
5330	if (fullPath) {
5331		/* Do not overflow the buffer length passed and reserve last byte for NULL termination */
5332		unsigned int bytesRemain = *fullPathLen - 1;
5333
5334		*fullPathLen = 0;
5335		while (listHeadPtr != NULL) {
5336			if (bytesRemain == 0) {
5337				break;
5338			}
5339			memcpy ((fullPath + *fullPathLen), "/", 1);
5340			*fullPathLen += 1;
5341			bytesRemain--;
5342
5343			if (bytesRemain == 0) {
5344				break;
5345			}
5346			if (bytesRemain < listHeadPtr->namelen) {
5347				namelen = bytesRemain;
5348				curStatus |= FPATH_BUF2SMALL;
5349			} else {
5350				namelen = listHeadPtr->namelen;
5351			}
5352			if (namelen > NAME_MAX) {
5353				curStatus |= FPATH_BIGNAME;
5354			}
5355			memcpy ((fullPath + *fullPathLen), listHeadPtr->name, namelen);
5356			*fullPathLen += namelen;
5357			bytesRemain -= namelen;
5358
5359			curPtr = listHeadPtr;
5360			listHeadPtr = listHeadPtr->childPtr;
5361			free(curPtr->name);
5362			free(curPtr);
5363		}
5364
5365		fullPath[*fullPathLen] = '\0';
5366	}
5367
5368	err = noErr;
5369
5370out:
5371	if (status) {
5372		*status = curStatus;
5373	}
5374
5375	/* free any remaining allocated memory */
5376	while (listHeadPtr != NULL) {
5377		curPtr = listHeadPtr;
5378		listHeadPtr = listHeadPtr->childPtr;
5379		if (curPtr->name) {
5380			free (curPtr->name);
5381		}
5382		free (curPtr);
5383	}
5384	if (filename) {
5385		free (filename);
5386	}
5387
5388	return err;
5389} /* GetFileNamePathByID */
5390
5391/* Function: CopyDiskBlocks
5392 *
5393 * Description: Copy data from source extent to destination extent
5394 * for blockCount on the disk.
5395 *
5396 * Input:
5397 *	1. GPtr - pointer to global scavenger structure.
5398 * 	2. startAllocationBlock - startBlock for old extent
5399 * 	3. blockCount - total blocks to copy
5400 * 	4. newStartAllocationBlock - startBlock for new extent
5401 *
5402 * Output:
5403 * 	err, zero on success, non-zero on failure.
5404 */
5405OSErr CopyDiskBlocks(SGlobPtr GPtr, const UInt32 startAllocationBlock, const UInt32 blockCount, const UInt32 newStartAllocationBlock )
5406{
5407	OSErr err = noErr;
5408	SVCB *vcb;
5409	uint64_t old_offset;
5410	uint64_t new_offset;
5411	uint32_t sectorsPerBlock;
5412
5413	vcb = GPtr->calculatedVCB;
5414	sectorsPerBlock = vcb->vcbBlockSize / Blk_Size;
5415
5416	old_offset = (vcb->vcbAlBlSt + (sectorsPerBlock * startAllocationBlock)) << Log2BlkLo;
5417	new_offset = (vcb->vcbAlBlSt + (sectorsPerBlock * newStartAllocationBlock)) << Log2BlkLo;
5418
5419	err = CacheCopyDiskBlocks (vcb->vcbBlockCache, old_offset, new_offset,
5420							   blockCount * vcb->vcbBlockSize);
5421	return err;
5422} /* CopyDiskBlocks */
5423
5424/* Function: WriteBufferToDisk
5425 *
5426 * Description: Write given buffer data to disk blocks.
5427 * If the length of the buffer is not a multiple of allocation block size,
5428 * the disk is filled with zero from the length of buffer upto the
5429 * end of allocation blocks (specified by blockCount).
5430 *
5431 * Input:
5432 *	1. GPtr - global scavenger structure pointer
5433 *	2. startBlock - starting block number for writing data.
5434 *	3. blockCount - total number of contiguous blocks to be written
5435 *	4. buffer - data buffer to be written to disk
5436 *	5. bufLen - length of data buffer to be written to disk.
5437 *
5438 * Output:
5439 * 	returns zero on success, non-zero on failure.
5440 */
5441OSErr WriteBufferToDisk(SGlobPtr GPtr, UInt32 startBlock, UInt32 blockCount, u_char *buffer, int bufLen)
5442{
5443	OSErr err = noErr;
5444	SVCB *vcb;
5445	uint64_t offset;
5446	uint32_t write_len;
5447
5448	vcb = GPtr->calculatedVCB;
5449
5450	/* Calculate offset and length */
5451	offset = (vcb->vcbAlBlSt + ((vcb->vcbBlockSize / Blk_Size) * startBlock)) << Log2BlkLo;
5452	write_len = blockCount * vcb->vcbBlockSize;
5453
5454	/* Write buffer to disk */
5455	err = CacheWriteBufferToDisk (vcb->vcbBlockCache, offset, write_len, buffer, bufLen);
5456
5457	return err;
5458} /* WriteBufferToDisk */
5459
5460//	2210409, in System 8.1, moving file or folder would cause HFS+ thread records to be
5461//	520 bytes in size.  We only shrink the threads if other repairs are needed.
5462static	OSErr	FixBloatedThreadRecords( SGlob *GPtr )
5463{
5464	CatalogRecord		record;
5465	CatalogKey			foundKey;
5466	UInt32				hint;
5467	UInt16 				recordSize;
5468	SInt16				i = 0;
5469	OSErr				err;
5470	SInt16				selCode				= 0x8001;										//	 Start with 1st record
5471
5472	err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint );
5473	ReturnIfError( err );
5474
5475	selCode = 1;																//	 Get next record from now on
5476
5477	do
5478	{
5479		if ( ++i > 10 ) { (void) CheckForStop( GPtr ); i = 0; }					//	Spin the cursor every 10 entries
5480
5481		if (  (recordSize == sizeof(HFSPlusCatalogThread)) && ((record.recordType == kHFSPlusFolderThreadRecord) || (record.recordType == kHFSPlusFileThreadRecord)) )
5482		{
5483			// HFS Plus has varaible sized threads so adjust to actual length
5484			recordSize -= ( sizeof(record.hfsPlusThread.nodeName.unicode) - (record.hfsPlusThread.nodeName.length * sizeof(UniChar)) );
5485
5486			err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
5487			ReturnIfError( err );
5488		}
5489
5490		err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint );
5491	} while ( err == noErr );
5492
5493	if ( err == btNotFound )
5494		err = noErr;
5495
5496	return( err );
5497}
5498
5499
5500static OSErr
5501FixMissingThreadRecords( SGlob *GPtr )
5502{
5503	struct MissingThread *	mtp;
5504	FSBufferDescriptor    	btRecord;
5505	BTreeIterator         	iterator;
5506	OSStatus           		result;
5507	UInt16              	dataSize;
5508	Boolean					headsUp;
5509	UInt32					lostAndFoundDirID;
5510
5511	lostAndFoundDirID = 0;
5512	headsUp = false;
5513	for (mtp = GPtr->missingThreadList; mtp != NULL; mtp = mtp->link) {
5514		if ( mtp->threadID == 0 )
5515			continue;
5516
5517		// if the thread record information in the MissingThread struct is not there
5518		// then we have a missing directory in addition to a missing thread record
5519		// for that directory.  We will recreate the missing directory in our
5520		// lost+found directory.
5521		if ( mtp->thread.parentID == 0 ) {
5522			if (embedded == 1 && debug == 0) {
5523				return( R_RFail );
5524			}
5525			if ( lostAndFoundDirID == 0 )
5526				lostAndFoundDirID = CreateDirByName( GPtr , (u_char *)"lost+found", kHFSRootFolderID);
5527			if ( lostAndFoundDirID == 0 ) {
5528				if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
5529					plog( "\tCould not create lost+found directory \n" );
5530				return( R_RFail );
5531			}
5532			fsckPrint(GPtr->context, E_NoDir, mtp->threadID);
5533			result = FixMissingDirectory( GPtr, mtp->threadID, lostAndFoundDirID );
5534			if ( result != 0 ) {
5535				if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
5536					plog( "\tCould not recreate a missing directory (error %d)\n", result );
5537				return( R_RFail );
5538			}
5539			else
5540				headsUp = true;
5541			continue;
5542		}
5543
5544		dataSize = 10 + (mtp->thread.nodeName.length * 2);
5545		btRecord.bufferAddress = (void *)&mtp->thread;
5546		btRecord.itemSize = dataSize;
5547		btRecord.itemCount = 1;
5548		iterator.hint.nodeNum = 0;
5549		BuildCatalogKey(mtp->threadID, NULL, true, (CatalogKey*)&iterator.key);
5550
5551		result = BTInsertRecord(GPtr->calculatedCatalogFCB, &iterator, &btRecord, dataSize);
5552		if (result)
5553			return (IntError(GPtr, R_IntErr));
5554		mtp->threadID = 0;
5555	}
5556	if ( headsUp )
5557		fsckPrint(GPtr->context, fsckLostFoundDirectory, "lost+found");
5558
5559	return (0);
5560}
5561
5562
5563static OSErr
5564FixMissingDirectory( SGlob *GPtr, UInt32 theObjID, UInt32 theParID )
5565{
5566	Boolean				isHFSPlus;
5567	UInt16				recSize;
5568	OSErr				result;
5569	int					nameLen;
5570	UInt32				hint;
5571	char 				myString[ 32 ];
5572	CatalogName			myName;
5573	CatalogRecord		catRec;
5574	CatalogKey			myKey, myThreadKey;
5575
5576	isHFSPlus = VolumeObjectIsHFSPlus( );
5577
5578	// we will use the object ID of the missing directory as the name since we have
5579	// no way to find the original name and this should make it unique within our
5580	// lost+found directory.
5581	sprintf( myString, "%ld", (long)theObjID );
5582	nameLen = strlen( myString );
5583
5584    if ( isHFSPlus )
5585    {
5586        int		i;
5587        myName.ustr.length = nameLen;
5588        for ( i = 0; i < myName.ustr.length; i++ )
5589            myName.ustr.unicode[ i ] = (u_int16_t) myString[ i ];
5590    }
5591    else
5592    {
5593        myName.pstr[0] = nameLen;
5594        memcpy( &myName.pstr[1], &myString[0], nameLen );
5595    }
5596
5597	// make sure the name is not already used
5598	BuildCatalogKey( theParID, &myName, isHFSPlus, &myKey );
5599	result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &myKey, kNoHint,
5600								NULL, &catRec, &recSize, &hint );
5601	if ( result == noErr )
5602		return( R_IntErr );
5603
5604    // insert new directory and thread record into the catalog
5605	recSize = BuildThreadRec( &myKey, &catRec, isHFSPlus, true );
5606	BuildCatalogKey( theObjID, NULL, isHFSPlus, &myThreadKey );
5607	result	= InsertBTreeRecord( GPtr->calculatedCatalogFCB, &myThreadKey, &catRec, recSize, &hint );
5608	if ( result != noErr )
5609		return( result );
5610
5611	recSize = BuildFolderRec( GPtr, 01777, theObjID, isHFSPlus, &catRec );
5612
5613	result	= InsertBTreeRecord( GPtr->calculatedCatalogFCB, &myKey, &catRec, recSize, &hint );
5614	if ( result != noErr )
5615		return( result );
5616
5617	/* update parent directory to reflect addition of new directory */
5618	result = UpdateFolderCount( GPtr->calculatedVCB, theParID, NULL,
5619								((isHFSPlus) ? kHFSPlusFolderRecord : kHFSFolderRecord),
5620								kNoHint, 1 );
5621
5622	/* update our header node on disk from our BTreeControlBlock */
5623	UpdateBTreeHeader( GPtr->calculatedCatalogFCB );
5624
5625	return( result );
5626
5627} /* FixMissingDirectory */
5628
5629
5630static HFSCatalogNodeID
5631GetObjectID( CatalogRecord * theRecPtr )
5632{
5633    HFSCatalogNodeID	myObjID;
5634
5635	switch ( theRecPtr->recordType ) {
5636	case kHFSPlusFolderRecord:
5637        myObjID = theRecPtr->hfsPlusFolder.folderID;
5638		break;
5639	case kHFSPlusFileRecord:
5640        myObjID = theRecPtr->hfsPlusFile.fileID;
5641		break;
5642	case kHFSFolderRecord:
5643        myObjID = theRecPtr->hfsFolder.folderID;
5644		break;
5645	case kHFSFileRecord:
5646        myObjID = theRecPtr->hfsFile.fileID;
5647		break;
5648	default:
5649        myObjID = 0;
5650	}
5651
5652    return( myObjID );
5653
5654} /* GetObjectID */
5655
5656/* Function: CreateFileByName
5657 *
5658 * Description: Create a file with given fileName of type fileType containing
5659 * data of length dataLen.  This function assumes that the name of symlink
5660 * to be created is passed as UTF8
5661 *
5662 * Input:
5663 *	1. GPtr - pointer to global scavenger structure
5664 *	2. parentID - ID of parent directory to create the new file.
5665 *	3. fileName - name of the file to create in UTF8 format.
5666 *	4. fileNameLen - length of the filename to be created.
5667 *			If the volume is HFS Plus, the filename is delimited to
5668 *			kHFSPlusMaxFileNameChars characters.
5669 *			If the volume is plain HFS, the filename is delimited to
5670 *			kHFSMaxFileNameChars characters.
5671 *	5. fileType - file type (currently supported S_IFREG, S_IFLNK).
5672 *	6. data - data content of first data fork of the file
5673 *	7. dataLen - length of data to be written
5674 *
5675 * Output:
5676 *	returns zero on success, non-zero on failure.
5677 *		memFullErr - Not enough memory
5678 *		paramErr - Invalid paramter
5679 */
5680OSErr CreateFileByName(SGlobPtr GPtr, UInt32 parentID, UInt16 fileType, u_char *fileName, unsigned int filenameLen, u_char *data, unsigned int dataLen)
5681{
5682	OSErr err = noErr;
5683	Boolean isHFSPlus;
5684	Boolean isCatUpdated = false;
5685
5686	CatalogName fName;
5687	CatalogRecord catRecord;
5688	CatalogKey catKey;
5689	CatalogKey threadKey;
5690	UInt32 hint;
5691	UInt16 recordSize;
5692
5693	UInt32 startBlock = 0;
5694	UInt32 blockCount = 0;
5695	UInt32 nextCNID;
5696
5697	isHFSPlus = VolumeObjectIsHFSPlus();
5698
5699	/* Construct unicode name for file name to construct catalog key */
5700	if (isHFSPlus) {
5701		/* Convert utf8 filename to Unicode filename */
5702		size_t namelen;
5703
5704		if (filenameLen < kHFSPlusMaxFileNameChars) {
5705			(void) utf_decodestr (fileName, filenameLen, fName.ustr.unicode, &namelen, sizeof(fName.ustr.unicode));
5706			namelen /= 2;
5707			fName.ustr.length = namelen;
5708		} else {
5709			/* The resulting may result in more than kHFSPlusMaxFileNameChars chars */
5710			UInt16 *unicodename;
5711
5712			/* Allocate a large array to convert the utf-8 to utf-16 */
5713			unicodename = malloc (filenameLen * 4);
5714			if (unicodename == NULL) {
5715				err = memFullErr;
5716				goto out;
5717			}
5718
5719			(void) utf_decodestr (fileName, filenameLen, unicodename, &namelen, filenameLen * 4);
5720			namelen /= 2;
5721
5722			/* Chopping unicode string based on length might affect unicode
5723			 * chars that take more than one UInt16 - very rare possiblity.
5724			 */
5725			if (namelen > kHFSPlusMaxFileNameChars) {
5726				namelen = kHFSPlusMaxFileNameChars;
5727			}
5728
5729			memcpy (fName.ustr.unicode, unicodename, (namelen * 2));
5730			free (unicodename);
5731			fName.ustr.length = namelen;
5732		}
5733	} else {
5734		if (filenameLen > kHFSMaxFileNameChars) {
5735			filenameLen = kHFSMaxFileNameChars;
5736		}
5737		fName.pstr[0] = filenameLen;
5738		memcpy(&fName.pstr[1], fileName, filenameLen);
5739	}
5740
5741	/* Make sure that a file with same name does not exist in parent dir */
5742	BuildCatalogKey(parentID, &fName, isHFSPlus, &catKey);
5743	err = SearchBTreeRecord(GPtr->calculatedCatalogFCB, &catKey, kNoHint, NULL,
5744							&catRecord, &recordSize, &hint);
5745	if (err != fsBTRecordNotFoundErr) {
5746#if DEBUG_OVERLAP
5747		plog ("%s: %s probably exists in dirID = %d (err=%d)\n", __FUNCTION__, fileName, parentID, err);
5748#endif
5749		err = EEXIST;
5750		goto out;
5751	}
5752
5753	if (data) {
5754		/* Calculate correct number of blocks required for data */
5755		if (dataLen % (GPtr->calculatedVCB->vcbBlockSize)) {
5756			blockCount = (dataLen / (GPtr->calculatedVCB->vcbBlockSize)) + 1;
5757		} else {
5758			blockCount = dataLen / (GPtr->calculatedVCB->vcbBlockSize);
5759		}
5760
5761		if (blockCount) {
5762			/* Allocate disk space for the data */
5763			err = AllocateContigBitmapBits (GPtr->calculatedVCB, blockCount, &startBlock);
5764			if (err != noErr) {
5765#if DEBUG_OVERLAP
5766				plog ("%s: Not enough disk space (err=%d)\n", __FUNCTION__, err);
5767#endif
5768				goto out;
5769			}
5770
5771			/* Write the data to the blocks */
5772			err = WriteBufferToDisk(GPtr, startBlock, blockCount, data, dataLen);
5773			if (err != noErr) {
5774#if DEBUG_OVERLAP
5775				plog ("%s: Error in writing data of %s to disk (err=%d)\n", __FUNCTION__, fileName, err);
5776#endif
5777				goto out;
5778			}
5779		}
5780	}
5781
5782	/* Build and insert thread record */
5783	nextCNID = GPtr->calculatedVCB->vcbNextCatalogID;
5784	if (!isHFSPlus && nextCNID == 0xffffFFFF) {
5785		goto out;
5786	}
5787	recordSize = BuildThreadRec(&catKey, &catRecord, isHFSPlus, false);
5788	for (;;) {
5789		BuildCatalogKey(nextCNID, NULL, isHFSPlus, &threadKey);
5790		err	= InsertBTreeRecord(GPtr->calculatedCatalogFCB, &threadKey, &catRecord,
5791								recordSize, &hint );
5792		if (err == fsBTDuplicateRecordErr && isHFSPlus) {
5793			/* Allow CNIDs on HFS Plus volumes to wrap around */
5794			++nextCNID;
5795			if (nextCNID < kHFSFirstUserCatalogNodeID) {
5796				GPtr->calculatedVCB->vcbAttributes |= kHFSCatalogNodeIDsReusedMask;
5797				MarkVCBDirty(GPtr->calculatedVCB);
5798				nextCNID = kHFSFirstUserCatalogNodeID;
5799			}
5800			continue;
5801		}
5802		break;
5803	}
5804	if (err != noErr) {
5805#if DEBUG_OVERLAP
5806		plog ("%s: Error inserting thread record for file = %s (err=%d)\n", __FUNCTION__, fileName, err);
5807#endif
5808		goto out;
5809	}
5810
5811	/* Build file record */
5812	recordSize = BuildFileRec(fileType, 0666, nextCNID, isHFSPlus, &catRecord);
5813	if (recordSize == 0) {
5814#if DEBUG_OVERLAP
5815		plog ("%s: Incorrect fileType\n", __FUNCTION__);
5816#endif
5817
5818		/* Remove the thread record inserted above */
5819		err = DeleteBTreeRecord (GPtr->calculatedCatalogFCB, &threadKey);
5820		if (err != noErr) {
5821#if DEBUG_OVERLAP
5822			plog ("%s: Error in removing thread record\n", __FUNCTION__);
5823#endif
5824		}
5825		err = paramErr;
5826		goto out;
5827	}
5828
5829	/* Update startBlock, blockCount, etc */
5830	if (isHFSPlus) {
5831		catRecord.hfsPlusFile.dataFork.logicalSize = dataLen;
5832		catRecord.hfsPlusFile.dataFork.totalBlocks = blockCount;
5833		catRecord.hfsPlusFile.dataFork.extents[0].startBlock = startBlock;
5834		catRecord.hfsPlusFile.dataFork.extents[0].blockCount = blockCount;
5835	} else {
5836		catRecord.hfsFile.dataLogicalSize = dataLen;
5837		catRecord.hfsFile.dataPhysicalSize = blockCount * GPtr->calculatedVCB->vcbBlockSize;
5838		catRecord.hfsFile.dataExtents[0].startBlock = startBlock;
5839		catRecord.hfsFile.dataExtents[0].blockCount = blockCount;
5840	}
5841
5842	/* Insert catalog file record */
5843    err	= InsertBTreeRecord(GPtr->calculatedCatalogFCB, &catKey, &catRecord, recordSize, &hint );
5844	if (err == noErr) {
5845		isCatUpdated = true;
5846
5847#if DEBUG_OVERLAP
5848		plog ("Created \"%s\" with ID = %d startBlock = %d, blockCount = %d, dataLen = %d\n", fileName, nextCNID, startBlock, blockCount, dataLen);
5849#endif
5850	} else {
5851#if DEBUG_OVERLAP
5852		plog ("%s: Error in inserting file record for file = %s (err=%d)\n", __FUNCTION__, fileName, err);
5853#endif
5854
5855		/* remove the thread record inserted above */
5856		err = DeleteBTreeRecord (GPtr->calculatedCatalogFCB, &threadKey);
5857		if (err != noErr) {
5858#if DEBUG_OVERLAP
5859			plog ("%s: Error in removing thread record\n", __FUNCTION__);
5860#endif
5861		}
5862		err = paramErr;
5863		goto out;
5864	}
5865
5866	/* Update volume header */
5867	GPtr->calculatedVCB->vcbNextCatalogID = nextCNID + 1;
5868	if (GPtr->calculatedVCB->vcbNextCatalogID < kHFSFirstUserCatalogNodeID) {
5869		GPtr->calculatedVCB->vcbAttributes |= kHFSCatalogNodeIDsReusedMask;
5870		GPtr->calculatedVCB->vcbNextCatalogID = kHFSFirstUserCatalogNodeID;
5871	}
5872	MarkVCBDirty( GPtr->calculatedVCB );
5873
5874	/* update our header node on disk from our BTreeControlBlock */
5875	UpdateBTreeHeader(GPtr->calculatedCatalogFCB);
5876
5877	/* update parent directory to reflect addition of new file */
5878	err = UpdateFolderCount(GPtr->calculatedVCB, parentID, NULL, kHFSPlusFileRecord, kNoHint, 1);
5879	if (err != noErr) {
5880#if DEBUG_OVERLAP
5881		plog ("%s: Error in updating parent folder count (err=%d)\n", __FUNCTION__, err);
5882#endif
5883		goto out;
5884	}
5885
5886out:
5887	/* On error, if catalog record was not inserted and disk block were allocated,
5888	 * deallocate the blocks
5889	 */
5890	if (err && (isCatUpdated == false) && startBlock) {
5891		ReleaseBitmapBits (startBlock, blockCount);
5892	}
5893
5894	return err;
5895} /* CreateFileByName */
5896
5897/* Function: CreateDirByName
5898 *
5899 * Description: Create directory with name dirName in a directory with ID as
5900 * parentID.  The function assumes that the dirName passed is ASCII.
5901 *
5902 * Input:
5903 *	GPtr - global scavenger structure pointer
5904 * 	dirName - name of directory to be created
5905 *	parentID - dirID of the parent directory for new directory
5906 *
5907 * Output:
5908 * 	on success, ID of the new directory created.
5909 * 	on failure, zero.
5910 *
5911 */
5912UInt32 CreateDirByName(SGlob *GPtr , const u_char *dirName, const UInt32 parentID)
5913{
5914	Boolean				isHFSPlus;
5915	UInt16				recSize;
5916	UInt16				myMode;
5917	int					result;
5918	int					nameLen;
5919	UInt32				hint;
5920	UInt32				nextCNID;
5921	SFCB *				fcbPtr;
5922	CatalogKey			myKey;
5923	CatalogName			myName;
5924	CatalogRecord		catRec;
5925
5926	isHFSPlus = VolumeObjectIsHFSPlus( );
5927  	fcbPtr = GPtr->calculatedCatalogFCB;
5928	nameLen = strlen( (char *)dirName );
5929
5930    if ( isHFSPlus )
5931    {
5932        int		i;
5933        myName.ustr.length = nameLen;
5934        for ( i = 0; i < myName.ustr.length; i++ )
5935            myName.ustr.unicode[ i ] = (u_int16_t) dirName[ i ];
5936    }
5937    else
5938    {
5939        myName.pstr[0] = nameLen;
5940        memcpy( &myName.pstr[1], &dirName[0], nameLen );
5941    }
5942
5943	// see if we already have a lost and found directory
5944	BuildCatalogKey( parentID, &myName, isHFSPlus, &myKey );
5945	result = SearchBTreeRecord( fcbPtr, &myKey, kNoHint, NULL, &catRec, &recSize, &hint );
5946	if ( result == noErr ) {
5947		if ( isHFSPlus ) {
5948			if ( catRec.recordType == kHFSPlusFolderRecord )
5949				return( catRec.hfsPlusFolder.folderID );
5950		}
5951		else if ( catRec.recordType == kHFSFolderRecord )
5952			return( catRec.hfsFolder.folderID );
5953        return( 0 );  // something already there but not a directory
5954	}
5955
5956    // insert new directory and thread record into the catalog
5957	nextCNID = GPtr->calculatedVCB->vcbNextCatalogID;
5958	if ( !isHFSPlus && nextCNID == 0xFFFFFFFF )
5959		return( 0 );
5960
5961	recSize = BuildThreadRec( &myKey, &catRec, isHFSPlus, true );
5962	for (;;) {
5963		CatalogKey			key;
5964
5965		BuildCatalogKey( nextCNID, NULL, isHFSPlus, &key );
5966		result	= InsertBTreeRecord( fcbPtr, &key, &catRec, recSize, &hint );
5967		if ( result == fsBTDuplicateRecordErr && isHFSPlus ) {
5968			/*
5969			 * Allow CNIDs on HFS Plus volumes to wrap around
5970			 */
5971			++nextCNID;
5972			if ( nextCNID < kHFSFirstUserCatalogNodeID ) {
5973				GPtr->calculatedVCB->vcbAttributes |= kHFSCatalogNodeIDsReusedMask;
5974				MarkVCBDirty( GPtr->calculatedVCB );
5975				nextCNID = kHFSFirstUserCatalogNodeID;
5976			}
5977			continue;
5978		}
5979		break;
5980	}
5981	if ( result != 0 )
5982		return( 0 );
5983
5984	myMode = ( GPtr->lostAndFoundMode == 0 ) ? 01777 : GPtr->lostAndFoundMode;
5985	recSize = BuildFolderRec( GPtr, myMode, nextCNID, isHFSPlus, &catRec );
5986    result	= InsertBTreeRecord( fcbPtr, &myKey, &catRec, recSize, &hint );
5987	if ( result != 0 )
5988		return( 0 );
5989
5990	/* Update volume header */
5991	GPtr->calculatedVCB->vcbNextCatalogID = nextCNID + 1;
5992	if ( GPtr->calculatedVCB->vcbNextCatalogID < kHFSFirstUserCatalogNodeID ) {
5993		GPtr->calculatedVCB->vcbAttributes |= kHFSCatalogNodeIDsReusedMask;
5994		GPtr->calculatedVCB->vcbNextCatalogID = kHFSFirstUserCatalogNodeID;
5995	}
5996	MarkVCBDirty( GPtr->calculatedVCB );
5997
5998	/* update parent directory to reflect addition of new directory */
5999	result = UpdateFolderCount( GPtr->calculatedVCB, parentID, NULL, kHFSPlusFolderRecord, kNoHint, 1 );
6000
6001	/* update our header node on disk from our BTreeControlBlock */
6002	UpdateBTreeHeader( GPtr->calculatedCatalogFCB );
6003
6004	return( nextCNID );
6005
6006} /* CreateDirByName */
6007
6008static void
6009CountFolderItems(SGlobPtr GPtr,  UInt32 folderID, Boolean isHFSPlus, UInt32 *itemCount, UInt32 *folderCount)
6010{
6011	SFCB *fcb = GPtr->calculatedCatalogFCB;
6012	OSErr err = 0;
6013	BTreeIterator iterator;
6014	FSBufferDescriptor btRecord;
6015	union {
6016		HFSPlusCatalogFolder catRecord;
6017		HFSPlusCatalogFile catFile;
6018	} catRecord;
6019	HFSPlusCatalogKey *key;
6020	UInt16 recordSize = 0;
6021	int fCount = 0, iCount = 0;
6022
6023	ClearMemory(&iterator, sizeof(iterator));
6024	key = (HFSPlusCatalogKey*)&iterator.key;
6025	BuildCatalogKey(folderID, NULL, isHFSPlus, (CatalogKey*)key);
6026	btRecord.bufferAddress = &catRecord;
6027	btRecord.itemCount = 1;
6028	btRecord.itemSize = sizeof(catRecord);
6029
6030	for (err = BTSearchRecord(fcb, &iterator, kNoHint, &btRecord, &recordSize, &iterator);
6031		err == 0;
6032		err = BTIterateRecord(fcb, kBTreeNextRecord, &iterator, &btRecord, &recordSize)) {
6033		if (catRecord.catRecord.recordType == kHFSPlusFolderThreadRecord ||
6034		    catRecord.catRecord.recordType == kHFSPlusFileThreadRecord ||
6035			catRecord.catRecord.recordType == kHFSFolderThreadRecord ||
6036			catRecord.catRecord.recordType == kHFSFileThreadRecord)
6037			continue;
6038		if (key->parentID != folderID)
6039			break;
6040		if (isHFSPlus &&
6041			(catRecord.catRecord.recordType == kHFSPlusFileRecord) &&
6042			(catRecord.catFile.flags & kHFSHasLinkChainMask) &&
6043			(catRecord.catFile.userInfo.fdType == kHFSAliasType) &&
6044			(catRecord.catFile.userInfo.fdCreator == kHFSAliasCreator) &&
6045			(key->parentID != GPtr->filelink_priv_dir_id)) {
6046			// It's a directory hard link, which counts as a directory here
6047			fCount++;
6048		}
6049		if (catRecord.catRecord.recordType == kHFSPlusFolderRecord)
6050			fCount++;
6051		iCount++;
6052	}
6053	if (itemCount)
6054		*itemCount = iCount;
6055	if (folderCount)
6056		*folderCount = fCount;
6057	return;
6058}
6059/*
6060 * Build a catalog node folder record with the given input.
6061 */
6062static int
6063BuildFolderRec( SGlob *GPtr, u_int16_t theMode, UInt32 theObjID, Boolean isHFSPlus, CatalogRecord * theRecPtr )
6064{
6065	UInt16				recSize;
6066	UInt32 				createTime;
6067	UInt32 vCount = 0, fCount = 0;
6068
6069	ClearMemory( (Ptr)theRecPtr, sizeof(*theRecPtr) );
6070
6071	CountFolderItems(GPtr, theObjID, isHFSPlus, &vCount, &fCount);
6072	if ( isHFSPlus ) {
6073		createTime = GetTimeUTC();
6074		theRecPtr->hfsPlusFolder.recordType = kHFSPlusFolderRecord;
6075		theRecPtr->hfsPlusFolder.folderID = theObjID;
6076		theRecPtr->hfsPlusFolder.createDate = createTime;
6077		theRecPtr->hfsPlusFolder.contentModDate = createTime;
6078		theRecPtr->hfsPlusFolder.attributeModDate = createTime;
6079		theRecPtr->hfsPlusFolder.bsdInfo.ownerID = getuid( );
6080		theRecPtr->hfsPlusFolder.bsdInfo.groupID = getgid( );
6081		theRecPtr->hfsPlusFolder.bsdInfo.fileMode = S_IFDIR;
6082		theRecPtr->hfsPlusFolder.bsdInfo.fileMode |= theMode;
6083		theRecPtr->hfsPlusFolder.valence = vCount;
6084		recSize= sizeof(HFSPlusCatalogFolder);
6085		if (VolumeObjectIsHFSX(GPtr)) {
6086			theRecPtr->hfsPlusFolder.flags |= kHFSHasFolderCountMask;
6087			theRecPtr->hfsPlusFolder.folderCount = fCount;
6088		}
6089	}
6090	else {
6091		createTime = GetTimeLocal( true );
6092		theRecPtr->hfsFolder.recordType = kHFSFolderRecord;
6093		theRecPtr->hfsFolder.folderID = theObjID;
6094		theRecPtr->hfsFolder.createDate = createTime;
6095		theRecPtr->hfsFolder.modifyDate = createTime;
6096		theRecPtr->hfsFolder.valence = vCount;
6097		recSize= sizeof(HFSCatalogFolder);
6098	}
6099
6100	return( recSize );
6101
6102} /* BuildFolderRec */
6103
6104
6105/*
6106 * Build a catalog node thread record from a catalog key
6107 * and return the size of the record.
6108 */
6109static int
6110BuildThreadRec( CatalogKey * theKeyPtr, CatalogRecord * theRecPtr,
6111				Boolean isHFSPlus, Boolean isDirectory )
6112{
6113	int size = 0;
6114
6115	if ( isHFSPlus ) {
6116		HFSPlusCatalogKey *key = (HFSPlusCatalogKey *)theKeyPtr;
6117		HFSPlusCatalogThread *rec = (HFSPlusCatalogThread *)theRecPtr;
6118
6119		size = sizeof(HFSPlusCatalogThread);
6120		if ( isDirectory )
6121			rec->recordType = kHFSPlusFolderThreadRecord;
6122		else
6123			rec->recordType = kHFSPlusFileThreadRecord;
6124		rec->reserved = 0;
6125		rec->parentID = key->parentID;
6126		bcopy(&key->nodeName, &rec->nodeName,
6127			sizeof(UniChar) * (key->nodeName.length + 1));
6128
6129		/* HFS Plus has varaible sized thread records */
6130		size -= (sizeof(rec->nodeName.unicode) -
6131			  (rec->nodeName.length * sizeof(UniChar)));
6132	}
6133	else /* HFS standard */ {
6134		HFSCatalogKey *key = (HFSCatalogKey *)theKeyPtr;
6135		HFSCatalogThread *rec = (HFSCatalogThread *)theRecPtr;
6136
6137		size = sizeof(HFSCatalogThread);
6138		bzero(rec, size);
6139		if ( isDirectory )
6140			rec->recordType = kHFSFolderThreadRecord;
6141		else
6142			rec->recordType = kHFSFileThreadRecord;
6143		rec->parentID = key->parentID;
6144		bcopy(key->nodeName, rec->nodeName, key->nodeName[0]+1);
6145	}
6146
6147	return (size);
6148
6149} /* BuildThreadRec */
6150
6151/* Function: BuildFileRec
6152 *
6153 * Description: Build a catalog file record with given fileID, fileType
6154 * and fileMode.
6155 *
6156 * Input:
6157 *	1. fileType - currently supports S_IFREG, S_IFLNK
6158 *	2. fileMode - file mode desired.
6159 *	3. fileID - file ID
6160 *	4. isHFSPlus - indicates whether the record is being created for
6161 *	HFSPlus volume or plain HFS volume.
6162 *	5. catRecord - pointer to catalog record
6163 *
6164 * Output:
6165 *	returns size of the catalog record.
6166 *	on success, non-zero value; on failure, zero.
6167 */
6168static int BuildFileRec(UInt16 fileType, UInt16 fileMode, UInt32 fileID, Boolean isHFSPlus, CatalogRecord *catRecord)
6169{
6170	UInt16 recordSize = 0;
6171	UInt32 createTime;
6172
6173	/* We only support creating S_IFREG and S_IFLNK and S_IFLNK is not supported
6174	 * on plain HFS
6175	 */
6176	if (((fileType != S_IFREG) && (fileType != S_IFLNK)) ||
6177	 	((isHFSPlus == false) && (fileType == S_IFLNK))) {
6178		goto out;
6179	}
6180
6181	ClearMemory((Ptr)catRecord, sizeof(*catRecord));
6182
6183	if ( isHFSPlus ) {
6184		createTime = GetTimeUTC();
6185		catRecord->hfsPlusFile.recordType = kHFSPlusFileRecord;
6186		catRecord->hfsPlusFile.fileID = fileID;
6187		catRecord->hfsPlusFile.createDate = createTime;
6188		catRecord->hfsPlusFile.contentModDate = createTime;
6189		catRecord->hfsPlusFile.attributeModDate = createTime;
6190		catRecord->hfsPlusFile.bsdInfo.ownerID = getuid();
6191		catRecord->hfsPlusFile.bsdInfo.groupID = getgid();
6192		catRecord->hfsPlusFile.bsdInfo.fileMode = fileType;
6193		catRecord->hfsPlusFile.bsdInfo.fileMode |= fileMode;
6194		if (fileType == S_IFLNK) {
6195			catRecord->hfsPlusFile.userInfo.fdType = kSymLinkFileType;
6196			catRecord->hfsPlusFile.userInfo.fdCreator = kSymLinkCreator;
6197		} else {
6198			catRecord->hfsPlusFile.userInfo.fdType = kTextFileType;
6199			catRecord->hfsPlusFile.userInfo.fdCreator = kTextFileCreator;
6200		}
6201		recordSize= sizeof(HFSPlusCatalogFile);
6202	}
6203	else {
6204		createTime = GetTimeLocal(true);
6205		catRecord->hfsFile.recordType = kHFSFileRecord;
6206		catRecord->hfsFile.fileID = fileID;
6207		catRecord->hfsFile.createDate = createTime;
6208		catRecord->hfsFile.modifyDate = createTime;
6209		catRecord->hfsFile.userInfo.fdType = kTextFileType;
6210		catRecord->hfsFile.userInfo.fdCreator = kTextFileCreator;
6211		recordSize= sizeof(HFSCatalogFile);
6212	}
6213
6214out:
6215	return(recordSize);
6216} /* BuildFileRec */
6217
6218/* Function: BuildAttributeKey
6219 *
6220 * Build attribute key based on given information like -
6221 * fileID, startBlock, attribute name and attribute name length.
6222 *
6223 * Note that the attribute name is the UTF-8 format string.
6224 */
6225static void BuildAttributeKey(u_int32_t fileID, u_int32_t startBlock,
6226			unsigned char *attrName, u_int16_t attrNameLen, HFSPlusAttrKey *key)
6227{
6228	size_t attrNameLenBytes;
6229
6230	assert(VolumeObjectIsHFSPlus() == true);
6231
6232	key->pad = 0;
6233	key->fileID = fileID;
6234	key->startBlock = startBlock;
6235
6236	/* Convert UTF-8 attribute name to unicode */
6237	(void) utf_decodestr(attrName, attrNameLen, key->attrName, &attrNameLenBytes, sizeof(key->attrName));
6238	key->attrNameLen = attrNameLenBytes / 2;
6239
6240	key->keyLength = kHFSPlusAttrKeyMinimumLength + attrNameLenBytes;
6241}
6242
6243/* Delete catalog record and thread record for given ID.  On successful
6244 * deletion, this function also updates the valence and folder count for
6245 * the parent directory and the file/folder count in the volume header.
6246 *
6247 * Returns - zero on success, non-zero on failure.
6248 */
6249static int DeleteCatalogRecordByID(SGlobPtr GPtr, uint32_t id, Boolean for_rename)
6250{
6251	int retval;
6252	CatalogRecord rec;
6253	CatalogKey key;
6254	UInt16 recsize;
6255	Boolean isHFSPlus;
6256
6257	isHFSPlus = VolumeObjectIsHFSPlus();
6258
6259	/* Lookup the catalog record to move */
6260	retval = GetCatalogRecordByID(GPtr, id, isHFSPlus, &key, &rec, &recsize);
6261	if (retval) {
6262		return retval;
6263	}
6264
6265	/* Delete the record */
6266	if (isHFSPlus) {
6267		retval = DeleteCatalogNode(GPtr->calculatedVCB,
6268				key.hfsPlus.parentID,
6269				(const CatalogName *)&key.hfsPlus.nodeName,
6270				kNoHint, for_rename);
6271	} else {
6272		retval = DeleteCatalogNode(GPtr->calculatedVCB,
6273				key.hfs.parentID,
6274				(const CatalogName *)&key.hfs.nodeName,
6275				kNoHint, for_rename);
6276	}
6277
6278	/* If deletion of record succeeded, and the operation was not
6279	 * being performed for rename, and the volume is HFS+, try
6280	 * deleting all extended attributes for this file/folder
6281	 */
6282	if ((retval == 0) && (for_rename == false) && (isHFSPlus == true)) {
6283		/* Delete all attributes associated with this ID */
6284		retval = DeleteAllAttrsByID(GPtr, id);
6285	}
6286
6287	return retval;
6288}
6289
6290/* Move a catalog record with given ID to a new parent directory with given
6291 * parentID.  This function should only be called for HFS+ volumes.
6292 * This function removes the catalog record from old parent and inserts
6293 * it back with the new parent ID.  It also takes care of updating the
6294 * parent directory counts.  Note that this function clears kHFSHasLinkChainBit
6295 * from the catalog record flags.
6296 *
6297 * On success, returns zero.  On failure, returns non-zero.
6298 */
6299static int MoveCatalogRecordByID(SGlobPtr GPtr, uint32_t id, uint32_t new_parentid)
6300{
6301	int retval;
6302	CatalogRecord rec;
6303	CatalogKey key;
6304	UInt32 hint;
6305	UInt16 recsize;
6306	Boolean isFolder = false;
6307	BTreeIterator iterator;
6308
6309	assert (VolumeObjectIsHFSPlus() == true);
6310
6311	/* Lookup the catalog record to move */
6312	retval = GetCatalogRecordByID(GPtr, id, true, &key, &rec, &recsize);
6313	if (retval) {
6314		goto out;
6315	}
6316
6317	/* Delete the record and its thread from original location.
6318	 * For file records, do not deallocate original extents.
6319	 */
6320	retval = DeleteCatalogRecordByID(GPtr, id, true);
6321	if (retval) {
6322		goto out;
6323	}
6324
6325	key.hfsPlus.parentID = new_parentid;
6326	/* The record being moved should not have linkChainMask set */
6327	if (rec.recordType == kHFSPlusFolderRecord) {
6328		rec.hfsPlusFolder.flags &= ~kHFSHasLinkChainMask;
6329		isFolder = true;
6330	} else if (rec.recordType == kHFSPlusFileRecord) {
6331		rec.hfsPlusFile.flags &= ~kHFSHasLinkChainMask;
6332		isFolder = false;
6333	}
6334
6335	/* Insert the catalog record with new parent */
6336	retval = InsertBTreeRecord(GPtr->calculatedCatalogFCB, &key, &rec,
6337			recsize, &hint);
6338	if (retval) {
6339		goto out;
6340	}
6341
6342	/* Insert the new thread record */
6343	recsize = BuildThreadRec(&key, &rec, true, isFolder);
6344	BuildCatalogKey(id, NULL, true, &key);
6345	retval = InsertBTreeRecord(GPtr->calculatedCatalogFCB, &key, &rec,
6346			recsize, &hint);
6347	if (retval) {
6348		goto out;
6349	}
6350
6351	/* Update the counts in the new parent directory and volume header */
6352	ClearMemory(&iterator, sizeof(iterator));
6353	retval = GetCatalogRecordByID(GPtr, new_parentid, true, &key, &rec, &recsize);
6354	if (retval) {
6355		if ((retval == btNotFound) && (GPtr->CBTStat & S_Orphan)) {
6356			/* No need for re-repair minor repair order because
6357			 * we are failing on updating the parent directory.
6358			 */
6359			retval = 0;
6360		}
6361		goto out;
6362	}
6363	if (rec.recordType != kHFSPlusFolderRecord) {
6364		goto out;
6365	}
6366
6367	rec.hfsPlusFolder.valence++;
6368	if ((isFolder == true) &&
6369	    (rec.hfsPlusFolder.flags & kHFSHasFolderCountMask)) {
6370		rec.hfsPlusFolder.folderCount++;
6371	}
6372
6373	retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key,
6374			kNoHint, &rec, recsize, &hint);
6375	if (retval) {
6376		goto out;
6377	}
6378
6379	if (isFolder == true) {
6380		GPtr->calculatedVCB->vcbFolderCount++;
6381	} else {
6382		GPtr->calculatedVCB->vcbFileCount++;
6383	}
6384	GPtr->VIStat |= S_MDB;
6385	GPtr->CBTStat |= S_BTH;  /* leaf record count changed */
6386
6387out:
6388	return retval;
6389}
6390
6391/* The function deletes all extended attributes associated with a given
6392 * file/folder ID.  The function takes care of deallocating allocation blocks
6393 * associated with extent based attributes.
6394 *
6395 * Note: This function deletes *all* attributes for a given file/folder.
6396 * To delete a single attribute record using a key, use delete_attr_record().
6397 *
6398 * On success, returns zero.  On failure, returns non-zero.
6399 */
6400static int DeleteAllAttrsByID(SGlobPtr GPtr, uint32_t id)
6401{
6402	int retval;
6403	BTreeIterator iterator;
6404	FSBufferDescriptor btrec;
6405	HFSPlusAttrKey *attr_key;
6406	HFSPlusAttrRecord attr_record;
6407	UInt16 record_size;
6408
6409	/* Initialize the iterator, attribute key, and attribute record */
6410	ClearMemory(&iterator, sizeof(BTreeIterator));
6411	attr_key = (HFSPlusAttrKey *)&iterator.key;
6412	attr_key->keyLength = kHFSPlusAttrKeyMinimumLength;
6413	attr_key->fileID = id;
6414
6415	ClearMemory(&btrec, sizeof(FSBufferDescriptor));
6416	btrec.bufferAddress = &attr_record;
6417	btrec.itemCount = 1;
6418	btrec.itemSize = sizeof(HFSPlusAttrRecord);
6419
6420	/* Search for attribute with NULL name which will place the
6421	 * iterator just before the first record for given id.
6422	 */
6423	retval = BTSearchRecord(GPtr->calculatedAttributesFCB, &iterator,
6424	                        kInvalidMRUCacheKey, &btrec, &record_size, &iterator);
6425	if ((retval != 0) && (retval != btNotFound)) {
6426		goto out;
6427	}
6428
6429	retval = BTIterateRecord(GPtr->calculatedAttributesFCB, kBTreeNextRecord,
6430	                         &iterator, &btrec, &record_size);
6431	while ((retval == 0) && (attr_key->fileID == id)) {
6432		/* Delete attribute record and deallocate extents, if any */
6433		retval = delete_attr_record(GPtr, attr_key, &attr_record);
6434		if (retval) {
6435			break;
6436		}
6437
6438		retval = BTIterateRecord(GPtr->calculatedAttributesFCB,
6439		                         kBTreeNextRecord, &iterator, &btrec, &record_size);
6440	}
6441
6442	if (retval == btNotFound) {
6443		retval = 0;
6444	}
6445
6446out:
6447	return retval;
6448}
6449
6450/* The function deletes an extented attribute record when the corresponding
6451 * record and key are provided.  If the record is an extent-based attribute,
6452 * it also takes care to deallocate all allocation blocks associated with
6453 * the record.
6454 *
6455 * Note: This function does not delete all attribute records associated
6456 * with the file/folder ID in the attribute key.  To delete all attributes
6457 * for given file/folder ID, use DeleteAllAttrsByID().
6458 *
6459 * On success, returns zero.  On failure, returns non-zero.
6460 */
6461static int delete_attr_record(SGlobPtr GPtr, HFSPlusAttrKey *attr_key, HFSPlusAttrRecord *attr_record)
6462{
6463	int retval;
6464	UInt32 num_blocks_freed;
6465	Boolean last_extent;
6466
6467	retval = DeleteBTreeRecord(GPtr->calculatedAttributesFCB, attr_key);
6468	if (retval == 0) {
6469		/* Set bits to write back attribute btree header and map */
6470		GPtr->ABTStat |= S_BTH + S_BTM;
6471
6472		if (attr_record->recordType == kHFSPlusAttrForkData) {
6473			retval = ReleaseExtents(GPtr->calculatedVCB,
6474		    	                    attr_record->forkData.theFork.extents,
6475		        	                &num_blocks_freed, &last_extent);
6476		} else if (attr_record->recordType == kHFSPlusAttrExtents) {
6477			retval = ReleaseExtents(GPtr->calculatedVCB,
6478		    	                    attr_record->overflowExtents.extents,
6479		        	                &num_blocks_freed, &last_extent);
6480		}
6481	}
6482
6483	return retval;
6484}
6485
6486/*------------------------------------------------------------------------------
6487
6488Routine:	ZeroFillUnusedNodes
6489
6490Function:	Write zeroes to all unused nodes of a given B-tree.
6491
6492Input:		GPtr		- pointer to scavenger global area
6493			fileRefNum	- refnum of BTree file
6494
6495Output:		ZeroFillUnusedNodes - function result:
6496			0 = no error
6497			n = error
6498------------------------------------------------------------------------------*/
6499
6500static int ZeroFillUnusedNodes(SGlobPtr GPtr, short fileRefNum)
6501{
6502	BTreeControlBlock *btcb	= GetBTreeControlBlock(fileRefNum);
6503	unsigned char *bitmap = (unsigned char *) ((BTreeExtensionsRec*)btcb->refCon)->BTCBMPtr;
6504	unsigned char mask = 0x80;
6505	OSErr err;
6506	UInt32 nodeNum;
6507	BlockDescriptor node;
6508
6509	node.buffer = NULL;
6510
6511	for (nodeNum = 0; nodeNum < btcb->totalNodes; ++nodeNum)
6512	{
6513		if ((*bitmap & mask) == 0)
6514		{
6515			/* Read the raw node, without going through hfs_swap_BTNode. */
6516			err = btcb->getBlockProc(btcb->fcbPtr, nodeNum, kGetBlock|kGetEmptyBlock, &node);
6517			if (err)
6518			{
6519				if (debug) plog("Couldn't read node #%u\n", nodeNum);
6520				return err;
6521			}
6522
6523			/* Fill the node with zeroes. */
6524			bzero(node.buffer, node.blockSize);
6525
6526			/* Release and write the node without going through hfs_swap_BTNode. */
6527			(void) btcb->releaseBlockProc(btcb->fcbPtr, &node, kReleaseBlock|kMarkBlockDirty);
6528			node.buffer = NULL;
6529		}
6530
6531		/* Move to the next bit in the bitmap. */
6532		mask >>= 1;
6533		if (mask == 0)
6534		{
6535			mask = 0x80;
6536			++bitmap;
6537		}
6538	}
6539
6540	return 0;
6541} /* end ZeroFillUnusedNodes */
6542