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