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:		SVerify1.c
25
26	Contains:	xxx put contents here xxx
27
28	Version:	xxx put version here xxx
29
30	Copyright:	� 1997-1999 by Apple Computer, Inc., all rights reserved.
31
32*/
33
34#include "Scavenger.h"
35#include "../cache.h"
36#include <stdlib.h>
37#include <stddef.h>
38#include <unistd.h>
39#include <errno.h>
40#include <fcntl.h>
41#include <limits.h>
42
43#include <libkern/OSByteOrder.h>
44#define SW16(x)	OSSwapBigToHostInt16(x)
45#define	SW32(x)	OSSwapBigToHostInt32(x)
46#define	SW64(x)	OSSwapBigToHostInt64(x)
47
48extern int OpenDeviceByUUID(void *uuidp, char **nameptr);
49
50//	internal routine prototypes
51
52static	int	RcdValErr( SGlobPtr GPtr, OSErr type, UInt32 correct, UInt32 incorrect, HFSCatalogNodeID parid );
53
54static	int	RcdNameLockedErr( SGlobPtr GPtr, OSErr type, UInt32 incorrect );
55
56static	OSErr	RcdMDBEmbededVolDescriptionErr( SGlobPtr GPtr, OSErr type, HFSMasterDirectoryBlock *mdb );
57
58static	OSErr	CheckNodesFirstOffset( SGlobPtr GPtr, BTreeControlBlock *btcb );
59
60static OSErr	ScavengeVolumeType( SGlobPtr GPtr, HFSMasterDirectoryBlock *mdb, UInt32 *volumeType );
61static OSErr	SeekVolumeHeader( SGlobPtr GPtr, UInt64 startSector, UInt32 numSectors, UInt64 *vHSector );
62
63/* overlapping extents verification functions prototype */
64static OSErr	AddExtentToOverlapList( SGlobPtr GPtr, HFSCatalogNodeID fileNumber, const char *attrName, UInt32 extentStartBlock, UInt32 extentBlockCount, UInt8 forkType );
65
66static	Boolean	ExtentInfoExists( ExtentsTable **extentsTableH, ExtentInfo *extentInfo);
67
68static void CheckHFSPlusExtentRecords(SGlobPtr GPtr, UInt32 fileID, const char *attrname, HFSPlusExtentRecord extent, UInt8 forkType);
69
70static void CheckHFSExtentRecords(SGlobPtr GPtr, UInt32 fileID, HFSExtentRecord extent, UInt8 forkType);
71
72static Boolean DoesOverlap(SGlobPtr GPtr, UInt32 fileID, const char *attrname, UInt32 startBlock, UInt32 blockCount, UInt8 forkType);
73
74static int CompareExtentFileID(const void *first, const void *second);
75
76/*
77 * Check if a volume is journaled.
78 *
79 * If journal_bit_only is true, the function only checks
80 * if kHFSVolumeJournaledBit is set or not.  If the bit
81 * is set, function returns 1 otherwise 0.
82 *
83 * If journal_bit_only is false, in addition to checking
84 * kHFSVolumeJournaledBit, the function also checks if the
85 * last mounted version indicates failed journal replay,
86 * or runtime corruption was detected or simply the volume
87 * is not journaled and it was not unmounted cleanly.
88 * If all of the above conditions are false and the journal
89 * bit is set, function returns 1 to indicate that the
90 * volume is journaled truly otherwise returns 1 to fake
91 * that volume is not journaled.
92 *
93 * returns:    	0 not journaled or any of the above conditions are true
94 *		1 journaled
95 *
96 */
97int
98CheckIfJournaled(SGlobPtr GPtr, Boolean journal_bit_only)
99{
100#define kIDSector 2
101
102	OSErr err;
103	int result;
104	HFSMasterDirectoryBlock	*mdbp;
105	HFSPlusVolumeHeader *vhp;
106	SVCB *vcb = GPtr->calculatedVCB;
107	ReleaseBlockOptions rbOptions;
108	BlockDescriptor block;
109
110	vhp = (HFSPlusVolumeHeader *) NULL;
111	rbOptions = kReleaseBlock;
112
113	err = GetVolumeBlock(vcb, kIDSector, kGetBlock, &block);
114	if (err) return (0);
115
116	mdbp = (HFSMasterDirectoryBlock	*) block.buffer;
117
118	if (mdbp->drSigWord == kHFSPlusSigWord || mdbp->drSigWord == kHFSXSigWord) {
119		vhp = (HFSPlusVolumeHeader *) block.buffer;
120
121	} else if (mdbp->drSigWord == kHFSSigWord) {
122
123		if (mdbp->drEmbedSigWord == kHFSPlusSigWord) {
124			UInt32 vhSector;
125			UInt32 blkSectors;
126
127			blkSectors = mdbp->drAlBlkSiz / 512;
128			vhSector  = mdbp->drAlBlSt;
129			vhSector += blkSectors * mdbp->drEmbedExtent.startBlock;
130			vhSector += kIDSector;
131
132			(void) ReleaseVolumeBlock(vcb, &block, kReleaseBlock);
133			err = GetVolumeBlock(vcb, vhSector, kGetBlock, &block);
134			if (err) return (0);
135
136			vhp = (HFSPlusVolumeHeader *) block.buffer;
137			mdbp = (HFSMasterDirectoryBlock	*) NULL;
138
139		}
140	}
141
142	if ((vhp != NULL) && (ValidVolumeHeader(vhp) == noErr)) {
143		result = ((vhp->attributes & kHFSVolumeJournaledMask) != 0);
144		if (journal_bit_only == true) {
145			goto out;
146		}
147
148		// even if journaling is enabled for this volume, we'll return
149		// false if it wasn't unmounted cleanly and it was previously
150		// mounted by someone that doesn't know about journaling.
151		// or if lastMountedVersion is kFSKMountVersion
152		if ( vhp->lastMountedVersion == kFSKMountVersion ||
153			(vhp->attributes & kHFSVolumeInconsistentMask) ||
154			((vhp->lastMountedVersion != kHFSJMountVersion) &&
155			(vhp->attributes & kHFSVolumeUnmountedMask) == 0)) {
156			result = 0;
157		}
158	} else {
159		result = 0;
160	}
161
162out:
163	(void) ReleaseVolumeBlock(vcb, &block, rbOptions);
164
165	return (result);
166}
167
168/*
169 * Get the JournalInfoBlock from a volume.
170 *
171 * It borrows code to get the volume header.  Note that it
172 * uses the primary volume header, not the alternate one.
173 * It returns 0 on success, or an error value.
174 * If requested, it will also set the block size (as a 32-bit
175 * value), via bsizep -- this is useful because the journal code
176 * needs to know the volume blocksize, but it doesn't necessarily
177 * have the header.
178 *
179 * Note also that it does direct reads, rather than going through
180 * the cache code.  This simplifies getting the JIB.
181 */
182
183static OSErr
184GetJournalInfoBlock(SGlobPtr GPtr, JournalInfoBlock *jibp, UInt32 *bsizep)
185{
186#define kIDSector 2
187
188	OSErr err;
189	int result = 0;
190	UInt32 jiBlk = 0;
191	HFSMasterDirectoryBlock	*mdbp;
192	HFSPlusVolumeHeader *vhp;
193	SVCB *vcb = GPtr->calculatedVCB;
194	ReleaseBlockOptions rbOptions;
195	BlockDescriptor block;
196	size_t blockSize = 0;
197	off_t embeddedOffset = 0;
198
199	vhp = (HFSPlusVolumeHeader *) NULL;
200	rbOptions = kReleaseBlock;
201
202	if (jibp == NULL)
203		return paramErr;
204
205	err = GetVolumeBlock(vcb, kIDSector, kGetBlock, &block);
206	if (err) return (err);
207
208	mdbp = (HFSMasterDirectoryBlock	*) block.buffer;
209
210	if (mdbp->drSigWord == kHFSPlusSigWord || mdbp->drSigWord == kHFSXSigWord) {
211		vhp = (HFSPlusVolumeHeader *) block.buffer;
212
213	} else if (mdbp->drSigWord == kHFSSigWord) {
214
215		if (mdbp->drEmbedSigWord == kHFSPlusSigWord) {
216			UInt32 vhSector;
217			UInt32 blkSectors;
218
219			blkSectors = mdbp->drAlBlkSiz / 512;
220			vhSector  = mdbp->drAlBlSt;
221			vhSector += blkSectors * mdbp->drEmbedExtent.startBlock;
222			vhSector += kIDSector;
223
224			embeddedOffset = (mdbp->drEmbedExtent.startBlock * mdbp->drAlBlkSiz) + (mdbp->drAlBlSt * Blk_Size);
225			if (debug)
226				plog("Embedded offset is %lld\n", embeddedOffset);
227
228			(void) ReleaseVolumeBlock(vcb, &block, kReleaseBlock);
229			err = GetVolumeBlock(vcb, vhSector, kGetBlock, &block);
230			if (err) return (err);
231
232			vhp = (HFSPlusVolumeHeader *) block.buffer;
233			mdbp = (HFSMasterDirectoryBlock	*) NULL;
234
235		}
236	}
237
238	if (vhp == NULL) {
239		result = paramErr;
240		goto out;
241	}
242	if ((err = ValidVolumeHeader(vhp)) != noErr) {
243		result = err;
244		goto out;
245	}
246
247	// journalInfoBlock is not automatically swapped
248	jiBlk = SW32(vhp->journalInfoBlock);
249	blockSize = vhp->blockSize;
250	(void)ReleaseVolumeBlock(vcb, &block, rbOptions);
251
252	if (jiBlk) {
253		int jfd = GPtr->DrvNum;
254		uint8_t block[blockSize];
255		ssize_t nread;
256
257		nread = pread(jfd, block, blockSize, (off_t)jiBlk * blockSize + embeddedOffset);
258		if (nread == blockSize) {
259			if (jibp)
260				memcpy(jibp, block, sizeof(JournalInfoBlock));
261			if (bsizep)
262				*bsizep = blockSize;
263			result = 0;
264		} else {
265			if (debug) {
266				plog("%s: Tried to read JIB, got %zd\n", __FUNCTION__, nread);
267				result = EINVAL;
268			}
269		}
270	}
271
272out:
273	return (result);
274}
275
276/*
277 * Journal checksum calculation, taken directly from TN1150.
278 */
279static int
280calc_checksum(unsigned char *ptr, int len)
281{
282    int i, cksum=0;
283
284    for(i=0; i < len; i++, ptr++) {
285        cksum = (cksum << 8) ^ (cksum + *ptr);
286    }
287
288    return (~cksum);
289}
290
291/*
292 * The journal_header structure is not defined in <hfs/hfs_format.h>;
293 * it's described in TN1150.  It is on disk in the endian mode that was
294 * used to write it, so we may or may not need to swap the fields.
295 */
296typedef struct journal_header {
297	UInt32    magic;
298	UInt32    endian;
299	UInt64    start;
300	UInt64    end;
301	UInt64    size;
302	UInt32    blhdr_size;
303	UInt32    checksum;
304	UInt32    jhdr_size;
305	UInt32    sequence_num;
306} journal_header;
307
308#define JOURNAL_HEADER_MAGIC  0x4a4e4c78
309#define ENDIAN_MAGIC          0x12345678
310#define JOURNAL_HEADER_CKSUM_SIZE  (offsetof(struct journal_header, sequence_num))
311
312/*
313 * Determine if a journal is empty.
314 * This code can use an in-filesystem, or external, journal.
315 * In general, it returns 0 if the journal exists, and appears to
316 * be non-empty (that is, start and end in the journal header are
317 * the same); it will return 1 if it exists and is empty, or if
318 * there was a problem getting the journal.  (This behaviour was
319 * chosen because it mimics the existing behaviour of fsck_hfs,
320 * which has traditionally done nothing with the journal.  Future
321 * versions may be more demanding.)
322 *
323 * <jp> is an OUT parameter:  the contents of the structure it points
324 * to are filled in by this routine.  (The reasoning for doing this
325 * is because this rountine has to open the journal info block, and read
326 * from the journal device, so putting this in another function was
327 * duplicative and error-prone.  By making it a structure instead of
328 * discrete arguments, it can also be extended in the future if necessary.)
329 */
330int
331IsJournalEmpty(SGlobPtr GPtr, fsckJournalInfo_t *jp)
332{
333	int retval = 1;
334	OSErr result;
335	OSErr err = 0;
336	JournalInfoBlock jib;
337	UInt32 bsize;
338
339	result = GetJournalInfoBlock(GPtr, &jib, &bsize);
340	if (result == 0) {
341	/* jib is not byte swapped */
342		/* If the journal needs to be initialized, it's empty. */
343		if ((SW32(jib.flags) & kJIJournalNeedInitMask) == 0) {
344			off_t hdrOffset = SW64(jib.offset);
345			struct journal_header *jhdr;
346			uint8_t block[bsize];
347			ssize_t nread;
348			int jfd = -1;
349
350			/* If it's an external journal, kJIJournalInSFMask will not be set */
351			if (SW32(jib.flags) & kJIJournalInFSMask) {
352				jfd = dup(GPtr->DrvNum);
353				jp->name = strdup(GPtr->deviceNode);
354			} else {
355				char **namePtr = jp ? &jp->name : NULL;
356				if (debug)
357					plog("External Journal device\n");
358				jfd = OpenDeviceByUUID(&jib.ext_jnl_uuid, namePtr);
359			}
360			if (jfd == -1) {
361				if (debug) {
362					plog("Unable to get journal file descriptor, journal flags = %#x\n", SW32(jib.flags));
363				}
364				goto out;
365			}
366			if (jp) {
367				jp->jnlfd = jfd;
368				jp->jnlOffset = SW64(jib.offset);
369				jp->jnlSize = SW64(jib.size);
370			}
371
372			nread = pread(jfd, block, bsize, hdrOffset);
373			if (nread == -1) {
374				if (debug) {
375					plog("Could not read journal from descriptor %d: %s", jfd, strerror(errno));
376				}
377				err = errno;
378			} else if (nread != bsize) {
379				if (debug) {
380					plog("Only read %zd bytes from journal (expected %zd)", nread, bsize);
381					err = EINVAL;
382				}
383			}
384			if (jp == NULL)
385				close(jfd);
386			/* We got the journal header, now we need to check it */
387			if (err == noErr) {
388				int swap = 0;
389				UInt32 cksum = 0;
390
391				jhdr = (struct journal_header*)block;
392
393				if (jhdr->magic == JOURNAL_HEADER_MAGIC ||
394					SW32(jhdr->magic) == JOURNAL_HEADER_MAGIC) {
395					if (jhdr->endian == ENDIAN_MAGIC)
396						swap = 0;
397					else if (SW32(jhdr->endian) == ENDIAN_MAGIC)
398						swap = 1;
399					else
400						swap = 2;
401
402					if (swap != 2) {
403						cksum = swap ? SW32(jhdr->checksum) : jhdr->checksum;
404						UInt32 calc_sum;
405						jhdr->checksum = 0;
406						/* Checksum calculation needs the checksum field to be zero. */
407						calc_sum = calc_checksum((unsigned char*)jhdr, JOURNAL_HEADER_CKSUM_SIZE);
408						/* But, for now, this is for debugging purposes only */
409						if (calc_sum != cksum) {
410							if (debug)
411								plog("Journal checksum doesn't match:  orig %x != calc %x\n", cksum, calc_sum);
412						}
413						/* We have a journal, we got the header, now we check the start and end */
414						if (jhdr->start != jhdr->end) {
415							retval = 0;
416							if (debug)
417								plog("Non-empty journal:  start = %lld, end = %lld\n",
418									swap ? SW64(jhdr->start) : jhdr->start,
419									swap ? SW64(jhdr->end) : jhdr->end);
420						}
421					}
422				}
423			}
424		}
425	}
426out:
427	return retval;
428}
429
430/*
431 * The functions checks whether the volume is clean or dirty.  It
432 * also marks the volume as clean/dirty depending on the type
433 * of operation specified.  It modifies the volume header only
434 * if the old values are not same as the new values.  If the volume
435 * header is updated, it also sets the last mounted version for HFS+.
436 *
437 * Input:
438 * GPtr		- Pointer to scavenger global area
439 * operation	- Type of operation to perform
440 * 			kCheckVolume,		// check if volume is clean/dirty
441 *			kMarkVolumeDirty,	// mark the volume dirty
442 *			kMarkVolumeClean	// mark the volume clean
443 *
444 * Output:
445 * modified 	- true if the VH/MDB was modified, otherwise false.
446 * Return Value -
447 *			-1 - if the volume is not an HFS/HFS+ volume
448 *	 		 0 - if the volume was dirty or marked dirty
449 *	 		 1 - if the volume was clean or marked clean
450 * If the operation requested was to mark the volume clean/dirty,
451 * the return value is dependent on type of operation (described above).
452 */
453int CheckForClean(SGlobPtr GPtr, UInt8 operation, Boolean *modified)
454{
455	enum { unknownVolume = -1, cleanUnmount = 1, dirtyUnmount = 0};
456	int result = unknownVolume;
457	Boolean update = false;
458	HFSMasterDirectoryBlock	*mdbp;
459	HFSPlusVolumeHeader *vhp;
460	BlockDescriptor block;
461	ReleaseBlockOptions rbOptions;
462	UInt64 blockNum;
463	SVCB *vcb;
464
465	*modified = false;
466	vcb = GPtr->calculatedVCB;
467	block.buffer = NULL;
468	rbOptions = kReleaseBlock;
469
470	/* Get the block number for VH/MDB */
471	GetVolumeObjectBlockNum(&blockNum);
472	if (blockNum == 0) {
473		if (fsckGetVerbosity(GPtr->context) >= kDebugLog)
474			plog( "\t%s - unknown volume type \n", __FUNCTION__ );
475		goto ExitThisRoutine;
476	}
477
478	/* Get VH or MDB depending on the type of volume */
479	result = GetVolumeObjectPrimaryBlock(&block);
480	if (result) {
481		if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
482			plog( "\t%s - could not get VHB/MDB at block %qd \n", __FUNCTION__, blockNum );
483		result = unknownVolume;
484		goto ExitThisRoutine;
485	}
486
487	result = cleanUnmount;
488
489	if (VolumeObjectIsHFSPlus()) {
490		vhp = (HFSPlusVolumeHeader *) block.buffer;
491
492		/* Check unmount bit and volume inconsistent bit */
493		if (((vhp->attributes & kHFSVolumeUnmountedMask) == 0) ||
494		    (vhp->attributes & kHFSVolumeInconsistentMask))
495			result = dirtyUnmount;
496
497		/* Check last mounted version.  If kFSKMountVersion, bad
498		 * journal was encountered during mount.  Force dirty volume.
499		 */
500
501		if (vhp->lastMountedVersion == kFSKMountVersion) {
502			GPtr->JStat |= S_BadJournal;
503			RcdError (GPtr, E_BadJournal);
504			result = dirtyUnmount;
505		}
506
507		if (operation == kMarkVolumeDirty) {
508			/* Mark volume was not unmounted cleanly */
509			if (vhp->attributes & kHFSVolumeUnmountedMask) {
510				vhp->attributes &= ~kHFSVolumeUnmountedMask;
511				update = true;
512			}
513			/* Mark volume inconsistent */
514			if ((vhp->attributes & kHFSVolumeInconsistentMask) == 0) {
515				vhp->attributes |= kHFSVolumeInconsistentMask;
516				update = true;
517			}
518		} else if (operation == kMarkVolumeClean) {
519			/* Mark volume was unmounted cleanly */
520			if ((vhp->attributes & kHFSVolumeUnmountedMask) == 0)  {
521				vhp->attributes |= kHFSVolumeUnmountedMask;
522				update = true;
523			}
524			/* Mark volume consistent */
525			if (vhp->attributes & kHFSVolumeInconsistentMask) {
526				vhp->attributes &= ~kHFSVolumeInconsistentMask;
527				update = true;
528			}
529		}
530
531		/* If any changes to VH, update the last mounted version */
532		if (update == true) {
533			vhp->lastMountedVersion = kFSCKMountVersion;
534		}
535	} else if (VolumeObjectIsHFS()) {
536		mdbp = (HFSMasterDirectoryBlock	*) block.buffer;
537
538		/* Check unmount bit and volume inconsistent bit */
539		if (((mdbp->drAtrb & kHFSVolumeUnmountedMask) == 0) ||
540		    (mdbp->drAtrb & kHFSVolumeInconsistentMask))
541			result = dirtyUnmount;
542
543		if (operation == kMarkVolumeDirty) {
544			/* Mark volume was not unmounted cleanly */
545			if (mdbp->drAtrb & kHFSVolumeUnmountedMask) {
546				mdbp->drAtrb &= ~kHFSVolumeUnmountedMask;
547				update = true;
548			}
549			/* Mark volume inconsistent */
550			if ((mdbp->drAtrb & kHFSVolumeInconsistentMask) == 0) {
551				mdbp->drAtrb |= kHFSVolumeInconsistentMask;
552				update = true;
553			}
554		} else if (operation == kMarkVolumeClean) {
555			/* Mark volume was unmounted cleanly */
556			if ((mdbp->drAtrb & kHFSVolumeUnmountedMask) == 0)  {
557				mdbp->drAtrb |= kHFSVolumeUnmountedMask;
558				update = true;
559			}
560			/* Mark volume consistent */
561			if (mdbp->drAtrb & kHFSVolumeInconsistentMask) {
562				mdbp->drAtrb &= ~kHFSVolumeInconsistentMask;
563				update = true;
564			}
565		}
566	}
567
568ExitThisRoutine:
569	if (update == true) {
570		*modified = true;
571		rbOptions = kForceWriteBlock;
572		/* Set appropriate return value */
573		if (operation == kMarkVolumeDirty) {
574			result = dirtyUnmount;
575		} else if (operation == kMarkVolumeClean) {
576			result = cleanUnmount;
577		}
578	}
579	if (block.buffer != NULL)
580		(void) ReleaseVolumeBlock(vcb, &block, rbOptions);
581
582	return (result);
583}
584
585/*------------------------------------------------------------------------------
586
587Function:	IVChk - (Initial Volume Check)
588
589Function:	Performs an initial check of the volume to be scavenged to confirm
590			that the volume can be accessed and that it is a HFS/HFS+ volume.
591
592Input:		GPtr		-	pointer to scavenger global area
593
594Output:		IVChk	-	function result:
595								0	= no error
596								n 	= error code
597------------------------------------------------------------------------------*/
598#define					kBitsPerSector	4096
599
600OSErr IVChk( SGlobPtr GPtr )
601{
602	OSErr						err;
603	HFSMasterDirectoryBlock *	myMDBPtr;
604	HFSPlusVolumeHeader *		myVHBPtr;
605	UInt32					numABlks;
606	UInt32					minABlkSz;
607	UInt32					maxNumberOfAllocationBlocks;
608	UInt32					realAllocationBlockSize;
609	UInt32					realTotalBlocks;
610	UInt32					i;
611	BTreeControlBlock		*btcb;
612	SVCB					*vcb	= GPtr->calculatedVCB;
613	VolumeObjectPtr 		myVOPtr;
614	UInt64					blockNum;
615	UInt64					totalSectors;
616	BlockDescriptor			myBlockDescriptor;
617
618	//  Set up
619	GPtr->TarID = AMDB_FNum;	//	target = alt MDB
620	GPtr->TarBlock	= 0;
621	maxNumberOfAllocationBlocks	= 0xFFFFFFFF;
622	realAllocationBlockSize = 0;
623	realTotalBlocks = 0;
624
625	myBlockDescriptor.buffer = NULL;
626	myVOPtr = GetVolumeObjectPtr( );
627
628	// check volume size
629	if ( myVOPtr->totalDeviceSectors < 3 ) {
630		if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
631			plog("\tinvalid device information for volume - total sectors = %qd sector size = %d \n",
632				myVOPtr->totalDeviceSectors, myVOPtr->sectorSize);
633		return( 123 );
634	}
635
636	GetVolumeObjectBlockNum( &blockNum );
637	if ( blockNum == 0 || myVOPtr->volumeType == kUnknownVolumeType ) {
638		if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
639			plog( "\t%s - unknown volume type \n", __FUNCTION__ );
640		err = R_BadSig;  /* doesn't bear the HFS signature */
641		goto ReleaseAndBail;
642	}
643
644	// get Volume Header (HFS+) or Master Directory (HFS) block
645	err = GetVolumeObjectVHBorMDB( &myBlockDescriptor );
646	if ( err != noErr ) {
647		if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
648			plog( "\t%s - bad volume header - err %d \n", __FUNCTION__, err );
649		goto ReleaseAndBail;
650	}
651	myMDBPtr = (HFSMasterDirectoryBlock	*) myBlockDescriptor.buffer;
652
653	// if this is an HFS (kHFSVolumeType) volume and the MDB indicates this
654	// might contain an embedded HFS+ volume then we need to scan
655	// for an embedded HFS+ volume.  I'm told there were some old problems
656	// where we could lose track of the embedded volume.
657	if ( VolumeObjectIsHFS( ) &&
658		 (myMDBPtr->drEmbedSigWord != 0 ||
659		  myMDBPtr->drEmbedExtent.blockCount != 0 ||
660		  myMDBPtr->drEmbedExtent.startBlock != 0) ) {
661
662		err = ScavengeVolumeType( GPtr, myMDBPtr, &myVOPtr->volumeType );
663		if ( err == E_InvalidMDBdrAlBlSt )
664			err = RcdMDBEmbededVolDescriptionErr( GPtr, E_InvalidMDBdrAlBlSt, myMDBPtr );
665
666		if ( VolumeObjectIsEmbeddedHFSPlus( ) ) {
667			// we changed volume types so let's get the VHB
668			(void) ReleaseVolumeBlock( vcb, &myBlockDescriptor, kReleaseBlock );
669			myBlockDescriptor.buffer = NULL;
670			myMDBPtr = NULL;
671			err = GetVolumeObjectVHB( &myBlockDescriptor );
672			if ( err != noErr ) {
673				if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
674					plog( "\t%s - bad volume header - err %d \n", __FUNCTION__, err );
675				WriteError( GPtr, E_InvalidVolumeHeader, 1, 0 );
676				err = E_InvalidVolumeHeader;
677				goto ReleaseAndBail;
678			}
679
680			GetVolumeObjectBlockNum( &blockNum ); // get the new Volume header block number
681		}
682		else {
683			if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
684				plog( "\t%s - bad volume header - err %d \n", __FUNCTION__, err );
685			WriteError( GPtr, E_InvalidVolumeHeader, 1, 0 );
686			err = E_InvalidVolumeHeader;
687			goto ReleaseAndBail;
688		}
689	}
690
691	totalSectors = ( VolumeObjectIsEmbeddedHFSPlus( ) ) ? myVOPtr->totalEmbeddedSectors : myVOPtr->totalDeviceSectors;
692
693	// indicate what type of volume we are dealing with
694	if ( VolumeObjectIsHFSPlus( ) ) {
695
696		myVHBPtr = (HFSPlusVolumeHeader	*) myBlockDescriptor.buffer;
697		if (myVHBPtr->attributes & kHFSVolumeJournaledMask) {
698			fsckPrint(GPtr->context, hfsJournalVolCheck);
699		} else {
700			fsckPrint(GPtr->context, hfsCheckNoJnl);
701		}
702		GPtr->numExtents = kHFSPlusExtentDensity;
703		vcb->vcbSignature = kHFSPlusSigWord;
704
705		//	Further populate the VCB with VolumeHeader info
706		vcb->vcbAlBlSt = myVOPtr->embeddedOffset / 512;
707		vcb->vcbEmbeddedOffset = myVOPtr->embeddedOffset;
708		realAllocationBlockSize		= myVHBPtr->blockSize;
709		realTotalBlocks				= myVHBPtr->totalBlocks;
710		vcb->vcbNextCatalogID	= myVHBPtr->nextCatalogID;
711		vcb->vcbCreateDate	= myVHBPtr->createDate;
712		vcb->vcbAttributes = myVHBPtr->attributes & kHFSCatalogNodeIDsReused;
713
714		if ( myVHBPtr->attributesFile.totalBlocks == 0 )
715			vcb->vcbAttributesFile = NULL;	/* XXX memory leak ? */
716
717		//	Make sure the Extents B-Tree is set to use 16-bit key lengths.
718		//	We access it before completely setting up the control block.
719		btcb = (BTreeControlBlock *) vcb->vcbExtentsFile->fcbBtree;
720		btcb->attributes |= kBTBigKeysMask;
721
722		// catch the case where the volume allocation block count is greater than
723		// maximum number of device allocation blocks. - bug 2916021
724		numABlks = myVOPtr->totalDeviceSectors / ( myVHBPtr->blockSize / Blk_Size );
725		if ( myVHBPtr->totalBlocks > numABlks ) {
726			RcdError( GPtr, E_NABlks );
727			err = E_NABlks;
728			if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
729				plog( "\t%s - volume header total allocation blocks is greater than device size \n", __FUNCTION__ );
730				plog( "\tvolume allocation block count %d device allocation block count %d \n",
731						myVHBPtr->totalBlocks, numABlks );
732			}
733			goto ReleaseAndBail;
734		}
735	}
736	else if ( VolumeObjectIsHFS( ) ) {
737
738//		fsckPrint(GPtr->context, fsckCheckingVolume);
739		fsckPrint(GPtr->context, hfsCheckHFS);
740
741		GPtr->numExtents			= kHFSExtentDensity;
742		vcb->vcbSignature			= myMDBPtr->drSigWord;
743		maxNumberOfAllocationBlocks	= 0xFFFF;
744		//	set up next file ID, CheckBTreeKey makse sure we are under this value
745		vcb->vcbNextCatalogID		= myMDBPtr->drNxtCNID;
746		vcb->vcbCreateDate			= myMDBPtr->drCrDate;
747
748		realAllocationBlockSize		= myMDBPtr->drAlBlkSiz;
749		realTotalBlocks				= myMDBPtr->drNmAlBlks;
750	}
751
752	GPtr->TarBlock	= blockNum;							//	target block
753
754	//  verify volume allocation info
755	//	Note: i is the number of sectors per allocation block
756	numABlks = totalSectors;
757	minABlkSz = Blk_Size;							//	init minimum ablock size
758	//	loop while #ablocks won't fit
759	for( i = 2; numABlks > maxNumberOfAllocationBlocks; i++ ) {
760		minABlkSz = i * Blk_Size;					//	jack up minimum
761		numABlks  = totalSectors / i;				//	recompute #ablocks, assuming this size
762	 }
763
764	vcb->vcbBlockSize = realAllocationBlockSize;
765	numABlks = totalSectors / ( realAllocationBlockSize / Blk_Size );
766	if ( VolumeObjectIsHFSPlus( ) ) {
767		// HFS Plus allocation block size must be power of 2
768		if ( (realAllocationBlockSize < minABlkSz) ||
769			 (realAllocationBlockSize & (realAllocationBlockSize - 1)) != 0 )
770			realAllocationBlockSize = 0;
771	}
772	else {
773		if ( (realAllocationBlockSize < minABlkSz) ||
774			 (realAllocationBlockSize > Max_ABSiz) ||
775			 ((realAllocationBlockSize % Blk_Size) != 0) )
776			realAllocationBlockSize = 0;
777	}
778
779	if ( realAllocationBlockSize == 0 ) {
780		RcdError( GPtr, E_ABlkSz );
781		err = E_ABlkSz;	  //	bad allocation block size
782		goto ReleaseAndBail;
783	}
784
785	vcb->vcbTotalBlocks	= realTotalBlocks;
786	vcb->vcbFreeBlocks	= 0;
787
788	//	Only do these tests on HFS volumes, since they are either
789	//	or, getting the VolumeHeader would have already failed.
790	if ( VolumeObjectIsHFS( ) ) {
791		UInt32					bitMapSizeInSectors;
792
793		// Calculate the volume bitmap size
794		bitMapSizeInSectors = ( numABlks + kBitsPerSector - 1 ) / kBitsPerSector;			//	VBM size in blocks
795
796		//��	Calculate the validaty of HFS Allocation blocks, I think realTotalBlocks == numABlks
797		numABlks = (totalSectors - 3 - bitMapSizeInSectors) / (realAllocationBlockSize / Blk_Size);	//	actual # of alloc blks
798
799		if ( realTotalBlocks > numABlks ) {
800			RcdError( GPtr, E_NABlks );
801			err = E_NABlks;								//	invalid number of allocation blocks
802			goto ReleaseAndBail;
803		}
804
805		if ( myMDBPtr->drVBMSt <= MDB_BlkN ) {
806			RcdError(GPtr,E_VBMSt);
807			err = E_VBMSt;								//	invalid VBM start block
808			goto ReleaseAndBail;
809		}
810		vcb->vcbVBMSt = myMDBPtr->drVBMSt;
811
812		if (myMDBPtr->drAlBlSt < (myMDBPtr->drVBMSt + bitMapSizeInSectors)) {
813			RcdError(GPtr,E_ABlkSt);
814			err = E_ABlkSt;								//	invalid starting alloc block
815			goto ReleaseAndBail;
816		}
817		vcb->vcbAlBlSt = myMDBPtr->drAlBlSt;
818	}
819
820ReleaseAndBail:
821	if (myBlockDescriptor.buffer != NULL)
822		(void) ReleaseVolumeBlock(vcb, &myBlockDescriptor, kReleaseBlock);
823
824	return( err );
825}
826
827
828static OSErr ScavengeVolumeType( SGlobPtr GPtr, HFSMasterDirectoryBlock *mdb, UInt32 *volumeType  )
829{
830	UInt64					vHSector;
831	UInt64					startSector;
832	UInt64					altVHSector;
833	UInt64					hfsPlusSectors = 0;
834	UInt32					sectorsPerBlock;
835	UInt32					numSectorsToSearch;
836	OSErr					err;
837	HFSPlusVolumeHeader 	*volumeHeader;
838	HFSExtentDescriptor		embededExtent;
839	SVCB					*calculatedVCB			= GPtr->calculatedVCB;
840	VolumeObjectPtr			myVOPtr;
841	UInt16					embedSigWord			= mdb->drEmbedSigWord;
842	BlockDescriptor 		block;
843
844	/*
845	 * If all of the embedded volume information is zero, then assume
846	 * this really is a plain HFS disk like it says.  Otherwise, if
847	 * you reinitialize a large HFS Plus volume as HFS, the original
848	 * embedded volume's volume header and alternate volume header will
849	 * still be there, and we'll try to repair the embedded volume.
850	 */
851	if (embedSigWord == 0  &&
852		mdb->drEmbedExtent.blockCount == 0  &&
853		mdb->drEmbedExtent.startBlock == 0)
854	{
855		*volumeType = kHFSVolumeType;
856		return noErr;
857	}
858
859	myVOPtr = GetVolumeObjectPtr( );
860	*volumeType	= kEmbededHFSPlusVolumeType;		//	Assume HFS+
861
862	//
863	//	First see if it is an HFS+ volume and the relevent structures look OK
864	//
865	if ( embedSigWord == kHFSPlusSigWord )
866	{
867		/* look for primary volume header */
868		vHSector = (UInt64)mdb->drAlBlSt +
869			((UInt64)(mdb->drAlBlkSiz / Blk_Size) * (UInt64)mdb->drEmbedExtent.startBlock) + 2;
870
871		err = GetVolumeBlock(calculatedVCB, vHSector, kGetBlock, &block);
872		volumeHeader = (HFSPlusVolumeHeader *) block.buffer;
873		if ( err != noErr ) goto AssumeHFS;
874
875		myVOPtr->primaryVHB = vHSector;
876		err = ValidVolumeHeader( volumeHeader );
877		(void) ReleaseVolumeBlock(calculatedVCB, &block, kReleaseBlock);
878		if ( err == noErr ) {
879			myVOPtr->flags |= kVO_PriVHBOK;
880			return( noErr );
881		}
882	}
883
884	sectorsPerBlock = mdb->drAlBlkSiz / Blk_Size;
885
886	//	Search the end of the disk to see if a Volume Header is present at all
887	if ( embedSigWord != kHFSPlusSigWord )
888	{
889		numSectorsToSearch = mdb->drAlBlkSiz / Blk_Size;
890		startSector = myVOPtr->totalDeviceSectors - 4 - numSectorsToSearch;
891
892		err = SeekVolumeHeader( GPtr, startSector, numSectorsToSearch, &altVHSector );
893		if ( err != noErr ) goto AssumeHFS;
894
895		//	We found the Alt VH, so this must be a damaged embeded HFS+ volume
896		//	Now Scavenge for the Primary VolumeHeader
897		myVOPtr->alternateVHB = altVHSector;
898		myVOPtr->flags |= kVO_AltVHBOK;
899		startSector = mdb->drAlBlSt + (4 * sectorsPerBlock);		// Start looking at 4th HFS allocation block
900		numSectorsToSearch = 10 * sectorsPerBlock;			// search for VH in next 10 allocation blocks
901
902		err = SeekVolumeHeader( GPtr, startSector, numSectorsToSearch, &vHSector );
903		if ( err != noErr ) goto AssumeHFS;
904
905		myVOPtr->primaryVHB = vHSector;
906		myVOPtr->flags |= kVO_PriVHBOK;
907		hfsPlusSectors	= altVHSector - vHSector + 1 + 2 + 1;	// numSectors + BB + end
908
909		//	Fix the embeded extent
910		embededExtent.blockCount	= hfsPlusSectors / sectorsPerBlock;
911		embededExtent.startBlock	= (vHSector - 2 - mdb->drAlBlSt ) / sectorsPerBlock;
912		embedSigWord				= kHFSPlusSigWord;
913
914		myVOPtr->embeddedOffset =
915			(embededExtent.startBlock * mdb->drAlBlkSiz) + (mdb->drAlBlSt * Blk_Size);
916	}
917	else
918	{
919		embedSigWord				= mdb->drEmbedSigWord;
920		embededExtent.blockCount	= mdb->drEmbedExtent.blockCount;
921		embededExtent.startBlock	= mdb->drEmbedExtent.startBlock;
922	}
923
924	if ( embedSigWord == kHFSPlusSigWord )
925	{
926		startSector = 2 + mdb->drAlBlSt +
927			((UInt64)embededExtent.startBlock * (mdb->drAlBlkSiz / Blk_Size));
928
929		err = SeekVolumeHeader( GPtr, startSector, mdb->drAlBlkSiz / Blk_Size, &vHSector );
930		if ( err != noErr ) goto AssumeHFS;
931
932		//	Now replace the bad fields and mark the error
933		mdb->drEmbedExtent.blockCount	= embededExtent.blockCount;
934		mdb->drEmbedExtent.startBlock	= embededExtent.startBlock;
935		mdb->drEmbedSigWord				= kHFSPlusSigWord;
936		mdb->drAlBlSt					+= vHSector - startSector;								//	Fix the bad field
937		myVOPtr->totalEmbeddedSectors = (mdb->drAlBlkSiz / Blk_Size) * mdb->drEmbedExtent.blockCount;
938		myVOPtr->embeddedOffset =
939			(mdb->drEmbedExtent.startBlock * mdb->drAlBlkSiz) + (mdb->drAlBlSt * Blk_Size);
940		myVOPtr->primaryVHB = vHSector;
941		myVOPtr->flags |= kVO_PriVHBOK;
942
943		GPtr->VIStat = GPtr->VIStat | S_MDB;													//	write out our MDB
944		return( E_InvalidMDBdrAlBlSt );
945	}
946
947AssumeHFS:
948	*volumeType	= kHFSVolumeType;
949	return( noErr );
950
951} /* ScavengeVolumeType */
952
953
954static OSErr SeekVolumeHeader( SGlobPtr GPtr, UInt64 startSector, UInt32 numSectors, UInt64 *vHSector )
955{
956	OSErr  err;
957	HFSPlusVolumeHeader  *volumeHeader;
958	SVCB  *calculatedVCB = GPtr->calculatedVCB;
959	BlockDescriptor  block;
960
961	for ( *vHSector = startSector ; *vHSector < startSector + numSectors  ; (*vHSector)++ )
962	{
963		err = GetVolumeBlock(calculatedVCB, *vHSector, kGetBlock, &block);
964		volumeHeader = (HFSPlusVolumeHeader *) block.buffer;
965		if ( err != noErr ) return( err );
966
967		err = ValidVolumeHeader(volumeHeader);
968
969		(void) ReleaseVolumeBlock(calculatedVCB, &block, kReleaseBlock);
970		if ( err == noErr )
971			return( noErr );
972	}
973
974	return( fnfErr );
975}
976
977
978#if 0 // not used at this time
979static OSErr CheckWrapperExtents( SGlobPtr GPtr, HFSMasterDirectoryBlock *mdb )
980{
981	OSErr	err = noErr;
982
983	//	See if Norton Disk Doctor 2.0 corrupted the catalog's first extent
984	if ( mdb->drCTExtRec[0].startBlock >= mdb->drEmbedExtent.startBlock)
985	{
986		//	Fix the field in the in-memory copy, and record the error
987		mdb->drCTExtRec[0].startBlock = mdb->drXTExtRec[0].startBlock + mdb->drXTExtRec[0].blockCount;
988		GPtr->VIStat = GPtr->VIStat | S_MDB;													//	write out our MDB
989		err = RcdInvalidWrapperExtents( GPtr, E_InvalidWrapperExtents );
990	}
991
992	return err;
993}
994#endif
995
996/*------------------------------------------------------------------------------
997
998Function:	CreateExtentsBTreeControlBlock
999
1000Function:	Create the calculated ExtentsBTree Control Block
1001
1002Input:		GPtr	-	pointer to scavenger global area
1003
1004Output:				-	0	= no error
1005						n 	= error code
1006------------------------------------------------------------------------------*/
1007
1008OSErr	CreateExtentsBTreeControlBlock( SGlobPtr GPtr )
1009{
1010	OSErr					err;
1011	SInt32					size;
1012	UInt32					numABlks;
1013	BTHeaderRec				header;
1014	BTreeControlBlock *  	btcb;
1015	SVCB *  				vcb;
1016	BlockDescriptor 	 	block;
1017	Boolean					isHFSPlus;
1018
1019	//	Set up
1020	isHFSPlus = VolumeObjectIsHFSPlus( );
1021	GPtr->TarID = kHFSExtentsFileID;	// target = extent file
1022	GPtr->TarBlock	= kHeaderNodeNum;	// target block = header node
1023	vcb = GPtr->calculatedVCB;
1024	btcb = GPtr->calculatedExtentsBTCB;
1025	block.buffer = NULL;
1026
1027	// get Volume Header (HFS+) or Master Directory (HFS) block
1028	err = GetVolumeObjectVHBorMDB( &block );
1029	if (err) goto exit;
1030	//
1031	//	check out allocation info for the Extents File
1032	//
1033	if (isHFSPlus)
1034	{
1035		HFSPlusVolumeHeader *volumeHeader;
1036
1037		volumeHeader = (HFSPlusVolumeHeader *) block.buffer;
1038
1039		CopyMemory(volumeHeader->extentsFile.extents, GPtr->calculatedExtentsFCB->fcbExtents32, sizeof(HFSPlusExtentRecord) );
1040
1041		err = CheckFileExtents( GPtr, kHFSExtentsFileID, kDataFork, NULL, (void *)GPtr->calculatedExtentsFCB->fcbExtents32, &numABlks);	//	check out extent info
1042
1043		if (err) goto exit;
1044
1045		if ( volumeHeader->extentsFile.totalBlocks != numABlks )				//	check out the PEOF
1046		{
1047			RcdError( GPtr, E_ExtPEOF );
1048			err = E_ExtPEOF;
1049			if (debug)
1050				plog("Extents File totalBlocks = %u, numABlks = %u\n", volumeHeader->extentsFile.totalBlocks, numABlks);
1051			goto exit;
1052		}
1053		else
1054		{
1055			GPtr->calculatedExtentsFCB->fcbLogicalSize  = volumeHeader->extentsFile.logicalSize;					//	Set Extents tree's LEOF
1056			GPtr->calculatedExtentsFCB->fcbPhysicalSize = (UInt64)volumeHeader->extentsFile.totalBlocks *
1057														  (UInt64)volumeHeader->blockSize;	//	Set Extents tree's PEOF
1058		}
1059
1060		//
1061		//	Set up the minimal BTreeControlBlock structure
1062		//
1063
1064		//	Read the BTreeHeader from disk & also validate it's node size.
1065		err = GetBTreeHeader(GPtr, GPtr->calculatedExtentsFCB, &header);
1066		if (err) goto exit;
1067
1068		btcb->maxKeyLength		= kHFSPlusExtentKeyMaximumLength;				//	max key length
1069		btcb->keyCompareProc	= (void *)CompareExtentKeysPlus;
1070		btcb->attributes		|=kBTBigKeysMask;								//	HFS+ Extent files have 16-bit key length
1071		btcb->leafRecords		= header.leafRecords;
1072		btcb->treeDepth			= header.treeDepth;
1073		btcb->rootNode			= header.rootNode;
1074		btcb->firstLeafNode		= header.firstLeafNode;
1075		btcb->lastLeafNode		= header.lastLeafNode;
1076
1077		btcb->nodeSize			= header.nodeSize;
1078		btcb->totalNodes		= ( GPtr->calculatedExtentsFCB->fcbPhysicalSize / btcb->nodeSize );
1079		btcb->freeNodes			= btcb->totalNodes;								//	start with everything free
1080
1081		//	Make sure the header nodes size field is correct by looking at the 1st record offset
1082		err	= CheckNodesFirstOffset( GPtr, btcb );
1083		if ( (err != noErr) && (btcb->nodeSize != 1024) )		//	default HFS+ Extents node size is 1024
1084		{
1085			btcb->nodeSize			= 1024;
1086			btcb->totalNodes		= ( GPtr->calculatedExtentsFCB->fcbPhysicalSize / btcb->nodeSize );
1087			btcb->freeNodes			= btcb->totalNodes;								//	start with everything free
1088
1089			err = CheckNodesFirstOffset( GPtr, btcb );
1090			if (err) goto exit;
1091
1092			GPtr->EBTStat |= S_BTH;								//	update the Btree header
1093		}
1094	}
1095	else	// Classic HFS
1096	{
1097		HFSMasterDirectoryBlock	*alternateMDB;
1098
1099		alternateMDB = (HFSMasterDirectoryBlock *) block.buffer;
1100
1101		CopyMemory(alternateMDB->drXTExtRec, GPtr->calculatedExtentsFCB->fcbExtents16, sizeof(HFSExtentRecord) );
1102	//	ExtDataRecToExtents(alternateMDB->drXTExtRec, GPtr->calculatedExtentsFCB->fcbExtents);
1103
1104
1105		err = CheckFileExtents( GPtr, kHFSExtentsFileID, kDataFork, NULL, (void *)GPtr->calculatedExtentsFCB->fcbExtents16, &numABlks);	/* check out extent info */
1106		if (err) goto exit;
1107
1108		if (alternateMDB->drXTFlSize != ((UInt64)numABlks * (UInt64)GPtr->calculatedVCB->vcbBlockSize))//	check out the PEOF
1109		{
1110			RcdError(GPtr,E_ExtPEOF);
1111			err = E_ExtPEOF;
1112			if (debug)
1113				plog("Alternate MDB drXTFlSize = %llu, should be %llu\n", (long long)alternateMDB->drXTFlSize, (long long)numABlks * (UInt64)GPtr->calculatedVCB->vcbBlockSize);
1114			goto exit;
1115		}
1116		else
1117		{
1118			GPtr->calculatedExtentsFCB->fcbPhysicalSize = alternateMDB->drXTFlSize;		//	set up PEOF and EOF in FCB
1119			GPtr->calculatedExtentsFCB->fcbLogicalSize = GPtr->calculatedExtentsFCB->fcbPhysicalSize;
1120		}
1121
1122		//
1123		//	Set up the minimal BTreeControlBlock structure
1124		//
1125
1126		//	Read the BTreeHeader from disk & also validate it's node size.
1127		err = GetBTreeHeader(GPtr, GPtr->calculatedExtentsFCB, &header);
1128		if (err) goto exit;
1129
1130		btcb->maxKeyLength	= kHFSExtentKeyMaximumLength;						//	max key length
1131		btcb->keyCompareProc = (void *)CompareExtentKeys;
1132		btcb->leafRecords	= header.leafRecords;
1133		btcb->treeDepth		= header.treeDepth;
1134		btcb->rootNode		= header.rootNode;
1135		btcb->firstLeafNode	= header.firstLeafNode;
1136		btcb->lastLeafNode	= header.lastLeafNode;
1137
1138		btcb->nodeSize		= header.nodeSize;
1139		btcb->totalNodes	= (GPtr->calculatedExtentsFCB->fcbPhysicalSize / btcb->nodeSize );
1140		btcb->freeNodes		= btcb->totalNodes;									//	start with everything free
1141
1142		//	Make sure the header nodes size field is correct by looking at the 1st record offset
1143		err = CheckNodesFirstOffset( GPtr, btcb );
1144		if (err) goto exit;
1145	}
1146
1147	if ( header.btreeType != kHFSBTreeType )
1148	{
1149		GPtr->EBTStat |= S_ReservedBTH;						//	Repair reserved fields in Btree header
1150	}
1151
1152	//
1153	//	set up our DFA extended BTCB area.  Will we have enough memory on all HFS+ volumes.
1154	//
1155	btcb->refCon = AllocateClearMemory( sizeof(BTreeExtensionsRec) );			// allocate space for our BTCB extensions
1156	if ( btcb->refCon ==  nil ) {
1157		err = R_NoMem;
1158		goto exit;
1159	}
1160	size = (btcb->totalNodes + 7) / 8;											//	size of BTree bit map
1161	((BTreeExtensionsRec*)btcb->refCon)->BTCBMPtr = AllocateClearMemory(size);			//	get precleared bitmap
1162	if ( ((BTreeExtensionsRec*)btcb->refCon)->BTCBMPtr == nil )
1163	{
1164		err = R_NoMem;
1165		goto exit;
1166	}
1167
1168	((BTreeExtensionsRec*)btcb->refCon)->BTCBMSize = size;				//	remember how long it is
1169	((BTreeExtensionsRec*)btcb->refCon)->realFreeNodeCount = header.freeNodes;//	keep track of real free nodes for progress
1170exit:
1171	if ( block.buffer != NULL )
1172		(void) ReleaseVolumeBlock(vcb, &block, kReleaseBlock);
1173
1174	return (err);
1175}
1176
1177
1178
1179/*------------------------------------------------------------------------------
1180
1181Function:	CheckNodesFirstOffset
1182
1183Function:	Minimal check verifies that the 1st offset is within bounds.  If it's not
1184			the nodeSize may be wrong.  In the future this routine could be modified
1185			to try different size values until one fits.
1186
1187------------------------------------------------------------------------------*/
1188#define GetRecordOffset(btreePtr,node,index)		(*(short *) ((UInt8 *)(node) + (btreePtr)->nodeSize - ((index) << 1) - kOffsetSize))
1189static	OSErr	CheckNodesFirstOffset( SGlobPtr GPtr, BTreeControlBlock *btcb )
1190{
1191	NodeRec		nodeRec;
1192	UInt16		offset;
1193	OSErr		err;
1194
1195	(void) SetFileBlockSize(btcb->fcbPtr, btcb->nodeSize);
1196
1197	err = GetNode( btcb, kHeaderNodeNum, &nodeRec );
1198
1199	if ( err == noErr )
1200	{
1201		offset	= GetRecordOffset( btcb, (NodeDescPtr)nodeRec.buffer, 0 );
1202		if ( (offset < sizeof (BTNodeDescriptor)) ||			// offset < minimum
1203			 (offset & 1) ||									// offset is odd
1204			 (offset >= btcb->nodeSize) )						// offset beyond end of node
1205		{
1206			if (debug) fprintf(stderr, "%s(%d):  offset is wrong\n", __FUNCTION__, __LINE__);
1207			err	= fsBTInvalidNodeErr;
1208		}
1209	}
1210
1211	if ( err != noErr )
1212		RcdError( GPtr, E_InvalidNodeSize );
1213
1214	(void) ReleaseNode(btcb, &nodeRec);
1215
1216	return( err );
1217}
1218
1219
1220
1221/*------------------------------------------------------------------------------
1222
1223Function:	ExtBTChk - (Extent BTree Check)
1224
1225Function:	Verifies the extent BTree structure.
1226
1227Input:		GPtr		-	pointer to scavenger global area
1228
1229Output:		ExtBTChk	-	function result:
1230								0	= no error
1231								n 	= error code
1232------------------------------------------------------------------------------*/
1233
1234OSErr ExtBTChk( SGlobPtr GPtr )
1235{
1236	OSErr					err;
1237
1238	//	Set up
1239	GPtr->TarID		= kHFSExtentsFileID;				//	target = extent file
1240	GetVolumeObjectBlockNum( &GPtr->TarBlock );			//	target block = VHB/MDB
1241
1242	//
1243	//	check out the BTree structure
1244	//
1245
1246	err = BTCheck(GPtr, kCalculatedExtentRefNum, NULL);
1247	ReturnIfError( err );														//	invalid extent file BTree
1248
1249	//
1250	//	check out the allocation map structure
1251	//
1252
1253	err = BTMapChk( GPtr, kCalculatedExtentRefNum );
1254	ReturnIfError( err );														//	Invalid extent BTree map
1255
1256	//
1257	// Make sure unused nodes in the B-tree are zero filled.
1258	//
1259	err = BTCheckUnusedNodes(GPtr, kCalculatedExtentRefNum, &GPtr->EBTStat);
1260	ReturnIfError( err );
1261
1262	//
1263	//	compare BTree header record on disk with scavenger's BTree header record
1264	//
1265
1266	err = CmpBTH( GPtr, kCalculatedExtentRefNum );
1267	ReturnIfError( err );
1268
1269	//
1270	//	compare BTree map on disk with scavenger's BTree map
1271	//
1272
1273	err = CmpBTM( GPtr, kCalculatedExtentRefNum );
1274
1275	return( err );
1276}
1277
1278
1279
1280/*------------------------------------------------------------------------------
1281
1282Function:	BadBlockFileExtentCheck - (Check extents of bad block file)
1283
1284Function:
1285		Verifies the extents of bad block file (kHFSBadBlockFileID) that
1286		exist in extents Btree.
1287
1288		Note that the extents for other file IDs < kHFSFirstUserCatalogNodeID
1289		are being taken care in the following functions:
1290
1291		kHFSExtentsFileID    	- CreateExtentsBTreeControlBlock
1292		kHFSCatalogFileID     	- CreateCatalogBTreeControlBlock
1293		kHFSAllocationFileID	- CreateExtendedAllocationsFCB
1294		kHFSStartupFileID     	- CreateExtendedAllocationsFCB
1295		kHFSAttributesFileID	- CreateAttributesBTreeControlBlock
1296
1297Input:		GPtr		-	pointer to scavenger global area
1298
1299Output:		BadBlockFileExtentCheck		-	function result:
1300				0	= no error
1301				+n 	= error code
1302------------------------------------------------------------------------------*/
1303
1304OSErr BadBlockFileExtentCheck( SGlobPtr GPtr )
1305{
1306	UInt32			attributes;
1307	void			*p;
1308	OSErr			result;
1309	SVCB 			*vcb;
1310	Boolean			isHFSPlus;
1311	BlockDescriptor  block;
1312
1313	isHFSPlus = VolumeObjectIsHFSPlus( );
1314	block.buffer = NULL;
1315
1316	//
1317	//	process the bad block extents (created by the disk init pkg to hide badspots)
1318	//
1319	vcb = GPtr->calculatedVCB;
1320
1321	result = GetVolumeObjectVHBorMDB( &block );
1322	if ( result != noErr ) goto ExitThisRoutine;		//	error, could't get it
1323
1324	p = (void *) block.buffer;
1325	attributes = isHFSPlus == true ? ((HFSPlusVolumeHeader*)p)->attributes : ((HFSMasterDirectoryBlock*)p)->drAtrb;
1326
1327	//�� Does HFS+ honnor the same mask?
1328	if ( attributes & kHFSVolumeSparedBlocksMask )				//	if any badspots
1329	{
1330		HFSPlusExtentRecord		zeroXdr;						//	dummy passed to 'CheckFileExtents'
1331		UInt32					numBadBlocks;
1332
1333		ClearMemory ( zeroXdr, sizeof( HFSPlusExtentRecord ) );
1334		result = CheckFileExtents( GPtr, kHFSBadBlockFileID, kDataFork, NULL, (void *)zeroXdr, &numBadBlocks);	//	check and mark bitmap
1335	}
1336
1337ExitThisRoutine:
1338	if ( block.buffer != NULL )
1339		(void) ReleaseVolumeBlock(vcb, &block, kReleaseBlock);
1340
1341	return (result);
1342}
1343
1344
1345/*------------------------------------------------------------------------------
1346
1347Function:	CreateCatalogBTreeControlBlock
1348
1349Function:	Create the calculated CatalogBTree Control Block
1350
1351Input:		GPtr	-	pointer to scavenger global area
1352
1353Output:				-	0	= no error
1354						n 	= error code
1355------------------------------------------------------------------------------*/
1356OSErr	CreateCatalogBTreeControlBlock( SGlobPtr GPtr )
1357{
1358	OSErr					err;
1359	SInt32					size;
1360	UInt32					numABlks;
1361	BTHeaderRec				header;
1362	BTreeControlBlock *  	btcb;
1363	SVCB *  				vcb;
1364	BlockDescriptor  		block;
1365	Boolean					isHFSPlus;
1366
1367	//	Set up
1368	isHFSPlus = VolumeObjectIsHFSPlus( );
1369	GPtr->TarID		= kHFSCatalogFileID;
1370	GPtr->TarBlock	= kHeaderNodeNum;
1371	vcb = GPtr->calculatedVCB;
1372	btcb = GPtr->calculatedCatalogBTCB;
1373 	block.buffer = NULL;
1374
1375	err = GetVolumeObjectVHBorMDB( &block );
1376	if ( err != noErr ) goto ExitThisRoutine;		//	error, could't get it
1377	//
1378	//	check out allocation info for the Catalog File
1379	//
1380	if (isHFSPlus)
1381	{
1382		HFSPlusVolumeHeader * volumeHeader;
1383
1384		volumeHeader = (HFSPlusVolumeHeader *) block.buffer;
1385
1386		CopyMemory(volumeHeader->catalogFile.extents, GPtr->calculatedCatalogFCB->fcbExtents32, sizeof(HFSPlusExtentRecord) );
1387
1388		err = CheckFileExtents( GPtr, kHFSCatalogFileID, kDataFork, NULL, (void *)GPtr->calculatedCatalogFCB->fcbExtents32, &numABlks);
1389		if (err) goto exit;
1390
1391		if ( volumeHeader->catalogFile.totalBlocks != numABlks )
1392		{
1393			RcdError( GPtr, E_CatPEOF );
1394			err = E_CatPEOF;
1395			goto exit;
1396		}
1397		else
1398		{
1399			GPtr->calculatedCatalogFCB->fcbLogicalSize  = volumeHeader->catalogFile.logicalSize;
1400			GPtr->calculatedCatalogFCB->fcbPhysicalSize = (UInt64)volumeHeader->catalogFile.totalBlocks *
1401														  (UInt64)volumeHeader->blockSize;
1402		}
1403
1404		//
1405		//	Set up the minimal BTreeControlBlock structure
1406		//
1407
1408		//	read the BTreeHeader from disk & also validate it's node size.
1409		err = GetBTreeHeader(GPtr, GPtr->calculatedCatalogFCB, &header);
1410		if (err) goto exit;
1411
1412		btcb->maxKeyLength		= kHFSPlusCatalogKeyMaximumLength;					//	max key length
1413
1414		/*
1415		 * Figure out the type of key string compare
1416		 * (case-insensitive or case-sensitive)
1417		 *
1418		 * To do: should enforce an "HX" volume is require for kHFSBinaryCompare.
1419		 */
1420		if (header.keyCompareType == kHFSBinaryCompare)
1421		{
1422			btcb->keyCompareProc = (void *)CaseSensitiveCatalogKeyCompare;
1423			fsckPrint(GPtr->context, hfsCaseSensitive);
1424		}
1425		else
1426		{
1427			btcb->keyCompareProc = (void *)CompareExtendedCatalogKeys;
1428		}
1429		btcb->keyCompareType		= header.keyCompareType;
1430		btcb->leafRecords		= header.leafRecords;
1431		btcb->nodeSize			= header.nodeSize;
1432		btcb->totalNodes		= ( GPtr->calculatedCatalogFCB->fcbPhysicalSize / btcb->nodeSize );
1433		btcb->freeNodes			= btcb->totalNodes;									//	start with everything free
1434		btcb->attributes		|=(kBTBigKeysMask + kBTVariableIndexKeysMask);		//	HFS+ Catalog files have large, variable-sized keys
1435
1436		btcb->treeDepth		= header.treeDepth;
1437		btcb->rootNode		= header.rootNode;
1438		btcb->firstLeafNode	= header.firstLeafNode;
1439		btcb->lastLeafNode	= header.lastLeafNode;
1440
1441
1442		//	Make sure the header nodes size field is correct by looking at the 1st record offset
1443		err	= CheckNodesFirstOffset( GPtr, btcb );
1444		if ( (err != noErr) && (btcb->nodeSize != 4096) )		//	default HFS+ Catalog node size is 4096
1445		{
1446			btcb->nodeSize			= 4096;
1447			btcb->totalNodes		= ( GPtr->calculatedCatalogFCB->fcbPhysicalSize / btcb->nodeSize );
1448			btcb->freeNodes			= btcb->totalNodes;								//	start with everything free
1449
1450			err = CheckNodesFirstOffset( GPtr, btcb );
1451			if (err) goto exit;
1452
1453			GPtr->CBTStat |= S_BTH;								//	update the Btree header
1454		}
1455	}
1456	else	//	HFS
1457	{
1458		HFSMasterDirectoryBlock	*alternateMDB;
1459
1460		alternateMDB = (HFSMasterDirectoryBlock	*) block.buffer;
1461
1462		CopyMemory( alternateMDB->drCTExtRec, GPtr->calculatedCatalogFCB->fcbExtents16, sizeof(HFSExtentRecord) );
1463	//	ExtDataRecToExtents(alternateMDB->drCTExtRec, GPtr->calculatedCatalogFCB->fcbExtents);
1464
1465		err = CheckFileExtents( GPtr, kHFSCatalogFileID, kDataFork, NULL, (void *)GPtr->calculatedCatalogFCB->fcbExtents16, &numABlks);	/* check out extent info */
1466		if (err) goto exit;
1467
1468		if (alternateMDB->drCTFlSize != ((UInt64)numABlks * (UInt64)vcb->vcbBlockSize))	//	check out the PEOF
1469		{
1470			RcdError( GPtr, E_CatPEOF );
1471			err = E_CatPEOF;
1472			goto exit;
1473		}
1474		else
1475		{
1476			GPtr->calculatedCatalogFCB->fcbPhysicalSize	= alternateMDB->drCTFlSize;			//	set up PEOF and EOF in FCB
1477			GPtr->calculatedCatalogFCB->fcbLogicalSize	= GPtr->calculatedCatalogFCB->fcbPhysicalSize;
1478		}
1479
1480		//
1481		//	Set up the minimal BTreeControlBlock structure
1482		//
1483
1484		//	read the BTreeHeader from disk & also validate it's node size.
1485		err = GetBTreeHeader(GPtr, GPtr->calculatedCatalogFCB, &header);
1486		if (err) goto exit;
1487
1488		btcb->maxKeyLength		= kHFSCatalogKeyMaximumLength;						//	max key length
1489		btcb->keyCompareProc	= (void *) CompareCatalogKeys;
1490		btcb->leafRecords		= header.leafRecords;
1491		btcb->nodeSize			= header.nodeSize;
1492		btcb->totalNodes		= (GPtr->calculatedCatalogFCB->fcbPhysicalSize / btcb->nodeSize );
1493		btcb->freeNodes			= btcb->totalNodes;									//	start with everything free
1494
1495		btcb->treeDepth		= header.treeDepth;
1496		btcb->rootNode		= header.rootNode;
1497		btcb->firstLeafNode	= header.firstLeafNode;
1498		btcb->lastLeafNode	= header.lastLeafNode;
1499
1500		//	Make sure the header nodes size field is correct by looking at the 1st record offset
1501		err = CheckNodesFirstOffset( GPtr, btcb );
1502		if (err) goto exit;
1503	}
1504#if 0
1505	plog("   Catalog B-tree is %qd bytes\n", (UInt64)btcb->totalNodes * (UInt64) btcb->nodeSize);
1506#endif
1507
1508	if ( header.btreeType != kHFSBTreeType )
1509	{
1510		GPtr->CBTStat |= S_ReservedBTH;						//	Repair reserved fields in Btree header
1511	}
1512
1513	//
1514	//	set up our DFA extended BTCB area.  Will we have enough memory on all HFS+ volumes.
1515	//
1516
1517	btcb->refCon = AllocateClearMemory( sizeof(BTreeExtensionsRec) );			// allocate space for our BTCB extensions
1518	if ( btcb->refCon == nil ) {
1519		err = R_NoMem;
1520		goto exit;
1521	}
1522	size = (btcb->totalNodes + 7) / 8;											//	size of BTree bit map
1523	((BTreeExtensionsRec*)btcb->refCon)->BTCBMPtr = AllocateClearMemory(size);			//	get precleared bitmap
1524	if ( ((BTreeExtensionsRec*)btcb->refCon)->BTCBMPtr == nil )
1525	{
1526		err = R_NoMem;
1527		goto exit;
1528	}
1529
1530	((BTreeExtensionsRec*)btcb->refCon)->BTCBMSize			= size;						//	remember how long it is
1531	((BTreeExtensionsRec*)btcb->refCon)->realFreeNodeCount	= header.freeNodes;		//	keep track of real free nodes for progress
1532
1533    /* it should be OK at this point to get volume name and stuff it into our global */
1534    {
1535        OSErr				result;
1536        UInt16				recSize;
1537        CatalogKey			key;
1538        CatalogRecord		record;
1539
1540        BuildCatalogKey( kHFSRootFolderID, NULL, isHFSPlus, &key );
1541        result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, NULL, &record, &recSize, NULL );
1542        if ( result == noErr ) {
1543            if ( isHFSPlus ) {
1544                size_t 						len;
1545                HFSPlusCatalogThread *		recPtr = &record.hfsPlusThread;
1546                (void) utf_encodestr( recPtr->nodeName.unicode,
1547                                      recPtr->nodeName.length * 2,
1548                                      GPtr->volumeName, &len, sizeof(GPtr->volumeName) );
1549                GPtr->volumeName[len] = '\0';
1550            }
1551            else {
1552                HFSCatalogThread *		recPtr = &record.hfsThread;
1553                bcopy( &recPtr->nodeName[1], GPtr->volumeName, recPtr->nodeName[0] );
1554                GPtr->volumeName[ recPtr->nodeName[0] ] = '\0';
1555           }
1556		   fsckPrint(GPtr->context, fsckVolumeName, GPtr->volumeName);
1557        }
1558    }
1559
1560exit:
1561ExitThisRoutine:
1562	if ( block.buffer != NULL )
1563		(void) ReleaseVolumeBlock(vcb, &block, kReleaseBlock);
1564
1565	return (err);
1566}
1567
1568
1569/*------------------------------------------------------------------------------
1570
1571Function:	CreateExtendedAllocationsFCB
1572
1573Function:	Create the calculated ExtentsBTree Control Block for
1574			kHFSAllocationFileID and kHFSStartupFileID.
1575
1576Input:		GPtr	-	pointer to scavenger global area
1577
1578Output:				-	0	= no error
1579						n 	= error code
1580------------------------------------------------------------------------------*/
1581OSErr	CreateExtendedAllocationsFCB( SGlobPtr GPtr )
1582{
1583	OSErr					err = 0;
1584	UInt32					numABlks;
1585	SVCB * 					vcb;
1586	Boolean					isHFSPlus;
1587	BlockDescriptor 		block;
1588
1589	//	Set up
1590	isHFSPlus = VolumeObjectIsHFSPlus( );
1591	GPtr->TarID = kHFSAllocationFileID;
1592	GetVolumeObjectBlockNum( &GPtr->TarBlock );			//	target block = VHB/MDB
1593 	vcb = GPtr->calculatedVCB;
1594	block.buffer = NULL;
1595
1596	//
1597	//	check out allocation info for the allocation File
1598	//
1599
1600	if ( isHFSPlus )
1601	{
1602		SFCB * fcb;
1603		HFSPlusVolumeHeader *volumeHeader;
1604
1605		err = GetVolumeObjectVHB( &block );
1606		if ( err != noErr )
1607			goto exit;
1608		volumeHeader = (HFSPlusVolumeHeader *) block.buffer;
1609
1610		fcb = GPtr->calculatedAllocationsFCB;
1611		CopyMemory( volumeHeader->allocationFile.extents, fcb->fcbExtents32, sizeof(HFSPlusExtentRecord) );
1612
1613		err = CheckFileExtents( GPtr, kHFSAllocationFileID, kDataFork, NULL, (void *)fcb->fcbExtents32, &numABlks);
1614		if (err) goto exit;
1615
1616		//
1617		// The allocation file will get processed in whole allocation blocks, or
1618		// maximal-sized cache blocks, whichever is smaller.  This means the cache
1619		// doesn't need to cope with buffers that are larger than a cache block.
1620		if (vcb->vcbBlockSize < fscache.BlockSize)
1621			(void) SetFileBlockSize (fcb, vcb->vcbBlockSize);
1622		else
1623			(void) SetFileBlockSize (fcb, fscache.BlockSize);
1624
1625		if ( volumeHeader->allocationFile.totalBlocks != numABlks )
1626		{
1627			RcdError( GPtr, E_CatPEOF );
1628			err = E_CatPEOF;
1629			goto exit;
1630		}
1631		else
1632		{
1633			fcb->fcbLogicalSize  = volumeHeader->allocationFile.logicalSize;
1634			fcb->fcbPhysicalSize = (UInt64) volumeHeader->allocationFile.totalBlocks *
1635								   (UInt64) volumeHeader->blockSize;
1636		}
1637
1638		/* while we're here, also get startup file extents... */
1639		fcb = GPtr->calculatedStartupFCB;
1640		CopyMemory( volumeHeader->startupFile.extents, fcb->fcbExtents32, sizeof(HFSPlusExtentRecord) );
1641
1642		err = CheckFileExtents( GPtr, kHFSStartupFileID, kDataFork, NULL, (void *)fcb->fcbExtents32, &numABlks);
1643		if (err) goto exit;
1644
1645		fcb->fcbLogicalSize  = volumeHeader->startupFile.logicalSize;
1646		fcb->fcbPhysicalSize = (UInt64) volumeHeader->startupFile.totalBlocks *
1647								(UInt64) volumeHeader->blockSize;
1648	}
1649
1650exit:
1651	if (block.buffer)
1652		(void) ReleaseVolumeBlock(vcb, &block, kReleaseBlock);
1653
1654	return (err);
1655
1656}
1657
1658
1659/*------------------------------------------------------------------------------
1660
1661Function:	CatHChk - (Catalog Hierarchy Check)
1662
1663Function:	Verifies the catalog hierarchy.
1664
1665Input:		GPtr		-	pointer to scavenger global area
1666
1667Output:		CatHChk	-	function result:
1668								0	= no error
1669								n 	= error code
1670------------------------------------------------------------------------------*/
1671
1672OSErr CatHChk( SGlobPtr GPtr )
1673{
1674	SInt16					i;
1675	OSErr					result;
1676	UInt16 					recSize;
1677	SInt16					selCode;
1678	UInt32					hint;
1679	UInt32					dirCnt;
1680	UInt32					filCnt;
1681	SInt16					rtdirCnt;
1682	SInt16					rtfilCnt;
1683	SVCB					*calculatedVCB;
1684	SDPR					*dprP;
1685	SDPR					*dprP1;
1686	CatalogKey				foundKey;
1687	Boolean					validKeyFound;
1688	CatalogKey				key;
1689	CatalogRecord			record;
1690	CatalogRecord			record2;
1691	HFSPlusCatalogFolder	*largeCatalogFolderP;
1692	HFSPlusCatalogFile		*largeCatalogFileP;
1693	HFSCatalogFile			*smallCatalogFileP;
1694	HFSCatalogFolder		*smallCatalogFolderP;
1695	CatalogName				catalogName;
1696	UInt32					valence;
1697	CatalogRecord			threadRecord;
1698	HFSCatalogNodeID		parID;
1699	Boolean					isHFSPlus;
1700
1701	//	set up
1702	isHFSPlus = VolumeObjectIsHFSPlus( );
1703	calculatedVCB	= GPtr->calculatedVCB;
1704	GPtr->TarID		= kHFSCatalogFileID;						/* target = catalog file */
1705	GPtr->TarBlock	= 0;										/* no target block yet */
1706
1707	//
1708	//	position to the beginning of catalog
1709	//
1710
1711	//�� Can we ignore this part by just taking advantage of setting the selCode = 0x8001;
1712	{
1713		BuildCatalogKey( 1, (const CatalogName *)nil, isHFSPlus, &key );
1714		result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, &foundKey, &threadRecord, &recSize, &hint );
1715
1716		GPtr->TarBlock = hint;									/* set target block */
1717		if ( result != btNotFound )
1718		{
1719			RcdError( GPtr, E_CatRec );
1720			return( E_CatRec );
1721		}
1722	}
1723
1724	GPtr->DirLevel    = 1;
1725	dprP              = &(GPtr->DirPTPtr)[0];
1726	dprP->directoryID = 1;
1727
1728	dirCnt = filCnt = rtdirCnt = rtfilCnt = 0;
1729
1730	result	= noErr;
1731	selCode = 0x8001;  /* start with root directory */
1732
1733	//
1734	//	enumerate the entire catalog
1735	//
1736	while ( (GPtr->DirLevel > 0) && (result == noErr) )
1737	{
1738		dprP = &(GPtr->DirPTPtr)[GPtr->DirLevel - 1];
1739
1740		validKeyFound = true;
1741		record.recordType = 0;
1742
1743		//	get the next record
1744		result = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recSize, &hint );
1745
1746		GPtr->TarBlock = hint;									/* set target block */
1747		if ( result != noErr )
1748		{
1749			if ( result == btNotFound )
1750			{
1751				result = noErr;
1752				validKeyFound = false;
1753			}
1754			else
1755			{
1756				result = IntError( GPtr, result );				/* error from BTGetRecord */
1757				return( result );
1758			}
1759		}
1760		selCode = 1;											/* get next rec from now on */
1761
1762		GPtr->itemsProcessed++;
1763
1764		//
1765		//	 if same ParID ...
1766		//
1767		parID = isHFSPlus == true ? foundKey.hfsPlus.parentID : foundKey.hfs.parentID;
1768		if ( (validKeyFound == true) && (parID == dprP->directoryID) )
1769		{
1770			dprP->offspringIndex++;								/* increment offspring index */
1771
1772			//	if new directory ...
1773
1774			if ( record.recordType == kHFSPlusFolderRecord )
1775			{
1776 				result = CheckForStop( GPtr ); ReturnIfError( result );				//	Permit the user to interrupt
1777
1778				largeCatalogFolderP = (HFSPlusCatalogFolder *) &record;
1779				GPtr->TarID = largeCatalogFolderP->folderID;				//	target ID = directory ID
1780				GPtr->CNType = record.recordType;							//	target CNode type = directory ID
1781				CopyCatalogName( (const CatalogName *) &foundKey.hfsPlus.nodeName, &GPtr->CName, isHFSPlus );
1782
1783				if ( dprP->directoryID > 1 )
1784				{
1785					GPtr->DirLevel++;										//	we have a new directory level
1786					dirCnt++;
1787				}
1788				if ( dprP->directoryID == kHFSRootFolderID )				//	bump root dir count
1789					rtdirCnt++;
1790
1791				if ( GPtr->DirLevel > GPtr->dirPathCount )
1792				{
1793					void *ptr;
1794
1795					ptr = realloc(GPtr->DirPTPtr, (GPtr->dirPathCount + CMMaxDepth) * sizeof(SDPR));
1796					if (ptr == nil)
1797					{
1798						fsckPrint(GPtr->context, E_CatDepth, GPtr->dirPathCount);
1799						return noErr;											/* abort this check, but let other checks proceed */
1800					}
1801					ClearMemory((char *)ptr + (GPtr->dirPathCount * sizeof(SDPR)), (CMMaxDepth * sizeof(SDPR)));
1802					GPtr->dirPathCount += CMMaxDepth;
1803					GPtr->DirPTPtr = ptr;
1804				}
1805
1806				dprP = &(GPtr->DirPTPtr)[GPtr->DirLevel - 1];
1807				dprP->directoryID		= largeCatalogFolderP->folderID;
1808				dprP->offspringIndex	= 1;
1809				dprP->directoryHint		= hint;
1810				dprP->parentDirID		= foundKey.hfsPlus.parentID;
1811				CopyCatalogName( (const CatalogName *) &foundKey.hfsPlus.nodeName, &dprP->directoryName, isHFSPlus );
1812
1813				for ( i = 1; i < GPtr->DirLevel; i++ )
1814				{
1815					dprP1 = &(GPtr->DirPTPtr)[i - 1];
1816					if (dprP->directoryID == dprP1->directoryID)
1817					{
1818						RcdError( GPtr,E_DirLoop );							//	loop in directory hierarchy
1819						return( E_DirLoop );
1820					}
1821				}
1822
1823				/*
1824				 * Find thread record
1825				 */
1826				BuildCatalogKey( dprP->directoryID, (const CatalogName *) nil, isHFSPlus, &key );
1827				result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, &foundKey, &threadRecord, &recSize, &hint );
1828				if ( result != noErr ) {
1829					struct MissingThread *mtp;
1830
1831					/* Report the error */
1832					fsckPrint(GPtr->context, E_NoThd, dprP->directoryID);
1833
1834					/* HFS will exit here */
1835					if ( !isHFSPlus )
1836						return (E_NoThd);
1837					/*
1838					 * A directory thread is missing.  If we can find this
1839					 * ID on the missing-thread list then we know where the
1840					 * child entries reside and can resume our enumeration.
1841					 */
1842					for (mtp = GPtr->missingThreadList; mtp != NULL; mtp = mtp->link) {
1843						if (mtp->threadID == dprP->directoryID) {
1844							mtp->thread.recordType = kHFSPlusFolderThreadRecord;
1845							mtp->thread.parentID = dprP->parentDirID;
1846							CopyCatalogName(&dprP->directoryName, (CatalogName *)&mtp->thread.nodeName, isHFSPlus);
1847
1848							/* Reposition to the first child of target directory */
1849							result = SearchBTreeRecord(GPtr->calculatedCatalogFCB, &mtp->nextKey,
1850							                           kNoHint, &foundKey, &threadRecord, &recSize, &hint);
1851							if (result) {
1852								return (E_NoThd);
1853							}
1854							selCode = 0; /* use current record instead of next */
1855							break;
1856						}
1857					}
1858					if (selCode != 0) {
1859						/*
1860						 * A directory thread is missing but we know this
1861						 * directory has no children (since we didn't find
1862						 * its ID on the missing-thread list above).
1863						 *
1864						 * At this point we can resume the enumeration at
1865						 * our previous position in our parent directory.
1866						 */
1867						goto resumeAtParent;
1868					}
1869				}
1870				dprP->threadHint = hint;
1871				GPtr->TarBlock = hint;
1872			}
1873
1874			//	LargeCatalogFile
1875			else if ( record.recordType == kHFSPlusFileRecord )
1876			{
1877				largeCatalogFileP = (HFSPlusCatalogFile *) &record;
1878				GPtr->TarID = largeCatalogFileP->fileID;					//	target ID = file number
1879				GPtr->CNType = record.recordType;							//	target CNode type = thread
1880				CopyCatalogName( (const CatalogName *) &foundKey.hfsPlus.nodeName, &GPtr->CName, isHFSPlus );
1881				filCnt++;
1882				if (dprP->directoryID == kHFSRootFolderID)
1883					rtfilCnt++;
1884			}
1885
1886			else if ( record.recordType == kHFSFolderRecord )
1887			{
1888 				result = CheckForStop( GPtr ); ReturnIfError( result );				//	Permit the user to interrupt
1889
1890				smallCatalogFolderP = (HFSCatalogFolder *) &record;
1891				GPtr->TarID = smallCatalogFolderP->folderID;				/* target ID = directory ID */
1892				GPtr->CNType = record.recordType;							/* target CNode type = directory ID */
1893				CopyCatalogName( (const CatalogName *) &key.hfs.nodeName, &GPtr->CName, isHFSPlus );	/* target CName = directory name */
1894
1895				if (dprP->directoryID > 1)
1896				{
1897					GPtr->DirLevel++;										/* we have a new directory level */
1898					dirCnt++;
1899				}
1900				if (dprP->directoryID == kHFSRootFolderID)					/* bump root dir count */
1901					rtdirCnt++;
1902
1903				if ( GPtr->DirLevel > GPtr->dirPathCount )
1904				{
1905					void *ptr;
1906
1907					ptr = realloc(GPtr->DirPTPtr, (GPtr->dirPathCount + CMMaxDepth) * sizeof(SDPR));
1908					if (ptr == nil)
1909					{
1910						fsckPrint(GPtr->context, E_CatDepth, GPtr->dirPathCount);
1911						return noErr;											/* abort this check, but let other checks proceed */
1912					}
1913					ClearMemory((char *)ptr + (GPtr->dirPathCount * sizeof(SDPR)), (CMMaxDepth * sizeof(SDPR)));
1914					GPtr->dirPathCount += CMMaxDepth;
1915					GPtr->DirPTPtr = ptr;
1916				}
1917
1918				dprP = &(GPtr->DirPTPtr)[GPtr->DirLevel - 1];
1919				dprP->directoryID		= smallCatalogFolderP->folderID;
1920				dprP->offspringIndex	= 1;
1921				dprP->directoryHint		= hint;
1922				dprP->parentDirID		= foundKey.hfs.parentID;
1923
1924				CopyCatalogName( (const CatalogName *) &foundKey.hfs.nodeName, &dprP->directoryName, isHFSPlus );
1925
1926				for (i = 1; i < GPtr->DirLevel; i++)
1927				{
1928					dprP1 = &(GPtr->DirPTPtr)[i - 1];
1929					if (dprP->directoryID == dprP1->directoryID)
1930					{
1931						RcdError( GPtr,E_DirLoop );				/* loop in directory hierarchy */
1932						return( E_DirLoop );
1933					}
1934				}
1935
1936				BuildCatalogKey( dprP->directoryID, (const CatalogName *)0, isHFSPlus, &key );
1937				result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, &foundKey, &threadRecord, &recSize, &hint );
1938				if  (result != noErr )
1939				{
1940					result = IntError(GPtr,result);				/* error from BTSearch */
1941					return(result);
1942				}
1943				dprP->threadHint	= hint;						/* save hint for thread */
1944				GPtr->TarBlock		= hint;						/* set target block */
1945			}
1946
1947			//	HFSCatalogFile...
1948			else if ( record.recordType == kHFSFileRecord )
1949			{
1950				smallCatalogFileP = (HFSCatalogFile *) &record;
1951				GPtr->TarID = smallCatalogFileP->fileID;							/* target ID = file number */
1952				GPtr->CNType = record.recordType;									/* target CNode type = thread */
1953				CopyCatalogName( (const CatalogName *) &foundKey.hfs.nodeName, &GPtr->CName, isHFSPlus );	/* target CName = directory name */
1954				filCnt++;
1955				if (dprP->directoryID == kHFSRootFolderID)
1956					rtfilCnt++;
1957			}
1958
1959			//	Unknown/Bad record type
1960			else
1961			{
1962				M_DebugStr("\p Unknown-Bad record type");
1963				return( 123 );
1964			}
1965		}
1966
1967		//
1968		//	 if not same ParID or no record
1969		//
1970		else if ( (record.recordType == kHFSFileThreadRecord) || (record.recordType == kHFSPlusFileThreadRecord) )			/* it's a file thread, skip past it */
1971		{
1972			GPtr->TarID				= parID;						//	target ID = file number
1973			GPtr->CNType			= record.recordType;			//	target CNode type = thread
1974			GPtr->CName.ustr.length	= 0;							//	no target CName
1975		}
1976
1977		else
1978		{
1979resumeAtParent:
1980			GPtr->TarID = dprP->directoryID;						/* target ID = current directory ID */
1981			GPtr->CNType = record.recordType;						/* target CNode type = directory */
1982			CopyCatalogName( (const CatalogName *) &dprP->directoryName, &GPtr->CName, isHFSPlus );	// copy the string name
1983
1984			//	re-locate current directory
1985			CopyCatalogName( (const CatalogName *) &dprP->directoryName, &catalogName, isHFSPlus );
1986			BuildCatalogKey( dprP->parentDirID, (const CatalogName *)&catalogName, isHFSPlus, &key );
1987			result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, dprP->directoryHint, &foundKey, &record2, &recSize, &hint );
1988
1989			if ( result != noErr )
1990			{
1991				result = IntError(GPtr,result);						/* error from BTSearch */
1992				return(result);
1993			}
1994			GPtr->TarBlock = hint;									/* set target block */
1995
1996
1997			valence = isHFSPlus == true ? record2.hfsPlusFolder.valence : (UInt32)record2.hfsFolder.valence;
1998
1999			if ( valence != dprP->offspringIndex -1 ) 				/* check its valence */
2000				if ( ( result = RcdValErr( GPtr, E_DirVal, dprP->offspringIndex -1, valence, dprP->parentDirID ) ) )
2001					return( result );
2002
2003			GPtr->DirLevel--;										/* move up a level */
2004
2005			if(GPtr->DirLevel > 0)
2006			{
2007				dprP = &(GPtr->DirPTPtr)[GPtr->DirLevel - 1];
2008				GPtr->TarID	= dprP->directoryID;					/* target ID = current directory ID */
2009				GPtr->CNType = record.recordType;					/* target CNode type = directory */
2010				CopyCatalogName( (const CatalogName *) &dprP->directoryName, &GPtr->CName, isHFSPlus );
2011			}
2012		}
2013	}		//	end while
2014
2015	//
2016	//	verify directory and file counts (all nonfatal, repairable errors)
2017	//
2018	if (!isHFSPlus && (rtdirCnt != calculatedVCB->vcbNmRtDirs)) /* check count of dirs in root */
2019		if ( ( result = RcdValErr(GPtr,E_RtDirCnt,rtdirCnt,calculatedVCB->vcbNmRtDirs,0) ) )
2020			return( result );
2021
2022	if (!isHFSPlus && (rtfilCnt != calculatedVCB->vcbNmFls)) /* check count of files in root */
2023		if ( ( result = RcdValErr(GPtr,E_RtFilCnt,rtfilCnt,calculatedVCB->vcbNmFls,0) ) )
2024			return( result );
2025
2026	if (dirCnt != calculatedVCB->vcbFolderCount) /* check count of dirs in volume */
2027		if ( ( result = RcdValErr(GPtr,E_DirCnt,dirCnt,calculatedVCB->vcbFolderCount,0) ) )
2028			return( result );
2029
2030	if (filCnt != calculatedVCB->vcbFileCount) /* check count of files in volume */
2031		if ( ( result = RcdValErr(GPtr,E_FilCnt,filCnt,calculatedVCB->vcbFileCount,0) ) )
2032			return( result );
2033
2034	return( noErr );
2035
2036}	/* end of CatHChk */
2037
2038
2039
2040/*------------------------------------------------------------------------------
2041
2042Function:	CreateAttributesBTreeControlBlock
2043
2044Function:	Create the calculated AttributesBTree Control Block
2045
2046Input:		GPtr	-	pointer to scavenger global area
2047
2048Output:				-	0	= no error
2049						n 	= error code
2050------------------------------------------------------------------------------*/
2051OSErr	CreateAttributesBTreeControlBlock( SGlobPtr GPtr )
2052{
2053	OSErr					err = 0;
2054	SInt32					size;
2055	UInt32					numABlks;
2056	BTreeControlBlock *  	btcb;
2057	SVCB *  				vcb;
2058	Boolean					isHFSPlus;
2059	BTHeaderRec				header;
2060	BlockDescriptor  		block;
2061
2062	//	Set up
2063	isHFSPlus = VolumeObjectIsHFSPlus( );
2064	GPtr->TarID		= kHFSAttributesFileID;
2065	GPtr->TarBlock	= kHeaderNodeNum;
2066	block.buffer = NULL;
2067	btcb = GPtr->calculatedAttributesBTCB;
2068	vcb = GPtr->calculatedVCB;
2069
2070	//
2071	//	check out allocation info for the Attributes File
2072	//
2073
2074	if (isHFSPlus)
2075	{
2076		HFSPlusVolumeHeader *volumeHeader;
2077
2078		err = GetVolumeObjectVHB( &block );
2079		if ( err != noErr )
2080			goto exit;
2081		volumeHeader = (HFSPlusVolumeHeader *) block.buffer;
2082
2083		CopyMemory( volumeHeader->attributesFile.extents, GPtr->calculatedAttributesFCB->fcbExtents32, sizeof(HFSPlusExtentRecord) );
2084
2085		err = CheckFileExtents( GPtr, kHFSAttributesFileID, kDataFork, NULL, (void *)GPtr->calculatedAttributesFCB->fcbExtents32, &numABlks);
2086		if (err) goto exit;
2087
2088		if ( volumeHeader->attributesFile.totalBlocks != numABlks )					//	check out the PEOF
2089		{
2090			RcdError( GPtr, E_CatPEOF );
2091			err = E_CatPEOF;
2092			goto exit;
2093		}
2094		else
2095		{
2096			GPtr->calculatedAttributesFCB->fcbLogicalSize  = (UInt64) volumeHeader->attributesFile.logicalSize;						//	Set Attributes tree's LEOF
2097			GPtr->calculatedAttributesFCB->fcbPhysicalSize = (UInt64) volumeHeader->attributesFile.totalBlocks *
2098											(UInt64) volumeHeader->blockSize;	//	Set Attributes tree's PEOF
2099		}
2100
2101		//
2102		//	See if we actually have an attributes BTree
2103		//
2104		if (numABlks == 0)
2105		{
2106			btcb->maxKeyLength		= 0;
2107			btcb->keyCompareProc	= 0;
2108			btcb->leafRecords		= 0;
2109			btcb->nodeSize			= 0;
2110			btcb->totalNodes		= 0;
2111			btcb->freeNodes			= 0;
2112			btcb->attributes		= 0;
2113
2114			btcb->treeDepth		= 0;
2115			btcb->rootNode		= 0;
2116			btcb->firstLeafNode	= 0;
2117			btcb->lastLeafNode	= 0;
2118
2119		//	GPtr->calculatedVCB->attributesRefNum = 0;
2120			GPtr->calculatedVCB->vcbAttributesFile = NULL;
2121		}
2122		else
2123		{
2124			//	read the BTreeHeader from disk & also validate it's node size.
2125			err = GetBTreeHeader(GPtr, GPtr->calculatedAttributesFCB, &header);
2126			if (err) goto exit;
2127
2128			btcb->maxKeyLength		= kAttributeKeyMaximumLength;					//	max key length
2129			btcb->keyCompareProc	= (void *)CompareAttributeKeys;
2130			btcb->leafRecords		= header.leafRecords;
2131			btcb->nodeSize			= header.nodeSize;
2132			btcb->totalNodes		= ( GPtr->calculatedAttributesFCB->fcbPhysicalSize / btcb->nodeSize );
2133			btcb->freeNodes			= btcb->totalNodes;									//	start with everything free
2134			btcb->attributes		|=(kBTBigKeysMask + kBTVariableIndexKeysMask);		//	HFS+ Attributes files have large, variable-sized keys
2135
2136			btcb->treeDepth		= header.treeDepth;
2137			btcb->rootNode		= header.rootNode;
2138			btcb->firstLeafNode	= header.firstLeafNode;
2139			btcb->lastLeafNode	= header.lastLeafNode;
2140
2141			//
2142			//	Make sure the header nodes size field is correct by looking at the 1st record offset
2143			//
2144			err = CheckNodesFirstOffset( GPtr, btcb );
2145			if (err) goto exit;
2146		}
2147	}
2148	else
2149	{
2150		btcb->maxKeyLength		= 0;
2151		btcb->keyCompareProc	= 0;
2152		btcb->leafRecords		= 0;
2153		btcb->nodeSize			= 0;
2154		btcb->totalNodes		= 0;
2155		btcb->freeNodes			= 0;
2156		btcb->attributes		= 0;
2157
2158		btcb->treeDepth		= 0;
2159		btcb->rootNode		= 0;
2160		btcb->firstLeafNode	= 0;
2161		btcb->lastLeafNode	= 0;
2162
2163		GPtr->calculatedVCB->vcbAttributesFile = NULL;
2164	}
2165
2166	//
2167	//	set up our DFA extended BTCB area.  Will we have enough memory on all HFS+ volumes.
2168	//
2169	btcb->refCon = AllocateClearMemory( sizeof(BTreeExtensionsRec) );			// allocate space for our BTCB extensions
2170	if ( btcb->refCon == nil ) {
2171		err = R_NoMem;
2172		goto exit;
2173	}
2174
2175	if (btcb->totalNodes == 0)
2176	{
2177		((BTreeExtensionsRec*)btcb->refCon)->BTCBMPtr			= nil;
2178		((BTreeExtensionsRec*)btcb->refCon)->BTCBMSize			= 0;
2179		((BTreeExtensionsRec*)btcb->refCon)->realFreeNodeCount	= 0;
2180	}
2181	else
2182	{
2183		if ( btcb->refCon == nil ) {
2184			err = R_NoMem;
2185			goto exit;
2186		}
2187		size = (btcb->totalNodes + 7) / 8;											//	size of BTree bit map
2188		((BTreeExtensionsRec*)btcb->refCon)->BTCBMPtr = AllocateClearMemory(size);			//	get precleared bitmap
2189		if ( ((BTreeExtensionsRec*)btcb->refCon)->BTCBMPtr == nil )
2190		{
2191			err = R_NoMem;
2192			goto exit;
2193		}
2194
2195		((BTreeExtensionsRec*)btcb->refCon)->BTCBMSize			= size;						//	remember how long it is
2196		((BTreeExtensionsRec*)btcb->refCon)->realFreeNodeCount	= header.freeNodes;		//	keep track of real free nodes for progress
2197	}
2198
2199exit:
2200	if (block.buffer)
2201		(void) ReleaseVolumeBlock(vcb, &block, kReleaseBlock);
2202
2203	return (err);
2204}
2205
2206/*
2207 * Function: RecordLastAttrBits
2208 *
2209 * Description:
2210 *	Updates the Chinese Remainder Theorem buckets with extended attribute
2211 *  information for the previous fileID stored in the global structure.
2212 *
2213 * Input:
2214 *	GPtr - pointer to scavenger global area
2215 *		* GPtr->lastAttrInfo.fileID - fileID of last attribute seen
2216 *
2217 * Output:	Nothing
2218 */
2219static void RecordLastAttrBits(SGlobPtr GPtr)
2220{
2221	/* lastAttrInfo structure is initialized to zero and hence ignore
2222	 * recording information for fileID = 0.  fileIDs < 16 (except for
2223	 * fileID = 2) can have extended attributes but do not have
2224	 * corresponding entry in catalog Btree.  Ignore recording these
2225	 * fileIDs for Chinese Remainder Theorem buckets.  Currently we only
2226	 * set extended attributes for fileID = 1 among these fileIDs
2227     * and this can change in future (see 3984119)
2228	 */
2229	if ((GPtr->lastAttrInfo.fileID == 0) ||
2230		((GPtr->lastAttrInfo.fileID < kHFSFirstUserCatalogNodeID) &&
2231	    (GPtr->lastAttrInfo.fileID != kHFSRootFolderID))) {
2232		return;
2233	}
2234
2235	if (GPtr->lastAttrInfo.hasSecurity == true) {
2236		/* fileID has both extended attribute and ACL */
2237		RecordXAttrBits(GPtr, kHFSHasAttributesMask | kHFSHasSecurityMask,
2238			GPtr->lastAttrInfo.fileID, kCalculatedAttributesRefNum);
2239		GPtr->lastAttrInfo.hasSecurity = false;
2240	} else {
2241		/* fileID only has extended attribute */
2242		RecordXAttrBits(GPtr, kHFSHasAttributesMask,
2243			GPtr->lastAttrInfo.fileID, kCalculatedAttributesRefNum);
2244	}
2245}
2246
2247/*
2248 * Function: setLastAttrAllocInfo
2249 *
2250 * Description:
2251 *	Set the global structure of last extended attribute with
2252 * 	the allocation block information.  Also set the isValid to true
2253 *	to indicate that the data is valid and should be used to verify
2254 *  allocation blocks.
2255 *
2256 * Input:
2257 *	GPtr 		- pointer to scavenger global area
2258 *	totalBlocks - total blocks allocated by the attribute
2259 * 	logicalSize - logical size of the attribute
2260 *	calculatedBlocks - blocks accounted by the attribute in current extent
2261 *
2262 * Output: Nothing
2263 */
2264static void setLastAttrAllocInfo(SGlobPtr GPtr, u_int32_t totalBlocks,
2265				u_int64_t logicalSize, u_int32_t calculatedTotalBlocks)
2266{
2267	GPtr->lastAttrInfo.totalBlocks = totalBlocks;
2268	GPtr->lastAttrInfo.logicalSize = logicalSize;
2269	GPtr->lastAttrInfo.calculatedTotalBlocks = calculatedTotalBlocks;
2270	GPtr->lastAttrInfo.isValid = true;
2271}
2272
2273/*
2274 * Function: CheckLastAttrAllocation
2275 *
2276 * Description:
2277 *	Checks the allocation block information stored for the last
2278 * 	extended attribute seen during extended attribute BTree traversal.
2279 *  Always resets the information stored for last EA allocation.
2280 *
2281 * Input:	GPtr - pointer to scavenger global area
2282 *
2283 * Output:	int - function result:
2284 *				zero - no error
2285 *				non-zero - error
2286 */
2287static int CheckLastAttrAllocation(SGlobPtr GPtr)
2288{
2289	int result = 0;
2290	u_int64_t bytes;
2291
2292	if (GPtr->lastAttrInfo.isValid == true) {
2293		if (GPtr->lastAttrInfo.totalBlocks !=
2294			GPtr->lastAttrInfo.calculatedTotalBlocks) {
2295			result = RecordBadAllocation(GPtr->lastAttrInfo.fileID,
2296						GPtr->lastAttrInfo.attrname, kEAData,
2297						GPtr->lastAttrInfo.totalBlocks,
2298						GPtr->lastAttrInfo.calculatedTotalBlocks);
2299		} else {
2300			bytes = (u_int64_t)GPtr->lastAttrInfo.calculatedTotalBlocks *
2301					(u_int64_t)GPtr->calculatedVCB->vcbBlockSize;
2302			if (GPtr->lastAttrInfo.logicalSize > bytes) {
2303				result = RecordTruncation(GPtr->lastAttrInfo.fileID,
2304							GPtr->lastAttrInfo.attrname, kEAData,
2305							GPtr->lastAttrInfo.logicalSize, bytes);
2306			}
2307		}
2308
2309		/* Invalidate information in the global structure */
2310		GPtr->lastAttrInfo.isValid = false;
2311	}
2312
2313	return (result);
2314}
2315
2316/*------------------------------------------------------------------------------
2317Function:	CheckAttributeRecord
2318
2319Description:
2320		This is call back function called for all leaf records in
2321		Attribute BTree during the verify and repair stage.  The basic
2322		functionality of the function is same during verify and repair
2323		stages except that whenever it finds corruption, the verify
2324		stage prints message and the repair stage repairs it.  In the verify
2325		stage, this function accounts for allocation blocks used
2326		by extent-based extended attributes and also updates the chinese
2327		remainder theorem buckets corresponding the extended attribute
2328		and security bit.
2329
2330		1. Only in the verify stage, if the fileID or attribute name of current
2331		extended attribute are not same as the previous attribute, check the
2332		allocation block counts for the previous attribute.
2333
2334		2. Only in the verify stage, If the fileID of current attribute is not
2335		same as the previous attribute, record the previous fileID information
2336		for Chinese	Remainder Theorem.
2337
2338		3. For attribute type,
2339			kHFSPlusAttrForkData:
2340			---------------------
2341			Do all of the following during verify stage and nothing in repair
2342			stage -
2343
2344			Check the start block for extended attribute from the key.  If not
2345			zero, print error.
2346
2347			Account for blocks occupied by this extent and store the allocation
2348			information for this extent to check in future.  Also update the
2349			last attribute information in the global structure.
2350
2351			kHFSPlusAttrExtents:
2352			--------------------
2353			If the current attribute's fileID is not same as previous fileID, or
2354			if the previous recordType is not a valid forkData or overflow extent
2355			record, report an error in verify stage or mark it for deletion in
2356			repair stage.
2357
2358			Do all of the following during verify stage and nothing in repair
2359			stage -
2360
2361			Check the start block for extended attribute from the key.  If not
2362			equal to the total blocks seen uptil last attribtue, print error.
2363
2364			Account for blocks occupied by this extent.  Update previous
2365			attribute allocation information with blocks seen in current
2366			extent.  Also update last attribute block information in the global
2367			structure.
2368
2369			kHFSPlusAttrInlineData:
2370			-----------------------
2371			Only in the verify stage, check if the start block in the key is
2372			equal to zero.  If not, print error.
2373
2374			Unknown type:
2375			-------------
2376			In verify stage, report error.  In repair stage, mark the record
2377			to delete.
2378
2379		4. If a record is marked for deletion, delete the record.
2380
2381		5. Before exiting from the function, always do the following -
2382			a. Indicate if the extended attribute was an ACL
2383			b. Update previous fileID and recordType with current information.
2384			c. Update previous attribute name with current attribute name.
2385
2386Input:	GPtr	-	pointer to scavenger global area
2387		key		-	key for current attribute
2388		rec		- 	attribute record
2389		reclen	- 	length of the record
2390
2391Output:	int		-	function result:
2392			0	= no error
2393			n 	= error code
2394------------------------------------------------------------------------------*/
2395int
2396CheckAttributeRecord(SGlobPtr GPtr, const HFSPlusAttrKey *key, const HFSPlusAttrRecord *rec, UInt16 reclen)
2397{
2398	int result = 0;
2399	unsigned char attrname[XATTR_MAXNAMELEN+1];
2400	size_t attrlen;
2401	u_int32_t blocks;
2402	u_int32_t fileID;
2403	struct attributeInfo *prevAttr;
2404	Boolean isSameAttr = true;
2405	Boolean doDelete = false;
2406	u_int16_t dfaStage = GetDFAStage();
2407
2408	/* Assert if volume is not HFS Plus */
2409	assert(VolumeObjectIsHFSPlus() == true);
2410
2411	prevAttr = &(GPtr->lastAttrInfo);
2412	fileID = key->fileID;
2413	/* Convert unicode attribute name to UTF-8 string */
2414	(void) utf_encodestr(key->attrName, key->attrNameLen * 2, attrname, &attrlen, sizeof(attrname));
2415	attrname[attrlen] = '\0';
2416
2417	/* Compare the current attribute to last attribute seen */
2418	if ((fileID != prevAttr->fileID) ||
2419		(strcmp((char *)attrname, (char *)prevAttr->attrname) != 0)) {
2420		isSameAttr = false;
2421	}
2422
2423	/* We check allocation block information and record EA information for
2424	 * CRT bucket in verify stage and hence no need to do it again in
2425	 * repair stage.
2426	 */
2427	if (dfaStage == kVerifyStage) {
2428		/* Different attribute - check allocation block information */
2429		if (isSameAttr == false) {
2430			result = CheckLastAttrAllocation(GPtr);
2431			if (result) {
2432				goto update_out;
2433			}
2434		}
2435
2436		/* Different fileID - record information in CRT bucket */
2437		if (fileID != prevAttr->fileID) {
2438			RecordLastAttrBits(GPtr);
2439		}
2440	}
2441
2442	switch (rec->recordType) {
2443		case kHFSPlusAttrForkData: {
2444			/* Check start block only in verify stage to avoid printing message
2445			 * in repair stage.  Note that this corruption is not repairable
2446			 * currently.  Also check extents only in verify stage to avoid
2447			 * false overlap extents error.
2448			 */
2449			if (dfaStage == kVerifyStage) {
2450				/* Start block in the key should be zero */
2451				if (key->startBlock != 0) {
2452					RcdError(GPtr, E_ABlkSt);
2453					result = E_ABlkSt;
2454					goto err_out;
2455				}
2456
2457				/* Check the extent information and record overlapping extents, if any */
2458				result = CheckFileExtents (GPtr, fileID, kEAData, attrname,
2459							rec->forkData.theFork.extents, &blocks);
2460				if (result) {
2461					goto update_out;
2462				}
2463
2464				/* Store allocation information to check in future */
2465				(void) setLastAttrAllocInfo(GPtr, rec->forkData.theFork.totalBlocks,
2466							rec->forkData.theFork.logicalSize, blocks);
2467			}
2468			break;
2469		}
2470
2471		case kHFSPlusAttrExtents: {
2472			/* Different attribute/fileID or incorrect previous record type */
2473			if ((isSameAttr == false) ||
2474				((prevAttr->recordType != kHFSPlusAttrExtents) &&
2475				(prevAttr->recordType != kHFSPlusAttrForkData))) {
2476				if (dfaStage == kRepairStage) {
2477					/* Delete record in repair stage */
2478					doDelete = true;
2479				} else {
2480					/* Report error in verify stage */
2481					RcdError(GPtr, E_AttrRec);
2482					GPtr->ABTStat |= S_AttrRec;
2483					goto err_out;
2484				}
2485			}
2486
2487			/* Check start block only in verify stage to avoid printing message
2488			 * in repair stage.  Note that this corruption is not repairable
2489			 * currently.  Also check extents only in verify stage to avoid
2490			 * false overlap extents error.
2491			 */
2492			if (dfaStage == kVerifyStage) {
2493				/* startBlock in the key should be equal to total blocks
2494				 * seen uptil last attribute.
2495				 */
2496				if (key->startBlock != prevAttr->calculatedTotalBlocks) {
2497					RcdError(GPtr, E_ABlkSt);
2498					result = E_ABlkSt;
2499					goto err_out;
2500				}
2501
2502				/* Check the extent information and record overlapping extents, if any */
2503				result = CheckFileExtents (GPtr, fileID, kEAData, attrname,
2504							rec->overflowExtents.extents, &blocks);
2505				if (result) {
2506					goto update_out;
2507				}
2508
2509				/* Increment the blocks seen uptil now for this attribute */
2510				prevAttr->calculatedTotalBlocks += blocks;
2511			}
2512			break;
2513		}
2514
2515		case kHFSPlusAttrInlineData: {
2516			/* Check start block only in verify stage to avoid printing message
2517			 * in repair stage.
2518			 */
2519			if (dfaStage == kVerifyStage) {
2520				/* Start block in the key should be zero */
2521				if (key->startBlock != 0) {
2522					RcdError(GPtr, E_ABlkSt);
2523					result = E_ABlkSt;
2524					goto err_out;
2525				}
2526			}
2527			break;
2528		}
2529
2530		default: {
2531			/* Unknown attribute record */
2532			if (dfaStage == kRepairStage) {
2533				/* Delete record in repair stage */
2534				doDelete = true;
2535			} else {
2536				/* Report error in verify stage */
2537				RcdError(GPtr, E_AttrRec);
2538				GPtr->ABTStat |= S_AttrRec;
2539				goto err_out;
2540			}
2541			break;
2542		}
2543	};
2544
2545	if (doDelete == true) {
2546		result = DeleteBTreeRecord(GPtr->calculatedAttributesFCB, key);
2547		DPRINTF (d_info|d_xattr, "%s: Deleting attribute %s for fileID %d, type = %d\n", __FUNCTION__, attrname, key->fileID, rec->recordType);
2548		if (result) {
2549			DPRINTF (d_error|d_xattr, "%s: Error in deleting record for %s for fileID %d, type = %d\n", __FUNCTION__, attrname, key->fileID, rec->recordType);
2550		}
2551
2552		/* Set flags to mark header and map dirty */
2553		GPtr->ABTStat |= S_BTH + S_BTM;
2554		goto err_out;
2555	}
2556
2557update_out:
2558	/* Note that an ACL exists for this fileID */
2559	if (strcmp((char *)attrname, KAUTH_FILESEC_XATTR) == 0) {
2560		prevAttr->hasSecurity = true;
2561	}
2562
2563	/* Always update the last recordType, fileID and attribute name before exiting */
2564	prevAttr->recordType = rec->recordType;
2565	prevAttr->fileID = fileID;
2566	(void) strlcpy((char *)prevAttr->attrname, (char *)attrname, sizeof(prevAttr->attrname));
2567
2568	goto out;
2569
2570err_out:
2571	/* If the current record is invalid/bogus, decide whether to update
2572	 * fileID stored in global structure for future comparison based on the
2573	 * previous fileID.
2574	 * If the current bogus record's fileID is different from fileID of the
2575	 * previous good record, we do not want to account for bogus fileID in
2576	 * the Chinese Remainder Theorem when we see next good record.
2577	 * Hence reset the fileID in global structure to dummy value.  Example,
2578	 * if the fileIDs are 10 15 20 and record with ID=15 is bogus, we do not
2579	 * want to account for record with ID=15.
2580	 * If the current bogus record's fileID is same as the fileID of the
2581	 * previous good record, we want to account for this fileID in the
2582	 * next good record we see after this bogus record.  Hence do not
2583	 * reset the fileID to dummy value.  Example, if the records have fileID
2584	 * 10 10 30 and the second record with ID=10 is bogus, we want to
2585	 * account for ID=10 when we see record with ID=30.
2586	 */
2587	if (prevAttr->fileID != fileID) {
2588		prevAttr->fileID = 0;
2589	}
2590
2591out:
2592	return(result);
2593}
2594
2595/* Function:	RecordXAttrBits
2596 *
2597 * Description:
2598 * This function increments the prime number buckets for the associated
2599 * prime bucket set based on the flags and btreetype to determine
2600 * the discrepancy between the attribute btree and catalog btree for
2601 * extended attribute data consistency.  This function is based on
2602 * Chinese Remainder Theorem.
2603 *
2604 * Alogrithm:
2605 * 1. If none of kHFSHasAttributesMask or kHFSHasSecurity mask is set,
2606 *    return.
2607 * 2. Based on btreetype and the flags, determine which prime number
2608 *    bucket should be updated.  Initialize pointers accordingly.
2609 * 3. Divide the fileID with pre-defined prime numbers. Store the
2610 *    remainder.
2611 * 4. Increment each prime number bucket at an offset of the
2612 *    corresponding remainder with one.
2613 *
2614 * Input:	1. GPtr - pointer to global scavenger area
2615 *        	2. flags - can include kHFSHasAttributesMask and/or kHFSHasSecurityMask
2616 *        	3. fileid - fileID for which particular extended attribute is seen
2617 *     	   	4. btreetye - can be kHFSPlusCatalogRecord or kHFSPlusAttributeRecord
2618 *                            indicates which btree prime number bucket should be incremented
2619 *
2620 * Output:	nil
2621 */
2622void RecordXAttrBits(SGlobPtr GPtr, UInt16 flags, HFSCatalogNodeID fileid, UInt16 btreetype)
2623{
2624	PrimeBuckets *cur_attr = NULL;
2625	PrimeBuckets *cur_sec = NULL;
2626
2627	if ( ((flags & kHFSHasAttributesMask) == 0) &&
2628	     ((flags & kHFSHasSecurityMask) == 0) ) {
2629		/* No attributes exists for this fileID */
2630		goto out;
2631	}
2632
2633	/* Determine which bucket are we updating */
2634	if (btreetype ==  kCalculatedCatalogRefNum) {
2635		/* Catalog BTree buckets */
2636		if (flags & kHFSHasAttributesMask) {
2637			cur_attr = &(GPtr->CBTAttrBucket);
2638			GPtr->cat_ea_count++;
2639		}
2640		if (flags & kHFSHasSecurityMask) {
2641			cur_sec = &(GPtr->CBTSecurityBucket);
2642			GPtr->cat_acl_count++;
2643		}
2644	} else if (btreetype ==  kCalculatedAttributesRefNum) {
2645		/* Attribute BTree buckets */
2646		if (flags & kHFSHasAttributesMask) {
2647			cur_attr = &(GPtr->ABTAttrBucket);
2648			GPtr->attr_ea_count++;
2649		}
2650		if (flags & kHFSHasSecurityMask) {
2651			cur_sec = &(GPtr->ABTSecurityBucket);
2652			GPtr->attr_acl_count++;
2653		}
2654	} else {
2655		/* Incorrect btreetype found */
2656		goto out;
2657	}
2658
2659	if (cur_attr) {
2660		add_prime_bucket_uint32(cur_attr, fileid);
2661	}
2662
2663	if (cur_sec) {
2664		add_prime_bucket_uint32(cur_sec, fileid);
2665	}
2666
2667out:
2668	return;
2669}
2670
2671/* Function:	CompareXattrPrimeBuckets
2672 *
2673 * Description:
2674 * This function compares the prime number buckets for catalog btree
2675 * and attribute btree for the given attribute type (normal attribute
2676 * bit or security bit).
2677 *
2678 * Input:	1. GPtr - pointer to global scavenger area
2679 *         	2. BitMask - indicate which attribute type should be compared.
2680 *        	             can include kHFSHasAttributesMask and/or kHFSHasSecurityMask
2681 * Output:	zero - buckets were compared successfully
2682 *            	non-zero - buckets were not compared
2683 */
2684static int CompareXattrPrimeBuckets(SGlobPtr GPtr, UInt16 BitMask)
2685{
2686	int result = 1;
2687	PrimeBuckets *cat;	/* Catalog BTree */
2688	PrimeBuckets *attr;	/* Attribute BTree */
2689
2690	/* Find the correct PrimeBuckets to compare */
2691	if (BitMask & kHFSHasAttributesMask) {
2692		/* Compare buckets for attribute bit */
2693		cat = &(GPtr->CBTAttrBucket);
2694		attr = &(GPtr->ABTAttrBucket);
2695	} else if (BitMask & kHFSHasSecurityMask) {
2696		/* Compare buckets for security bit */
2697		cat = &(GPtr->CBTSecurityBucket);
2698		attr = &(GPtr->ABTSecurityBucket);
2699	} else {
2700		plog ("%s: Incorrect BitMask found.\n", __FUNCTION__);
2701		goto out;
2702	}
2703
2704	result = compare_prime_buckets(cat, attr);
2705	if (result) {
2706		char catbtree[32], attrbtree[32];
2707		/* Unequal values found, set the error bit in ABTStat */
2708		if (BitMask & kHFSHasAttributesMask) {
2709			fsckPrint(GPtr->context, E_IncorrectAttrCount);
2710			sprintf (catbtree, "%u", GPtr->cat_ea_count);
2711			sprintf (attrbtree, "%u", GPtr->attr_ea_count);
2712			fsckPrint(GPtr->context, E_BadValue, attrbtree, catbtree);
2713			GPtr->ABTStat |= S_AttributeCount;
2714		} else {
2715			fsckPrint(GPtr->context, E_IncorrectSecurityCount);
2716			sprintf (catbtree, "%u", GPtr->cat_acl_count);
2717			sprintf (attrbtree, "%u", GPtr->attr_acl_count);
2718			fsckPrint (GPtr->context, E_BadValue, attrbtree, catbtree);
2719			GPtr->ABTStat |= S_SecurityCount;
2720		}
2721	}
2722
2723	result = 0;
2724
2725out:
2726	return result;
2727}
2728
2729/*------------------------------------------------------------------------------
2730
2731Function:	AttrBTChk - (Attributes BTree Check)
2732
2733Function:	Verifies the attributes BTree structure.
2734
2735Input:		GPtr		-	pointer to scavenger global area
2736
2737Output:		ExtBTChk	-	function result:
2738								0	= no error
2739								n 	= error code
2740------------------------------------------------------------------------------*/
2741
2742OSErr AttrBTChk( SGlobPtr GPtr )
2743{
2744	OSErr					err;
2745
2746	//
2747	//	If this volume has no attributes BTree, then skip this check
2748	//
2749	if (GPtr->calculatedVCB->vcbAttributesFile == NULL)
2750		return noErr;
2751
2752	//	Write the status message here to avoid potential confusion to user.
2753	fsckPrint(GPtr->context, hfsExtAttrBTCheck);
2754
2755	//	Set up
2756	GPtr->TarID		= kHFSAttributesFileID;				//	target = attributes file
2757	GetVolumeObjectBlockNum( &GPtr->TarBlock );			//	target block = VHB/MDB
2758
2759	//
2760	//	check out the BTree structure
2761	//
2762
2763	err = BTCheck( GPtr, kCalculatedAttributesRefNum, (CheckLeafRecordProcPtr)CheckAttributeRecord);
2764	ReturnIfError( err );														//	invalid attributes file BTree
2765
2766	//  check the allocation block information about the last attribute
2767	err = CheckLastAttrAllocation(GPtr);
2768	ReturnIfError(err);
2769
2770	//  record the last fileID for Chinese Remainder Theorem comparison
2771	RecordLastAttrBits(GPtr);
2772
2773	//	compare the attributes prime buckets calculated from catalog btree and attribute btree
2774	err = CompareXattrPrimeBuckets(GPtr, kHFSHasAttributesMask);
2775	ReturnIfError( err );
2776
2777	//	compare the security prime buckets calculated from catalog btree and attribute btree
2778	err = CompareXattrPrimeBuckets(GPtr, kHFSHasSecurityMask);
2779	ReturnIfError( err );
2780
2781	//
2782	//	check out the allocation map structure
2783	//
2784
2785	err = BTMapChk( GPtr, kCalculatedAttributesRefNum );
2786	ReturnIfError( err );														//	Invalid attributes BTree map
2787
2788	//
2789	// Make sure unused nodes in the B-tree are zero filled.
2790	//
2791	err = BTCheckUnusedNodes(GPtr, kCalculatedAttributesRefNum, &GPtr->ABTStat);
2792	ReturnIfError( err );
2793
2794	//
2795	//	compare BTree header record on disk with scavenger's BTree header record
2796	//
2797
2798	err = CmpBTH( GPtr, kCalculatedAttributesRefNum );
2799	ReturnIfError( err );
2800
2801	//
2802	//	compare BTree map on disk with scavenger's BTree map
2803	//
2804
2805	err = CmpBTM( GPtr, kCalculatedAttributesRefNum );
2806
2807	return( err );
2808}
2809
2810
2811/*------------------------------------------------------------------------------
2812
2813Name:		RcdValErr - (Record Valence Error)
2814
2815Function:	Allocates a RepairOrder node and linkg it into the 'GPtr->RepairP'
2816			list, to describe an incorrect valence count for possible repair.
2817
2818Input:		GPtr		- ptr to scavenger global data
2819			type		- error code (E_xxx), which should be >0
2820			correct		- the correct valence, as computed here
2821			incorrect	- the incorrect valence as found in volume
2822			parid		- the parent id, if S_Valence error
2823
2824Output:		0 			- no error
2825			R_NoMem		- not enough mem to allocate record
2826------------------------------------------------------------------------------*/
2827
2828static int RcdValErr( SGlobPtr GPtr, OSErr type, UInt32 correct, UInt32 incorrect, HFSCatalogNodeID parid )	 /* the ParID, if needed */
2829{
2830	RepairOrderPtr	p;										/* the new node we compile */
2831	SInt16			n;										/* size of node we allocate */
2832	Boolean			isHFSPlus;
2833	char goodStr[32], badStr[32];
2834
2835	isHFSPlus = VolumeObjectIsHFSPlus( );
2836	fsckPrint(GPtr->context, type);
2837	sprintf(goodStr, "%u", correct);
2838	sprintf(badStr, "%u", incorrect);
2839	fsckPrint(GPtr->context, E_BadValue, goodStr, badStr);
2840
2841	if (type == E_DirVal)									/* if normal directory valence error */
2842		n = CatalogNameSize( &GPtr->CName, isHFSPlus);
2843	else
2844		n = 0;												/* other errors don't need the name */
2845
2846	p = AllocMinorRepairOrder( GPtr,n );					/* get the node */
2847	if (p==NULL) 											/* quit if out of room */
2848		return (R_NoMem);
2849
2850	p->type			= type;									/* save error info */
2851	p->correct		= correct;
2852	p->incorrect	= incorrect;
2853	p->parid		= parid;
2854
2855	if ( n != 0 ) 											/* if name needed */
2856		CopyCatalogName( (const CatalogName *) &GPtr->CName, (CatalogName*)&p->name, isHFSPlus );
2857
2858	GPtr->CatStat |= S_Valence;								/* set flag to trigger repair */
2859
2860	return( noErr );										/* successful return */
2861}
2862
2863/*------------------------------------------------------------------------------
2864
2865Name:		RcdHsFldCntErr - (Record HasFolderCount)
2866
2867Function:	Allocates a RepairOrder node and linkg it into the 'GPtr->RepairP'
2868			list, to describe folder flag missing the HasFolderCount bit
2869
2870Input:		GPtr		- ptr to scavenger global data
2871			type		- error code (E_xxx), which should be >0
2872			correct		- the folder mask, as computed here
2873			incorrect	- the folder mask, as found in volume
2874			fid			- the folder id
2875
2876Output:		0 			- no error
2877			R_NoMem		- not enough mem to allocate record
2878------------------------------------------------------------------------------*/
2879
2880int RcdHsFldCntErr( SGlobPtr GPtr, OSErr type, UInt32 correct, UInt32 incorrect, HFSCatalogNodeID fid )
2881{
2882	RepairOrderPtr	p;										/* the new node we compile */
2883	char goodStr[32], badStr[32];
2884	fsckPrint(GPtr->context, type, fid);
2885	sprintf(goodStr, "%#x", correct);
2886	sprintf(badStr, "%#x", incorrect);
2887	fsckPrint(GPtr->context, E_BadValue, goodStr, badStr);
2888
2889	p = AllocMinorRepairOrder( GPtr,0 );					/* get the node */
2890	if (p==NULL) 											/* quit if out of room */
2891		return (R_NoMem);
2892
2893	p->type			= type;									/* save error info */
2894	p->correct		= correct;
2895	p->incorrect	= incorrect;
2896	p->parid		= fid;
2897
2898	return( noErr );										/* successful return */
2899}
2900/*------------------------------------------------------------------------------
2901
2902Name:		RcdFCntErr - (Record Folder Count)
2903
2904Function:	Allocates a RepairOrder node and linkg it into the 'GPtr->RepairP'
2905			list, to describe an incorrect folder count for possible repair.
2906
2907Input:		GPtr		- ptr to scavenger global data
2908			type		- error code (E_xxx), which should be >0
2909			correct		- the correct folder count, as computed here
2910			incorrect	- the incorrect folder count as found in volume
2911			fid		- the folder id
2912
2913Output:		0 			- no error
2914			R_NoMem		- not enough mem to allocate record
2915------------------------------------------------------------------------------*/
2916
2917int RcdFCntErr( SGlobPtr GPtr, OSErr type, UInt32 correct, UInt32 incorrect, HFSCatalogNodeID fid )
2918{
2919	RepairOrderPtr	p;										/* the new node we compile */
2920	char goodStr[32], badStr[32];
2921
2922	fsckPrint(GPtr->context, type, fid);
2923	sprintf(goodStr, "%u", correct);
2924	sprintf(badStr, "%u", incorrect);
2925	fsckPrint(GPtr->context, E_BadValue, goodStr, badStr);
2926
2927	p = AllocMinorRepairOrder( GPtr,0 );					/* get the node */
2928	if (p==NULL) 											/* quit if out of room */
2929		return (R_NoMem);
2930
2931	p->type			= type;									/* save error info */
2932	p->correct		= correct;
2933	p->incorrect	= incorrect;
2934	p->parid		= fid;
2935
2936	return( noErr );										/* successful return */
2937}
2938
2939/*------------------------------------------------------------------------------
2940
2941Name:		RcdMDBAllocationBlockStartErr - (Record Allocation Block Start Error)
2942
2943Function:	Allocates a RepairOrder node and linking it into the 'GPtr->RepairP'
2944			list, to describe the error for possible repair.
2945
2946Input:		GPtr		- ptr to scavenger global data
2947			type		- error code (E_xxx), which should be >0
2948			correct		- the correct valence, as computed here
2949			incorrect	- the incorrect valence as found in volume
2950
2951Output:		0 			- no error
2952			R_NoMem		- not enough mem to allocate record
2953------------------------------------------------------------------------------*/
2954
2955static	OSErr	RcdMDBEmbededVolDescriptionErr( SGlobPtr GPtr, OSErr type, HFSMasterDirectoryBlock *mdb )
2956{
2957	RepairOrderPtr			p;											//	the new node we compile
2958	EmbededVolDescription	*desc;
2959
2960	RcdError( GPtr, type );												//	first, record the error
2961
2962	p = AllocMinorRepairOrder( GPtr, sizeof(EmbededVolDescription) );	//	get the node
2963	if ( p == nil )	return( R_NoMem );
2964
2965	p->type							=  type;							//	save error info
2966	desc							=  (EmbededVolDescription *) &(p->name);
2967	desc->drAlBlSt					=  mdb->drAlBlSt;
2968	desc->drEmbedSigWord			=  mdb->drEmbedSigWord;
2969	desc->drEmbedExtent.startBlock	=  mdb->drEmbedExtent.startBlock;
2970	desc->drEmbedExtent.blockCount	=  mdb->drEmbedExtent.blockCount;
2971
2972	GPtr->VIStat					|= S_InvalidWrapperExtents;			//	set flag to trigger repair
2973
2974	return( noErr );													//	successful return
2975}
2976
2977
2978#if 0 // not used at this time
2979/*------------------------------------------------------------------------------
2980
2981Name:		RcdInvalidWrapperExtents - (Record Invalid Wrapper Extents)
2982
2983Function:	Allocates a RepairOrder node and linking it into the 'GPtr->RepairP'
2984			list, to describe the error for possible repair.
2985
2986Input:		GPtr		- ptr to scavenger global data
2987			type		- error code (E_xxx), which should be >0
2988			correct		- the correct valence, as computed here
2989			incorrect	- the incorrect valence as found in volume
2990
2991Output:		0 			- no error
2992			R_NoMem		- not enough mem to allocate record
2993------------------------------------------------------------------------------*/
2994
2995static	OSErr	RcdInvalidWrapperExtents( SGlobPtr GPtr, OSErr type )
2996{
2997	RepairOrderPtr			p;											//	the new node we compile
2998
2999	RcdError( GPtr, type );												//	first, record the error
3000
3001	p = AllocMinorRepairOrder( GPtr, 0 );	//	get the node
3002	if ( p == nil )	return( R_NoMem );
3003
3004	p->type							=  type;							//	save error info
3005
3006	GPtr->VIStat					|= S_BadMDBdrAlBlSt;				//	set flag to trigger repair
3007
3008	return( noErr );													//	successful return
3009}
3010#endif
3011
3012
3013#if 0	//	We just check and fix them in SRepair.c
3014/*------------------------------------------------------------------------------
3015
3016Name:		RcdOrphanedExtentErr
3017
3018Function:	Allocates a RepairOrder node and linkg it into the 'GPtr->RepairP'
3019			list, to describe an locked volume name for possible repair.
3020
3021Input:		GPtr		- ptr to scavenger global data
3022			type		- error code (E_xxx), which should be >0
3023			incorrect	- the incorrect file flags as found in file record
3024
3025Output:		0 			- no error
3026			R_NoMem		- not enough mem to allocate record
3027------------------------------------------------------------------------------*/
3028
3029static OSErr RcdOrphanedExtentErr ( SGlobPtr GPtr, SInt16 type, void *theKey )
3030{
3031	RepairOrderPtr	p;										/* the new node we compile */
3032	SInt16			n;										/* size of node we allocate */
3033	Boolean			isHFSPlus;
3034
3035	isHFSPlus = VolumeObjectIsHFSPlus( );
3036	RcdError( GPtr,type );									/* first, record the error */
3037
3038	if ( isHFSPlus )
3039		n = sizeof( HFSPlusExtentKey );
3040	else
3041		n = sizeof( HFSExtentKey );
3042
3043	p = AllocMinorRepairOrder( GPtr, n );					/* get the node */
3044	if ( p == NULL ) 										/* quit if out of room */
3045		return( R_NoMem );
3046
3047	CopyMemory( theKey, p->name, n );					/* copy in the key */
3048
3049	p->type = type;											/* save error info */
3050
3051	GPtr->EBTStat |= S_OrphanedExtent;						/* set flag to trigger repair */
3052
3053	return( noErr );										/* successful return */
3054}
3055#endif
3056
3057
3058/*------------------------------------------------------------------------------
3059
3060Function:	VInfoChk - (Volume Info Check)
3061
3062Function:	Verifies volume level information.
3063
3064Input:		GPtr		-	pointer to scavenger global area
3065
3066Output:		VInfoChk	-	function result:
3067								0	= no error
3068								n 	= error code
3069------------------------------------------------------------------------------*/
3070
3071OSErr VInfoChk( SGlobPtr GPtr )
3072{
3073	OSErr					result;
3074	UInt16					recSize;
3075	Boolean					isHFSPlus;
3076	UInt32					hint;
3077	UInt64					maxClump;
3078	SVCB					*vcb;
3079	VolumeObjectPtr			myVOPtr;
3080	CatalogRecord			record;
3081	CatalogKey				foundKey;
3082	BlockDescriptor  		altBlock;
3083	BlockDescriptor  		priBlock;
3084
3085	vcb = GPtr->calculatedVCB;
3086	altBlock.buffer = priBlock.buffer = NULL;
3087	isHFSPlus = VolumeObjectIsHFSPlus( );
3088	myVOPtr = GetVolumeObjectPtr( );
3089
3090	// locate the catalog record for the root directory�
3091	result = GetBTreeRecord( GPtr->calculatedCatalogFCB, 0x8001, &foundKey, &record, &recSize, &hint );
3092	GPtr->TarID = kHFSCatalogFileID;							/* target = catalog */
3093	GPtr->TarBlock = hint;										/* target block = returned hint */
3094	if ( result != noErr )
3095	{
3096		result = IntError( GPtr, result );
3097		return( result );
3098	}
3099
3100	GPtr->TarID		= AMDB_FNum;								//	target = alternate MDB or VHB
3101	GetVolumeObjectAlternateBlockNum( &GPtr->TarBlock );
3102	result = GetVolumeObjectAlternateBlock( &altBlock );
3103
3104	// invalidate if we have not marked the alternate as OK
3105	if ( isHFSPlus ) {
3106		if ( (myVOPtr->flags & kVO_AltVHBOK) == 0 )
3107			result = badMDBErr;
3108	}
3109	else if ( (myVOPtr->flags & kVO_AltMDBOK) == 0 ) {
3110		result = badMDBErr;
3111	}
3112	if ( result != noErr ) {
3113		GPtr->VIStat = GPtr->VIStat | S_MDB;
3114		if ( VolumeObjectIsHFS( ) ) {
3115			WriteError( GPtr, E_MDBDamaged, 0, 0 );
3116			if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
3117				plog("\tinvalid alternate MDB at %qd result %d \n", GPtr->TarBlock, result);
3118		}
3119		else {
3120			WriteError( GPtr, E_VolumeHeaderDamaged, 0, 0 );
3121			if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
3122				plog("\tinvalid alternate VHB at %qd result %d \n", GPtr->TarBlock, result);
3123		}
3124		result = noErr;
3125		goto exit;
3126	}
3127
3128	GPtr->TarID		= MDB_FNum;								// target = primary MDB or VHB
3129	GetVolumeObjectPrimaryBlockNum( &GPtr->TarBlock );
3130	result = GetVolumeObjectPrimaryBlock( &priBlock );
3131
3132	// invalidate if we have not marked the primary as OK
3133	if ( isHFSPlus ) {
3134		if ( (myVOPtr->flags & kVO_PriVHBOK) == 0 )
3135			result = badMDBErr;
3136	}
3137	else if ( (myVOPtr->flags & kVO_PriMDBOK) == 0 ) {
3138		result = badMDBErr;
3139	}
3140	if ( result != noErr ) {
3141		GPtr->VIStat = GPtr->VIStat | S_MDB;
3142		if ( VolumeObjectIsHFS( ) ) {
3143			WriteError( GPtr, E_MDBDamaged, 1, 0 );
3144			if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
3145				plog("\tinvalid primary MDB at %qd result %d \n", GPtr->TarBlock, result);
3146		}
3147		else {
3148			WriteError( GPtr, E_VolumeHeaderDamaged, 1, 0 );
3149			if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
3150				plog("\tinvalid primary VHB at %qd result %d \n", GPtr->TarBlock, result);
3151		}
3152		result = noErr;
3153		goto exit;
3154	}
3155
3156	// check to see that embedded HFS plus volumes still have both (alternate and primary) MDBs
3157	if ( VolumeObjectIsEmbeddedHFSPlus( ) &&
3158		 ( (myVOPtr->flags & kVO_PriMDBOK) == 0 || (myVOPtr->flags & kVO_AltMDBOK) == 0 ) )
3159	{
3160		GPtr->VIStat |= S_WMDB;
3161		WriteError( GPtr, E_MDBDamaged, 0, 0 );
3162		if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
3163			plog("\tinvalid wrapper MDB \n");
3164	}
3165
3166	if ( isHFSPlus )
3167	{
3168		HFSPlusVolumeHeader *	volumeHeader;
3169		HFSPlusVolumeHeader *	alternateVolumeHeader;
3170
3171		alternateVolumeHeader = (HFSPlusVolumeHeader *) altBlock.buffer;
3172		volumeHeader = (HFSPlusVolumeHeader *) priBlock.buffer;
3173
3174		maxClump = (UInt64) (vcb->vcbTotalBlocks / 4) * vcb->vcbBlockSize; /* max clump = 1/4 volume size */
3175
3176		//	check out creation and last mod dates
3177		vcb->vcbCreateDate	= alternateVolumeHeader->createDate;	// use creation date in alt MDB
3178		vcb->vcbModifyDate	= volumeHeader->modifyDate;		// don't change last mod date
3179		vcb->vcbCheckedDate	= volumeHeader->checkedDate;		// don't change checked date
3180
3181		// 3882639: Removed check for volume attributes in HFS Plus
3182		vcb->vcbAttributes = volumeHeader->attributes;
3183
3184		//	verify allocation map ptr
3185		if ( volumeHeader->nextAllocation < vcb->vcbTotalBlocks )
3186			vcb->vcbNextAllocation = volumeHeader->nextAllocation;
3187		else
3188			vcb->vcbNextAllocation = 0;
3189
3190		//	verify default clump sizes
3191		if ( (volumeHeader->rsrcClumpSize > 0) &&
3192			 (volumeHeader->rsrcClumpSize <= kMaxClumpSize) &&
3193			 ((volumeHeader->rsrcClumpSize % vcb->vcbBlockSize) == 0) )
3194			vcb->vcbRsrcClumpSize = volumeHeader->rsrcClumpSize;
3195		else if ( (alternateVolumeHeader->rsrcClumpSize > 0) &&
3196				  (alternateVolumeHeader->rsrcClumpSize <= kMaxClumpSize) &&
3197				  ((alternateVolumeHeader->rsrcClumpSize % vcb->vcbBlockSize) == 0) )
3198			vcb->vcbRsrcClumpSize = alternateVolumeHeader->rsrcClumpSize;
3199		else if (4ULL * vcb->vcbBlockSize <= kMaxClumpSize)
3200			vcb->vcbRsrcClumpSize = 4 * vcb->vcbBlockSize;
3201		else
3202			vcb->vcbRsrcClumpSize = vcb->vcbBlockSize;	/* for very large volumes, just use 1 allocation block */
3203
3204		if ( vcb->vcbRsrcClumpSize > kMaxClumpSize )
3205			vcb->vcbRsrcClumpSize = vcb->vcbBlockSize;	/* for very large volumes, just use 1 allocation block */
3206
3207		if ( (volumeHeader->dataClumpSize > 0) && (volumeHeader->dataClumpSize <= kMaxClumpSize) &&
3208			 ((volumeHeader->dataClumpSize % vcb->vcbBlockSize) == 0) )
3209			vcb->vcbDataClumpSize = volumeHeader->dataClumpSize;
3210		else if ( (alternateVolumeHeader->dataClumpSize > 0) &&
3211				  (alternateVolumeHeader->dataClumpSize <= kMaxClumpSize) &&
3212				  ((alternateVolumeHeader->dataClumpSize % vcb->vcbBlockSize) == 0) )
3213			vcb->vcbDataClumpSize = alternateVolumeHeader->dataClumpSize;
3214		else if (4ULL * vcb->vcbBlockSize <= kMaxClumpSize)
3215			vcb->vcbDataClumpSize = 4 * vcb->vcbBlockSize;
3216		else
3217			vcb->vcbDataClumpSize = vcb->vcbBlockSize;	/* for very large volumes, just use 1 allocation block */
3218
3219		if ( vcb->vcbDataClumpSize > kMaxClumpSize )
3220			vcb->vcbDataClumpSize = vcb->vcbBlockSize;	/* for very large volumes, just use 1 allocation block */
3221
3222		/* Verify next CNode ID.
3223		 * If volumeHeader->nextCatalogID < vcb->vcbNextCatalogID, probably
3224		 * nextCatalogID has wrapped around.
3225		 * If volumeHeader->nextCatalogID > vcb->vcbNextCatalogID, probably
3226		 * many files were created and deleted, followed by no new file
3227		 * creation.
3228		 */
3229		if ( (volumeHeader->nextCatalogID > vcb->vcbNextCatalogID) )
3230			vcb->vcbNextCatalogID = volumeHeader->nextCatalogID;
3231
3232		//��TBD location and unicode? volumename
3233		//	verify the volume name
3234		result = ChkCName( GPtr, (const CatalogName*) &foundKey.hfsPlus.nodeName, isHFSPlus );
3235
3236		//	verify last backup date and backup seqence number
3237		vcb->vcbBackupDate = volumeHeader->backupDate;  /* don't change last backup date */
3238
3239		//	verify write count
3240		vcb->vcbWriteCount = volumeHeader->writeCount;	/* don't change write count */
3241
3242		//	check out extent file clump size
3243		if ( ((volumeHeader->extentsFile.clumpSize % vcb->vcbBlockSize) == 0) &&
3244			 (volumeHeader->extentsFile.clumpSize <= maxClump) )
3245			vcb->vcbExtentsFile->fcbClumpSize = volumeHeader->extentsFile.clumpSize;
3246		else if ( ((alternateVolumeHeader->extentsFile.clumpSize % vcb->vcbBlockSize) == 0) &&
3247				  (alternateVolumeHeader->extentsFile.clumpSize <= maxClump) )
3248			vcb->vcbExtentsFile->fcbClumpSize = alternateVolumeHeader->extentsFile.clumpSize;
3249		else
3250			vcb->vcbExtentsFile->fcbClumpSize =
3251			(alternateVolumeHeader->extentsFile.extents[0].blockCount * vcb->vcbBlockSize);
3252
3253		//	check out catalog file clump size
3254		if ( ((volumeHeader->catalogFile.clumpSize % vcb->vcbBlockSize) == 0) &&
3255			 (volumeHeader->catalogFile.clumpSize <= maxClump) )
3256			vcb->vcbCatalogFile->fcbClumpSize = volumeHeader->catalogFile.clumpSize;
3257		else if ( ((alternateVolumeHeader->catalogFile.clumpSize % vcb->vcbBlockSize) == 0) &&
3258				  (alternateVolumeHeader->catalogFile.clumpSize <= maxClump) )
3259			vcb->vcbCatalogFile->fcbClumpSize = alternateVolumeHeader->catalogFile.clumpSize;
3260		else
3261			vcb->vcbCatalogFile->fcbClumpSize =
3262			(alternateVolumeHeader->catalogFile.extents[0].blockCount * vcb->vcbBlockSize);
3263
3264		//	check out allocations file clump size
3265		if ( ((volumeHeader->allocationFile.clumpSize % vcb->vcbBlockSize) == 0) &&
3266			 (volumeHeader->allocationFile.clumpSize <= maxClump) )
3267			vcb->vcbAllocationFile->fcbClumpSize = volumeHeader->allocationFile.clumpSize;
3268		else if ( ((alternateVolumeHeader->allocationFile.clumpSize % vcb->vcbBlockSize) == 0) &&
3269				  (alternateVolumeHeader->allocationFile.clumpSize <= maxClump) )
3270			vcb->vcbAllocationFile->fcbClumpSize = alternateVolumeHeader->allocationFile.clumpSize;
3271		else
3272			vcb->vcbAllocationFile->fcbClumpSize =
3273			(alternateVolumeHeader->allocationFile.extents[0].blockCount * vcb->vcbBlockSize);
3274
3275		//	check out attribute file clump size
3276		if (vcb->vcbAttributesFile) {
3277			if ( ((volumeHeader->attributesFile.clumpSize % vcb->vcbBlockSize) == 0) &&
3278			     (volumeHeader->attributesFile.clumpSize <= maxClump) &&
3279			     (volumeHeader->attributesFile.clumpSize != 0))
3280				vcb->vcbAttributesFile->fcbClumpSize = volumeHeader->attributesFile.clumpSize;
3281			else if ( ((alternateVolumeHeader->attributesFile.clumpSize % vcb->vcbBlockSize) == 0) &&
3282				  (alternateVolumeHeader->attributesFile.clumpSize <= maxClump) &&
3283				  (alternateVolumeHeader->attributesFile.clumpSize != 0))
3284				vcb->vcbAttributesFile->fcbClumpSize = alternateVolumeHeader->attributesFile.clumpSize;
3285			else if (vcb->vcbCatalogFile->fcbClumpSize != 0)
3286				// The original attribute clump may be too small, use catalog's
3287				vcb->vcbAttributesFile->fcbClumpSize = vcb->vcbCatalogFile->fcbClumpSize;
3288			else
3289				vcb->vcbAttributesFile->fcbClumpSize =
3290				alternateVolumeHeader->attributesFile.extents[0].blockCount * vcb->vcbBlockSize;
3291		}
3292
3293		CopyMemory( volumeHeader->finderInfo, vcb->vcbFinderInfo, sizeof(vcb->vcbFinderInfo) );
3294
3295		//	Now compare verified Volume Header info (in the form of a vcb) with Volume Header info on disk
3296		result = CompareVolumeHeader( GPtr, volumeHeader );
3297
3298		// check to see that embedded volume info is correct in both wrapper MDBs
3299		CheckEmbeddedVolInfoInMDBs( GPtr );
3300
3301	}
3302	else		//	HFS
3303	{
3304		HFSMasterDirectoryBlock	*mdbP;
3305		HFSMasterDirectoryBlock	*alternateMDB;
3306
3307		//
3308		//	get volume name from BTree Key
3309		//
3310
3311		alternateMDB = (HFSMasterDirectoryBlock	*) altBlock.buffer;
3312		mdbP = (HFSMasterDirectoryBlock	*) priBlock.buffer;
3313
3314		maxClump = (UInt64) (vcb->vcbTotalBlocks / 4) * vcb->vcbBlockSize; /* max clump = 1/4 volume size */
3315
3316		//	check out creation and last mod dates
3317		vcb->vcbCreateDate	= alternateMDB->drCrDate;		/* use creation date in alt MDB */
3318		vcb->vcbModifyDate	= mdbP->drLsMod;			/* don't change last mod date */
3319
3320		//	verify volume attribute flags
3321		if ( (mdbP->drAtrb & VAtrb_Msk) == 0 )
3322			vcb->vcbAttributes = mdbP->drAtrb;
3323		else
3324			vcb->vcbAttributes = VAtrb_DFlt;
3325
3326		//	verify allocation map ptr
3327		if ( mdbP->drAllocPtr < vcb->vcbTotalBlocks )
3328			vcb->vcbNextAllocation = mdbP->drAllocPtr;
3329		else
3330			vcb->vcbNextAllocation = 0;
3331
3332		//	verify default clump size
3333		if ( (mdbP->drClpSiz > 0) &&
3334			 (mdbP->drClpSiz <= maxClump) &&
3335			 ((mdbP->drClpSiz % vcb->vcbBlockSize) == 0) )
3336			vcb->vcbDataClumpSize = mdbP->drClpSiz;
3337		else if ( (alternateMDB->drClpSiz > 0) &&
3338				  (alternateMDB->drClpSiz <= maxClump) &&
3339				  ((alternateMDB->drClpSiz % vcb->vcbBlockSize) == 0) )
3340			vcb->vcbDataClumpSize = alternateMDB->drClpSiz;
3341		else
3342			vcb->vcbDataClumpSize = 4 * vcb->vcbBlockSize;
3343
3344		if ( vcb->vcbDataClumpSize > kMaxClumpSize )
3345			vcb->vcbDataClumpSize = vcb->vcbBlockSize;	/* for very large volumes, just use 1 allocation block */
3346
3347		//	verify next CNode ID
3348		if ( (mdbP->drNxtCNID > vcb->vcbNextCatalogID) && (mdbP->drNxtCNID <= (vcb->vcbNextCatalogID + 4096)) )
3349			vcb->vcbNextCatalogID = mdbP->drNxtCNID;
3350
3351		//	verify the volume name
3352		result = ChkCName( GPtr, (const CatalogName*) &vcb->vcbVN, isHFSPlus );
3353		if ( result == noErr )
3354			if ( CmpBlock( mdbP->drVN, vcb->vcbVN, vcb->vcbVN[0] + 1 ) == 0 )
3355				CopyMemory( mdbP->drVN, vcb->vcbVN, kHFSMaxVolumeNameChars + 1 ); /* ...we have a good one */
3356
3357		//	verify last backup date and backup seqence number
3358		vcb->vcbBackupDate = mdbP->drVolBkUp;		/* don't change last backup date */
3359		vcb->vcbVSeqNum = mdbP->drVSeqNum;		/* don't change last backup sequence # */
3360
3361		//	verify write count
3362		vcb->vcbWriteCount = mdbP->drWrCnt;						/* don't change write count */
3363
3364		//	check out extent file and catalog clump sizes
3365		if ( ((mdbP->drXTClpSiz % vcb->vcbBlockSize) == 0) && (mdbP->drXTClpSiz <= maxClump) )
3366			vcb->vcbExtentsFile->fcbClumpSize = mdbP->drXTClpSiz;
3367		else if ( ((alternateMDB->drXTClpSiz % vcb->vcbBlockSize) == 0) && (alternateMDB->drXTClpSiz <= maxClump) )
3368			vcb->vcbExtentsFile->fcbClumpSize = alternateMDB->drXTClpSiz;
3369		else
3370			vcb->vcbExtentsFile->fcbClumpSize = (alternateMDB->drXTExtRec[0].blockCount * vcb->vcbBlockSize);
3371
3372		if ( ((mdbP->drCTClpSiz % vcb->vcbBlockSize) == 0) && (mdbP->drCTClpSiz <= maxClump) )
3373			vcb->vcbCatalogFile->fcbClumpSize = mdbP->drCTClpSiz;
3374		else if ( ((alternateMDB->drCTClpSiz % vcb->vcbBlockSize) == 0) && (alternateMDB->drCTClpSiz <= maxClump) )
3375			vcb->vcbCatalogFile->fcbClumpSize = alternateMDB->drCTClpSiz;
3376		else
3377			vcb->vcbCatalogFile->fcbClumpSize = (alternateMDB->drCTExtRec[0].blockCount * vcb->vcbBlockSize);
3378
3379		//	just copy Finder info for now
3380		CopyMemory(mdbP->drFndrInfo, vcb->vcbFinderInfo, sizeof(mdbP->drFndrInfo));
3381
3382		//	now compare verified MDB info with MDB info on disk
3383		result = CmpMDB( GPtr, mdbP);
3384	}
3385
3386exit:
3387	if (priBlock.buffer)
3388		(void) ReleaseVolumeBlock(vcb, &priBlock, kReleaseBlock);
3389	if (altBlock.buffer)
3390		(void) ReleaseVolumeBlock(vcb, &altBlock, kReleaseBlock);
3391
3392	return (result);
3393
3394}	/* end of VInfoChk */
3395
3396
3397/*------------------------------------------------------------------------------
3398
3399Function:	VLockedChk - (Volume Name Locked Check)
3400
3401Function:	Makes sure the volume name isn't locked.  If it is locked, generate a repair order.
3402
3403			This function is not called if file sharing is operating.
3404
3405Input:		GPtr		-	pointer to scavenger global area
3406
3407Output:		VInfoChk	-	function result:
3408								0	= no error
3409								n 	= error code
3410------------------------------------------------------------------------------*/
3411
3412OSErr	VLockedChk( SGlobPtr GPtr )
3413{
3414	UInt32				hint;
3415	CatalogKey			foundKey;
3416	CatalogRecord		record;
3417	UInt16				recSize;
3418	OSErr				result;
3419	UInt16				frFlags;
3420	Boolean				isHFSPlus;
3421	SVCB				*calculatedVCB	= GPtr->calculatedVCB;
3422	VolumeObjectPtr		myVOPtr;
3423
3424	myVOPtr = GetVolumeObjectPtr( );
3425	isHFSPlus = VolumeObjectIsHFSPlus( );
3426	GPtr->TarID		= kHFSCatalogFileID;								/* target = catalog file */
3427	GPtr->TarBlock	= 0;												/* no target block yet */
3428
3429	//
3430	//	locate the catalog record for the root directory
3431	//
3432	result = GetBTreeRecord( GPtr->calculatedCatalogFCB, 0x8001, &foundKey, &record, &recSize, &hint );
3433
3434	if ( result)
3435	{
3436		RcdError( GPtr, E_EntryNotFound );
3437		return( E_EntryNotFound );
3438	}
3439
3440	//	put the volume name in the VCB
3441	if ( isHFSPlus == false )
3442	{
3443		CopyMemory( foundKey.hfs.nodeName, calculatedVCB->vcbVN, sizeof(calculatedVCB->vcbVN) );
3444	}
3445	else if ( myVOPtr->volumeType != kPureHFSPlusVolumeType )
3446	{
3447		HFSMasterDirectoryBlock	*mdbP;
3448		BlockDescriptor  block;
3449
3450		block.buffer = NULL;
3451		if ( (myVOPtr->flags & kVO_PriMDBOK) != 0 )
3452			result = GetVolumeObjectPrimaryMDB( &block );
3453		else
3454			result = GetVolumeObjectAlternateMDB( &block );
3455		if ( result == noErr ) {
3456			mdbP = (HFSMasterDirectoryBlock	*) block.buffer;
3457			CopyMemory( mdbP->drVN, calculatedVCB->vcbVN, sizeof(mdbP->drVN) );
3458		}
3459		if ( block.buffer != NULL )
3460			(void) ReleaseVolumeBlock(calculatedVCB, &block, kReleaseBlock );
3461		ReturnIfError(result);
3462	}
3463	else		//	Because we don't have the unicode converters, just fill it with a dummy name.
3464	{
3465		CopyMemory( "\x0dPure HFS Plus", calculatedVCB->vcbVN, sizeof(Str27) );
3466	}
3467
3468	GPtr->TarBlock = hint;
3469	if ( isHFSPlus )
3470		CopyCatalogName( (const CatalogName *)&foundKey.hfsPlus.nodeName, &GPtr->CName, isHFSPlus );
3471	else
3472		CopyCatalogName( (const CatalogName *)&foundKey.hfs.nodeName, &GPtr->CName, isHFSPlus );
3473
3474	if ( (record.recordType == kHFSPlusFolderRecord) || (record.recordType == kHFSFolderRecord) )
3475	{
3476		frFlags = record.recordType == kHFSPlusFolderRecord ?
3477			record.hfsPlusFolder.userInfo.frFlags :
3478			record.hfsFolder.userInfo.frFlags;
3479
3480		if ( frFlags & fNameLocked )												// name locked bit set?
3481			RcdNameLockedErr( GPtr, E_LockedDirName, frFlags );
3482	}
3483
3484	return( noErr );
3485}
3486
3487
3488/*------------------------------------------------------------------------------
3489
3490Name:		RcdNameLockedErr
3491
3492Function:	Allocates a RepairOrder node and linkg it into the 'GPtr->RepairP'
3493			list, to describe an locked volume name for possible repair.
3494
3495Input:		GPtr		- ptr to scavenger global data
3496			type		- error code (E_xxx), which should be >0
3497			incorrect	- the incorrect file flags as found in file record
3498
3499Output:		0 			- no error
3500			R_NoMem		- not enough mem to allocate record
3501------------------------------------------------------------------------------*/
3502
3503static int RcdNameLockedErr( SGlobPtr GPtr, SInt16 type, UInt32 incorrect )									/* for a consistency check */
3504{
3505	RepairOrderPtr	p;										/* the new node we compile */
3506	int				n;										/* size of node we allocate */
3507	Boolean			isHFSPlus;
3508
3509	isHFSPlus = VolumeObjectIsHFSPlus( );
3510	RcdError( GPtr, type );									/* first, record the error */
3511
3512	n = CatalogNameSize( &GPtr->CName, isHFSPlus );
3513
3514	p = AllocMinorRepairOrder( GPtr, n );					/* get the node */
3515	if ( p==NULL ) 											/* quit if out of room */
3516		return ( R_NoMem );
3517
3518	CopyCatalogName( (const CatalogName *) &GPtr->CName, (CatalogName*)&p->name, isHFSPlus );
3519
3520	p->type				= type;								/* save error info */
3521	p->correct			= incorrect & ~fNameLocked;			/* mask off the name locked bit */
3522	p->incorrect		= incorrect;
3523	p->maskBit			= (UInt16)fNameLocked;
3524	p->parid			= 1;
3525
3526	GPtr->CatStat |= S_LockedDirName;						/* set flag to trigger repair */
3527
3528	return( noErr );										/* successful return */
3529}
3530
3531/*------------------------------------------------------------------------------
3532
3533Name:		RecordBadExtent
3534
3535Function:	Allocates a RepairOrder for repairing bad extent.
3536
3537Input:		GPtr		- ptr to scavenger global data
3538			fileID		- fileID of the file with bad extent
3539			forkType	- bad extent's fork type
3540			startBlock	- start block of the bad extent record
3541			badExtentIndex - index of bad extent entry in the extent record
3542
3543Output:		0 			- no error
3544			R_NoMem		- not enough mem to allocate record
3545------------------------------------------------------------------------------*/
3546
3547static int RecordBadExtent(SGlobPtr GPtr, UInt32 fileID, UInt8 forkType,
3548				UInt32 startBlock, UInt32 badExtentIndex)
3549{
3550	RepairOrderPtr p;
3551	Boolean isHFSPlus;
3552
3553	isHFSPlus = VolumeObjectIsHFSPlus();
3554
3555	p = AllocMinorRepairOrder(GPtr, 0);
3556	if (p == NULL) {
3557		return(R_NoMem);
3558	}
3559
3560	p->type = E_ExtEnt;
3561	p->forkType = forkType;
3562	p->correct = badExtentIndex;
3563	p->hint = startBlock;
3564	p->parid = fileID;
3565
3566	GPtr->CatStat |= S_BadExtent;
3567	return (0);
3568}
3569
3570/*
3571 * Build a catalog node thread key.
3572 */
3573__unused static void
3574buildthreadkey(UInt32 parentID, int std_hfs, CatalogKey *key)
3575{
3576	if (std_hfs) {
3577		key->hfs.keyLength = kHFSCatalogKeyMinimumLength;
3578		key->hfs.reserved = 0;
3579		key->hfs.parentID = parentID;
3580		key->hfs.nodeName[0] = 0;
3581	} else {
3582		key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength;
3583		key->hfsPlus.parentID = parentID;
3584		key->hfsPlus.nodeName.length = 0;
3585	}
3586}
3587
3588
3589static void
3590printpath(SGlobPtr GPtr, UInt32 fileID)
3591{
3592	int result;
3593	char path[PATH_MAX * 4];
3594	unsigned int pathlen = PATH_MAX * 4;
3595
3596	if (fileID < kHFSFirstUserCatalogNodeID) {
3597		switch(fileID) {
3598		case kHFSExtentsFileID:
3599			printf("$Extents_Overflow_File\n");
3600			return;
3601		case kHFSCatalogFileID:
3602			printf("$Catalog_File\n");
3603			return;
3604		case kHFSAllocationFileID:
3605			printf("$Allocation_Bitmap_File\n");
3606			return;
3607		case kHFSAttributesFileID:
3608			printf("$Attributes_File\n");
3609			return;
3610		default:
3611			printf("$File_ID_%d\n", fileID);
3612			return;
3613		}
3614	}
3615
3616	result = GetFileNamePathByID(GPtr, fileID, path, &pathlen, NULL, NULL, NULL);
3617	if (result) {
3618		printf ("error %d getting path for id=%u\n", result, fileID);
3619	}
3620
3621	printf("\"ROOT_OF_VOLUME%s\" (file id=%u)\n", path, fileID);
3622}
3623
3624void
3625CheckPhysicalMatch(SVCB *vcb, UInt32 startblk, UInt32 blkcount, UInt32 fileNumber, UInt8 forkType)
3626{
3627	int i;
3628	u_int64_t blk, blk1, blk2;
3629	u_int64_t offset;
3630
3631	offset = (u_int64_t) startblk * (u_int64_t) vcb->vcbBlockSize;
3632
3633	if (vcb->vcbSignature == kHFSPlusSigWord)
3634		offset += vcb->vcbEmbeddedOffset;	// offset into the wrapper
3635	else
3636		offset += vcb->vcbAlBlSt * 512ULL;	// offset to start of volume
3637
3638	blk1 = offset / gBlockSize;
3639	blk2 = blk1 + ((blkcount * vcb->vcbBlockSize) / gBlockSize);
3640
3641	for (i = 0; i < gBlkListEntries; ++i) {
3642		blk = gBlockList[i];
3643
3644		if (blk >= blk1 && blk < blk2) {
3645		//	printf("block %d is in file %d\n", blk, fileNumber);
3646			/* Do we need to grow the found blocks list? */
3647			if (gFoundBlockEntries % FOUND_BLOCKS_QUANTUM == 0) {
3648				struct found_blocks *new_blocks;
3649				new_blocks = realloc(gFoundBlocksList, (gFoundBlockEntries + FOUND_BLOCKS_QUANTUM) * sizeof(struct found_blocks));
3650				if (new_blocks == NULL) {
3651					fprintf(stderr, "CheckPhysicalMatch: Out of memory!\n");
3652					return;
3653				}
3654				gFoundBlocksList = new_blocks;
3655			}
3656			gFoundBlocksList[gFoundBlockEntries].block = blk;
3657			gFoundBlocksList[gFoundBlockEntries].fileID = fileNumber;
3658			++gFoundBlockEntries;
3659		}
3660	}
3661}
3662
3663static int compare_found_blocks(const void *x1_arg, const void *x2_arg)
3664{
3665	const struct found_blocks *x1 = x1_arg;
3666	const struct found_blocks *x2 = x2_arg;
3667
3668	if (x1->block < x2->block)
3669		return -1;
3670	else if (x1->block > x2->block)
3671		return 1;
3672	else {
3673		if (x1->fileID < x2->fileID)
3674			return -1;
3675		else if (x1->fileID > x2->fileID)
3676			return 1;
3677	}
3678
3679	return 0;
3680}
3681
3682void
3683dumpblocklist(SGlobPtr GPtr)
3684{
3685	int i, j;
3686	u_int64_t block;
3687
3688	/* Sort the found blocks */
3689	qsort(gFoundBlocksList, gFoundBlockEntries, sizeof(struct found_blocks), compare_found_blocks);
3690
3691	/*
3692	 * Print out the blocks with matching files.  In the case of overlapped
3693	 * extents, the same block number will be printed multiple times, with
3694	 * each file containing an overlapping extent.  If overlapping extents
3695	 * come from the same file, then that path will be printed multiple times.
3696	 */
3697	for (i = 0; i < gFoundBlockEntries; ++i) {
3698		block = gFoundBlocksList[i].block;
3699
3700		printf("block %llu:\t", (unsigned long long) block);
3701		printpath(GPtr, gFoundBlocksList[i].fileID);
3702
3703		/* Remove block from the gBlockList */
3704		for (j = 0; j < gBlkListEntries; ++j) {
3705			if (gBlockList[j] == block) {
3706				gBlockList[j] = gBlockList[--gBlkListEntries];
3707				break;
3708			}
3709		}
3710	}
3711
3712	/* Print out the blocks without matching files */
3713	for (j = 0; j < gBlkListEntries; ++j) {
3714		printf("block %llu:\t*** NO MATCH ***\n", (unsigned long long) gBlockList[j]);
3715	}
3716}
3717
3718/*------------------------------------------------------------------------------
3719
3720Function:	CheckFileExtents - (Check File Extents)
3721
3722Description:
3723		Verifies the extent info for a file data or extented attribute data.  It
3724		checks the correctness of extent data.  If the extent information is
3725		correct/valid, it updates in-memory volume bitmap, total number of valid
3726		blocks for given file, and if overlapping extents exist, adds them to
3727		the overlap extents list.  If the extent information is not correct, it
3728		considers the file truncated beyond the bad extent entry and reports
3729		only the total number of good blocks seen.  Therefore the caller detects
3730		adds the extent information to repair order. It does not include the
3731		invalid extent and any extents after it for checking volume bitmap and
3732		hence overlapping extents.  Note that currently the function
3733		returns error if invalid extent is found for system files or for
3734		extended attributes.
3735
3736		For data fork and resource fork of file - This function checks extent
3737		record present in catalog record as well as extent overflow records, if
3738		any, for given fileID.
3739
3740		For extended attribute data - This function only checks the extent record
3741		passed as parameter.  If any extended attribute has overflow extents in
3742		the attribute btree, this function does not look them up.  It is the left
3743		to the caller to check remaining extents for given file's extended attribute.
3744
3745Input:
3746			GPtr		-	pointer to scavenger global area
3747			fileNumber	-	file number for fork/extended attribute
3748			forkType	-	fork type
3749								00 - kDataFork - data fork
3750								01 - kEAData   - extended attribute data extent
3751								ff - kRsrcFork - resource fork
3752			attrname 	-	if fork type is kEAData, attrname contains pointer to the
3753							name of extended attribute whose extent is being checked; else
3754							it should be NULL.  Note that the function assumes that this is
3755							NULL-terminated string.
3756			extents		-	ptr to 1st extent record for the file
3757
3758Output:
3759			CheckFileExtents	-	function result:
3760								noErr	= no error
3761								n 		= error code
3762			blocksUsed	-	number of allocation blocks allocated to the file
3763------------------------------------------------------------------------------*/
3764
3765OSErr	CheckFileExtents( SGlobPtr GPtr, UInt32 fileNumber, UInt8 forkType,
3766						  const unsigned char *attrname, const void *extents,
3767						  UInt32 *blocksUsed)
3768{
3769	UInt32				blockCount = 0;
3770	UInt32				extentBlockCount;
3771	UInt32				extentStartBlock;
3772	UInt32				hint;
3773	HFSPlusExtentKey	key;
3774	HFSPlusExtentKey	extentKey;
3775	HFSPlusExtentRecord	extentRecord;
3776	UInt16 				recSize;
3777	OSErr				err = noErr;
3778	SInt16				i;
3779	Boolean				firstRecord;
3780	Boolean				isHFSPlus;
3781	unsigned int		lastExtentIndex;
3782	Boolean 			foundBadExtent;
3783
3784	/* For all extended attribute extents, the attrname should not be NULL */
3785	if (forkType == kEAData) {
3786		assert(attrname != NULL);
3787	}
3788
3789	isHFSPlus = VolumeObjectIsHFSPlus( );
3790	firstRecord = true;
3791	foundBadExtent = false;
3792	lastExtentIndex = GPtr->numExtents;
3793
3794	while ( (extents != nil) && (err == noErr) )
3795	{
3796		//	checkout the extent record first
3797		err = ChkExtRec( GPtr, fileNumber, extents, &lastExtentIndex );
3798		if (err != noErr) {
3799			DPRINTF (d_info, "%s: Bad extent for fileID %u in extent %u for startblock %u\n", __FUNCTION__, fileNumber, lastExtentIndex, blockCount);
3800			if (cur_debug_level & d_dump_record)
3801			{
3802				plog("Extents:\n");
3803				HexDump(extents, sizeof(HFSPlusExtentRecord), FALSE);
3804				plog("\n");
3805			}
3806
3807			/* Stop verification if bad extent is found for system file or EA */
3808			if ((fileNumber < kHFSFirstUserCatalogNodeID) ||
3809				(forkType == kEAData)) {
3810				break;
3811			}
3812
3813			/* store information about bad extent in repair order */
3814			(void) RecordBadExtent(GPtr, fileNumber, forkType, blockCount, lastExtentIndex);
3815			foundBadExtent = true;
3816			err = noErr;
3817		}
3818
3819		/* Check only till the last valid extent entry reported by ChkExtRec */
3820		for ( i=0 ; i<lastExtentIndex ; i++ )		//	now checkout the extents
3821		{
3822			//	HFS+/HFS moving extent fields into local variables for evaluation
3823			if ( isHFSPlus == true )
3824			{
3825				extentBlockCount = ((HFSPlusExtentDescriptor *)extents)[i].blockCount;
3826				extentStartBlock = ((HFSPlusExtentDescriptor *)extents)[i].startBlock;
3827			}
3828			else
3829			{
3830				extentBlockCount = ((HFSExtentDescriptor *)extents)[i].blockCount;
3831				extentStartBlock = ((HFSExtentDescriptor *)extents)[i].startBlock;
3832			}
3833
3834			if ( extentBlockCount == 0 )
3835				break;
3836
3837			if (gBlkListEntries != 0)
3838				CheckPhysicalMatch(GPtr->calculatedVCB, extentStartBlock, extentBlockCount, fileNumber, forkType);
3839
3840			err = CaptureBitmapBits(extentStartBlock, extentBlockCount);
3841			if (err == E_OvlExt) {
3842				err = AddExtentToOverlapList(GPtr, fileNumber, (char *)attrname, extentStartBlock, extentBlockCount, forkType);
3843			}
3844
3845			blockCount += extentBlockCount;
3846		}
3847
3848		if ( fileNumber == kHFSExtentsFileID )		//	Extents file has no overflow extents
3849			break;
3850
3851		/* Found bad extent for this file, do not find any extents after
3852		 * current extent.  We assume that the file is truncated at the
3853		 * bad extent entry
3854		 */
3855		if (foundBadExtent == true) {
3856			break;
3857		}
3858
3859		/* For extended attributes, only check the extent passed as parameter.  The
3860		 * caller will take care of checking other extents, if any, for given
3861		 * extended attribute.
3862		 */
3863		if (forkType == kEAData) {
3864			break;
3865		}
3866
3867		if ( firstRecord == true )
3868		{
3869			firstRecord = false;
3870
3871			//	Set up the extent key
3872			BuildExtentKey( isHFSPlus, forkType, fileNumber, blockCount, (void *)&key );
3873
3874			err = SearchBTreeRecord( GPtr->calculatedExtentsFCB, &key, kNoHint, (void *) &extentKey, (void *) &extentRecord, &recSize, &hint );
3875
3876			if ( err == btNotFound )
3877			{
3878				err = noErr;								//	 no more extent records
3879				extents = nil;
3880				break;
3881			}
3882			else if ( err != noErr )
3883			{
3884		 		err = IntError( GPtr, err );		//	error from SearchBTreeRecord
3885				return( err );
3886			}
3887		}
3888		else
3889		{
3890			err = GetBTreeRecord( GPtr->calculatedExtentsFCB, 1, &extentKey, extentRecord, &recSize, &hint );
3891
3892			if ( err == btNotFound )
3893			{
3894				err = noErr;								//	 no more extent records
3895				extents = nil;
3896				break;
3897			}
3898			else if ( err != noErr )
3899			{
3900		 		err = IntError( GPtr, err ); 		/* error from BTGetRecord */
3901				return( err );
3902			}
3903
3904			//	Check same file and fork
3905			if ( isHFSPlus )
3906			{
3907				if ( (extentKey.fileID != fileNumber) || (extentKey.forkType != forkType) )
3908					break;
3909			}
3910			else
3911			{
3912				if ( (((HFSExtentKey *) &extentKey)->fileID != fileNumber) || (((HFSExtentKey *) &extentKey)->forkType != forkType) )
3913					break;
3914			}
3915		}
3916
3917		extents = (void *) &extentRecord;
3918	}
3919
3920	*blocksUsed = blockCount;
3921
3922	return( err );
3923}
3924
3925
3926void	BuildExtentKey( Boolean isHFSPlus, UInt8 forkType, HFSCatalogNodeID fileNumber, UInt32 blockNumber, void * key )
3927{
3928	if ( isHFSPlus )
3929	{
3930		HFSPlusExtentKey *hfsPlusKey	= (HFSPlusExtentKey*) key;
3931
3932		hfsPlusKey->keyLength	= kHFSPlusExtentKeyMaximumLength;
3933		hfsPlusKey->forkType	= forkType;
3934		hfsPlusKey->pad			= 0;
3935		hfsPlusKey->fileID		= fileNumber;
3936		hfsPlusKey->startBlock	= blockNumber;
3937	}
3938	else
3939	{
3940		HFSExtentKey *hfsKey	= (HFSExtentKey*) key;
3941
3942		hfsKey->keyLength		= kHFSExtentKeyMaximumLength;
3943		hfsKey->forkType		= forkType;
3944		hfsKey->fileID			= fileNumber;
3945		hfsKey->startBlock		= (UInt16) blockNumber;
3946	}
3947}
3948
3949
3950
3951//
3952//	Adds this extent to our OverlappedExtentList for later repair.
3953//
3954static OSErr	AddExtentToOverlapList( SGlobPtr GPtr, HFSCatalogNodeID fileNumber, const char *attrname, UInt32 extentStartBlock, UInt32 extentBlockCount, UInt8 forkType )
3955{
3956	size_t			newHandleSize;
3957	ExtentInfo		extentInfo;
3958	ExtentsTable	**extentsTableH;
3959	size_t attrlen;
3960
3961	ClearMemory(&extentInfo, sizeof(extentInfo));
3962	extentInfo.fileID		= fileNumber;
3963	extentInfo.startBlock	= extentStartBlock;
3964	extentInfo.blockCount	= extentBlockCount;
3965	extentInfo.forkType		= forkType;
3966	/* store the name of extended attribute */
3967	if (forkType == kEAData) {
3968		assert(attrname != NULL);
3969
3970		attrlen = strlen(attrname) + 1;
3971		extentInfo.attrname = malloc(attrlen);
3972		if (extentInfo.attrname == NULL) {
3973			return(memFullErr);
3974		}
3975		strlcpy(extentInfo.attrname, attrname, attrlen);
3976	}
3977
3978	//	If it's uninitialized
3979	if ( GPtr->overlappedExtents == nil )
3980	{
3981		GPtr->overlappedExtents	= (ExtentsTable **) NewHandleClear( sizeof(ExtentsTable) );
3982		extentsTableH	= GPtr->overlappedExtents;
3983	}
3984	else
3985	{
3986		extentsTableH	= GPtr->overlappedExtents;
3987
3988		if ( ExtentInfoExists( extentsTableH, &extentInfo) == true )
3989			return( noErr );
3990
3991		//	Grow the Extents table for a new entry.
3992		newHandleSize = ( sizeof(ExtentInfo) ) + ( GetHandleSize( (Handle)extentsTableH ) );
3993		SetHandleSize( (Handle)extentsTableH, newHandleSize );
3994	}
3995
3996	//	Copy the new extents into the end of the table
3997	CopyMemory( &extentInfo, &((**extentsTableH).extentInfo[(**extentsTableH).count]), sizeof(ExtentInfo) );
3998
3999	// 	Update the overlap extent bit
4000	GPtr->VIStat |= S_OverlappingExtents;
4001
4002	//	Update the extent table count
4003	(**extentsTableH).count++;
4004
4005	return( noErr );
4006}
4007
4008
4009/* Compare if the given extentInfo exsists in the extents table */
4010static	Boolean	ExtentInfoExists( ExtentsTable **extentsTableH, ExtentInfo *extentInfo)
4011{
4012	UInt32		i;
4013	ExtentInfo	*aryExtentInfo;
4014
4015
4016	for ( i = 0 ; i < (**extentsTableH).count ; i++ )
4017	{
4018		aryExtentInfo	= &((**extentsTableH).extentInfo[i]);
4019
4020		if ( extentInfo->fileID == aryExtentInfo->fileID )
4021		{
4022			if (	(extentInfo->startBlock == aryExtentInfo->startBlock)	&&
4023					(extentInfo->blockCount == aryExtentInfo->blockCount)	&&
4024					(extentInfo->forkType	== aryExtentInfo->forkType)		)
4025			{
4026				/* startBlock, blockCount, forkType are same.
4027				 * Compare the extended attribute names, if they exist.
4028				 */
4029
4030				/* If no attribute name exists, the two extents are same */
4031				if ((extentInfo->attrname == NULL) &&
4032					(aryExtentInfo->attrname == NULL)) {
4033					return(true);
4034				}
4035
4036				/* If only one attribute name exists, the two extents are not same */
4037				if (((extentInfo->attrname != NULL) && (aryExtentInfo->attrname == NULL)) ||
4038					((extentInfo->attrname == NULL) && (aryExtentInfo->attrname != NULL))) {
4039					return(false);
4040				}
4041
4042				/* Both attribute name exist.  Compare the names */
4043				if (!strcmp(extentInfo->attrname, aryExtentInfo->attrname)) {
4044					return (true);
4045				} else {
4046					return (false);
4047				}
4048
4049			}
4050		}
4051	}
4052
4053	return( false );
4054}
4055
4056/* Function :  DoesOverlap
4057 *
4058 * Description:
4059 * This function takes a start block and the count of blocks in a
4060 * given extent and compares it against the list of overlapped
4061 * extents in the global structure.
4062 * This is useful in finding the original files that overlap with
4063 * the files found in catalog btree check.  If a file is found
4064 * overlapping, it is added to the overlap list.
4065 *
4066 * Input:
4067 * 1. GPtr - global scavenger pointer.
4068 * 2. fileID - file ID being checked.
4069 * 3. attrname - name of extended attribute being checked, should be NULL for regular files
4070 * 4. startBlock - start block in extent.
4071 * 5. blockCount - total number of blocks in extent.
4072 * 6. forkType - type of fork being check (kDataFork, kRsrcFork, kEAData).
4073 *
4074 * Output: isOverlapped - Boolean value of true or false.
4075 */
4076static Boolean DoesOverlap(SGlobPtr GPtr, UInt32 fileID, const char *attrname, UInt32 startBlock, UInt32 blockCount, UInt8 forkType)
4077{
4078	int i;
4079	Boolean isOverlapped = false;
4080	ExtentInfo	*curExtentInfo;
4081	ExtentsTable **extentsTableH = GPtr->overlappedExtents;
4082
4083	for (i = 0; i < (**extentsTableH).count; i++) {
4084		curExtentInfo = &((**extentsTableH).extentInfo[i]);
4085		/* Check extents */
4086		if (curExtentInfo->startBlock < startBlock) {
4087			if ((curExtentInfo->startBlock + curExtentInfo->blockCount) > startBlock) {
4088				isOverlapped = true;
4089				break;
4090			}
4091		} else {	/* curExtentInfo->startBlock >= startBlock */
4092			if (curExtentInfo->startBlock < (startBlock + blockCount)) {
4093				isOverlapped = true;
4094				break;
4095			}
4096		}
4097	} /* for loop Extents Table */
4098
4099	/* Add this extent to overlap list */
4100	if (isOverlapped) {
4101		AddExtentToOverlapList(GPtr, fileID, attrname, startBlock, blockCount, forkType);
4102	}
4103
4104	return isOverlapped;
4105} /* DoesOverlap */
4106
4107/* Function : CheckHFSPlusExtentRecords
4108 *
4109 * Description:
4110 * For all valid extents, this function calls DoesOverlap to find
4111 * if a given extent is overlapping with another extent existing
4112 * in the overlap list.
4113 *
4114 * Input:
4115 * 1. GPtr - global scavenger pointer.
4116 * 2. fileID - file ID being checked.
4117 * 3. attrname - name of extended attribute being checked, should be NULL for regular files
4118 * 4. extent - extent information to check.
4119 * 5. forkType - type of fork being check (kDataFork, kRsrcFork, kEAData).
4120 *
4121 * Output: None.
4122 */
4123static void CheckHFSPlusExtentRecords(SGlobPtr GPtr, UInt32 fileID, const char *attrname, HFSPlusExtentRecord extent, UInt8 forkType)
4124{
4125	int i;
4126
4127	/* Check for overlapping extents for all extents in given extent data */
4128	for (i = 0; i < kHFSPlusExtentDensity; i++) {
4129		if (extent[i].startBlock == 0) {
4130			break;
4131		}
4132		DoesOverlap(GPtr, fileID, attrname, extent[i].startBlock, extent[i].blockCount, forkType);
4133	}
4134	return;
4135} /* CheckHFSPlusExtentRecords */
4136
4137/* Function : CheckHFSExtentRecords
4138 *
4139 * Description:
4140 * For all valid extents, this function calls DoesOverlap to find
4141 * if a given extent is overlapping with another extent existing
4142 * in the overlap list.
4143 *
4144 * Input:
4145 * 1. GPtr - global scavenger pointer.
4146 * 2. fileID - file ID being checked.
4147 * 3. extent - extent information to check.
4148 * 4. forkType - type of fork being check (kDataFork, kRsrcFork).
4149 *
4150 * Output: None.
4151 */
4152static void CheckHFSExtentRecords(SGlobPtr GPtr, UInt32 fileID, HFSExtentRecord extent, UInt8 forkType)
4153{
4154	int i;
4155
4156	/* Check for overlapping extents for all extents in given extents */
4157	for (i = 0; i < kHFSExtentDensity; i++) {
4158		if (extent[i].startBlock == 0) {
4159			break;
4160		}
4161		DoesOverlap(GPtr, fileID, NULL, extent[i].startBlock, extent[i].blockCount, forkType);
4162	}
4163	return;
4164} /* CheckHFSExtentRecords */
4165
4166/* Function: FindOrigOverlapFiles
4167 *
4168 * Description:
4169 * This function is called only if btree check results in
4170 * overlapped extents errors.  The btree checks do not find
4171 * out the original files whose extents are overlapping with one
4172 * being reported in its check.  This function finds out all the
4173 * original files whose that are being overlapped.
4174 *
4175 * This function relies on comparison of extents with Overlap list
4176 * created in verify stage.  The list is also updated with the
4177 * overlapped extents found in this function.
4178 *
4179 * 1. Compare extents for all the files located in volume header.
4180 * 2. Traverse catalog btree and compare extents of all files.
4181 * 3. Traverse extents btree and compare extents for all entries.
4182 *
4183 * Input: GPtr - pointer to global scanvenger area.
4184 *
4185 * Output: err - function result
4186 *			zero means success
4187 *			non-zero means failure
4188 */
4189int FindOrigOverlapFiles(SGlobPtr GPtr)
4190{
4191	OSErr err = noErr;
4192	Boolean isHFSPlus;
4193
4194	UInt16 selCode;		/* select access pattern for BTree */
4195	UInt16 recordSize;
4196	UInt32 hint;
4197
4198	CatalogRecord catRecord;
4199	CatalogKey catKey;
4200
4201	ExtentRecord extentRecord;
4202	ExtentKey extentKey;
4203
4204	HFSPlusAttrRecord attrRecord;
4205	HFSPlusAttrKey attrKey;
4206	char attrName[XATTR_MAXNAMELEN];
4207	size_t len;
4208
4209	SVCB *calculatedVCB = GPtr->calculatedVCB;
4210
4211	isHFSPlus = VolumeObjectIsHFSPlus();
4212
4213	/* Check file extents from volume header */
4214	if (isHFSPlus) {
4215		/* allocation file */
4216		if (calculatedVCB->vcbAllocationFile) {
4217			CheckHFSPlusExtentRecords(GPtr, calculatedVCB->vcbAllocationFile->fcbFileID, NULL,
4218		                              calculatedVCB->vcbAllocationFile->fcbExtents32, kDataFork);
4219		}
4220
4221		/* extents file */
4222		if (calculatedVCB->vcbExtentsFile) {
4223			CheckHFSPlusExtentRecords(GPtr, calculatedVCB->vcbExtentsFile->fcbFileID, NULL,
4224		                              calculatedVCB->vcbExtentsFile->fcbExtents32, kDataFork);
4225		}
4226
4227		/* catalog file */
4228		if (calculatedVCB->vcbCatalogFile) {
4229			CheckHFSPlusExtentRecords(GPtr, calculatedVCB->vcbCatalogFile->fcbFileID, NULL,
4230		                              calculatedVCB->vcbCatalogFile->fcbExtents32, kDataFork);
4231		}
4232
4233		/* attributes file */
4234		if (calculatedVCB->vcbAttributesFile) {
4235			CheckHFSPlusExtentRecords(GPtr, calculatedVCB->vcbAttributesFile->fcbFileID, NULL,
4236		                              calculatedVCB->vcbAttributesFile->fcbExtents32, kDataFork);
4237	   	}
4238
4239		/* startup file */
4240		if (calculatedVCB->vcbStartupFile) {
4241			CheckHFSPlusExtentRecords(GPtr, calculatedVCB->vcbStartupFile->fcbFileID, NULL,
4242		                              calculatedVCB->vcbStartupFile->fcbExtents32, kDataFork);
4243		}
4244	} else {
4245		/* extents file */
4246		if (calculatedVCB->vcbExtentsFile) {
4247			CheckHFSExtentRecords(GPtr, calculatedVCB->vcbExtentsFile->fcbFileID,
4248		                          calculatedVCB->vcbExtentsFile->fcbExtents16, kDataFork);
4249		}
4250
4251		/* catalog file */
4252		if (calculatedVCB->vcbCatalogFile) {
4253			CheckHFSExtentRecords(GPtr, calculatedVCB->vcbCatalogFile->fcbFileID,
4254		                          calculatedVCB->vcbCatalogFile->fcbExtents16, kDataFork);
4255		}
4256	}
4257
4258	/* Traverse the catalog btree */
4259	selCode = 0x8001;	/* Get first record from BTree */
4260	err = GetBTreeRecord(GPtr->calculatedCatalogFCB, selCode, &catKey, &catRecord, &recordSize, &hint);
4261	if (err != noErr) {
4262		goto traverseExtents;
4263	}
4264	selCode = 1;	/* Get next record */
4265	do {
4266		if ((catRecord.recordType == kHFSPlusFileRecord) ||
4267		    (catRecord.recordType == kHFSFileRecord)) {
4268
4269			if (isHFSPlus) {
4270				/* HFSPlus data fork */
4271				CheckHFSPlusExtentRecords(GPtr, catRecord.hfsPlusFile.fileID, NULL,
4272			    	                      catRecord.hfsPlusFile.dataFork.extents, kDataFork);
4273
4274				/* HFSPlus resource fork */
4275				CheckHFSPlusExtentRecords(GPtr, catRecord.hfsPlusFile.fileID, NULL,
4276			    	                      catRecord.hfsPlusFile.resourceFork.extents, kRsrcFork);
4277			} else {
4278				/* HFS data extent */
4279				CheckHFSExtentRecords(GPtr, catRecord.hfsFile.fileID,
4280			    	                  catRecord.hfsFile.dataExtents, kDataFork);
4281
4282				/* HFS resource extent */
4283				CheckHFSExtentRecords(GPtr, catRecord.hfsFile.fileID,
4284				                      catRecord.hfsFile.rsrcExtents, kRsrcFork);
4285			}
4286		}
4287
4288		/* Access the next record */
4289		err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &catKey, &catRecord, &recordSize, &hint );
4290	} while (err == noErr);
4291
4292traverseExtents:
4293	/* Traverse the extents btree */
4294	selCode = 0x8001;	/* Get first record from BTree */
4295	err = GetBTreeRecord(GPtr->calculatedExtentsFCB, selCode, &extentKey, &extentRecord, &recordSize, &hint);
4296	if (err != noErr) {
4297		goto traverseAttribute;
4298	}
4299	selCode = 1;	/* Get next record */
4300	do {
4301		if (isHFSPlus) {
4302			CheckHFSPlusExtentRecords(GPtr, extentKey.hfsPlus.fileID, NULL,
4303			                          extentRecord.hfsPlus, extentKey.hfsPlus.forkType);
4304		} else {
4305			CheckHFSExtentRecords(GPtr, extentKey.hfs.fileID, extentRecord.hfs,
4306			                      extentKey.hfs.forkType);
4307		}
4308
4309		/* Access the next record */
4310		err = GetBTreeRecord(GPtr->calculatedExtentsFCB, selCode, &extentKey, &extentRecord, &recordSize, &hint);
4311	} while (err == noErr);
4312
4313traverseAttribute:
4314	/* Extended attributes are only supported in HFS Plus */
4315	if (!isHFSPlus) {
4316		goto out;
4317	}
4318
4319	/* Traverse the attribute btree */
4320	selCode = 0x8001;	/* Get first record from BTree */
4321	/* Warning: Attribute record of type kHFSPlusAttrInlineData may be
4322	 * truncated on read! (4425232).  This function only uses recordType
4323	 * field from inline attribute record.
4324	 */
4325	err = GetBTreeRecord(GPtr->calculatedAttributesFCB, selCode, &attrKey, &attrRecord, &recordSize, &hint);
4326	if (err != noErr) {
4327		goto out;
4328	}
4329	selCode = 1;	/* Get next record */
4330	do {
4331		if (attrRecord.recordType == kHFSPlusAttrForkData) {
4332			(void) utf_encodestr(attrKey.attrName, attrKey.attrNameLen * 2, (unsigned char *)attrName, &len, sizeof(attrName));
4333			attrName[len] = '\0';
4334
4335			CheckHFSPlusExtentRecords(GPtr, attrKey.fileID, attrName, attrRecord.forkData.theFork.extents, kEAData);
4336		} else if (attrRecord.recordType == kHFSPlusAttrExtents) {
4337			(void) utf_encodestr(attrKey.attrName, attrKey.attrNameLen * 2, (unsigned char *)attrName, &len, sizeof(attrName));
4338			attrName[len] = '\0';
4339
4340			CheckHFSPlusExtentRecords(GPtr, attrKey.fileID, attrName, attrRecord.overflowExtents.extents, kEAData);
4341		}
4342
4343		/* Access the next record
4344		 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
4345		 * truncated on read! (4425232).  This function only uses recordType
4346		 * field from inline attribute record.
4347		 */
4348		err = GetBTreeRecord(GPtr->calculatedAttributesFCB, selCode, &attrKey, &attrRecord, &recordSize, &hint);
4349	} while (err == noErr);
4350
4351out:
4352	if (err == btNotFound) {
4353		err = noErr;
4354	}
4355	return err;
4356} /* FindOrigOverlapFiles */
4357
4358/* Function: PrintOverlapFiles
4359 *
4360 * Description: Print the information about all unique overlapping files.
4361 * 1. Sort the overlap extent in increasing order of fileID
4362 * 2. For every unique fileID, prefix the string with fileID and find the
4363 *    filename/path based on fileID.
4364 *		If fileID > kHFSFirstUserCatalogNodeID, find path to file
4365 *		Else, find name of the system file.
4366 * 3. Print the new string.
4367 * Note that the path is printed only for HFS Plus volumes and not for
4368 * plain HFS volumes.  This is done by not allocating buffer for finding
4369 * file path.
4370 *
4371 * Input:
4372 *	GPtr - Global scavenger structure pointer.
4373 *
4374 * Output:
4375 *	nothing (void)
4376 */
4377void PrintOverlapFiles (SGlobPtr GPtr)
4378{
4379	OSErr err;
4380	ExtentsTable **extentsTableH;
4381	ExtentInfo *extentInfo;
4382	unsigned int numOverlapExtents;
4383	unsigned int buflen, filepathlen;
4384	char *filepath = NULL;
4385	UInt32 lastID = 0;
4386	Boolean printMsg;
4387	Boolean	isHFSPlus;
4388	int i;
4389
4390	isHFSPlus = VolumeObjectIsHFSPlus();
4391
4392	extentsTableH = GPtr->overlappedExtents;
4393	numOverlapExtents = (**extentsTableH).count;
4394
4395	/* Sort the list according to file ID */
4396	qsort((**extentsTableH).extentInfo, numOverlapExtents, sizeof(ExtentInfo),
4397		  CompareExtentFileID);
4398
4399	buflen = PATH_MAX * 4;
4400	/* Allocate buffer to read data */
4401	if (isHFSPlus) {
4402		filepath = malloc (buflen);
4403	}
4404
4405	for (i = 0; i < numOverlapExtents; i++) {
4406		extentInfo = &((**extentsTableH).extentInfo[i]);
4407
4408		/* Skip the same fileID */
4409		if (lastID == extentInfo->fileID) {
4410			continue;
4411		}
4412
4413		lastID = extentInfo->fileID;
4414		printMsg = false;
4415
4416		if (filepath) {
4417			filepathlen = buflen;
4418			if (extentInfo->fileID >= kHFSFirstUserCatalogNodeID) {
4419				/* Lookup the file path */
4420				err = GetFileNamePathByID (GPtr, extentInfo->fileID, filepath, &filepathlen, NULL, NULL, NULL);
4421			} else {
4422				/* Get system filename */
4423			 	err = GetSystemFileName (extentInfo->fileID, filepath, &filepathlen);
4424			}
4425
4426			if (err == noErr) {
4427				/* print fileID, filepath */
4428				fsckPrint(GPtr->context, E_OvlExt, extentInfo->fileID, filepath);
4429				printMsg = true;
4430			}
4431
4432			if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
4433				plog ("\textentType=0x%x, startBlock=0x%x, blockCount=0x%x, attrName=%s\n",
4434						 extentInfo->forkType, extentInfo->startBlock, extentInfo->blockCount, extentInfo->attrname);
4435			}
4436		}
4437
4438		if (printMsg == false) {
4439			/* print only fileID */
4440			fsckPrint(GPtr->context, E_OvlExtID, extentInfo->fileID);
4441		}
4442	}
4443
4444	if (filepath) {
4445		free (filepath);
4446	}
4447
4448	return;
4449} /* PrintOverlapFiles */
4450
4451/* Function: CompareExtentFileID
4452 *
4453 * Description: Compares the fileID from two ExtentInfo and return the
4454 * comparison result. (since we have to arrange in ascending order)
4455 *
4456 * Input:
4457 *	first and second - void pointers to ExtentInfo structure.
4458 *
4459 * Output:
4460 *	>0 if first > second
4461 * 	=0 if first == second
4462 *	<0 if first < second
4463 */
4464static int CompareExtentFileID(const void *first, const void *second)
4465{
4466	return (((ExtentInfo *)first)->fileID -
4467			((ExtentInfo *)second)->fileID);
4468} /* CompareExtentFileID */
4469
4470/* Function: journal_replay
4471 *
4472 * Description: Replay journal on a journaled HFS+ volume.  This function
4473 * returns success if the volume is not journaled or the journal was not
4474 * dirty.  If there was any error in replaying the journal, a non-zero value
4475 * is returned.
4476 *
4477 * Output:
4478 * 	0 - success, non-zero - failure.
4479 */
4480//int journal_replay(SGlobPtr gptr)
4481int journal_replay(const char *block_device)
4482{
4483	int retval = 0;
4484	struct vfsconf vfc;
4485	int mib[4];
4486	int jfd;
4487
4488	jfd = open(block_device, O_RDWR);
4489	if (jfd == -1) {
4490		retval = errno;
4491		if (debug)
4492			fplog(stderr, "Unable to open block device %s: %s", block_device, strerror(errno));
4493		goto out;
4494	}
4495
4496	retval = getvfsbyname("hfs", &vfc);
4497	if (retval) {
4498		goto out;
4499	}
4500
4501	mib[0] = CTL_VFS;
4502	mib[1] = vfc.vfc_typenum;
4503	mib[2] = HFS_REPLAY_JOURNAL;
4504	mib[3] = jfd;
4505	retval = sysctl(mib, 4, NULL, NULL, NULL, 0);
4506	if (retval) {
4507		retval = errno;
4508	}
4509	(void)close(jfd);
4510
4511out:
4512	return retval;
4513}
4514
4515