]> git.saurik.com Git - apple/hfs.git/blob - fsck_hfs/dfalib/SRepair.c
hfs-522.100.5.tar.gz
[apple/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 if (debug) 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 if (debug) 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 if (debug) plog("%s: Could not get overflow extents record for fileID %u, fork %u, start block %u\n", __FUNCTION__, fileID, forkType,
2956 extentStartBlock);
2957 goto out;
2958 }
2959
2960 /* Zero out the bad extent entry and all entries after it */
2961 if (isHFSPlus) {
2962 for (i = badExtentIndex; i < GPtr->numExtents; i++) {
2963 extentRecord.hfsPlus[i].startBlock = 0;
2964 extentRecord.hfsPlus[i].blockCount = 0;
2965 }
2966 } else {
2967 for (i = badExtentIndex; i < GPtr->numExtents; i++) {
2968 extentRecord.hfs[i].startBlock = 0;
2969 extentRecord.hfs[i].blockCount = 0;
2970 }
2971 }
2972
2973 /* Write the extent record back */
2974 err = ReplaceBTreeRecord(GPtr->calculatedExtentsFCB, &extentKey, hint,
2975 &extentRecord, recordSize, &hint);
2976 if (err) {
2977 if (debug) plog("%s: Could not replace overflow extents record for fileID %u, fork %u, start block %u\n", __FUNCTION__, fileID,
2978 forkType, extentStartBlock);
2979 goto out;
2980 }
2981 didRepair = true;
2982
2983 /* The startBlock for extent record with bad extent entry is updated
2984 * because we use this startBlock later to lookup next extent record
2985 * for this file and forktype in overflow extent btree which should
2986 * be deleted. By incrementing the startBlock by one, we ensure that
2987 * we find the next record, if any, that should be deleted instead of
2988 * finding the same record that was updated above.
2989 */
2990 extentStartBlock++;
2991 }
2992
2993 del_overflow_extents:
2994 /* Search for overflow extent records. We should get a valid record only
2995 * if the bad extent entry was the first entry in the extent overflow
2996 * record. For all other cases, the search record will return an error
2997 */
2998 BuildExtentKey (isHFSPlus, forkType, fileID, extentStartBlock, &extentKey);
2999 err = SearchBTreeRecord (GPtr->calculatedExtentsFCB, &extentKey, kNoHint,
3000 &extentKey, &extentRecord, &recordSize, &hint);
3001 if ((err != noErr) && (err != btNotFound)) {
3002 goto create_symlink;
3003 }
3004
3005 /* If we got error, check the next record */
3006 if (err == btNotFound) {
3007 err = GetBTreeRecord(GPtr->calculatedExtentsFCB, 1, &extentKey, &extentRecord,
3008 &recordSize, &hint);
3009 }
3010
3011 while (err == noErr) {
3012 /* Check if the record has correct fileID, forkType */
3013 if (isHFSPlus) {
3014 if ((fileID != extentKey.hfsPlus.fileID) ||
3015 (forkType != extentKey.hfsPlus.forkType)) {
3016 break;
3017 }
3018 foundStartBlock = extentKey.hfsPlus.startBlock;
3019 } else {
3020 if ((fileID != extentKey.hfs.fileID) ||
3021 (forkType != extentKey.hfs.forkType)) {
3022 break;
3023 }
3024 foundStartBlock = extentKey.hfs.startBlock;
3025 }
3026
3027 /* Delete the extent record */
3028 err = DeleteBTreeRecord(GPtr->calculatedExtentsFCB, &extentKey);
3029 DPRINTF (d_info, "%s: Deleting extent overflow for fileID=%u, forkType=%u, startBlock=%u\n", __FUNCTION__, fileID, forkType, foundStartBlock);
3030 if (err) {
3031 goto create_symlink;
3032 }
3033 didRepair = true;
3034
3035 /* Get the next extent record */
3036 err = GetBTreeRecord(GPtr->calculatedExtentsFCB, 1, &extentKey, &extentRecord,
3037 &recordSize, &hint);
3038 }
3039
3040 if (err == btNotFound) {
3041 err = noErr;
3042 }
3043
3044 UpdateBTreeHeader(GPtr->calculatedExtentsFCB);
3045
3046 create_symlink:
3047 /* Create symlink for repaired files in damaged files folder */
3048 if (didRepair == true) {
3049 /* Create symlink for damaged files */
3050 (void) CreateCorruptFileSymlink(GPtr, fileID);
3051 }
3052
3053 out:
3054 return err;
3055 }
3056
3057 /* Function: FixOrphanedFiles
3058 *
3059 * Description:
3060 * Incorrect number of thread records get fixed in this function.
3061 *
3062 * The function traverses the entire catalog Btree.
3063 *
3064 * For a file/folder record, it tries to lookup its corresponding thread
3065 * record. If the thread record does not exist, or is not correct, a new
3066 * thread record is created. The parent ID, record type, and the name of
3067 * the file/folder are compared for correctness.
3068 * For plain HFS, a thread record is only looked-up if kHFSThreadExistsMask is set.
3069 *
3070 * For a thread record, it tries to lookup its corresponding file/folder
3071 * record. If its does not exist or is not correct, the thread record
3072 * is deleted. The file/folder ID is compared for correctness.
3073 *
3074 * Input: 1. GPtr - pointer to global scavenger area
3075 *
3076 * Return value:
3077 * zero means success
3078 * non-zero means failure
3079 */
3080 static OSErr FixOrphanedFiles ( SGlobPtr GPtr )
3081 {
3082 CatalogKey key;
3083 CatalogKey foundKey;
3084 CatalogKey tempKey;
3085 CatalogRecord record;
3086 CatalogRecord threadRecord;
3087 CatalogRecord record2;
3088 HFSCatalogNodeID parentID;
3089 HFSCatalogNodeID cNodeID = 0;
3090 BTreeIterator savedIterator;
3091 UInt32 hint;
3092 UInt32 hint2;
3093 UInt32 threadHint;
3094 OSErr err;
3095 UInt16 recordSize;
3096 UInt16 threadRecordSize;
3097 SInt16 recordType;
3098 SInt16 foundRecType;
3099 SInt16 selCode = 0x8001; /* Get first record */
3100 Boolean isHFSPlus;
3101 BTreeControlBlock *btcb = GetBTreeControlBlock( kCalculatedCatalogRefNum );
3102 Boolean isDirectory;
3103
3104 isHFSPlus = VolumeObjectIsHFSPlus( );
3105 CopyMemory( &btcb->lastIterator, &savedIterator, sizeof(BTreeIterator) );
3106
3107 do
3108 {
3109 // Save/Restore Iterator around calls to GetBTreeRecord
3110 CopyMemory( &savedIterator, &btcb->lastIterator, sizeof(BTreeIterator) );
3111 err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint );
3112 if ( err != noErr ) break;
3113 CopyMemory( &btcb->lastIterator, &savedIterator, sizeof(BTreeIterator) );
3114
3115 selCode = 1; // kNextRecord
3116 recordType = record.recordType;
3117
3118 isDirectory = false;
3119
3120 switch( recordType )
3121 {
3122 case kHFSFileRecord:
3123 // If the small file is not supposed to have a thread, just break
3124 if ( ( record.hfsFile.flags & kHFSThreadExistsMask ) == 0 )
3125 break;
3126
3127 case kHFSFolderRecord:
3128 case kHFSPlusFolderRecord:
3129 case kHFSPlusFileRecord:
3130
3131 // Locate the thread associated with this record
3132
3133 (void) CheckForStop( GPtr ); // rotate cursor
3134
3135 parentID = isHFSPlus == true ? foundKey.hfsPlus.parentID : foundKey.hfs.parentID;
3136 threadHint = hint;
3137
3138 switch( recordType )
3139 {
3140 case kHFSFolderRecord:
3141 cNodeID = record.hfsFolder.folderID;
3142 isDirectory = true;
3143 break;
3144 case kHFSFileRecord:
3145 cNodeID = record.hfsFile.fileID;
3146 break;
3147 case kHFSPlusFolderRecord:
3148 cNodeID = record.hfsPlusFolder.folderID;
3149 isDirectory = true;
3150 break;
3151 case kHFSPlusFileRecord:
3152 cNodeID = record.hfsPlusFile.fileID;
3153 break;
3154 }
3155
3156 //-- Build the key for the file thread
3157 BuildCatalogKey( cNodeID, nil, isHFSPlus, &key );
3158
3159 err = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint,
3160 &tempKey, &threadRecord, &threadRecordSize, &hint2 );
3161
3162 /* We found a thread record for this file/folder record. */
3163 if (err == noErr) {
3164 /* Check if the parent ID and nodeName are same, and recordType is as
3165 * expected. If not, we are missing a correct thread record. Force
3166 * btNotFound in such case.
3167 */
3168 if (isHFSPlus) {
3169 /* Check thread's recordType */
3170 foundRecType = threadRecord.hfsPlusThread.recordType;
3171 if (isDirectory == true) {
3172 if (foundRecType != kHFSPlusFolderThreadRecord) {
3173 err = btNotFound;
3174 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3175 plog ("\t%s: Folder thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
3176 }
3177 }
3178 } else {
3179 if (foundRecType != kHFSPlusFileThreadRecord) {
3180 err = btNotFound;
3181 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3182 plog ("\t%s: File thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
3183 }
3184 }
3185 }
3186
3187 /* Compare parent ID */
3188 if (parentID != threadRecord.hfsPlusThread.parentID) {
3189 err = btNotFound;
3190 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3191 plog ("\t%s: parentID for id=%u do not match (fileKey=%u threadRecord=%u)\n", __FUNCTION__, cNodeID, parentID, threadRecord.hfsPlusThread.parentID);
3192 }
3193 }
3194
3195 /* Compare nodeName from file/folder key and thread reocrd */
3196 if (!((foundKey.hfsPlus.nodeName.length == threadRecord.hfsPlusThread.nodeName.length)
3197 && (!bcmp(foundKey.hfsPlus.nodeName.unicode,
3198 threadRecord.hfsPlusThread.nodeName.unicode,
3199 foundKey.hfsPlus.nodeName.length * 2)))) {
3200 err = btNotFound;
3201 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3202 unsigned maxLength = foundKey.hfsPlus.nodeName.length;
3203 if (maxLength < threadRecord.hfsPlusThread.nodeName.length)
3204 maxLength = threadRecord.hfsPlusThread.nodeName.length;
3205
3206 plog ("\t%s: nodeName for id=%u do not match\n", __FUNCTION__, cNodeID);
3207 if (cur_debug_level & d_dump_record)
3208 {
3209 plog("\tFile/Folder record:\n");
3210 HexDump(&foundKey, foundKey.hfsPlus.keyLength + 2, FALSE);
3211 plog("--\n");
3212 HexDump(&record, recordSize, FALSE);
3213 plog("\n");
3214 plog("\tThread record:\n");
3215 HexDump(&tempKey, tempKey.hfsPlus.keyLength + 2, FALSE);
3216 plog("--\n");
3217 HexDump(&threadRecord, threadRecordSize, FALSE);
3218 plog("\n");
3219 }
3220 }
3221 }
3222
3223 /* If any of the above checks failed, delete the bad thread record */
3224 if (err == btNotFound) {
3225 (void) DeleteBTreeRecord(GPtr->calculatedCatalogFCB, &tempKey);
3226 }
3227 } else { /* plain HFS */
3228 /* Check thread's recordType */
3229 foundRecType = threadRecord.hfsThread.recordType;
3230 if (isDirectory == true) {
3231 if (foundRecType != kHFSFolderThreadRecord) {
3232 err = btNotFound;
3233 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3234 plog ("\t%s: Folder thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
3235 }
3236 }
3237 } else {
3238 if (foundRecType != kHFSFileThreadRecord) {
3239 err = btNotFound;
3240 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3241 plog ("\t%s: File thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
3242 }
3243 }
3244 }
3245
3246 /* Compare parent ID */
3247 if (parentID != threadRecord.hfsThread.parentID) {
3248 err = btNotFound;
3249 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3250 plog ("\t%s: parentID for id=%u do not match (fileKey=%u threadRecord=%u)\n", __FUNCTION__, cNodeID, parentID, threadRecord.hfsThread.parentID);
3251 }
3252 }
3253
3254 /* Compare nodeName from file/folder key and thread reocrd */
3255 if (!((foundKey.hfs.nodeName[0] == threadRecord.hfsThread.nodeName[0])
3256 && (!bcmp(&foundKey.hfs.nodeName[1],
3257 &threadRecord.hfsThread.nodeName[1],
3258 foundKey.hfs.nodeName[0])))) {
3259 err = btNotFound;
3260 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3261 plog ("\t%s: nodeName for id=%u do not match\n", __FUNCTION__, cNodeID);
3262 }
3263 }
3264
3265 /* If any of the above checks failed, delete the bad thread record */
3266 if (err == btNotFound) {
3267 (void) DeleteBTreeRecord(GPtr->calculatedCatalogFCB, &tempKey);
3268 }
3269 }
3270 } /* err == noErr */
3271
3272 // For missing thread records, just create the thread
3273 if ( err == btNotFound )
3274 {
3275 // Create the missing thread record.
3276
3277 isDirectory = false;
3278 switch( recordType )
3279 {
3280 case kHFSFolderRecord:
3281 case kHFSPlusFolderRecord:
3282 isDirectory = true;
3283 break;
3284 }
3285
3286 //-- Fill out the data for the new file thread from the key
3287 // of catalog file/folder record
3288 recordSize = BuildThreadRec( &foundKey, &threadRecord, isHFSPlus,
3289 isDirectory );
3290 err = InsertBTreeRecord( GPtr->calculatedCatalogFCB, &key,
3291 &threadRecord, recordSize, &threadHint );
3292 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3293 plog ("\t%s: Created thread record for id=%u (err=%u)\n", __FUNCTION__, cNodeID, err);
3294 }
3295 }
3296
3297 break;
3298
3299
3300 case kHFSFolderThreadRecord:
3301 case kHFSPlusFolderThreadRecord:
3302 isDirectory = true;
3303
3304 case kHFSFileThreadRecord:
3305 case kHFSPlusFileThreadRecord:
3306
3307 // Find the catalog record, if it does not exist, delete the existing thread.
3308 if ( isHFSPlus )
3309 BuildCatalogKey( record.hfsPlusThread.parentID, (const CatalogName *)&record.hfsPlusThread.nodeName, isHFSPlus, &key );
3310 else
3311 BuildCatalogKey( record.hfsThread.parentID, (const CatalogName *)&record.hfsThread.nodeName, isHFSPlus, &key );
3312
3313 err = SearchBTreeRecord ( GPtr->calculatedCatalogFCB, &key, kNoHint, &tempKey, &record2, &recordSize, &hint2 );
3314
3315 /* We found a file/folder record for this thread record. */
3316 if (err == noErr) {
3317 /* Check if the file/folder ID are same and if the recordType is as
3318 * expected. If not, we are missing a correct file/folder record.
3319 * Delete the extra thread record
3320 */
3321 if (isHFSPlus) {
3322 /* Check recordType */
3323 foundRecType = record2.hfsPlusFile.recordType;
3324 if (isDirectory == true) {
3325 if (foundRecType != kHFSPlusFolderRecord) {
3326 err = btNotFound;
3327 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3328 plog ("\t%s: Folder recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
3329 }
3330 }
3331 } else {
3332 if (foundRecType != kHFSPlusFileRecord) {
3333 err = btNotFound;
3334 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3335 plog ("\t%s: File recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
3336 }
3337 }
3338 }
3339
3340 /* Compare file/folder ID */
3341 if (foundKey.hfsPlus.parentID != record2.hfsPlusFile.fileID) {
3342 err = btNotFound;
3343 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3344 plog ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__, foundKey.hfsPlus.parentID, record2.hfsPlusFile.fileID, record.hfsPlusThread.parentID);
3345 }
3346 }
3347 } else { /* plain HFS */
3348 /* Check recordType */
3349 foundRecType = record2.hfsFile.recordType;
3350 if (isDirectory == true) {
3351 if (foundRecType != kHFSFolderRecord) {
3352 err = btNotFound;
3353 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3354 plog ("\t%s: Folder recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
3355 }
3356 }
3357 } else {
3358 if (foundRecType != kHFSFileRecord) {
3359 err = btNotFound;
3360 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3361 plog ("\t%s: File recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
3362 }
3363 }
3364 }
3365
3366 /* Compare file/folder ID */
3367 if (foundKey.hfs.parentID != record2.hfsFile.fileID) {
3368 err = btNotFound;
3369 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3370 if (recordType == kHFSFolderThreadRecord) {
3371 plog ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__, foundKey.hfs.parentID, record2.hfsFolder.folderID, record.hfsThread.parentID);
3372 } else {
3373 plog ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__, foundKey.hfs.parentID, record2.hfsFile.fileID, record.hfsThread.parentID);
3374 }
3375 }
3376 }
3377 }
3378 } /* if (err == noErr) */
3379
3380 if ( err != noErr )
3381 {
3382 err = DeleteBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey );
3383 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3384 if (isHFSPlus) {
3385 plog ("\t%s: Deleted thread record for id=%d (err=%d)\n", __FUNCTION__, foundKey.hfsPlus.parentID, err);
3386 } else {
3387 plog ("\t%s: Deleted thread record for id=%d (err=%d)\n", __FUNCTION__, foundKey.hfs.parentID, err);
3388 }
3389 }
3390 }
3391
3392 break;
3393
3394 default:
3395 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3396 plog ("\t%s: Unknown record type.\n", __FUNCTION__);
3397 }
3398 break;
3399
3400 }
3401 } while ( err == noErr );
3402
3403 if ( err == btNotFound )
3404 err = noErr; // all done, no more catalog records
3405
3406 // if (err == noErr)
3407 // err = BTFlushPath( GPtr->calculatedCatalogFCB );
3408
3409 return( err );
3410 }
3411
3412
3413 static OSErr RepairReservedBTreeFields ( SGlobPtr GPtr )
3414 {
3415 CatalogRecord record;
3416 CatalogKey foundKey;
3417 UInt16 recordSize;
3418 SInt16 selCode;
3419 UInt32 hint;
3420 UInt32 *reserved;
3421 OSErr err;
3422
3423 selCode = 0x8001; // start with 1st record
3424
3425 err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint );
3426 if ( err != noErr ) goto EXIT;
3427
3428 selCode = 1; // get next record from now on
3429
3430 do
3431 {
3432 switch( record.recordType )
3433 {
3434 case kHFSPlusFolderRecord:
3435 /* XXX -- this should not always be cleared out (but doesn't appear to being called) */
3436 if ( record.hfsPlusFolder.flags != 0 )
3437 {
3438 record.hfsPlusFolder.flags = 0;
3439 err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
3440 }
3441 break;
3442
3443 case kHFSPlusFileRecord:
3444 // Note: bit 7 (mask 0x80) of flags is unused in HFS or HFS Plus. However, Inside Macintosh: Files
3445 // describes it as meaning the file record is in use. Some non-Apple implementations end up setting
3446 // this bit, so we just ignore it.
3447 if ( ( record.hfsPlusFile.flags & (UInt16) ~(0X83) ) != 0 )
3448 {
3449 record.hfsPlusFile.flags &= 0X83;
3450 err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
3451 }
3452 break;
3453
3454 case kHFSFolderRecord:
3455 if ( record.hfsFolder.flags != 0 )
3456 {
3457 record.hfsFolder.flags = 0;
3458 err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
3459 }
3460 break;
3461
3462 case kHFSFolderThreadRecord:
3463 case kHFSFileThreadRecord:
3464 reserved = (UInt32*) &(record.hfsThread.reserved);
3465 if ( reserved[0] || reserved[1] )
3466 {
3467 reserved[0] = 0;
3468 reserved[1] = 0;
3469 err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
3470 }
3471 break;
3472
3473 case kHFSFileRecord:
3474 // Note: bit 7 (mask 0x80) of flags is unused in HFS or HFS Plus. However, Inside Macintosh: Files
3475 // describes it as meaning the file record is in use. Some non-Apple implementations end up setting
3476 // this bit, so we just ignore it.
3477 if ( ( ( record.hfsFile.flags & (UInt8) ~(0X83) ) != 0 )
3478 || ( record.hfsFile.dataStartBlock != 0 )
3479 || ( record.hfsFile.rsrcStartBlock != 0 )
3480 || ( record.hfsFile.reserved != 0 ) )
3481 {
3482 record.hfsFile.flags &= 0X83;
3483 record.hfsFile.dataStartBlock = 0;
3484 record.hfsFile.rsrcStartBlock = 0;
3485 record.hfsFile.reserved = 0;
3486 err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
3487 }
3488 break;
3489
3490 default:
3491 break;
3492 }
3493
3494 if ( err != noErr ) goto EXIT;
3495
3496 err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint );
3497
3498 } while ( err == noErr );
3499
3500 if ( err == btNotFound )
3501 err = noErr; // all done, no more catalog records
3502
3503 EXIT:
3504 return( err );
3505 }
3506
3507
3508 /* Function: FixOrphanAttrRecord
3509 *
3510 * Description:
3511 * The function traverses the attribute BTree completely and for every
3512 * leaf record, calls CheckAttributeRecord. CheckAttributeRecord function
3513 * is common function for verify and repair stage. CheckAttributeRecord
3514 * deletes invalid/orphaned extended attribute records under following
3515 * conditions -
3516 * 1. record is overflow extents with no valid fork data or overflow extents
3517 * preceeding it.
3518 * 2. record type is unknown.
3519 *
3520 * Input:
3521 * GPtr - global scavenger structure pointer
3522 *
3523 * Output:
3524 * error code - zero on success, non-zero on failure.
3525 */
3526 static OSErr FixOrphanAttrRecord(SGlobPtr GPtr)
3527 {
3528 OSErr err = noErr;
3529 UInt16 selCode;
3530 UInt32 hint;
3531
3532 HFSPlusAttrRecord record;
3533 HFSPlusAttrKey key;
3534 UInt16 recordSize;
3535
3536 /* Zero out last attribute information from global scavenger structure */
3537 bzero (&(GPtr->lastAttrInfo), sizeof(GPtr->lastAttrInfo));
3538
3539 /* Warning: Attribute record of type kHFSPlusAttrInlineData may be
3540 * truncated on read! (4425232). CheckAttributeRecord only uses recordType
3541 * field from inline attribute record.
3542 */
3543 selCode = 0x8001;
3544 err = GetBTreeRecord(GPtr->calculatedAttributesFCB, selCode, &key,
3545 &record, &recordSize, &hint);
3546 if (err != noErr) {
3547 goto out;
3548 }
3549
3550 selCode = 1;
3551 do {
3552 err = CheckAttributeRecord(GPtr, &key, &record, recordSize);
3553 if (err) {
3554 break;
3555 }
3556
3557 /* Access the next record.
3558 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
3559 * truncated on read! (4425232). CheckAttributeRecord only uses recordType
3560 * field from inline attribute record.
3561 */
3562 err = GetBTreeRecord(GPtr->calculatedAttributesFCB, selCode, &key,
3563 &record, &recordSize, &hint);
3564 } while (err == noErr);
3565
3566 if (err == btNotFound) {
3567 err = noErr;
3568 }
3569
3570 out:
3571 return(err);
3572 }
3573
3574 /* Function: GetCatalogRecord
3575 *
3576 * Description:
3577 * This function returns a catalog file/folder record for a given
3578 * file/folder ID from the catalog BTree.
3579 *
3580 * Input: 1. GPtr - pointer to global scavenger area
3581 * 2. fileID - file ID to search the file/folder record
3582 * 3. isHFSPlus - boolean value to indicate if volume is HFSPlus
3583 *
3584 * Output: 1. catKey - catalog key
3585 * 2. catRecord - catalog record for given ID
3586 * 3. recordSize - size of catalog record return back
3587 *
3588 * Return value:
3589 * zero means success
3590 * non-zero means failure
3591 */
3592 static OSErr GetCatalogRecord(SGlobPtr GPtr, UInt32 fileID, Boolean isHFSPlus, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize)
3593 {
3594 OSErr err = noErr;
3595 CatalogKey catThreadKey;
3596 CatalogName catalogName;
3597 UInt32 hint;
3598 uint32_t thread_key_parentID;
3599
3600 /* Look up for catalog thread record for the file that owns attribute */
3601 BuildCatalogKey(fileID, NULL, isHFSPlus, &catThreadKey);
3602 err = SearchBTreeRecord(GPtr->calculatedCatalogFCB, &catThreadKey, kNoHint, catKey, catRecord, recordSize, &hint);
3603 if (err) {
3604 if (debug) plog ("%s: No matching catalog thread record found\n", __FUNCTION__);
3605 goto out;
3606 }
3607
3608 #if DEBUG_XATTR
3609 plog ("%s(%s,%d):1 recordType=%x, flags=%x\n", __FUNCTION__, __FILE__, __LINE__,
3610 catRecord->hfsPlusFile.recordType,
3611 catRecord->hfsPlusFile.flags);
3612 #endif
3613
3614 /* We were expecting a thread record. The recordType says it is a file
3615 * record or folder record. Return error.
3616 */
3617 if ((catRecord->hfsPlusFile.recordType == kHFSPlusFolderRecord) ||
3618 (catRecord->hfsPlusFile.recordType == kHFSPlusFileRecord)) {
3619 err = fsBTRecordNotFoundErr;
3620 goto out;
3621 }
3622 thread_key_parentID = catKey->hfsPlus.parentID;
3623
3624 /* It is either a file thread record or folder thread record.
3625 * Look up for catalog record for the file that owns attribute */
3626 CopyCatalogName((CatalogName *)&(catRecord->hfsPlusThread.nodeName), &catalogName, isHFSPlus);
3627 BuildCatalogKey(catRecord->hfsPlusThread.parentID, &catalogName, isHFSPlus, catKey);
3628 err = SearchBTreeRecord(GPtr->calculatedCatalogFCB, catKey, kNoHint, catKey, catRecord, recordSize, &hint);
3629 if (err) {
3630 if (debug) plog ("%s: No matching catalog record found\n", __FUNCTION__);
3631 if (cur_debug_level & d_dump_record)
3632 {
3633 plog ("Searching for key:\n");
3634 HexDump(catKey, CalcKeySize(GPtr->calculatedCatalogBTCB, (BTreeKey *)catKey), FALSE);
3635 }
3636 goto out;
3637 }
3638
3639 #if DEBUG_XATTR
3640 plog ("%s(%s,%d):2 recordType=%x, flags=%x\n", __FUNCTION__, __FILE__, __LINE__,
3641 catRecord->hfsPlusFile.recordType,
3642 catRecord->hfsPlusFile.flags);
3643 #endif
3644
3645 /* For catalog file or folder record, the parentID in the thread
3646 * record's key should be equal to the fileID in the file/folder
3647 * record --- which is equal to the ID of the file/folder record
3648 * that is being looked up. If not, mark the volume for repair.
3649 */
3650 if (thread_key_parentID != catRecord->hfsPlusFile.fileID) {
3651 RcdError(GPtr, E_IncorrectNumThdRcd);
3652 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3653 plog("\t%s: fileID=%u, thread.key.parentID=%u, record.fileID=%u\n",
3654 __FUNCTION__, fileID, thread_key_parentID, catRecord->hfsPlusFile.fileID);
3655 }
3656 GPtr->CBTStat |= S_Orphan;
3657 }
3658 out:
3659 return err;
3660 }
3661
3662 /* Function: RepairAttributesCheckABT
3663 *
3664 * Description:
3665 * This function is called from RepairAttributes (to repair extended
3666 * attributes) during repair stage of fsck_hfs.
3667 *
3668 * 1. Make full pass through attribute BTree.
3669 * 2. For every unique fileID, lookup its catalog record in Catalog BTree.
3670 * 3. If found, check the attributes/security bit in catalog record.
3671 * If not set correctly, set it and replace the catalog record.
3672 * 4. If not found, return error
3673 *
3674 * Input: 1. GPtr - pointer to global scavenger area
3675 * 2. isHFSPlus - boolean value to indicate if volume is HFSPlus
3676 *
3677 * Output: err - Function result
3678 * zero means success
3679 * non-zero means failure
3680 */
3681 static OSErr RepairAttributesCheckABT(SGlobPtr GPtr, Boolean isHFSPlus)
3682 {
3683 OSErr err = noErr;
3684 UInt16 selCode; /* select access pattern for BTree */
3685 UInt32 hint;
3686
3687 HFSPlusAttrRecord attrRecord;
3688 HFSPlusAttrKey attrKey;
3689 UInt16 attrRecordSize;
3690 CatalogRecord catRecord;
3691 CatalogKey catKey;
3692 UInt16 catRecordSize;
3693
3694 attributeInfo lastID; /* fileID for the last attribute searched */
3695 Boolean didRecordChange = false; /* whether catalog record was changed after checks */
3696
3697 #if DEBUG_XATTR
3698 char attrName[XATTR_MAXNAMELEN];
3699 size_t len;
3700 #endif
3701
3702 lastID.fileID = 0;
3703 lastID.hasSecurity = false;
3704
3705 selCode = 0x8001; /* Get first record from BTree */
3706 /* Warning: Attribute record of type kHFSPlusAttrInlineData may be
3707 * truncated on read! (4425232). This function only uses recordType
3708 * field from inline attribute record.
3709 */
3710 err = GetBTreeRecord(GPtr->calculatedAttributesFCB, selCode, &attrKey, &attrRecord, &attrRecordSize, &hint);
3711 if (err == btNotFound) {
3712 /* The attributes B-tree is empty, which is OK; nothing to do. */
3713 err = noErr;
3714 goto out;
3715 }
3716 if (err != noErr) goto out;
3717
3718 selCode = 1; /* Get next record */
3719 do {
3720 #if DEBUG_XATTR
3721 /* Convert unicode attribute name to char for ACL check */
3722 (void) utf_encodestr(attrKey.attrName, attrKey.attrNameLen * 2, attrName, &len, sizeof(attrName));
3723 attrName[len] = '\0';
3724 plog ("%s(%s,%d): Found attrName=%s for fileID=%d\n", __FUNCTION__, __FILE__, __LINE__, attrName, attrKey.fileID);
3725 #endif
3726
3727 if (attrKey.fileID != lastID.fileID) {
3728 /* We found an attribute with new file ID */
3729
3730 /* Replace the previous catalog record only if we updated the flags */
3731 if (didRecordChange == true) {
3732 err = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &catKey , kNoHint, &catRecord, catRecordSize, &hint);
3733 if (err) {
3734 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3735 plog ("\t%s: Error in replacing catalog record for id=%u\n", __FUNCTION__, lastID.fileID);
3736 }
3737 goto out;
3738 }
3739 }
3740
3741 didRecordChange = false; /* reset to indicate new record has not changed */
3742
3743 /* Get the catalog record for the new fileID */
3744 err = GetCatalogRecord(GPtr, attrKey.fileID, isHFSPlus, &catKey, &catRecord, &catRecordSize);
3745 if (err) {
3746 /* No catalog record was found for this fileID. */
3747 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3748 plog ("\t%s: No matching catalog record found for id=%u\n", __FUNCTION__, attrKey.fileID);
3749 }
3750
3751 /* 3984119 - Do not delete extended attributes for file IDs less
3752 * kHFSFirstUserCatalogNodeID but not equal to kHFSRootFolderID
3753 * in prime modulus checksum. These file IDs do not have
3754 * any catalog record
3755 */
3756 if ((attrKey.fileID < kHFSFirstUserCatalogNodeID) &&
3757 (attrKey.fileID != kHFSRootFolderID)) {
3758 #if DEBUG_XATTR
3759 plog ("%s: Ignore catalog check for fileID=%d for attribute=%s\n", __FUNCTION__, attrKey.fileID, attrName);
3760 #endif
3761 goto getnext;
3762 }
3763
3764 /* Delete this orphan extended attribute */
3765 err = delete_attr_record(GPtr, &attrKey, &attrRecord);
3766 if (err) {
3767 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
3768 plog ("\t%s: Error in deleting attribute record for id=%u\n", __FUNCTION__, attrKey.fileID);
3769 }
3770 goto out;
3771 }
3772 goto getnext;
3773 }
3774
3775 lastID.fileID = attrKey.fileID; /* set last fileID to the new ID */
3776 lastID.hasSecurity = false; /* reset to indicate new fileID does not have security */
3777
3778 /* Check the Attribute bit */
3779 if (!(catRecord.hfsPlusFile.flags & kHFSHasAttributesMask)) {
3780 /* kHFSHasAttributeBit should be set */
3781 catRecord.hfsPlusFile.flags |= kHFSHasAttributesMask;
3782 didRecordChange = true;
3783 }
3784
3785 /* Check if attribute is ACL */
3786 if (!bcmp(attrKey.attrName, GPtr->securityAttrName, GPtr->securityAttrLen)) {
3787 lastID.hasSecurity = true;
3788 /* Check the security bit */
3789 if (!(catRecord.hfsPlusFile.flags & kHFSHasSecurityMask)) {
3790 /* kHFSHasSecurityBit should be set */
3791 catRecord.hfsPlusFile.flags |= kHFSHasSecurityMask;
3792 didRecordChange = true;
3793 }
3794 }
3795 } else {
3796 /* We have seen attribute for fileID in past */
3797
3798 /* If last time we saw this fileID did not have an ACL and this
3799 * extended attribute is an ACL, ONLY check consistency of
3800 * security bit from Catalog record
3801 */
3802 if ((lastID.hasSecurity == false) && !bcmp(attrKey.attrName, GPtr->securityAttrName, GPtr->securityAttrLen)) {
3803 lastID.hasSecurity = true;
3804 /* Check the security bit */
3805 if (!(catRecord.hfsPlusFile.flags & kHFSHasSecurityMask)) {
3806 /* kHFSHasSecurityBit should be set */
3807 catRecord.hfsPlusFile.flags |= kHFSHasSecurityMask;
3808 didRecordChange = true;
3809 }
3810 }
3811 }
3812
3813 getnext:
3814 /* Access the next record
3815 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
3816 * truncated on read! (4425232). This function only uses recordType
3817 * field from inline attribute record.
3818 */
3819 err = GetBTreeRecord(GPtr->calculatedAttributesFCB, selCode, &attrKey, &attrRecord, &attrRecordSize, &hint);
3820 } while (err == noErr);
3821
3822 err = noErr;
3823
3824 /* Replace the catalog record for last extended attribute in the attributes BTree
3825 * only if we updated the flags
3826 */
3827 if (didRecordChange == true) {
3828 err = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &catKey , kNoHint, &catRecord, catRecordSize, &hint);
3829 if (err) {
3830 #if DEBUG_XATTR
3831 plog ("%s: Error in replacing Catalog Record\n", __FUNCTION__);
3832 #endif
3833 goto out;
3834 }
3835 }
3836
3837 out:
3838 return err;
3839 }
3840
3841 /* Function: RepairAttributesCheckCBT
3842 *
3843 * Description:
3844 * This function is called from RepairAttributes (to repair extended
3845 * attributes) during repair stage of fsck_hfs.
3846 *
3847 * NOTE: The case where attribute exists and bit is not set is being taken care in
3848 * RepairAttributesCheckABT. This function determines relationship from catalog
3849 * Btree to attribute Btree, and not vice-versa.
3850
3851 * 1. Make full pass through catalog BTree.
3852 * 2. For every fileID, if the attributes/security bit is set,
3853 * lookup all the extended attributes in the attributes BTree.
3854 * 3. If found, check that if bits are set correctly.
3855 * 4. If not found, clear the bits.
3856 *
3857 * Input: 1. GPtr - pointer to global scavenger area
3858 * 2. isHFSPlus - boolean value to indicate if volume is HFSPlus
3859 *
3860 * Output: err - Function result
3861 * zero means success
3862 * non-zero means failure
3863 */
3864 static OSErr RepairAttributesCheckCBT(SGlobPtr GPtr, Boolean isHFSPlus)
3865 {
3866 OSErr err = noErr;
3867 UInt16 selCode; /* select access pattern for BTree */
3868 UInt16 recordSize;
3869 UInt32 hint;
3870
3871 HFSPlusAttrKey *attrKey;
3872 CatalogRecord catRecord;
3873 CatalogKey catKey;
3874
3875 Boolean didRecordChange = false; /* whether catalog record was changed after checks */
3876
3877 BTreeIterator iterator;
3878
3879 UInt32 curFileID;
3880 Boolean curRecordHasAttributes = false;
3881 Boolean curRecordHasSecurity = false;
3882
3883 selCode = 0x8001; /* Get first record from BTree */
3884 err = GetBTreeRecord(GPtr->calculatedCatalogFCB, selCode, &catKey, &catRecord, &recordSize, &hint);
3885 if ( err != noErr ) goto out;
3886
3887 selCode = 1; /* Get next record */
3888 do {
3889 /* Check only file record and folder record, else skip to next record */
3890 if ( (catRecord.hfsPlusFile.recordType != kHFSPlusFileRecord) &&
3891 (catRecord.hfsPlusFile.recordType != kHFSPlusFolderRecord)) {
3892 goto getnext;
3893 }
3894
3895 /* Check if catalog record has attribute and/or security bit set, else
3896 * skip to next record
3897 */
3898 if ( ((catRecord.hfsPlusFile.flags & kHFSHasAttributesMask) == 0) &&
3899 ((catRecord.hfsPlusFile.flags & kHFSHasSecurityMask) == 0) ) {
3900 goto getnext;
3901 }
3902
3903 /* Initialize some flags */
3904 didRecordChange = false;
3905 curRecordHasSecurity = false;
3906 curRecordHasAttributes = false;
3907
3908 /* Access all extended attributes for this fileID */
3909 curFileID = catRecord.hfsPlusFile.fileID;
3910
3911 /* Initialize the iterator and attribute key */
3912 ClearMemory(&iterator, sizeof(BTreeIterator));
3913 attrKey = (HFSPlusAttrKey *)&iterator.key;
3914 attrKey->keyLength = kHFSPlusAttrKeyMinimumLength;
3915 attrKey->fileID = curFileID;
3916
3917 /* Search for attribute with NULL name. This will place the iterator at correct fileID location in BTree */
3918 err = BTSearchRecord(GPtr->calculatedAttributesFCB, &iterator, kInvalidMRUCacheKey, NULL, NULL, &iterator);
3919 if (err && (err != btNotFound)) {
3920 #if DEBUG_XATTR
3921 plog ("%s: No matching attribute record found\n", __FUNCTION__);
3922 #endif
3923 goto out;
3924 }
3925
3926 /* Iterate over to all extended attributes for given fileID */
3927 err = BTIterateRecord(GPtr->calculatedAttributesFCB, kBTreeNextRecord, &iterator, NULL, NULL);
3928
3929 /* Check only if we did _find_ an attribute record for the current fileID */
3930 while ((err == noErr) && (attrKey->fileID == curFileID)) {
3931 /* Current record should have attribute bit set */
3932 curRecordHasAttributes = true;
3933
3934 /* Current record should have security bit set */
3935 if (!bcmp(attrKey->attrName, GPtr->securityAttrName, GPtr->securityAttrLen)) {
3936 curRecordHasSecurity = true;
3937 }
3938
3939 /* Get the next record */
3940 err = BTIterateRecord(GPtr->calculatedAttributesFCB, kBTreeNextRecord, &iterator, NULL, NULL);
3941 }
3942
3943 /* Determine if we need to update the catalog record */
3944 if ((curRecordHasAttributes == false) && (catRecord.hfsPlusFile.flags & kHFSHasAttributesMask)) {
3945 /* If no attribute exists and attributes bit is set, clear it */
3946 catRecord.hfsPlusFile.flags &= ~kHFSHasAttributesMask;
3947 didRecordChange = true;
3948 }
3949
3950 if ((curRecordHasSecurity == false) && (catRecord.hfsPlusFile.flags & kHFSHasSecurityMask)) {
3951 /* If no security attribute exists and security bit is set, clear it */
3952 catRecord.hfsPlusFile.flags &= ~kHFSHasSecurityMask;
3953 didRecordChange = true;
3954 }
3955
3956 /* If there was any change in catalog record, write it back to disk */
3957 if (didRecordChange == true) {
3958 err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &catKey , kNoHint, &catRecord, recordSize, &hint );
3959 if (err) {
3960 #if DEBUG_XATTR
3961 plog ("%s: Error writing catalog record\n", __FUNCTION__);
3962 #endif
3963 goto out;
3964 }
3965 }
3966
3967 getnext:
3968 /* Access the next record */
3969 err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &catKey, &catRecord, &recordSize, &hint );
3970 } while (err == noErr);
3971
3972 err = noErr;
3973
3974 out:
3975 return err;
3976 }
3977
3978 /* Function: RepairAttributes
3979 *
3980 * Description:
3981 * This function fixes the extended attributes consistency by
3982 * calling two functions:
3983 * 1. RepairAttributesCheckABT: Traverses attributes Btree and
3984 * checks if each attribute has correct bits set in its corresponding
3985 * catalog record.
3986 * 2. RepairAttributesCheckCBT: Traverses catalog Btree and checks
3987 * if each catalog record that has attribute/security bit set have
3988 * corresponding extended attributes.
3989 *
3990 * Input: 1. GPtr - pointer to global scavenger area
3991 *
3992 * Output: err - Function result
3993 * zero means success
3994 * non-zero means failure
3995 */
3996 static OSErr RepairAttributes(SGlobPtr GPtr)
3997 {
3998 OSErr err = noErr;
3999 Boolean isHFSPlus;
4000
4001 /* Check if the volume is HFS Plus volume */
4002 isHFSPlus = VolumeObjectIsHFSPlus();
4003 if (!isHFSPlus) {
4004 goto out;
4005 }
4006
4007 /* Traverse Attributes BTree and access required records in Catalog BTree */
4008 err = RepairAttributesCheckABT(GPtr, isHFSPlus);
4009 if (err) {
4010 goto out;
4011 }
4012
4013 /* Traverse Catalog BTree and access required records in Attributes BTree */
4014 err = RepairAttributesCheckCBT(GPtr, isHFSPlus);
4015 if (err) {
4016 goto out;
4017 }
4018
4019 out:
4020 return err;
4021 }
4022
4023 /*------------------------------------------------------------------------------
4024
4025 Function: cmpLongs
4026
4027 Function: compares two longs.
4028
4029 Input: *a: pointer to first number
4030 *b: pointer to second number
4031
4032 Output: <0 if *a < *b
4033 0 if a == b
4034 >0 if a > b
4035 ------------------------------------------------------------------------------*/
4036
4037 int cmpLongs ( const void *a, const void *b )
4038 {
4039 return( *(long*)a - *(long*)b );
4040 }
4041
4042 /* Function: FixOverlappingExtents
4043 *
4044 * Description: Fix overlapping extents problem. The implementation copies all
4045 * the extents existing in overlapping extents to a new location and updates the
4046 * extent record to point to the new extent. At the end of repair, symlinks are
4047 * created with name "fileID filename" to point to the file involved in
4048 * overlapping extents problem. Note that currently only HFS Plus volumes are
4049 * repaired.
4050 *
4051 * PARTIAL SUCCESS: This function handles partial success in the following
4052 * two ways:
4053 * a. The function pre-allocates space for the all the extents. If the
4054 * allocation fails, it proceeds to allocate space for other extents
4055 * instead of returning error.
4056 * b. If moving an extent succeeds and symlink creation fails, the function
4057 * proceeds to another extent.
4058 * If the repair encounters either a or b condition, appropriate message is
4059 * printed at the end of the function.
4060 * If even a single repair operation succeeds (moving of extent), the function
4061 * returns zero.
4062 *
4063 * Current limitations:
4064 * 1. A regular file instead of symlink is created under following conditions:
4065 * a. The volume is plain HFS volume. HFS does not support symlink
4066 * creation.
4067 * b. The path the new symlink points to is greater than PATH_MAX bytes.
4068 * c. The path the new symlink points has some intermediate component
4069 * greater than NAME_MAX bytes.
4070 * 2. Contiguous disk space for every new extent is expected. The extent is
4071 * not broken into multiple extents if contiguous space is not available on the
4072 * disk.
4073 * 3. The current fix for overlapping extent only works for HFS Plus volumes.
4074 * Plain HFS volumes have problem in accessing the catalog record by fileID.
4075 * 4. Plain HFS volumes might have encoding issues with the newly created
4076 * symlink and its data.
4077 *
4078 * Input:
4079 * GPtr - global scavenger pointer
4080 *
4081 * Output:
4082 * returns zero on success/partial success (moving of one extent succeeds),
4083 * non-zero on failure.
4084 */
4085 static OSErr FixOverlappingExtents(SGlobPtr GPtr)
4086 {
4087 OSErr err = noErr;
4088 Boolean isHFSPlus;
4089 unsigned int i;
4090 unsigned int numOverlapExtents = 0;
4091 ExtentInfo *extentInfo;
4092 ExtentsTable **extentsTableH = GPtr->overlappedExtents;
4093
4094 unsigned int status = 0;
4095 #define S_DISKFULL 0x01 /* error due to disk full */
4096 #define S_MOVEEXTENT 0x02 /* moving extent succeeded */
4097
4098 isHFSPlus = VolumeObjectIsHFSPlus();
4099 if (isHFSPlus == false) {
4100 /* Do not repair plain HFS volumes */
4101 err = R_RFail;
4102 goto out;
4103 }
4104
4105 if (extentsTableH == NULL) {
4106 /* No overlapping extents exist */
4107 goto out;
4108 }
4109
4110 numOverlapExtents = (**extentsTableH).count;
4111
4112 /* Optimization - sort the overlap extent list based on blockCount to
4113 * start allocating contiguous space for largest number of blocks first
4114 */
4115 qsort((**extentsTableH).extentInfo, numOverlapExtents, sizeof(ExtentInfo),
4116 CompareExtentBlockCount);
4117
4118 #if DEBUG_OVERLAP
4119 /* Print all overlapping extents structure */
4120 for (i=0; i<numOverlapExtents; i++) {
4121 extentInfo = &((**extentsTableH).extentInfo[i]);
4122 plog ("%d: fileID = %d, startBlock = %d, blockCount = %d\n", i, extentInfo->fileID, extentInfo->startBlock, extentInfo->blockCount);
4123 }
4124 #endif
4125
4126 /* Pre-allocate free space for all overlapping extents */
4127 for (i=0; i<numOverlapExtents; i++) {
4128 extentInfo = &((**extentsTableH).extentInfo[i]);
4129 err = AllocateContigBitmapBits (GPtr->calculatedVCB, extentInfo->blockCount, &(extentInfo->newStartBlock));
4130 if ((err != noErr)) {
4131 /* Not enough disk space */
4132 status |= S_DISKFULL;
4133 #if DEBUG_OVERLAP
4134 plog ("%s: Not enough disk space to allocate extent for fileID = %d (start=%d, count=%d)\n", __FUNCTION__, extentInfo->fileID, extentInfo->startBlock, extentInfo->blockCount);
4135 #endif
4136 }
4137 }
4138
4139 /* For every extent info, copy the extent into new location and create symlink */
4140 for (i=0; i<numOverlapExtents; i++) {
4141 extentInfo = &((**extentsTableH).extentInfo[i]);
4142
4143 /* Do not repair this extent as no new extent was allocated */
4144 if (extentInfo->newStartBlock == 0) {
4145 continue;
4146 }
4147
4148 /* Move extent data to new location */
4149 err = MoveExtent(GPtr, extentInfo);
4150 if (err != noErr) {
4151 extentInfo->didRepair = false;
4152 #if DEBUG_OVERLAP
4153 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);
4154 #endif
4155 } else {
4156 /* Mark the overlapping extent as repaired */
4157 extentInfo->didRepair = true;
4158 status |= S_MOVEEXTENT;
4159 #if DEBUG_OVERLAP
4160 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);
4161 #endif
4162 }
4163
4164 /* Create symlink for the corrupt file */
4165 err = CreateCorruptFileSymlink(GPtr, extentInfo->fileID);
4166 if (err != noErr) {
4167 #if DEBUG_OVERLAP
4168 plog ("%s: Error in creating symlink for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
4169 #endif
4170 } else {
4171 #if DEBUG_OVERLAP
4172 plog ("%s: Created symlink for fileID = %u (old=%u, new=%u, count=%u)\n", __FUNCTION__, extentInfo->fileID, extentInfo->startBlock, extentInfo->newStartBlock, extentInfo->blockCount);
4173 #endif
4174 }
4175 }
4176
4177 out:
4178 /* Release all blocks used by overlap extents that are repaired */
4179 for (i=0; i<numOverlapExtents; i++) {
4180 extentInfo = &((**extentsTableH).extentInfo[i]);
4181 if (extentInfo->didRepair == true) {
4182 ReleaseBitmapBits (extentInfo->startBlock, extentInfo->blockCount);
4183 }
4184 }
4185
4186 /* For all un-repaired extents,
4187 * 1. Release all blocks allocated for new extent.
4188 * 2. Mark all blocks used for the old extent (since the overlapping region
4189 * have been marked free in the for loop above.
4190 */
4191 for (i=0; i<numOverlapExtents; i++) {
4192 extentInfo = &((**extentsTableH).extentInfo[i]);
4193 if (extentInfo->didRepair == false) {
4194 CaptureBitmapBits (extentInfo->startBlock, extentInfo->blockCount);
4195
4196 if (extentInfo->newStartBlock != 0) {
4197 ReleaseBitmapBits (extentInfo->newStartBlock, extentInfo->blockCount);
4198 }
4199 }
4200 }
4201
4202 /* Update the volume free block count since the release and alloc above might
4203 * have worked on same bit multiple times.
4204 */
4205 UpdateFreeBlockCount (GPtr);
4206
4207 /* Print correct status messages */
4208 if (status & S_DISKFULL) {
4209 fsckPrint(GPtr->context, E_DiskFull);
4210 }
4211
4212 /* If moving of even one extent succeeds, return success */
4213 if (status & S_MOVEEXTENT) {
4214 err = noErr;
4215 }
4216
4217 return err;
4218 } /* FixOverlappingExtents */
4219
4220 /* Function: CompareExtentBlockCount
4221 *
4222 * Description: Compares the blockCount from two ExtentInfo and return the
4223 * comparison result. (since we have to arrange in descending order)
4224 *
4225 * Input:
4226 * first and second - void pointers to ExtentInfo structure.
4227 *
4228 * Output:
4229 * <0 if first > second
4230 * =0 if first == second
4231 * >0 if first < second
4232 */
4233 static int CompareExtentBlockCount(const void *first, const void *second)
4234 {
4235 return (((ExtentInfo *)second)->blockCount -
4236 ((ExtentInfo *)first)->blockCount);
4237 } /* CompareExtentBlockCount */
4238
4239 /* Function: MoveExtent
4240 *
4241 * Description: Move data from old extent to new extent and update corresponding
4242 * records.
4243 * 1. Search the extent record for the overlapping extent.
4244 * If the fileID < kHFSFirstUserCatalogNodeID,
4245 * Ignore repair for BadBlock, RepairCatalog, BogusExtent files.
4246 * Search for extent record in volume header.
4247 * Else,
4248 * Search for extent record in catalog BTree. If the extent list does
4249 * not end in catalog record and extent record not found in catalog
4250 * record, search in extents BTree.
4251 * 2. If found, copy disk blocks from old extent to new extent.
4252 * 3. If it succeeds, update extent record with new start block and write back
4253 * to disk.
4254 * This function does not take care to deallocate blocks from old start block.
4255 *
4256 * Input:
4257 * GPtr - Global Scavenger structure pointer
4258 * extentInfo - Current overlapping extent details.
4259 *
4260 * Output:
4261 * err: zero on success, non-zero on failure
4262 * paramErr - Invalid paramter, ex. file ID is less than
4263 * kHFSFirstUserCatalogNodeID.
4264 */
4265 static OSErr MoveExtent(SGlobPtr GPtr, ExtentInfo *extentInfo)
4266 {
4267 OSErr err = noErr;
4268 Boolean isHFSPlus;
4269
4270 CatalogRecord catRecord;
4271 CatalogKey catKey;
4272 HFSPlusExtentKey extentKey;
4273 HFSPlusExtentRecord extentData;
4274 HFSPlusAttrKey attrKey;
4275 HFSPlusAttrRecord attrRecord;
4276 UInt16 recordSize;
4277
4278 enum locationTypes {volumeHeader, catalogBTree, extentsBTree, attributeBTree} foundLocation;
4279
4280 UInt32 foundExtentIndex = 0;
4281 Boolean noMoreExtents = true;
4282
4283 isHFSPlus = VolumeObjectIsHFSPlus();
4284
4285 /* Find correct location of this overlapping extent */
4286 if (extentInfo->forkType == kEAData) {
4287 assert(isHFSPlus == true);
4288
4289 /* Search extent in attribute btree */
4290 err = SearchExtentInAttributeBT (GPtr, extentInfo, &attrKey, &attrRecord,
4291 &recordSize, &foundExtentIndex);
4292 if (err != noErr) {
4293 goto out;
4294 }
4295 foundLocation = attributeBTree;
4296 } else { /* kDataFork or kRsrcFork */
4297 if (extentInfo->fileID < kHFSFirstUserCatalogNodeID) {
4298 /* Ignore these fileIDs in repair. Bad block file blocks should
4299 * never be moved. kHFSRepairCatalogFileID and kHFSBogusExtentFileID
4300 * are temporary runtime files. We need to return error to the caller
4301 * to deallocate disk blocks preallocated during preflight
4302 * to move the overlapping extents. Any other extent that overlaps
4303 * with these extents might have moved successfully, thus repairing
4304 * the problem.
4305 */
4306 if ((extentInfo->fileID == kHFSBadBlockFileID) ||
4307 (extentInfo->fileID == kHFSBogusExtentFileID) ||
4308 (extentInfo->fileID == kHFSRepairCatalogFileID)) {
4309 err = paramErr;
4310 goto out;
4311 }
4312
4313 /* Search for extent record in the volume header */
4314 err = SearchExtentInVH (GPtr, extentInfo, &foundExtentIndex, &noMoreExtents);
4315 foundLocation = volumeHeader;
4316 } else {
4317 /* Search the extent record from the catalog btree */
4318 err = SearchExtentInCatalogBT (GPtr, extentInfo, &catKey, &catRecord,
4319 &recordSize, &foundExtentIndex, &noMoreExtents);
4320 foundLocation = catalogBTree;
4321 }
4322 if (err != noErr) {
4323 if (noMoreExtents == false) {
4324 /* search extent in extents overflow btree */
4325 err = SearchExtentInExtentBT (GPtr, extentInfo, &extentKey,
4326 &extentData, &recordSize, &foundExtentIndex);
4327 foundLocation = extentsBTree;
4328 if (err != noErr) {
4329 DPRINTF (d_error|d_overlap, "%s: No matching extent record found in extents btree for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
4330 goto out;
4331 }
4332 } else {
4333 /* No more extents exist for this file */
4334 DPRINTF (d_error|d_overlap, "%s: No matching extent record found for fileID = %d\n", __FUNCTION__, extentInfo->fileID);
4335 goto out;
4336 }
4337 }
4338 }
4339 /* Copy disk blocks from old extent to new extent */
4340 err = CopyDiskBlocks(GPtr, extentInfo->startBlock, extentInfo->blockCount,
4341 extentInfo->newStartBlock);
4342 if (err != noErr) {
4343 DPRINTF (d_error|d_overlap, "%s: Error in copying disk blocks for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
4344 goto out;
4345 }
4346
4347 /* Replace the old start block in extent record with new start block */
4348 if (foundLocation == catalogBTree) {
4349 err = UpdateExtentInCatalogBT(GPtr, extentInfo, &catKey, &catRecord,
4350 &recordSize, foundExtentIndex);
4351 } else if (foundLocation == volumeHeader) {
4352 err = UpdateExtentInVH(GPtr, extentInfo, foundExtentIndex);
4353 } else if (foundLocation == extentsBTree) {
4354 extentData[foundExtentIndex].startBlock = extentInfo->newStartBlock;
4355 err = UpdateExtentRecord(GPtr->calculatedVCB, NULL, &extentKey, extentData, kNoHint);
4356 } else if (foundLocation == attributeBTree) {
4357 err = UpdateExtentInAttributeBT(GPtr, extentInfo, &attrKey, &attrRecord,
4358 &recordSize, foundExtentIndex);
4359
4360 }
4361 if (err != noErr) {
4362 DPRINTF (d_error|d_overlap, "%s: Error in updating extent record for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
4363 goto out;
4364 }
4365
4366 out:
4367 return err;
4368 } /* MoveExtent */
4369
4370 /* Function: CreateCorruptFileSymlink
4371 *
4372 * Description: Create symlink to point to the corrupt files that might
4373 * have data loss due to repair (overlapping extents, bad extents)
4374 *
4375 * The function looks up directory with name "DamagedFiles" in the
4376 * root of the file system being repaired. If it does not exists, it
4377 * creates the directory. The symlink to damaged file is created in this
4378 * directory.
4379 *
4380 * If fileID >= kHFSFirstUserCatalogNodeID,
4381 * Lookup the filename and path to the file based on file ID. Create the
4382 * new file name as "fileID filename" and data as the relative path of the file
4383 * from the root of the volume.
4384 * If either
4385 * the volume is plain HFS, or
4386 * the length of the path pointed by data is greater than PATH_MAX, or
4387 * the length of any intermediate path component is greater than NAME_MAX,
4388 * Create a plain file with given data.
4389 * Else
4390 * Create a symlink.
4391 * Else
4392 * Find the name of file based on ID (ie. Catalog BTree, etc), and create plain
4393 * regular file with name "fileID filename" and data as "System File:
4394 * filename".
4395 *
4396 * Input:
4397 * 1. GPtr - global scavenger structure pointer.
4398 * 2. fileID - fileID of the source for creating symlink
4399 *
4400 * Output:
4401 * returns zero on success, non-zero on failure.
4402 * memFullErr - Not enough memory
4403 */
4404 static OSErr CreateCorruptFileSymlink(SGlobPtr GPtr, UInt32 fileID)
4405 {
4406 OSErr err = noErr;
4407 Boolean isHFSPlus;
4408 char *filename = NULL;
4409 unsigned int filenamelen;
4410 char *data = NULL;
4411 unsigned int datalen;
4412 unsigned int filenameoffset;
4413 unsigned int dataoffset;
4414 UInt32 damagedDirID;
4415 UInt16 status;
4416 UInt16 fileType;
4417
4418 isHFSPlus = VolumeObjectIsHFSPlus();
4419
4420 /* Lookup and create, if required, the DamagedFiles folder */
4421 damagedDirID = CreateDirByName(GPtr, (u_char *)"DamagedFiles", kHFSRootFolderID);
4422 if (damagedDirID == 0) {
4423 goto out;
4424 }
4425
4426 /* Allocate (kHFSPlusMaxFileNameChars * 4) for unicode - utf8 conversion */
4427 filenamelen = kHFSPlusMaxFileNameChars * 4;
4428 filename = malloc(filenamelen);
4429 if (!filename) {
4430 err = memFullErr;
4431 goto out;
4432 }
4433
4434 /* Allocate (PATH_MAX * 4) instead of PATH_MAX for unicode - utf8 conversion */
4435 datalen = PATH_MAX * 4;
4436 data = malloc(datalen);
4437 if (!data) {
4438 err = memFullErr;
4439 goto out;
4440 }
4441
4442 /* Find filename, path for fileID >= 16 and determine new fileType */
4443 if (fileID >= kHFSFirstUserCatalogNodeID) {
4444 char *name;
4445 char *path;
4446
4447 /* construct symlink data with .. prefix */
4448 dataoffset = sprintf (data, "..");
4449 path = data + dataoffset;
4450 datalen -= dataoffset;
4451
4452 /* construct new filename prefix with fileID<space> */
4453 filenameoffset = sprintf (filename, "%08x ", fileID);
4454 name = filename + filenameoffset;
4455 filenamelen -= filenameoffset;
4456
4457 /* find file name and path (data for symlink) for fileID */
4458 err = GetFileNamePathByID(GPtr, fileID, path, &datalen,
4459 name, &filenamelen, &status);
4460 if (err != noErr) {
4461 #if DEBUG_OVERLAP
4462 plog ("%s: Error in getting name/path for fileID = %d (err=%d)\n", __FUNCTION__, fileID, err);
4463 #endif
4464 goto out;
4465 }
4466
4467 /* update length of path and filename */
4468 filenamelen += filenameoffset;
4469 datalen += dataoffset;
4470
4471 /* If
4472 * (1) the volume is plain HFS volume, or
4473 * (2) any intermediate component in path was more than NAME_MAX bytes, or
4474 * (3) the entire path was greater than PATH_MAX bytes
4475 * then create regular file
4476 * else create symlink.
4477 */
4478 if (!isHFSPlus || (status & FPATH_BIGNAME) || (datalen > PATH_MAX)) {
4479 /* create file */
4480 fileType = S_IFREG;
4481 } else {
4482 /* create symlink */
4483 fileType = S_IFLNK;
4484 }
4485 } else {
4486 /* for fileID < 16, create regular file */
4487 fileType = S_IFREG;
4488
4489 /* construct the name of the file */
4490 filenameoffset = sprintf (filename, "%08x ", fileID);
4491 filenamelen -= filenameoffset;
4492 err = GetSystemFileName (fileID, (filename + filenameoffset), &filenamelen);
4493 filenamelen += filenameoffset;
4494
4495 /* construct the data of the file */
4496 dataoffset = sprintf (data, "System File: ");
4497 datalen -= dataoffset;
4498 err = GetSystemFileName (fileID, (data + dataoffset), &datalen);
4499 datalen += dataoffset;
4500 }
4501
4502 /* Create new file */
4503 err = CreateFileByName (GPtr, damagedDirID, fileType, (u_char *)filename,
4504 filenamelen, (u_char *)data, datalen);
4505 /* Mask error if file already exists */
4506 if (err == EEXIST) {
4507 err = noErr;
4508 }
4509 if (err != noErr) {
4510 #if DEBUG_OVERLAP
4511 plog ("%s: Error in creating fileType = %d for fileID = %d (err=%d)\n", __FUNCTION__, fileType, fileID, err);
4512 #endif
4513 goto out;
4514 }
4515
4516 out:
4517 if (err) {
4518 if ((GPtr->PrintStat & S_SymlinkCreate) == 0) {
4519 fsckPrint(GPtr->context, E_SymlinkCreate);
4520 GPtr->PrintStat|= S_SymlinkCreate;
4521 }
4522 } else {
4523 if ((GPtr->PrintStat & S_DamagedDir) == 0) {
4524 fsckPrint(GPtr->context, fsckCorruptFilesDirectory, "DamagedFiles");
4525 GPtr->PrintStat|= S_DamagedDir;
4526 }
4527 }
4528
4529 if (data) {
4530 free (data);
4531 }
4532 if (filename) {
4533 free (filename);
4534 }
4535
4536 return err;
4537 } /* CreateCorruptFileSymlink */
4538
4539 /* Function: SearchExtentInAttributeBT
4540 *
4541 * Description: Search extent with given information (fileID, attribute name,
4542 * startBlock, blockCount) in the attribute BTree.
4543 *
4544 * Input:
4545 * 1. GPtr - global scavenger structure pointer.
4546 * 2. extentInfo - Information about extent to be searched.
4547 *
4548 * Output:
4549 * Returns zero on success, fnfErr on failure.
4550 * 1. *attrKey - Attribute key for given fileID and attribute name, if found.
4551 * 2. *attrRecord - Attribute record for given fileID and attribute name, if found.
4552 * 3. *recordSize - Size of the record being returned.
4553 * 4. *foundExtentIndex - Index in extent record which matches the input data.
4554 */
4555 static OSErr SearchExtentInAttributeBT(SGlobPtr GPtr, ExtentInfo *extentInfo,
4556 HFSPlusAttrKey *attrKey, HFSPlusAttrRecord *attrRecord,
4557 UInt16 *recordSize, UInt32 *foundExtentIndex)
4558 {
4559 OSErr result = fnfErr;
4560 BTreeIterator iterator;
4561 FSBufferDescriptor btRecord;
4562 HFSPlusAttrKey *key;
4563 Boolean noMoreExtents;
4564 unsigned char *attrname = NULL;
4565 size_t attrnamelen;
4566
4567 assert((extentInfo->attrname != NULL));
4568
4569 attrname = malloc (XATTR_MAXNAMELEN + 1);
4570 if (!attrname) {
4571 result = memFullErr;
4572 goto out;
4573 }
4574
4575 /* Initialize the iterator, attribute record buffer, and attribute key */
4576 ClearMemory(&iterator, sizeof(BTreeIterator));
4577 key = (HFSPlusAttrKey *)&iterator.key;
4578 attrnamelen = strlen(extentInfo->attrname);
4579 BuildAttributeKey(extentInfo->fileID, 0, (unsigned char *)extentInfo->attrname, attrnamelen, key);
4580
4581 btRecord.bufferAddress = attrRecord;
4582 btRecord.itemCount = 1;
4583 btRecord.itemSize = sizeof(HFSPlusAttrRecord);
4584
4585 /* Search for the attribute record
4586 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
4587 * truncated on read! (4425232). This function only uses recordType
4588 * field from inline attribute record.
4589 */
4590 result = BTSearchRecord(GPtr->calculatedAttributesFCB, &iterator,
4591 kInvalidMRUCacheKey, &btRecord, recordSize, &iterator);
4592 if (result) {
4593 DPRINTF (d_error|d_overlap, "%s: Error finding attribute record (err=%d) for fileID = %d, attrname = %d\n", __FUNCTION__, result, extentInfo->fileID, extentInfo->attrname);
4594 goto out;
4595 }
4596
4597 /* Search the attribute record for overlapping extent. If found, return
4598 * success. If not, iterate to the next record. If it is a valid
4599 * attribute extent record belonging to the same attribute, search
4600 * for the desired extent.
4601 */
4602 while (1) {
4603 if (attrRecord->recordType == kHFSPlusAttrForkData) {
4604 result = FindExtentInExtentRec(true, extentInfo->startBlock,
4605 extentInfo->blockCount, attrRecord->forkData.theFork.extents,
4606 foundExtentIndex, &noMoreExtents);
4607 if ((result == noErr) || (noMoreExtents == true)) {
4608 goto out;
4609 }
4610 } else if (attrRecord->recordType == kHFSPlusAttrExtents) {
4611 result = FindExtentInExtentRec(true, extentInfo->startBlock,
4612 extentInfo->blockCount, attrRecord->overflowExtents.extents,
4613 foundExtentIndex, &noMoreExtents);
4614 if ((result == noErr) || (noMoreExtents == true)) {
4615 goto out;
4616 }
4617 } else {
4618 /* Invalid attribute record. This function should not find any
4619 * attribute record except forkData and AttrExtents.
4620 */
4621 result = fnfErr;
4622 goto out;
4623 }
4624
4625 /* Iterate to the next record
4626 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
4627 * truncated on read! (4425232). This function only uses recordType
4628 * field from inline attribute record.
4629 */
4630 result = BTIterateRecord(GPtr->calculatedAttributesFCB, kBTreeNextRecord,
4631 &iterator, &btRecord, recordSize);
4632 if (result) {
4633 goto out;
4634 }
4635
4636 (void) utf_encodestr(key->attrName, key->attrNameLen * 2, attrname, &attrnamelen, XATTR_MAXNAMELEN + 1);
4637 attrname[attrnamelen] = '\0';
4638
4639 /* Check if the attribute record belongs to the same attribute */
4640 if ((key->fileID != extentInfo->fileID) ||
4641 (strcmp((char *)attrname, extentInfo->attrname))) {
4642 /* The attribute record belongs to another attribute */
4643 result = fnfErr;
4644 goto out;
4645 }
4646 }
4647
4648 out:
4649 /* Copy the correct key to the caller */
4650 if (result == noErr) {
4651 CopyMemory(key, attrKey, sizeof(HFSPlusAttrKey));
4652 }
4653
4654 if (attrname != NULL) {
4655 free (attrname);
4656 }
4657
4658 return (result);
4659 }
4660
4661 /* Function: UpdateExtentInAttributeBT
4662 *
4663 * Description: Update extent record with given information (fileID, startBlock,
4664 * blockCount) in attribute BTree.
4665 *
4666 * Input:
4667 * 1. GPtr - global scavenger structure pointer.
4668 * 2. extentInfo - Information about extent to be searched.
4669 * 3. *attrKey - Attribute key for record to update.
4670 * 4. *attrRecord - Attribute record to update.
4671 * 5. *recordSize - Size of the record.
4672 * 6. foundExtentIndex - Index in extent record to update.
4673 *
4674 * Output:
4675 * Returns zero on success, non-zero on failure.
4676 */
4677 static OSErr UpdateExtentInAttributeBT (SGlobPtr GPtr, ExtentInfo *extentInfo,
4678 HFSPlusAttrKey *attrKey, HFSPlusAttrRecord *attrRecord,
4679 UInt16 *recordSize, UInt32 foundInExtentIndex)
4680 {
4681 OSErr err;
4682 UInt32 foundHint;
4683
4684 assert ((attrRecord->recordType == kHFSPlusAttrForkData) ||
4685 (attrRecord->recordType == kHFSPlusAttrExtents));
4686
4687 /* Update the new start block count in the extent */
4688 if (attrRecord->recordType == kHFSPlusAttrForkData) {
4689 attrRecord->forkData.theFork.extents[foundInExtentIndex].startBlock =
4690 extentInfo->newStartBlock;
4691 } else if (attrRecord->recordType == kHFSPlusAttrExtents) {
4692 attrRecord->overflowExtents.extents[foundInExtentIndex].startBlock =
4693 extentInfo->newStartBlock;
4694 }
4695
4696 /* Replace the attribute record.
4697 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
4698 * truncated on read! (4425232).
4699 */
4700 err = ReplaceBTreeRecord (GPtr->calculatedAttributesFCB, attrKey, kNoHint,
4701 attrRecord, *recordSize, &foundHint);
4702
4703 return (err);
4704 }
4705
4706 /* Function: SearchExtentInVH
4707 *
4708 * Description: Search extent with given information (fileID, startBlock,
4709 * blockCount) in volume header.
4710 *
4711 * Input:
4712 * 1. GPtr - global scavenger structure pointer.
4713 * 2. extentInfo - Information about extent to be searched.
4714 *
4715 * Output:
4716 * Returns zero on success, fnfErr on failure.
4717 * 1. *foundExtentIndex - Index in extent record which matches the input data.
4718 * 2. *noMoreExtents - Indicates that no more extents will exist for this
4719 * fileID in extents BTree.
4720 */
4721 static OSErr SearchExtentInVH(SGlobPtr GPtr, ExtentInfo *extentInfo, UInt32 *foundExtentIndex, Boolean *noMoreExtents)
4722 {
4723 OSErr err = fnfErr;
4724 Boolean isHFSPlus;
4725 SFCB *fcb = NULL;
4726
4727 isHFSPlus = VolumeObjectIsHFSPlus();
4728 *noMoreExtents = true;
4729
4730 /* Find correct FCB structure */
4731 switch (extentInfo->fileID) {
4732 case kHFSExtentsFileID:
4733 fcb = GPtr->calculatedVCB->vcbExtentsFile;
4734 break;
4735
4736 case kHFSCatalogFileID:
4737 fcb = GPtr->calculatedVCB->vcbCatalogFile;
4738 break;
4739
4740 case kHFSAllocationFileID:
4741 fcb = GPtr->calculatedVCB->vcbAllocationFile;
4742 break;
4743
4744 case kHFSStartupFileID:
4745 fcb = GPtr->calculatedVCB->vcbStartupFile;
4746 break;
4747
4748 case kHFSAttributesFileID:
4749 fcb = GPtr->calculatedVCB->vcbAttributesFile;
4750 break;
4751 };
4752
4753 /* If extent found, find correct extent index */
4754 if (fcb != NULL) {
4755 if (isHFSPlus) {
4756 err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
4757 extentInfo->blockCount, fcb->fcbExtents32,
4758 foundExtentIndex, noMoreExtents);
4759 } else {
4760 err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
4761 extentInfo->blockCount,
4762 (*(HFSPlusExtentRecord *)fcb->fcbExtents16),
4763 foundExtentIndex, noMoreExtents);
4764 }
4765 }
4766 return err;
4767 } /* SearchExtentInVH */
4768
4769 /* Function: UpdateExtentInVH
4770 *
4771 * Description: Update the extent record for given fileID and index in the
4772 * volume header with new start block.
4773 *
4774 * Input:
4775 * 1. GPtr - global scavenger structure pointer.
4776 * 2. extentInfo - Information about extent to be searched.
4777 * 3. foundExtentIndex - Index in extent record to update.
4778 *
4779 * Output:
4780 * Returns zero on success, fnfErr on failure. This function will fail an
4781 * incorrect fileID is passed.
4782 */
4783 static OSErr UpdateExtentInVH (SGlobPtr GPtr, ExtentInfo *extentInfo, UInt32 foundExtentIndex)
4784 {
4785 OSErr err = fnfErr;
4786 Boolean isHFSPlus;
4787 SFCB *fcb = NULL;
4788
4789 isHFSPlus = VolumeObjectIsHFSPlus();
4790
4791 /* Find correct FCB structure */
4792 switch (extentInfo->fileID) {
4793 case kHFSExtentsFileID:
4794 fcb = GPtr->calculatedVCB->vcbExtentsFile;
4795 break;
4796
4797 case kHFSCatalogFileID:
4798 fcb = GPtr->calculatedVCB->vcbCatalogFile;
4799 break;
4800
4801 case kHFSAllocationFileID:
4802 fcb = GPtr->calculatedVCB->vcbAllocationFile;
4803 break;
4804
4805 case kHFSStartupFileID:
4806 fcb = GPtr->calculatedVCB->vcbStartupFile;
4807 break;
4808
4809 case kHFSAttributesFileID:
4810 fcb = GPtr->calculatedVCB->vcbAttributesFile;
4811 break;
4812 };
4813
4814 /* If extent found, find correct extent index */
4815 if (fcb != NULL) {
4816 if (isHFSPlus) {
4817 fcb->fcbExtents32[foundExtentIndex].startBlock = extentInfo->newStartBlock;
4818 } else {
4819 fcb->fcbExtents16[foundExtentIndex].startBlock = extentInfo->newStartBlock;
4820 }
4821 MarkVCBDirty(GPtr->calculatedVCB);
4822 err = noErr;
4823 }
4824 return err;
4825 } /* UpdateExtentInVH */
4826
4827 /* Function: SearchExtentInCatalogBT
4828 *
4829 * Description: Search extent with given information (fileID, startBlock,
4830 * blockCount) in catalog BTree.
4831 *
4832 * Input:
4833 * 1. GPtr - global scavenger structure pointer.
4834 * 2. extentInfo - Information about extent to be searched.
4835 *
4836 * Output:
4837 * Returns zero on success, non-zero on failure.
4838 * 1. *catKey - Catalog key for given fileID, if found.
4839 * 2. *catRecord - Catalog record for given fileID, if found.
4840 * 3. *recordSize - Size of the record being returned.
4841 * 4. *foundExtentIndex - Index in extent record which matches the input data.
4842 * 5. *noMoreExtents - Indicates that no more extents will exist for this
4843 * fileID in extents BTree.
4844 */
4845 static OSErr SearchExtentInCatalogBT(SGlobPtr GPtr, ExtentInfo *extentInfo, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize, UInt32 *foundExtentIndex, Boolean *noMoreExtents)
4846 {
4847 OSErr err;
4848 Boolean isHFSPlus;
4849
4850 isHFSPlus = VolumeObjectIsHFSPlus();
4851
4852 /* Search catalog btree for this file ID */
4853 err = GetCatalogRecord(GPtr, extentInfo->fileID, isHFSPlus, catKey, catRecord,
4854 recordSize);
4855 if (err != noErr) {
4856 #if DEBUG_OVERLAP
4857 plog ("%s: No matching catalog record found for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
4858 #endif
4859 goto out;
4860 }
4861
4862 if (isHFSPlus) {
4863 /* HFS Plus */
4864 if (extentInfo->forkType == kDataFork) {
4865 /* data fork */
4866 err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
4867 extentInfo->blockCount,
4868 catRecord->hfsPlusFile.dataFork.extents,
4869 foundExtentIndex, noMoreExtents);
4870 } else {
4871 /* resource fork */
4872 err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
4873 extentInfo->blockCount,
4874 catRecord->hfsPlusFile.resourceFork.extents,
4875 foundExtentIndex, noMoreExtents);
4876 }
4877 } else {
4878 /* HFS */
4879 if (extentInfo->forkType == kDataFork) {
4880 /* data fork */
4881 err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
4882 extentInfo->blockCount,
4883 (*(HFSPlusExtentRecord *)catRecord->hfsFile.dataExtents),
4884 foundExtentIndex, noMoreExtents);
4885 } else {
4886 /* resource fork */
4887 err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
4888 extentInfo->blockCount,
4889 (*(HFSPlusExtentRecord *)catRecord->hfsFile.rsrcExtents),
4890 foundExtentIndex, noMoreExtents);
4891 }
4892 }
4893
4894 out:
4895 return err;
4896 } /* SearchExtentInCatalogBT */
4897
4898 /* Function: UpdateExtentInCatalogBT
4899 *
4900 * Description: Update extent record with given information (fileID, startBlock,
4901 * blockCount) in catalog BTree.
4902 *
4903 * Input:
4904 * 1. GPtr - global scavenger structure pointer.
4905 * 2. extentInfo - Information about extent to be searched.
4906 * 3. *catKey - Catalog key for record to update.
4907 * 4. *catRecord - Catalog record to update.
4908 * 5. *recordSize - Size of the record.
4909 * 6. foundExtentIndex - Index in extent record to update.
4910 *
4911 * Output:
4912 * Returns zero on success, non-zero on failure.
4913 */
4914 static OSErr UpdateExtentInCatalogBT (SGlobPtr GPtr, ExtentInfo *extentInfo, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize, UInt32 foundInExtentIndex)
4915 {
4916 OSErr err;
4917 Boolean isHFSPlus;
4918 UInt32 foundHint;
4919
4920 isHFSPlus = VolumeObjectIsHFSPlus();
4921
4922 /* Modify the catalog record */
4923 if (isHFSPlus) {
4924 if (extentInfo->forkType == kDataFork) {
4925 catRecord->hfsPlusFile.dataFork.extents[foundInExtentIndex].startBlock = extentInfo->newStartBlock;
4926 } else {
4927 catRecord->hfsPlusFile.resourceFork.extents[foundInExtentIndex].startBlock = extentInfo->newStartBlock;
4928 }
4929 } else {
4930 if (extentInfo->forkType == kDataFork) {
4931 catRecord->hfsFile.dataExtents[foundInExtentIndex].startBlock = extentInfo->newStartBlock;
4932 } else {
4933 catRecord->hfsFile.rsrcExtents[foundInExtentIndex].startBlock = extentInfo->newStartBlock;
4934 }
4935 }
4936
4937 /* Replace the catalog record */
4938 err = ReplaceBTreeRecord (GPtr->calculatedCatalogFCB, catKey, kNoHint,
4939 catRecord, *recordSize, &foundHint);
4940 if (err != noErr) {
4941 #if DEBUG_OVERLAP
4942 plog ("%s: Error in replacing catalog record for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
4943 #endif
4944 }
4945 return err;
4946 } /* UpdateExtentInCatalogBT */
4947
4948 /* Function: SearchExtentInExtentBT
4949 *
4950 * Description: Search extent with given information (fileID, startBlock,
4951 * blockCount) in Extent BTree.
4952 *
4953 * Input:
4954 * 1. GPtr - global scavenger structure pointer.
4955 * 2. extentInfo - Information about extent to be searched.
4956 *
4957 * Output:
4958 * Returns zero on success, non-zero on failure.
4959 * fnfErr - desired extent record was not found.
4960 * 1. *extentKey - Extent key, if found.
4961 * 2. *extentRecord - Extent record, if found.
4962 * 3. *recordSize - Size of the record being returned.
4963 * 4. *foundExtentIndex - Index in extent record which matches the input data.
4964 */
4965 static OSErr SearchExtentInExtentBT(SGlobPtr GPtr, ExtentInfo *extentInfo, HFSPlusExtentKey *extentKey, HFSPlusExtentRecord *extentRecord, UInt16 *recordSize, UInt32 *foundExtentIndex)
4966 {
4967 OSErr err = noErr;
4968 Boolean isHFSPlus;
4969 Boolean noMoreExtents = true;
4970 UInt32 hint;
4971
4972 isHFSPlus = VolumeObjectIsHFSPlus();
4973
4974 /* set up extent key */
4975 BuildExtentKey (isHFSPlus, extentInfo->forkType, extentInfo->fileID, 0, extentKey);
4976 err = SearchBTreeRecord (GPtr->calculatedExtentsFCB, extentKey, kNoHint,
4977 extentKey, extentRecord, recordSize, &hint);
4978 if ((err != noErr) && (err != btNotFound)) {
4979 #if DEBUG_OVERLAP
4980 plog ("%s: Error on searching first record for fileID = %d in Extents Btree (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
4981 #endif
4982 goto out;
4983 }
4984
4985 if (err == btNotFound)
4986 {
4987 /* Position to the first record for the given fileID */
4988 err = GetBTreeRecord (GPtr->calculatedExtentsFCB, 1, extentKey,
4989 extentRecord, recordSize, &hint);
4990 }
4991
4992 while (err == noErr)
4993 {
4994 /* Break out if we see different fileID, forkType in the BTree */
4995 if (isHFSPlus) {
4996 if ((extentKey->fileID != extentInfo->fileID) ||
4997 (extentKey->forkType != extentInfo->forkType)) {
4998 err = fnfErr;
4999 break;
5000 }
5001 } else {
5002 if ((((HFSExtentKey *)extentKey)->fileID != extentInfo->fileID) ||
5003 (((HFSExtentKey *)extentKey)->forkType != extentInfo->forkType)) {
5004 err = fnfErr;
5005 break;
5006 }
5007 }
5008
5009 /* Check the extents record for corresponding startBlock, blockCount */
5010 err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
5011 extentInfo->blockCount, *extentRecord,
5012 foundExtentIndex, &noMoreExtents);
5013 if (err == noErr) {
5014 break;
5015 }
5016 if (noMoreExtents == true) {
5017 err = fnfErr;
5018 break;
5019 }
5020
5021 /* Try next record for this fileID and forkType */
5022 err = GetBTreeRecord (GPtr->calculatedExtentsFCB, 1, extentKey,
5023 extentRecord, recordSize, &hint);
5024 }
5025
5026 out:
5027 return err;
5028 } /* SearchExtentInExtentBT */
5029
5030 /* Function: FindExtentInExtentRec
5031 *
5032 * Description: Traverse the given extent record (size based on if the volume is
5033 * HFS or HFSPlus volume) and find the index location if the given startBlock
5034 * and blockCount match.
5035 *
5036 * Input:
5037 * 1. isHFSPlus - If the volume is plain HFS or HFS Plus.
5038 * 2. startBlock - startBlock to be searched in extent record.
5039 * 3. blockCount - blockCount to be searched in extent record.
5040 * 4. extentData - Extent Record to be searched.
5041 *
5042 * Output:
5043 * Returns zero if the match is found, else fnfErr on failure.
5044 * 1. *foundExtentIndex - Index in extent record which matches the input data.
5045 * 2. *noMoreExtents - Indicates that no more extents exist after this extent
5046 * record.
5047 */
5048 static OSErr FindExtentInExtentRec (Boolean isHFSPlus, UInt32 startBlock, UInt32 blockCount, const HFSPlusExtentRecord extentData, UInt32 *foundExtentIndex, Boolean *noMoreExtents)
5049 {
5050 OSErr err = noErr;
5051 UInt32 numOfExtents;
5052 Boolean foundExtent;
5053 int i;
5054
5055 foundExtent = false;
5056 *noMoreExtents = false;
5057 *foundExtentIndex = 0;
5058
5059 if (isHFSPlus) {
5060 numOfExtents = kHFSPlusExtentDensity;
5061 } else {
5062 numOfExtents = kHFSExtentDensity;
5063 }
5064
5065 for (i=0; i<numOfExtents; i++) {
5066 if (extentData[i].blockCount == 0) {
5067 /* no more extents left to check */
5068 *noMoreExtents = true;
5069 break;
5070 }
5071 if ((startBlock == extentData[i].startBlock) &&
5072 (blockCount == extentData[i].blockCount)) {
5073 foundExtent = true;
5074 *foundExtentIndex = i;
5075 break;
5076 }
5077 }
5078
5079 if (foundExtent == false) {
5080 err = fnfErr;
5081 }
5082
5083 return err;
5084 } /* FindExtentInExtentRec */
5085
5086 /* Function: GetSystemFileName
5087 *
5088 * Description: Return the name of the system file based on fileID
5089 *
5090 * Input:
5091 * 1. fileID - fileID whose name is to be returned.
5092 * 2. *filenamelen - length of filename buffer.
5093 *
5094 * Output:
5095 * 1. *filename - filename, is limited by the length of filename buffer passed
5096 * in *filenamelen.
5097 * 2. *filenamelen - length of the filename
5098 * Always returns zero.
5099 */
5100 OSErr GetSystemFileName(UInt32 fileID, char *filename, unsigned int *filenamelen)
5101 {
5102 OSErr err = noErr;
5103 unsigned int len;
5104
5105 if (filename) {
5106 len = *filenamelen - 1;
5107 switch (fileID) {
5108 case kHFSExtentsFileID:
5109 strncpy (filename, "Extents Overflow BTree", len);
5110 break;
5111
5112 case kHFSCatalogFileID:
5113 strncpy (filename, "Catalog BTree", len);
5114 break;
5115
5116 case kHFSAllocationFileID:
5117 strncpy (filename, "Allocation File", len);
5118 break;
5119
5120 case kHFSStartupFileID:
5121 strncpy (filename, "Startup File", len);
5122 break;
5123
5124 case kHFSAttributesFileID:
5125 strncpy (filename, "Attributes BTree", len);
5126 break;
5127
5128 case kHFSBadBlockFileID:
5129 strncpy (filename, "Bad Allocation File", len);
5130 break;
5131
5132 case kHFSRepairCatalogFileID:
5133 strncpy (filename, "Repair Catalog File", len);
5134 break;
5135
5136 case kHFSBogusExtentFileID:
5137 strncpy (filename, "Bogus Extents File", len);
5138 break;
5139
5140 default:
5141 strncpy (filename, "Unknown File", len);
5142 break;
5143 };
5144 filename[len] = '\0';
5145 *filenamelen = strlen (filename);
5146 }
5147 return err;
5148 }
5149
5150 /* structure to store the intermediate path components during BTree traversal.
5151 * This is used as a LIFO linked list
5152 */
5153 struct fsPathString
5154 {
5155 char *name;
5156 unsigned int namelen;
5157 struct fsPathString *childPtr;
5158 };
5159
5160 /* Function: GetFileNamePathByID
5161 *
5162 * Description: Return the file/directory name and/or full path by ID. The
5163 * length of the strings returned is limited by string lengths passed as
5164 * parameters.
5165 * The function lookups catalog thread record for given fileID and its parents
5166 * until it reaches the Root Folder.
5167 *
5168 * Note:
5169 * 1. The path returned currently does not return mangled names.
5170 * 2. Either one or both of fullPath and fileName can be NULL.
5171 * 3. fullPath and fileName are returned as NULL-terminated UTF8 strings.
5172 * 4. Returns error if fileID < kHFSFirstUserCatalogID.
5173 *
5174 * Input:
5175 * 1. GPtr - global scavenger structure pointer
5176 * 2. fileID - fileID for the target file/directory for finding the path
5177 * 3. fullPathLen - size of array to return full path
5178 * 4. fileNameLen - size of array to return file name
5179 *
5180 * Output:
5181 * Return value: zero on success, non-zero on failure
5182 * memFullErr - Not enough memory
5183 * paramErr - Invalid paramter
5184 *
5185 * The data in *fileNameLen and *fullPathLen is undefined on error.
5186 *
5187 * 1. fullPath - If fullPath is non-NULL, full path of file/directory is
5188 * returned (size limited by fullPathLen)
5189 * 2. *fullPathLen - length of fullPath returned.
5190 * 3. fileName - If fileName is non-NULL, file name of fileID is returned (size
5191 * limited by fileNameLen).
5192 * 4. *fileNameLen - length of fileName returned.
5193 * 5. *status - status of operation, any of the following bits can be set
5194 * (defined in dfalib/Scavenger.h).
5195 * FNAME_BUF2SMALL - filename buffer is too small.
5196 * FNAME_BIGNAME - filename is more than NAME_MAX bytes.
5197 * FPATH_BUF2SMALL - path buffer is too small.
5198 * FPATH_BIGNAME - one or more intermediate path component is greater
5199 * than NAME_MAX bytes.
5200 * F_RESERVE_FILEID- fileID is less than kHFSFirstUserCatalogNodeID.
5201 */
5202 OSErr GetFileNamePathByID(SGlobPtr GPtr, UInt32 fileID, char *fullPath, unsigned int *fullPathLen, char *fileName, unsigned int *fileNameLen, u_int16_t *status)
5203 {
5204 OSErr err = noErr;
5205 Boolean isHFSPlus;
5206 UInt16 recordSize;
5207 UInt16 curStatus = 0;
5208 UInt32 hint;
5209 CatalogKey catKey;
5210 CatalogRecord catRecord;
5211 struct fsPathString *listHeadPtr = NULL;
5212 struct fsPathString *listTailPtr = NULL;
5213 struct fsPathString *curPtr = NULL;
5214 u_char *filename = NULL;
5215 size_t namelen;
5216
5217 if (!fullPath && !fileName) {
5218 goto out;
5219 }
5220
5221 if (fileID < kHFSFirstUserCatalogNodeID) {
5222 curStatus = F_RESERVE_FILEID;
5223 err = paramErr;
5224 goto out;
5225 }
5226
5227 isHFSPlus = VolumeObjectIsHFSPlus();
5228
5229 if (isHFSPlus) {
5230 filename = malloc(kHFSPlusMaxFileNameChars * 3 + 1);
5231 } else {
5232 filename = malloc(kHFSMaxFileNameChars + 1);
5233 }
5234 if (!filename) {
5235 err = memFullErr;
5236 #if DEBUG_OVERLAP
5237 plog ("%s: Not enough memory (err=%d)\n", __FUNCTION__, err);
5238 #endif
5239 goto out;
5240 }
5241
5242 while (fileID != kHFSRootFolderID) {
5243 /* lookup for thread record for this fileID */
5244 BuildCatalogKey(fileID, NULL, isHFSPlus, &catKey);
5245 err = SearchBTreeRecord(GPtr->calculatedCatalogFCB, &catKey, kNoHint,
5246 &catKey, &catRecord, &recordSize, &hint);
5247 if (err) {
5248 #if DEBUG_OVERLAP
5249 plog ("%s: Error finding thread record for fileID = %d (err=%d)\n", __FUNCTION__, fileID, err);
5250 #endif
5251 goto out;
5252 }
5253
5254 /* Check if this is indeed a thread record */
5255 if ((catRecord.hfsPlusThread.recordType != kHFSPlusFileThreadRecord) &&
5256 (catRecord.hfsPlusThread.recordType != kHFSPlusFolderThreadRecord) &&
5257 (catRecord.hfsThread.recordType != kHFSFileThreadRecord) &&
5258 (catRecord.hfsThread.recordType != kHFSFolderThreadRecord)) {
5259 err = paramErr;
5260 #if DEBUG_OVERLAP
5261 plog ("%s: Error finding valid thread record for fileID = %d\n", __FUNCTION__, fileID);
5262 #endif
5263 goto out;
5264 }
5265
5266 /* Convert the name string to utf8 */
5267 if (isHFSPlus) {
5268 (void) utf_encodestr(catRecord.hfsPlusThread.nodeName.unicode,
5269 catRecord.hfsPlusThread.nodeName.length * 2,
5270 filename, &namelen, kHFSPlusMaxFileNameChars * 3 + 1);
5271 } else {
5272 namelen = catRecord.hfsThread.nodeName[0];
5273 memcpy (filename, catKey.hfs.nodeName, namelen);
5274 }
5275
5276 /* Store the path name in LIFO linked list */
5277 curPtr = malloc(sizeof(struct fsPathString));
5278 if (!curPtr) {
5279 err = memFullErr;
5280 #if DEBUG_OVERLAP
5281 plog ("%s: Not enough memory (err=%d)\n", __FUNCTION__, err);
5282 #endif
5283 goto out;
5284 }
5285
5286 /* Do not NULL terminate the string */
5287 curPtr->namelen = namelen;
5288 curPtr->name = malloc(namelen);
5289 if (!curPtr->name) {
5290 err = memFullErr;
5291 #if DEBUG_OVERLAP
5292 plog ("%s: Not enough memory (err=%d)\n", __FUNCTION__, err);
5293 #endif
5294 }
5295 memcpy (curPtr->name, filename, namelen);
5296 curPtr->childPtr = listHeadPtr;
5297 listHeadPtr = curPtr;
5298 if (listTailPtr == NULL) {
5299 listTailPtr = curPtr;
5300 }
5301
5302 /* lookup for parentID */
5303 if (isHFSPlus) {
5304 fileID = catRecord.hfsPlusThread.parentID;
5305 } else {
5306 fileID = catRecord.hfsThread.parentID;
5307 }
5308
5309 /* no need to find entire path, bail out */
5310 if (fullPath == NULL) {
5311 break;
5312 }
5313 }
5314
5315 /* return the name of the file/directory */
5316 if (fileName) {
5317 /* Do not overflow the buffer length passed */
5318 if (*fileNameLen < (listTailPtr->namelen + 1)) {
5319 *fileNameLen = *fileNameLen - 1;
5320 curStatus |= FNAME_BUF2SMALL;
5321 } else {
5322 *fileNameLen = listTailPtr->namelen;
5323 }
5324 if (*fileNameLen > NAME_MAX) {
5325 curStatus |= FNAME_BIGNAME;
5326 }
5327 memcpy (fileName, listTailPtr->name, *fileNameLen);
5328 fileName[*fileNameLen] = '\0';
5329 }
5330
5331 /* return the full path of the file/directory */
5332 if (fullPath) {
5333 /* Do not overflow the buffer length passed and reserve last byte for NULL termination */
5334 unsigned int bytesRemain = *fullPathLen - 1;
5335
5336 *fullPathLen = 0;
5337 while (listHeadPtr != NULL) {
5338 if (bytesRemain == 0) {
5339 break;
5340 }
5341 memcpy ((fullPath + *fullPathLen), "/", 1);
5342 *fullPathLen += 1;
5343 bytesRemain--;
5344
5345 if (bytesRemain == 0) {
5346 break;
5347 }
5348 if (bytesRemain < listHeadPtr->namelen) {
5349 namelen = bytesRemain;
5350 curStatus |= FPATH_BUF2SMALL;
5351 } else {
5352 namelen = listHeadPtr->namelen;
5353 }
5354 if (namelen > NAME_MAX) {
5355 curStatus |= FPATH_BIGNAME;
5356 }
5357 memcpy ((fullPath + *fullPathLen), listHeadPtr->name, namelen);
5358 *fullPathLen += namelen;
5359 bytesRemain -= namelen;
5360
5361 curPtr = listHeadPtr;
5362 listHeadPtr = listHeadPtr->childPtr;
5363 free(curPtr->name);
5364 free(curPtr);
5365 }
5366
5367 fullPath[*fullPathLen] = '\0';
5368 }
5369
5370 err = noErr;
5371
5372 out:
5373 if (status) {
5374 *status = curStatus;
5375 }
5376
5377 /* free any remaining allocated memory */
5378 while (listHeadPtr != NULL) {
5379 curPtr = listHeadPtr;
5380 listHeadPtr = listHeadPtr->childPtr;
5381 if (curPtr->name) {
5382 free (curPtr->name);
5383 }
5384 free (curPtr);
5385 }
5386 if (filename) {
5387 free (filename);
5388 }
5389
5390 return err;
5391 } /* GetFileNamePathByID */
5392
5393 /* Function: CopyDiskBlocks
5394 *
5395 * Description: Copy data from source extent to destination extent
5396 * for blockCount on the disk.
5397 *
5398 * Input:
5399 * 1. GPtr - pointer to global scavenger structure.
5400 * 2. startAllocationBlock - startBlock for old extent
5401 * 3. blockCount - total blocks to copy
5402 * 4. newStartAllocationBlock - startBlock for new extent
5403 *
5404 * Output:
5405 * err, zero on success, non-zero on failure.
5406 */
5407 OSErr CopyDiskBlocks(SGlobPtr GPtr, const UInt32 startAllocationBlock, const UInt32 blockCount, const UInt32 newStartAllocationBlock )
5408 {
5409 OSErr err = noErr;
5410 SVCB *vcb;
5411 uint64_t old_offset;
5412 uint64_t new_offset;
5413 uint32_t sectorsPerBlock;
5414
5415 vcb = GPtr->calculatedVCB;
5416 sectorsPerBlock = vcb->vcbBlockSize / Blk_Size;
5417
5418 old_offset = (vcb->vcbAlBlSt + (sectorsPerBlock * startAllocationBlock)) << Log2BlkLo;
5419 new_offset = (vcb->vcbAlBlSt + (sectorsPerBlock * newStartAllocationBlock)) << Log2BlkLo;
5420
5421 err = CacheCopyDiskBlocks (vcb->vcbBlockCache, old_offset, new_offset,
5422 blockCount * vcb->vcbBlockSize);
5423 return err;
5424 } /* CopyDiskBlocks */
5425
5426 /* Function: WriteBufferToDisk
5427 *
5428 * Description: Write given buffer data to disk blocks.
5429 * If the length of the buffer is not a multiple of allocation block size,
5430 * the disk is filled with zero from the length of buffer upto the
5431 * end of allocation blocks (specified by blockCount).
5432 *
5433 * Input:
5434 * 1. GPtr - global scavenger structure pointer
5435 * 2. startBlock - starting block number for writing data.
5436 * 3. blockCount - total number of contiguous blocks to be written
5437 * 4. buffer - data buffer to be written to disk
5438 * 5. bufLen - length of data buffer to be written to disk.
5439 *
5440 * Output:
5441 * returns zero on success, non-zero on failure.
5442 */
5443 OSErr WriteBufferToDisk(SGlobPtr GPtr, UInt32 startBlock, UInt32 blockCount, u_char *buffer, int bufLen)
5444 {
5445 OSErr err = noErr;
5446 SVCB *vcb;
5447 uint64_t offset;
5448 uint32_t write_len;
5449
5450 vcb = GPtr->calculatedVCB;
5451
5452 /* Calculate offset and length */
5453 offset = (vcb->vcbAlBlSt + ((vcb->vcbBlockSize / Blk_Size) * startBlock)) << Log2BlkLo;
5454 write_len = blockCount * vcb->vcbBlockSize;
5455
5456 /* Write buffer to disk */
5457 err = CacheWriteBufferToDisk (vcb->vcbBlockCache, offset, write_len, buffer, bufLen);
5458
5459 return err;
5460 } /* WriteBufferToDisk */
5461
5462 // 2210409, in System 8.1, moving file or folder would cause HFS+ thread records to be
5463 // 520 bytes in size. We only shrink the threads if other repairs are needed.
5464 static OSErr FixBloatedThreadRecords( SGlob *GPtr )
5465 {
5466 CatalogRecord record;
5467 CatalogKey foundKey;
5468 UInt32 hint;
5469 UInt16 recordSize;
5470 SInt16 i = 0;
5471 OSErr err;
5472 SInt16 selCode = 0x8001; // Start with 1st record
5473
5474 err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint );
5475 ReturnIfError( err );
5476
5477 selCode = 1; // Get next record from now on
5478
5479 do
5480 {
5481 if ( ++i > 10 ) { (void) CheckForStop( GPtr ); i = 0; } // Spin the cursor every 10 entries
5482
5483 if ( (recordSize == sizeof(HFSPlusCatalogThread)) && ((record.recordType == kHFSPlusFolderThreadRecord) || (record.recordType == kHFSPlusFileThreadRecord)) )
5484 {
5485 // HFS Plus has varaible sized threads so adjust to actual length
5486 recordSize -= ( sizeof(record.hfsPlusThread.nodeName.unicode) - (record.hfsPlusThread.nodeName.length * sizeof(UniChar)) );
5487
5488 err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
5489 ReturnIfError( err );
5490 }
5491
5492 err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint );
5493 } while ( err == noErr );
5494
5495 if ( err == btNotFound )
5496 err = noErr;
5497
5498 return( err );
5499 }
5500
5501
5502 static OSErr
5503 FixMissingThreadRecords( SGlob *GPtr )
5504 {
5505 struct MissingThread * mtp;
5506 FSBufferDescriptor btRecord;
5507 BTreeIterator iterator;
5508 OSStatus result;
5509 UInt16 dataSize;
5510 Boolean headsUp;
5511 UInt32 lostAndFoundDirID;
5512
5513 lostAndFoundDirID = 0;
5514 headsUp = false;
5515 for (mtp = GPtr->missingThreadList; mtp != NULL; mtp = mtp->link) {
5516 if ( mtp->threadID == 0 )
5517 continue;
5518
5519 // if the thread record information in the MissingThread struct is not there
5520 // then we have a missing directory in addition to a missing thread record
5521 // for that directory. We will recreate the missing directory in our
5522 // lost+found directory.
5523 if ( mtp->thread.parentID == 0 ) {
5524 if (embedded == 1 && debug == 0) {
5525 return( R_RFail );
5526 }
5527 if ( lostAndFoundDirID == 0 )
5528 lostAndFoundDirID = CreateDirByName( GPtr , (u_char *)"lost+found", kHFSRootFolderID);
5529 if ( lostAndFoundDirID == 0 ) {
5530 if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
5531 plog( "\tCould not create lost+found directory \n" );
5532 return( R_RFail );
5533 }
5534 fsckPrint(GPtr->context, E_NoDir, mtp->threadID);
5535 result = FixMissingDirectory( GPtr, mtp->threadID, lostAndFoundDirID );
5536 if ( result != 0 ) {
5537 if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
5538 plog( "\tCould not recreate a missing directory (error %d)\n", result );
5539 return( R_RFail );
5540 }
5541 else
5542 headsUp = true;
5543 continue;
5544 }
5545
5546 dataSize = 10 + (mtp->thread.nodeName.length * 2);
5547 btRecord.bufferAddress = (void *)&mtp->thread;
5548 btRecord.itemSize = dataSize;
5549 btRecord.itemCount = 1;
5550 iterator.hint.nodeNum = 0;
5551 BuildCatalogKey(mtp->threadID, NULL, true, (CatalogKey*)&iterator.key);
5552
5553 result = BTInsertRecord(GPtr->calculatedCatalogFCB, &iterator, &btRecord, dataSize);
5554 if (result)
5555 return (IntError(GPtr, R_IntErr));
5556 mtp->threadID = 0;
5557 }
5558 if ( headsUp )
5559 fsckPrint(GPtr->context, fsckLostFoundDirectory, "lost+found");
5560
5561 return (0);
5562 }
5563
5564
5565 static OSErr
5566 FixMissingDirectory( SGlob *GPtr, UInt32 theObjID, UInt32 theParID )
5567 {
5568 Boolean isHFSPlus;
5569 UInt16 recSize;
5570 OSErr result;
5571 int nameLen;
5572 UInt32 hint;
5573 char myString[ 32 ];
5574 CatalogName myName;
5575 CatalogRecord catRec;
5576 CatalogKey myKey, myThreadKey;
5577
5578 isHFSPlus = VolumeObjectIsHFSPlus( );
5579
5580 // we will use the object ID of the missing directory as the name since we have
5581 // no way to find the original name and this should make it unique within our
5582 // lost+found directory.
5583 sprintf( myString, "%ld", (long)theObjID );
5584 nameLen = strlen( myString );
5585
5586 if ( isHFSPlus )
5587 {
5588 int i;
5589 myName.ustr.length = nameLen;
5590 for ( i = 0; i < myName.ustr.length; i++ )
5591 myName.ustr.unicode[ i ] = (u_int16_t) myString[ i ];
5592 }
5593 else
5594 {
5595 myName.pstr[0] = nameLen;
5596 memcpy( &myName.pstr[1], &myString[0], nameLen );
5597 }
5598
5599 // make sure the name is not already used
5600 BuildCatalogKey( theParID, &myName, isHFSPlus, &myKey );
5601 result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &myKey, kNoHint,
5602 NULL, &catRec, &recSize, &hint );
5603 if ( result == noErr )
5604 return( R_IntErr );
5605
5606 // insert new directory and thread record into the catalog
5607 recSize = BuildThreadRec( &myKey, &catRec, isHFSPlus, true );
5608 BuildCatalogKey( theObjID, NULL, isHFSPlus, &myThreadKey );
5609 result = InsertBTreeRecord( GPtr->calculatedCatalogFCB, &myThreadKey, &catRec, recSize, &hint );
5610 if ( result != noErr )
5611 return( result );
5612
5613 recSize = BuildFolderRec( GPtr, 01777, theObjID, isHFSPlus, &catRec );
5614
5615 result = InsertBTreeRecord( GPtr->calculatedCatalogFCB, &myKey, &catRec, recSize, &hint );
5616 if ( result != noErr )
5617 return( result );
5618
5619 /* update parent directory to reflect addition of new directory */
5620 result = UpdateFolderCount( GPtr->calculatedVCB, theParID, NULL,
5621 ((isHFSPlus) ? kHFSPlusFolderRecord : kHFSFolderRecord),
5622 kNoHint, 1 );
5623
5624 /* update our header node on disk from our BTreeControlBlock */
5625 UpdateBTreeHeader( GPtr->calculatedCatalogFCB );
5626
5627 return( result );
5628
5629 } /* FixMissingDirectory */
5630
5631
5632 static HFSCatalogNodeID
5633 GetObjectID( CatalogRecord * theRecPtr )
5634 {
5635 HFSCatalogNodeID myObjID;
5636
5637 switch ( theRecPtr->recordType ) {
5638 case kHFSPlusFolderRecord:
5639 myObjID = theRecPtr->hfsPlusFolder.folderID;
5640 break;
5641 case kHFSPlusFileRecord:
5642 myObjID = theRecPtr->hfsPlusFile.fileID;
5643 break;
5644 case kHFSFolderRecord:
5645 myObjID = theRecPtr->hfsFolder.folderID;
5646 break;
5647 case kHFSFileRecord:
5648 myObjID = theRecPtr->hfsFile.fileID;
5649 break;
5650 default:
5651 myObjID = 0;
5652 }
5653
5654 return( myObjID );
5655
5656 } /* GetObjectID */
5657
5658 /* Function: CreateFileByName
5659 *
5660 * Description: Create a file with given fileName of type fileType containing
5661 * data of length dataLen. This function assumes that the name of symlink
5662 * to be created is passed as UTF8
5663 *
5664 * Input:
5665 * 1. GPtr - pointer to global scavenger structure
5666 * 2. parentID - ID of parent directory to create the new file.
5667 * 3. fileName - name of the file to create in UTF8 format.
5668 * 4. fileNameLen - length of the filename to be created.
5669 * If the volume is HFS Plus, the filename is delimited to
5670 * kHFSPlusMaxFileNameChars characters.
5671 * If the volume is plain HFS, the filename is delimited to
5672 * kHFSMaxFileNameChars characters.
5673 * 5. fileType - file type (currently supported S_IFREG, S_IFLNK).
5674 * 6. data - data content of first data fork of the file
5675 * 7. dataLen - length of data to be written
5676 *
5677 * Output:
5678 * returns zero on success, non-zero on failure.
5679 * memFullErr - Not enough memory
5680 * paramErr - Invalid paramter
5681 */
5682 OSErr CreateFileByName(SGlobPtr GPtr, UInt32 parentID, UInt16 fileType, u_char *fileName, unsigned int filenameLen, u_char *data, unsigned int dataLen)
5683 {
5684 OSErr err = noErr;
5685 Boolean isHFSPlus;
5686 Boolean isCatUpdated = false;
5687
5688 CatalogName fName;
5689 CatalogRecord catRecord;
5690 CatalogKey catKey;
5691 CatalogKey threadKey;
5692 UInt32 hint;
5693 UInt16 recordSize;
5694
5695 UInt32 startBlock = 0;
5696 UInt32 blockCount = 0;
5697 UInt32 nextCNID;
5698
5699 isHFSPlus = VolumeObjectIsHFSPlus();
5700
5701 /* Construct unicode name for file name to construct catalog key */
5702 if (isHFSPlus) {
5703 /* Convert utf8 filename to Unicode filename */
5704 size_t namelen;
5705
5706 if (filenameLen < kHFSPlusMaxFileNameChars) {
5707 (void) utf_decodestr (fileName, filenameLen, fName.ustr.unicode, &namelen, sizeof(fName.ustr.unicode));
5708 namelen /= 2;
5709 fName.ustr.length = namelen;
5710 } else {
5711 /* The resulting may result in more than kHFSPlusMaxFileNameChars chars */
5712 UInt16 *unicodename;
5713
5714 /* Allocate a large array to convert the utf-8 to utf-16 */
5715 unicodename = malloc (filenameLen * 4);
5716 if (unicodename == NULL) {
5717 err = memFullErr;
5718 goto out;
5719 }
5720
5721 (void) utf_decodestr (fileName, filenameLen, unicodename, &namelen, filenameLen * 4);
5722 namelen /= 2;
5723
5724 /* Chopping unicode string based on length might affect unicode
5725 * chars that take more than one UInt16 - very rare possiblity.
5726 */
5727 if (namelen > kHFSPlusMaxFileNameChars) {
5728 namelen = kHFSPlusMaxFileNameChars;
5729 }
5730
5731 memcpy (fName.ustr.unicode, unicodename, (namelen * 2));
5732 free (unicodename);
5733 fName.ustr.length = namelen;
5734 }
5735 } else {
5736 if (filenameLen > kHFSMaxFileNameChars) {
5737 filenameLen = kHFSMaxFileNameChars;
5738 }
5739 fName.pstr[0] = filenameLen;
5740 memcpy(&fName.pstr[1], fileName, filenameLen);
5741 }
5742
5743 /* Make sure that a file with same name does not exist in parent dir */
5744 BuildCatalogKey(parentID, &fName, isHFSPlus, &catKey);
5745 err = SearchBTreeRecord(GPtr->calculatedCatalogFCB, &catKey, kNoHint, NULL,
5746 &catRecord, &recordSize, &hint);
5747 if (err != fsBTRecordNotFoundErr) {
5748 #if DEBUG_OVERLAP
5749 plog ("%s: %s probably exists in dirID = %d (err=%d)\n", __FUNCTION__, fileName, parentID, err);
5750 #endif
5751 err = EEXIST;
5752 goto out;
5753 }
5754
5755 if (data) {
5756 /* Calculate correct number of blocks required for data */
5757 if (dataLen % (GPtr->calculatedVCB->vcbBlockSize)) {
5758 blockCount = (dataLen / (GPtr->calculatedVCB->vcbBlockSize)) + 1;
5759 } else {
5760 blockCount = dataLen / (GPtr->calculatedVCB->vcbBlockSize);
5761 }
5762
5763 if (blockCount) {
5764 /* Allocate disk space for the data */
5765 err = AllocateContigBitmapBits (GPtr->calculatedVCB, blockCount, &startBlock);
5766 if (err != noErr) {
5767 #if DEBUG_OVERLAP
5768 plog ("%s: Not enough disk space (err=%d)\n", __FUNCTION__, err);
5769 #endif
5770 goto out;
5771 }
5772
5773 /* Write the data to the blocks */
5774 err = WriteBufferToDisk(GPtr, startBlock, blockCount, data, dataLen);
5775 if (err != noErr) {
5776 #if DEBUG_OVERLAP
5777 plog ("%s: Error in writing data of %s to disk (err=%d)\n", __FUNCTION__, fileName, err);
5778 #endif
5779 goto out;
5780 }
5781 }
5782 }
5783
5784 /* Build and insert thread record */
5785 nextCNID = GPtr->calculatedVCB->vcbNextCatalogID;
5786 if (!isHFSPlus && nextCNID == 0xffffFFFF) {
5787 goto out;
5788 }
5789 recordSize = BuildThreadRec(&catKey, &catRecord, isHFSPlus, false);
5790 for (;;) {
5791 BuildCatalogKey(nextCNID, NULL, isHFSPlus, &threadKey);
5792 err = InsertBTreeRecord(GPtr->calculatedCatalogFCB, &threadKey, &catRecord,
5793 recordSize, &hint );
5794 if (err == fsBTDuplicateRecordErr && isHFSPlus) {
5795 /* Allow CNIDs on HFS Plus volumes to wrap around */
5796 ++nextCNID;
5797 if (nextCNID < kHFSFirstUserCatalogNodeID) {
5798 GPtr->calculatedVCB->vcbAttributes |= kHFSCatalogNodeIDsReusedMask;
5799 MarkVCBDirty(GPtr->calculatedVCB);
5800 nextCNID = kHFSFirstUserCatalogNodeID;
5801 }
5802 continue;
5803 }
5804 break;
5805 }
5806 if (err != noErr) {
5807 #if DEBUG_OVERLAP
5808 plog ("%s: Error inserting thread record for file = %s (err=%d)\n", __FUNCTION__, fileName, err);
5809 #endif
5810 goto out;
5811 }
5812
5813 /* Build file record */
5814 recordSize = BuildFileRec(fileType, 0666, nextCNID, isHFSPlus, &catRecord);
5815 if (recordSize == 0) {
5816 #if DEBUG_OVERLAP
5817 plog ("%s: Incorrect fileType\n", __FUNCTION__);
5818 #endif
5819
5820 /* Remove the thread record inserted above */
5821 err = DeleteBTreeRecord (GPtr->calculatedCatalogFCB, &threadKey);
5822 if (err != noErr) {
5823 #if DEBUG_OVERLAP
5824 plog ("%s: Error in removing thread record\n", __FUNCTION__);
5825 #endif
5826 }
5827 err = paramErr;
5828 goto out;
5829 }
5830
5831 /* Update startBlock, blockCount, etc */
5832 if (isHFSPlus) {
5833 catRecord.hfsPlusFile.dataFork.logicalSize = dataLen;
5834 catRecord.hfsPlusFile.dataFork.totalBlocks = blockCount;
5835 catRecord.hfsPlusFile.dataFork.extents[0].startBlock = startBlock;
5836 catRecord.hfsPlusFile.dataFork.extents[0].blockCount = blockCount;
5837 } else {
5838 catRecord.hfsFile.dataLogicalSize = dataLen;
5839 catRecord.hfsFile.dataPhysicalSize = blockCount * GPtr->calculatedVCB->vcbBlockSize;
5840 catRecord.hfsFile.dataExtents[0].startBlock = startBlock;
5841 catRecord.hfsFile.dataExtents[0].blockCount = blockCount;
5842 }
5843
5844 /* Insert catalog file record */
5845 err = InsertBTreeRecord(GPtr->calculatedCatalogFCB, &catKey, &catRecord, recordSize, &hint );
5846 if (err == noErr) {
5847 isCatUpdated = true;
5848
5849 #if DEBUG_OVERLAP
5850 plog ("Created \"%s\" with ID = %d startBlock = %d, blockCount = %d, dataLen = %d\n", fileName, nextCNID, startBlock, blockCount, dataLen);
5851 #endif
5852 } else {
5853 #if DEBUG_OVERLAP
5854 plog ("%s: Error in inserting file record for file = %s (err=%d)\n", __FUNCTION__, fileName, err);
5855 #endif
5856
5857 /* remove the thread record inserted above */
5858 err = DeleteBTreeRecord (GPtr->calculatedCatalogFCB, &threadKey);
5859 if (err != noErr) {
5860 #if DEBUG_OVERLAP
5861 plog ("%s: Error in removing thread record\n", __FUNCTION__);
5862 #endif
5863 }
5864 err = paramErr;
5865 goto out;
5866 }
5867
5868 /* Update volume header */
5869 GPtr->calculatedVCB->vcbNextCatalogID = nextCNID + 1;
5870 if (GPtr->calculatedVCB->vcbNextCatalogID < kHFSFirstUserCatalogNodeID) {
5871 GPtr->calculatedVCB->vcbAttributes |= kHFSCatalogNodeIDsReusedMask;
5872 GPtr->calculatedVCB->vcbNextCatalogID = kHFSFirstUserCatalogNodeID;
5873 }
5874 MarkVCBDirty( GPtr->calculatedVCB );
5875
5876 /* update our header node on disk from our BTreeControlBlock */
5877 UpdateBTreeHeader(GPtr->calculatedCatalogFCB);
5878
5879 /* update parent directory to reflect addition of new file */
5880 err = UpdateFolderCount(GPtr->calculatedVCB, parentID, NULL, kHFSPlusFileRecord, kNoHint, 1);
5881 if (err != noErr) {
5882 #if DEBUG_OVERLAP
5883 plog ("%s: Error in updating parent folder count (err=%d)\n", __FUNCTION__, err);
5884 #endif
5885 goto out;
5886 }
5887
5888 out:
5889 /* On error, if catalog record was not inserted and disk block were allocated,
5890 * deallocate the blocks
5891 */
5892 if (err && (isCatUpdated == false) && startBlock) {
5893 ReleaseBitmapBits (startBlock, blockCount);
5894 }
5895
5896 return err;
5897 } /* CreateFileByName */
5898
5899 /* Function: CreateDirByName
5900 *
5901 * Description: Create directory with name dirName in a directory with ID as
5902 * parentID. The function assumes that the dirName passed is ASCII.
5903 *
5904 * Input:
5905 * GPtr - global scavenger structure pointer
5906 * dirName - name of directory to be created
5907 * parentID - dirID of the parent directory for new directory
5908 *
5909 * Output:
5910 * on success, ID of the new directory created.
5911 * on failure, zero.
5912 *
5913 */
5914 UInt32 CreateDirByName(SGlob *GPtr , const u_char *dirName, const UInt32 parentID)
5915 {
5916 Boolean isHFSPlus;
5917 UInt16 recSize;
5918 UInt16 myMode;
5919 int result;
5920 int nameLen;
5921 UInt32 hint;
5922 UInt32 nextCNID;
5923 SFCB * fcbPtr;
5924 CatalogKey myKey;
5925 CatalogName myName;
5926 CatalogRecord catRec;
5927
5928 isHFSPlus = VolumeObjectIsHFSPlus( );
5929 fcbPtr = GPtr->calculatedCatalogFCB;
5930 nameLen = strlen( (char *)dirName );
5931
5932 if ( isHFSPlus )
5933 {
5934 int i;
5935 myName.ustr.length = nameLen;
5936 for ( i = 0; i < myName.ustr.length; i++ )
5937 myName.ustr.unicode[ i ] = (u_int16_t) dirName[ i ];
5938 }
5939 else
5940 {
5941 myName.pstr[0] = nameLen;
5942 memcpy( &myName.pstr[1], &dirName[0], nameLen );
5943 }
5944
5945 // see if we already have a lost and found directory
5946 BuildCatalogKey( parentID, &myName, isHFSPlus, &myKey );
5947 result = SearchBTreeRecord( fcbPtr, &myKey, kNoHint, NULL, &catRec, &recSize, &hint );
5948 if ( result == noErr ) {
5949 if ( isHFSPlus ) {
5950 if ( catRec.recordType == kHFSPlusFolderRecord )
5951 return( catRec.hfsPlusFolder.folderID );
5952 }
5953 else if ( catRec.recordType == kHFSFolderRecord )
5954 return( catRec.hfsFolder.folderID );
5955 return( 0 ); // something already there but not a directory
5956 }
5957
5958 // insert new directory and thread record into the catalog
5959 nextCNID = GPtr->calculatedVCB->vcbNextCatalogID;
5960 if ( !isHFSPlus && nextCNID == 0xFFFFFFFF )
5961 return( 0 );
5962
5963 recSize = BuildThreadRec( &myKey, &catRec, isHFSPlus, true );
5964 for (;;) {
5965 CatalogKey key;
5966
5967 BuildCatalogKey( nextCNID, NULL, isHFSPlus, &key );
5968 result = InsertBTreeRecord( fcbPtr, &key, &catRec, recSize, &hint );
5969 if ( result == fsBTDuplicateRecordErr && isHFSPlus ) {
5970 /*
5971 * Allow CNIDs on HFS Plus volumes to wrap around
5972 */
5973 ++nextCNID;
5974 if ( nextCNID < kHFSFirstUserCatalogNodeID ) {
5975 GPtr->calculatedVCB->vcbAttributes |= kHFSCatalogNodeIDsReusedMask;
5976 MarkVCBDirty( GPtr->calculatedVCB );
5977 nextCNID = kHFSFirstUserCatalogNodeID;
5978 }
5979 continue;
5980 }
5981 break;
5982 }
5983 if ( result != 0 )
5984 return( 0 );
5985
5986 myMode = ( GPtr->lostAndFoundMode == 0 ) ? 01777 : GPtr->lostAndFoundMode;
5987 recSize = BuildFolderRec( GPtr, myMode, nextCNID, isHFSPlus, &catRec );
5988 result = InsertBTreeRecord( fcbPtr, &myKey, &catRec, recSize, &hint );
5989 if ( result != 0 )
5990 return( 0 );
5991
5992 /* Update volume header */
5993 GPtr->calculatedVCB->vcbNextCatalogID = nextCNID + 1;
5994 if ( GPtr->calculatedVCB->vcbNextCatalogID < kHFSFirstUserCatalogNodeID ) {
5995 GPtr->calculatedVCB->vcbAttributes |= kHFSCatalogNodeIDsReusedMask;
5996 GPtr->calculatedVCB->vcbNextCatalogID = kHFSFirstUserCatalogNodeID;
5997 }
5998 MarkVCBDirty( GPtr->calculatedVCB );
5999
6000 /* update parent directory to reflect addition of new directory */
6001 result = UpdateFolderCount( GPtr->calculatedVCB, parentID, NULL, kHFSPlusFolderRecord, kNoHint, 1 );
6002
6003 /* update our header node on disk from our BTreeControlBlock */
6004 UpdateBTreeHeader( GPtr->calculatedCatalogFCB );
6005
6006 return( nextCNID );
6007
6008 } /* CreateDirByName */
6009
6010 static void
6011 CountFolderItems(SGlobPtr GPtr, UInt32 folderID, Boolean isHFSPlus, UInt32 *itemCount, UInt32 *folderCount)
6012 {
6013 SFCB *fcb = GPtr->calculatedCatalogFCB;
6014 OSErr err = 0;
6015 BTreeIterator iterator;
6016 FSBufferDescriptor btRecord;
6017 union {
6018 HFSPlusCatalogFolder catRecord;
6019 HFSPlusCatalogFile catFile;
6020 } catRecord;
6021 HFSPlusCatalogKey *key;
6022 UInt16 recordSize = 0;
6023 int fCount = 0, iCount = 0;
6024
6025 ClearMemory(&iterator, sizeof(iterator));
6026 key = (HFSPlusCatalogKey*)&iterator.key;
6027 BuildCatalogKey(folderID, NULL, isHFSPlus, (CatalogKey*)key);
6028 btRecord.bufferAddress = &catRecord;
6029 btRecord.itemCount = 1;
6030 btRecord.itemSize = sizeof(catRecord);
6031
6032 for (err = BTSearchRecord(fcb, &iterator, kNoHint, &btRecord, &recordSize, &iterator);
6033 err == 0;
6034 err = BTIterateRecord(fcb, kBTreeNextRecord, &iterator, &btRecord, &recordSize)) {
6035 if (catRecord.catRecord.recordType == kHFSPlusFolderThreadRecord ||
6036 catRecord.catRecord.recordType == kHFSPlusFileThreadRecord ||
6037 catRecord.catRecord.recordType == kHFSFolderThreadRecord ||
6038 catRecord.catRecord.recordType == kHFSFileThreadRecord)
6039 continue;
6040 if (key->parentID != folderID)
6041 break;
6042 if (isHFSPlus &&
6043 (catRecord.catRecord.recordType == kHFSPlusFileRecord) &&
6044 (catRecord.catFile.flags & kHFSHasLinkChainMask) &&
6045 (catRecord.catFile.userInfo.fdType == kHFSAliasType) &&
6046 (catRecord.catFile.userInfo.fdCreator == kHFSAliasCreator) &&
6047 (key->parentID != GPtr->filelink_priv_dir_id)) {
6048 // It's a directory hard link, which counts as a directory here
6049 fCount++;
6050 }
6051 if (catRecord.catRecord.recordType == kHFSPlusFolderRecord)
6052 fCount++;
6053 iCount++;
6054 }
6055 if (itemCount)
6056 *itemCount = iCount;
6057 if (folderCount)
6058 *folderCount = fCount;
6059 return;
6060 }
6061 /*
6062 * Build a catalog node folder record with the given input.
6063 */
6064 static int
6065 BuildFolderRec( SGlob *GPtr, u_int16_t theMode, UInt32 theObjID, Boolean isHFSPlus, CatalogRecord * theRecPtr )
6066 {
6067 UInt16 recSize;
6068 UInt32 createTime;
6069 UInt32 vCount = 0, fCount = 0;
6070
6071 ClearMemory( (Ptr)theRecPtr, sizeof(*theRecPtr) );
6072
6073 CountFolderItems(GPtr, theObjID, isHFSPlus, &vCount, &fCount);
6074 if ( isHFSPlus ) {
6075 createTime = GetTimeUTC();
6076 theRecPtr->hfsPlusFolder.recordType = kHFSPlusFolderRecord;
6077 theRecPtr->hfsPlusFolder.folderID = theObjID;
6078 theRecPtr->hfsPlusFolder.createDate = createTime;
6079 theRecPtr->hfsPlusFolder.contentModDate = createTime;
6080 theRecPtr->hfsPlusFolder.attributeModDate = createTime;
6081 theRecPtr->hfsPlusFolder.bsdInfo.ownerID = getuid( );
6082 theRecPtr->hfsPlusFolder.bsdInfo.groupID = getgid( );
6083 theRecPtr->hfsPlusFolder.bsdInfo.fileMode = S_IFDIR;
6084 theRecPtr->hfsPlusFolder.bsdInfo.fileMode |= theMode;
6085 theRecPtr->hfsPlusFolder.valence = vCount;
6086 recSize= sizeof(HFSPlusCatalogFolder);
6087 if (VolumeObjectIsHFSX(GPtr)) {
6088 theRecPtr->hfsPlusFolder.flags |= kHFSHasFolderCountMask;
6089 theRecPtr->hfsPlusFolder.folderCount = fCount;
6090 }
6091 }
6092 else {
6093 createTime = GetTimeLocal( true );
6094 theRecPtr->hfsFolder.recordType = kHFSFolderRecord;
6095 theRecPtr->hfsFolder.folderID = theObjID;
6096 theRecPtr->hfsFolder.createDate = createTime;
6097 theRecPtr->hfsFolder.modifyDate = createTime;
6098 theRecPtr->hfsFolder.valence = vCount;
6099 recSize= sizeof(HFSCatalogFolder);
6100 }
6101
6102 return( recSize );
6103
6104 } /* BuildFolderRec */
6105
6106
6107 /*
6108 * Build a catalog node thread record from a catalog key
6109 * and return the size of the record.
6110 */
6111 static int
6112 BuildThreadRec( CatalogKey * theKeyPtr, CatalogRecord * theRecPtr,
6113 Boolean isHFSPlus, Boolean isDirectory )
6114 {
6115 int size = 0;
6116
6117 if ( isHFSPlus ) {
6118 HFSPlusCatalogKey *key = (HFSPlusCatalogKey *)theKeyPtr;
6119 HFSPlusCatalogThread *rec = (HFSPlusCatalogThread *)theRecPtr;
6120
6121 size = sizeof(HFSPlusCatalogThread);
6122 if ( isDirectory )
6123 rec->recordType = kHFSPlusFolderThreadRecord;
6124 else
6125 rec->recordType = kHFSPlusFileThreadRecord;
6126 rec->reserved = 0;
6127 rec->parentID = key->parentID;
6128 bcopy(&key->nodeName, &rec->nodeName,
6129 sizeof(UniChar) * (key->nodeName.length + 1));
6130
6131 /* HFS Plus has varaible sized thread records */
6132 size -= (sizeof(rec->nodeName.unicode) -
6133 (rec->nodeName.length * sizeof(UniChar)));
6134 }
6135 else /* HFS standard */ {
6136 HFSCatalogKey *key = (HFSCatalogKey *)theKeyPtr;
6137 HFSCatalogThread *rec = (HFSCatalogThread *)theRecPtr;
6138
6139 size = sizeof(HFSCatalogThread);
6140 bzero(rec, size);
6141 if ( isDirectory )
6142 rec->recordType = kHFSFolderThreadRecord;
6143 else
6144 rec->recordType = kHFSFileThreadRecord;
6145 rec->parentID = key->parentID;
6146 bcopy(key->nodeName, rec->nodeName, key->nodeName[0]+1);
6147 }
6148
6149 return (size);
6150
6151 } /* BuildThreadRec */
6152
6153 /* Function: BuildFileRec
6154 *
6155 * Description: Build a catalog file record with given fileID, fileType
6156 * and fileMode.
6157 *
6158 * Input:
6159 * 1. fileType - currently supports S_IFREG, S_IFLNK
6160 * 2. fileMode - file mode desired.
6161 * 3. fileID - file ID
6162 * 4. isHFSPlus - indicates whether the record is being created for
6163 * HFSPlus volume or plain HFS volume.
6164 * 5. catRecord - pointer to catalog record
6165 *
6166 * Output:
6167 * returns size of the catalog record.
6168 * on success, non-zero value; on failure, zero.
6169 */
6170 static int BuildFileRec(UInt16 fileType, UInt16 fileMode, UInt32 fileID, Boolean isHFSPlus, CatalogRecord *catRecord)
6171 {
6172 UInt16 recordSize = 0;
6173 UInt32 createTime;
6174
6175 /* We only support creating S_IFREG and S_IFLNK and S_IFLNK is not supported
6176 * on plain HFS
6177 */
6178 if (((fileType != S_IFREG) && (fileType != S_IFLNK)) ||
6179 ((isHFSPlus == false) && (fileType == S_IFLNK))) {
6180 goto out;
6181 }
6182
6183 ClearMemory((Ptr)catRecord, sizeof(*catRecord));
6184
6185 if ( isHFSPlus ) {
6186 createTime = GetTimeUTC();
6187 catRecord->hfsPlusFile.recordType = kHFSPlusFileRecord;
6188 catRecord->hfsPlusFile.fileID = fileID;
6189 catRecord->hfsPlusFile.createDate = createTime;
6190 catRecord->hfsPlusFile.contentModDate = createTime;
6191 catRecord->hfsPlusFile.attributeModDate = createTime;
6192 catRecord->hfsPlusFile.bsdInfo.ownerID = getuid();
6193 catRecord->hfsPlusFile.bsdInfo.groupID = getgid();
6194 catRecord->hfsPlusFile.bsdInfo.fileMode = fileType;
6195 catRecord->hfsPlusFile.bsdInfo.fileMode |= fileMode;
6196 if (fileType == S_IFLNK) {
6197 catRecord->hfsPlusFile.userInfo.fdType = kSymLinkFileType;
6198 catRecord->hfsPlusFile.userInfo.fdCreator = kSymLinkCreator;
6199 } else {
6200 catRecord->hfsPlusFile.userInfo.fdType = kTextFileType;
6201 catRecord->hfsPlusFile.userInfo.fdCreator = kTextFileCreator;
6202 }
6203 recordSize= sizeof(HFSPlusCatalogFile);
6204 }
6205 else {
6206 createTime = GetTimeLocal(true);
6207 catRecord->hfsFile.recordType = kHFSFileRecord;
6208 catRecord->hfsFile.fileID = fileID;
6209 catRecord->hfsFile.createDate = createTime;
6210 catRecord->hfsFile.modifyDate = createTime;
6211 catRecord->hfsFile.userInfo.fdType = kTextFileType;
6212 catRecord->hfsFile.userInfo.fdCreator = kTextFileCreator;
6213 recordSize= sizeof(HFSCatalogFile);
6214 }
6215
6216 out:
6217 return(recordSize);
6218 } /* BuildFileRec */
6219
6220 /* Function: BuildAttributeKey
6221 *
6222 * Build attribute key based on given information like -
6223 * fileID, startBlock, attribute name and attribute name length.
6224 *
6225 * Note that the attribute name is the UTF-8 format string.
6226 */
6227 static void BuildAttributeKey(u_int32_t fileID, u_int32_t startBlock,
6228 unsigned char *attrName, u_int16_t attrNameLen, HFSPlusAttrKey *key)
6229 {
6230 size_t attrNameLenBytes;
6231
6232 assert(VolumeObjectIsHFSPlus() == true);
6233
6234 key->pad = 0;
6235 key->fileID = fileID;
6236 key->startBlock = startBlock;
6237
6238 /* Convert UTF-8 attribute name to unicode */
6239 (void) utf_decodestr(attrName, attrNameLen, key->attrName, &attrNameLenBytes, sizeof(key->attrName));
6240 key->attrNameLen = attrNameLenBytes / 2;
6241
6242 key->keyLength = kHFSPlusAttrKeyMinimumLength + attrNameLenBytes;
6243 }
6244
6245 /* Delete catalog record and thread record for given ID. On successful
6246 * deletion, this function also updates the valence and folder count for
6247 * the parent directory and the file/folder count in the volume header.
6248 *
6249 * Returns - zero on success, non-zero on failure.
6250 */
6251 static int DeleteCatalogRecordByID(SGlobPtr GPtr, uint32_t id, Boolean for_rename)
6252 {
6253 int retval;
6254 CatalogRecord rec;
6255 CatalogKey key;
6256 UInt16 recsize;
6257 Boolean isHFSPlus;
6258
6259 isHFSPlus = VolumeObjectIsHFSPlus();
6260
6261 /* Lookup the catalog record to move */
6262 retval = GetCatalogRecordByID(GPtr, id, isHFSPlus, &key, &rec, &recsize);
6263 if (retval) {
6264 return retval;
6265 }
6266
6267 /* Delete the record */
6268 if (isHFSPlus) {
6269 retval = DeleteCatalogNode(GPtr->calculatedVCB,
6270 key.hfsPlus.parentID,
6271 (const CatalogName *)&key.hfsPlus.nodeName,
6272 kNoHint, for_rename);
6273 } else {
6274 retval = DeleteCatalogNode(GPtr->calculatedVCB,
6275 key.hfs.parentID,
6276 (const CatalogName *)&key.hfs.nodeName,
6277 kNoHint, for_rename);
6278 }
6279
6280 /* If deletion of record succeeded, and the operation was not
6281 * being performed for rename, and the volume is HFS+, try
6282 * deleting all extended attributes for this file/folder
6283 */
6284 if ((retval == 0) && (for_rename == false) && (isHFSPlus == true)) {
6285 /* Delete all attributes associated with this ID */
6286 retval = DeleteAllAttrsByID(GPtr, id);
6287 }
6288
6289 return retval;
6290 }
6291
6292 /* Move a catalog record with given ID to a new parent directory with given
6293 * parentID. This function should only be called for HFS+ volumes.
6294 * This function removes the catalog record from old parent and inserts
6295 * it back with the new parent ID. It also takes care of updating the
6296 * parent directory counts. Note that this function clears kHFSHasLinkChainBit
6297 * from the catalog record flags.
6298 *
6299 * On success, returns zero. On failure, returns non-zero.
6300 */
6301 static int MoveCatalogRecordByID(SGlobPtr GPtr, uint32_t id, uint32_t new_parentid)
6302 {
6303 int retval;
6304 CatalogRecord rec;
6305 CatalogKey key;
6306 UInt32 hint;
6307 UInt16 recsize;
6308 Boolean isFolder = false;
6309 BTreeIterator iterator;
6310
6311 assert (VolumeObjectIsHFSPlus() == true);
6312
6313 /* Lookup the catalog record to move */
6314 retval = GetCatalogRecordByID(GPtr, id, true, &key, &rec, &recsize);
6315 if (retval) {
6316 goto out;
6317 }
6318
6319 /* Delete the record and its thread from original location.
6320 * For file records, do not deallocate original extents.
6321 */
6322 retval = DeleteCatalogRecordByID(GPtr, id, true);
6323 if (retval) {
6324 goto out;
6325 }
6326
6327 key.hfsPlus.parentID = new_parentid;
6328 /* The record being moved should not have linkChainMask set */
6329 if (rec.recordType == kHFSPlusFolderRecord) {
6330 rec.hfsPlusFolder.flags &= ~kHFSHasLinkChainMask;
6331 isFolder = true;
6332 } else if (rec.recordType == kHFSPlusFileRecord) {
6333 rec.hfsPlusFile.flags &= ~kHFSHasLinkChainMask;
6334 isFolder = false;
6335 }
6336
6337 /* Insert the catalog record with new parent */
6338 retval = InsertBTreeRecord(GPtr->calculatedCatalogFCB, &key, &rec,
6339 recsize, &hint);
6340 if (retval) {
6341 goto out;
6342 }
6343
6344 /* Insert the new thread record */
6345 recsize = BuildThreadRec(&key, &rec, true, isFolder);
6346 BuildCatalogKey(id, NULL, true, &key);
6347 retval = InsertBTreeRecord(GPtr->calculatedCatalogFCB, &key, &rec,
6348 recsize, &hint);
6349 if (retval) {
6350 goto out;
6351 }
6352
6353 /* Update the counts in the new parent directory and volume header */
6354 ClearMemory(&iterator, sizeof(iterator));
6355 retval = GetCatalogRecordByID(GPtr, new_parentid, true, &key, &rec, &recsize);
6356 if (retval) {
6357 if ((retval == btNotFound) && (GPtr->CBTStat & S_Orphan)) {
6358 /* No need for re-repair minor repair order because
6359 * we are failing on updating the parent directory.
6360 */
6361 retval = 0;
6362 }
6363 goto out;
6364 }
6365 if (rec.recordType != kHFSPlusFolderRecord) {
6366 goto out;
6367 }
6368
6369 rec.hfsPlusFolder.valence++;
6370 if ((isFolder == true) &&
6371 (rec.hfsPlusFolder.flags & kHFSHasFolderCountMask)) {
6372 rec.hfsPlusFolder.folderCount++;
6373 }
6374
6375 retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key,
6376 kNoHint, &rec, recsize, &hint);
6377 if (retval) {
6378 goto out;
6379 }
6380
6381 if (isFolder == true) {
6382 GPtr->calculatedVCB->vcbFolderCount++;
6383 } else {
6384 GPtr->calculatedVCB->vcbFileCount++;
6385 }
6386 GPtr->VIStat |= S_MDB;
6387 GPtr->CBTStat |= S_BTH; /* leaf record count changed */
6388
6389 out:
6390 return retval;
6391 }
6392
6393 /* The function deletes all extended attributes associated with a given
6394 * file/folder ID. The function takes care of deallocating allocation blocks
6395 * associated with extent based attributes.
6396 *
6397 * Note: This function deletes *all* attributes for a given file/folder.
6398 * To delete a single attribute record using a key, use delete_attr_record().
6399 *
6400 * On success, returns zero. On failure, returns non-zero.
6401 */
6402 static int DeleteAllAttrsByID(SGlobPtr GPtr, uint32_t id)
6403 {
6404 int retval;
6405 BTreeIterator iterator;
6406 FSBufferDescriptor btrec;
6407 HFSPlusAttrKey *attr_key;
6408 HFSPlusAttrRecord attr_record;
6409 UInt16 record_size;
6410
6411 /* Initialize the iterator, attribute key, and attribute record */
6412 ClearMemory(&iterator, sizeof(BTreeIterator));
6413 attr_key = (HFSPlusAttrKey *)&iterator.key;
6414 attr_key->keyLength = kHFSPlusAttrKeyMinimumLength;
6415 attr_key->fileID = id;
6416
6417 ClearMemory(&btrec, sizeof(FSBufferDescriptor));
6418 btrec.bufferAddress = &attr_record;
6419 btrec.itemCount = 1;
6420 btrec.itemSize = sizeof(HFSPlusAttrRecord);
6421
6422 /* Search for attribute with NULL name which will place the
6423 * iterator just before the first record for given id.
6424 */
6425 retval = BTSearchRecord(GPtr->calculatedAttributesFCB, &iterator,
6426 kInvalidMRUCacheKey, &btrec, &record_size, &iterator);
6427 if ((retval != 0) && (retval != btNotFound)) {
6428 goto out;
6429 }
6430
6431 retval = BTIterateRecord(GPtr->calculatedAttributesFCB, kBTreeNextRecord,
6432 &iterator, &btrec, &record_size);
6433 while ((retval == 0) && (attr_key->fileID == id)) {
6434 /* Delete attribute record and deallocate extents, if any */
6435 retval = delete_attr_record(GPtr, attr_key, &attr_record);
6436 if (retval) {
6437 break;
6438 }
6439
6440 retval = BTIterateRecord(GPtr->calculatedAttributesFCB,
6441 kBTreeNextRecord, &iterator, &btrec, &record_size);
6442 }
6443
6444 if (retval == btNotFound) {
6445 retval = 0;
6446 }
6447
6448 out:
6449 return retval;
6450 }
6451
6452 /* The function deletes an extented attribute record when the corresponding
6453 * record and key are provided. If the record is an extent-based attribute,
6454 * it also takes care to deallocate all allocation blocks associated with
6455 * the record.
6456 *
6457 * Note: This function does not delete all attribute records associated
6458 * with the file/folder ID in the attribute key. To delete all attributes
6459 * for given file/folder ID, use DeleteAllAttrsByID().
6460 *
6461 * On success, returns zero. On failure, returns non-zero.
6462 */
6463 static int delete_attr_record(SGlobPtr GPtr, HFSPlusAttrKey *attr_key, HFSPlusAttrRecord *attr_record)
6464 {
6465 int retval;
6466 UInt32 num_blocks_freed;
6467 Boolean last_extent;
6468
6469 retval = DeleteBTreeRecord(GPtr->calculatedAttributesFCB, attr_key);
6470 if (retval == 0) {
6471 /* Set bits to write back attribute btree header and map */
6472 GPtr->ABTStat |= S_BTH + S_BTM;
6473
6474 if (attr_record->recordType == kHFSPlusAttrForkData) {
6475 retval = ReleaseExtents(GPtr->calculatedVCB,
6476 attr_record->forkData.theFork.extents,
6477 &num_blocks_freed, &last_extent);
6478 } else if (attr_record->recordType == kHFSPlusAttrExtents) {
6479 retval = ReleaseExtents(GPtr->calculatedVCB,
6480 attr_record->overflowExtents.extents,
6481 &num_blocks_freed, &last_extent);
6482 }
6483 }
6484
6485 return retval;
6486 }
6487
6488 /*------------------------------------------------------------------------------
6489
6490 Routine: ZeroFillUnusedNodes
6491
6492 Function: Write zeroes to all unused nodes of a given B-tree.
6493
6494 Input: GPtr - pointer to scavenger global area
6495 fileRefNum - refnum of BTree file
6496
6497 Output: ZeroFillUnusedNodes - function result:
6498 0 = no error
6499 n = error
6500 ------------------------------------------------------------------------------*/
6501
6502 static int ZeroFillUnusedNodes(SGlobPtr GPtr, short fileRefNum)
6503 {
6504 BTreeControlBlock *btcb = GetBTreeControlBlock(fileRefNum);
6505 unsigned char *bitmap = (unsigned char *) ((BTreeExtensionsRec*)btcb->refCon)->BTCBMPtr;
6506 unsigned char mask = 0x80;
6507 OSErr err;
6508 UInt32 nodeNum;
6509 BlockDescriptor node;
6510
6511 node.buffer = NULL;
6512
6513 for (nodeNum = 0; nodeNum < btcb->totalNodes; ++nodeNum)
6514 {
6515 if ((*bitmap & mask) == 0)
6516 {
6517 /* Read the raw node, without going through hfs_swap_BTNode. */
6518 err = btcb->getBlockProc(btcb->fcbPtr, nodeNum, kGetBlock|kGetEmptyBlock, &node);
6519 if (err)
6520 {
6521 if (debug) plog("Couldn't read node #%u\n", nodeNum);
6522 return err;
6523 }
6524
6525 /* Fill the node with zeroes. */
6526 bzero(node.buffer, node.blockSize);
6527
6528 /* Release and write the node without going through hfs_swap_BTNode. */
6529 (void) btcb->releaseBlockProc(btcb->fcbPtr, &node, kReleaseBlock|kMarkBlockDirty);
6530 node.buffer = NULL;
6531 }
6532
6533 /* Move to the next bit in the bitmap. */
6534 mask >>= 1;
6535 if (mask == 0)
6536 {
6537 mask = 0x80;
6538 ++bitmap;
6539 }
6540 }
6541
6542 return 0;
6543 } /* end ZeroFillUnusedNodes */