OSErr FlushCatalog(ExtendedVCB *volume) { FCB * fcb; OSErr result; fcb = GetFileControlBlock(volume->catalogRefNum); result = BTFlushPath(fcb); if (result == noErr) { //--- check if catalog's fcb is dirty... if ( 0 /*fcb->fcbFlags & fcbModifiedMask*/ ) { HFS_MOUNT_LOCK(volume, TRUE); MarkVCBDirty(volume); // Mark the VCB dirty volume->vcbLsMod = GetTimeUTC(); // update last modified date HFS_MOUNT_UNLOCK(volume, TRUE); // result = FlushVolumeControlBlock(volume); } } return result; }
OSErr ReplaceBTreeRecord(FileReference refNum, const void* key, u_int32_t hint, void *newData, u_int16_t dataSize, u_int32_t *newHint) { FSBufferDescriptor btRecord; struct BTreeIterator *iterator = NULL; FCB *fcb; BTreeControlBlock *btcb; OSStatus result; MALLOC (iterator, struct BTreeIterator *, sizeof (struct BTreeIterator), M_TEMP, M_WAITOK); if (iterator == NULL) { return memFullErr; //translates to ENOMEM } bzero (iterator, sizeof (*iterator)); fcb = GetFileControlBlock(refNum); btcb = (BTreeControlBlock*) fcb->fcbBTCBPtr; btRecord.bufferAddress = newData; btRecord.itemSize = dataSize; btRecord.itemCount = 1; iterator->hint.nodeNum = hint; result = CheckBTreeKey((const BTreeKey *) key, btcb); if (result) { goto ErrorExit; } BlockMoveData(key, &iterator->key, CalcKeySize(btcb, (const BTreeKey *) key)); //€€ should we range check against maxkeylen? if ( DEBUG_BUILD && !ValidHFSRecord(newData, btcb, dataSize) ) DebugStr("ReplaceBTreeRecord: bad record?"); result = BTReplaceRecord( fcb, iterator, &btRecord, dataSize ); *newHint = iterator->hint.nodeNum; ErrorExit: FREE (iterator, M_TEMP); return result; }
//-- Delete all extents in extent file that have the ID given. static OSErr DeleteExtents( ExtendedVCB *vcb, u_int32_t fileID, Boolean isHFSPlus ) { FCB * fcb; ExtentKey * extentKeyPtr; ExtentRecord extentData; BTreeIterator btIterator; FSBufferDescriptor btRecord; u_int16_t btRecordSize; OSErr err; fcb = GetFileControlBlock(vcb->extentsRefNum); (void) BTInvalidateHint(&btIterator); extentKeyPtr = (ExtentKey*) &btIterator.key; btRecord.bufferAddress = &extentData; btRecord.itemCount = 1; // The algorithm is to position the BTree just before any extent records for fileID. // Then just keep getting successive records. If the record is still for fileID, // then delete it. if (isHFSPlus) { btRecord.itemSize = sizeof(HFSPlusExtentRecord); extentKeyPtr->hfsPlus.keyLength = kHFSPlusExtentKeyMaximumLength; extentKeyPtr->hfsPlus.forkType = 0; extentKeyPtr->hfsPlus.pad = 0; extentKeyPtr->hfsPlus.fileID = fileID; extentKeyPtr->hfsPlus.startBlock = 0; } else { btRecord.itemSize = sizeof(HFSExtentRecord); extentKeyPtr->hfs.keyLength = kHFSExtentKeyMaximumLength; extentKeyPtr->hfs.forkType = 0; extentKeyPtr->hfs.fileID = fileID; extentKeyPtr->hfs.startBlock = 0; } err = BTSearchRecord(fcb, &btIterator, &btRecord, &btRecordSize, &btIterator); if ( err != btNotFound ) { if (err == noErr) { // Did we find a bogus extent record? err = cmBadNews; // Yes, so indicate things are messed up. } return err; // Got some unexpected error, so return it } do { BTreeIterator tmpIterator; HFSCatalogNodeID foundFileID; err = BTIterateRecord(fcb, kBTreeNextRecord, &btIterator, &btRecord, &btRecordSize); if ( err != noErr ) { if (err == btNotFound) // If we hit the end of the BTree err = noErr; // then it's OK break; // We're done now. } foundFileID = isHFSPlus ? extentKeyPtr->hfsPlus.fileID : extentKeyPtr->hfs.fileID; if ( foundFileID != fileID ) break; // numbers don't match, we must be done tmpIterator = btIterator; err = BTDeleteRecord( fcb, &tmpIterator ); if (err != noErr) break; } while ( true ); return( err ); }
static OSErr MoveExtents( ExtendedVCB *vcb, u_int32_t srcFileID, u_int32_t destFileID, Boolean isHFSPlus ) { FCB * fcb; ExtentsRecBuffer extentsBuffer[kNumExtentsToCache]; ExtentKey * extentKeyPtr; ExtentRecord extentData; BTreeIterator btIterator; FSBufferDescriptor btRecord; u_int16_t btKeySize; u_int16_t btRecordSize; int16_t i, j; OSErr err; fcb = GetFileControlBlock(vcb->extentsRefNum); (void) BTInvalidateHint(&btIterator); extentKeyPtr = (ExtentKey*) &btIterator.key; btRecord.bufferAddress = &extentData; btRecord.itemCount = 1; //-- Collect the extent records // // A search on the following key will cause the BTree to be positioned immediately // before the first extent record for file #srcFileID, but not actually positioned // on any record. This is because there cannot be an extent record with FABN = 0 // (the first extent of the fork, which would be in the catalog entry, not an extent // record). // // Using BTIterateRecord with kBTreeNextRecord will then get that first extent record. // if (isHFSPlus) { btRecord.itemSize = sizeof(HFSPlusExtentRecord); btKeySize = sizeof(HFSPlusExtentKey); extentKeyPtr->hfsPlus.keyLength = kHFSPlusExtentKeyMaximumLength; extentKeyPtr->hfsPlus.forkType = 0; extentKeyPtr->hfsPlus.pad = 0; extentKeyPtr->hfsPlus.fileID = srcFileID; extentKeyPtr->hfsPlus.startBlock = 0; } else { btRecord.itemSize = sizeof(HFSExtentRecord); btKeySize = sizeof(HFSExtentKey); extentKeyPtr->hfs.keyLength = kHFSExtentKeyMaximumLength; extentKeyPtr->hfs.forkType = 0; extentKeyPtr->hfs.fileID = srcFileID; extentKeyPtr->hfs.startBlock = 0; } // // We do an initial BTSearchRecord to position the BTree's iterator just before any extent // records for srcFileID. We then do a few BTIterateRecord and BTInsertRecord of those found // records, but with destFileID as the file number in the key. Keep doing this sequence of // BTIterateRecord and BTInsertRecord until we find an extent for another file, or there are // no more extent records in the tree. // // Basically, we're copying records kNumExtentsToCache at a time. The copies have their file ID // set to destFileID. // // This depends on BTInsertRecord not effecting the iterator used by BTIterateRecord. If it // _did_ effect the iterator, then we would need to do a BTSearchRecord before each series // of BTIterateRecord. We'd need to set up the key for BTSearchRecord to find the last record // we found, so that BTIterateRecord would get the next one (the first we haven't processed). // err = BTSearchRecord(fcb, &btIterator, &btRecord, &btRecordSize, &btIterator); // We expect a btNotFound here, since there shouldn't be an extent record with FABN = 0. if (err != btNotFound) { if ( DEBUG_BUILD ) DebugStr("Unexpected error from SearchBTreeRecord"); if (err == noErr) // If we found such a bogus extent record, then the tree is really messed up err = cmBadNews; // so return an error that conveys the disk is hosed. return err; } do { btRecord.bufferAddress = &extentData; btRecord.itemCount = 1; for ( i=0 ; i<kNumExtentsToCache ; i++ ) { HFSCatalogNodeID foundFileID; err = BTIterateRecord(fcb, kBTreeNextRecord, &btIterator, &btRecord, &btRecordSize); if ( err == btNotFound ) // Did we run out of extent records in the extents tree? break; // if xkrFNum(A0) is cleared on this error, then this test is bogus! else if ( err != noErr ) return( err ); // must be ioError foundFileID = isHFSPlus ? extentKeyPtr->hfsPlus.fileID : extentKeyPtr->hfs.fileID; if ( foundFileID == srcFileID ) { CopyExtentInfo(extentKeyPtr, &extentData, extentsBuffer, i); } else { break; } } //-- edit each extent key, and reinsert each extent record in the extent file if (isHFSPlus) btRecordSize = sizeof(HFSPlusExtentRecord); else btRecordSize = sizeof(HFSExtentRecord); for ( j=0 ; j<i ; j++ ) { BTreeIterator tmpIterator; if (isHFSPlus) extentsBuffer[j].extentKey.hfsPlus.fileID = destFileID; // change only the id in the key to dest ID else extentsBuffer[j].extentKey.hfs.fileID = destFileID; // change only the id in the key to dest ID // get iterator and buffer descriptor ready... (void) BTInvalidateHint(&tmpIterator); BlockMoveData(&(extentsBuffer[j].extentKey), &tmpIterator.key, btKeySize); btRecord.bufferAddress = &(extentsBuffer[j].extentData); err = BTInsertRecord(fcb, &tmpIterator, &btRecord, btRecordSize); if ( err != noErr ) { // parse the error if ( err == btExists ) { if ( DEBUG_BUILD ) DebugStr("Can't insert record -- already exists"); return( cmBadNews ); } else return( err ); } } //-- okay, done with this buffered batch, go get the next set of extent records // If our buffer is not full, we must be done, or recieved an error if ( i != kNumExtentsToCache ) // if the buffer is not full, we must be done { err = DeleteExtents( vcb, srcFileID, isHFSPlus ); // Now delete all the extent entries with the sourceID if ( DEBUG_BUILD && err != noErr ) DebugStr("Error from DeleteExtents"); break; // we're done! } } while ( true ); return( err ); }
static OSErr MoveExtents( ExtendedVCB *vcb, u_int32_t srcFileID, u_int32_t destFileID, int quitEarly, u_int8_t forkType, Boolean isHFSPlus ) { FCB * fcb; ExtentsRecBuffer extentsBuffer[kNumExtentsToCache]; ExtentKey * extentKeyPtr; ExtentRecord extentData; struct BTreeIterator *btIterator = NULL; struct BTreeIterator *tmpIterator = NULL; FSBufferDescriptor btRecord; u_int16_t btKeySize; u_int16_t btRecordSize; int16_t i, j; OSErr err; MALLOC (btIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK); if (btIterator == NULL) { return memFullErr; // translates to ENOMEM } MALLOC (tmpIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK); if (tmpIterator == NULL) { FREE (btIterator, M_TEMP); return memFullErr; // translates to ENOMEM } bzero(btIterator, sizeof(*btIterator)); bzero (tmpIterator, sizeof(*tmpIterator)); fcb = GetFileControlBlock(vcb->extentsRefNum); (void) BTInvalidateHint(btIterator); extentKeyPtr = (ExtentKey*) &btIterator->key; btRecord.bufferAddress = &extentData; btRecord.itemCount = 1; //-- Collect the extent records // // A search on the following key will cause the BTree to be positioned immediately // before the first extent record for file #srcFileID, but not actually positioned // on any record. This is because there cannot be an extent record with FABN = 0 // (the first extent of the fork, which would be in the catalog entry, not an extent // record). // // Using BTIterateRecord with kBTreeNextRecord will then get that first extent record. // if (isHFSPlus) { btRecord.itemSize = sizeof(HFSPlusExtentRecord); btKeySize = sizeof(HFSPlusExtentKey); extentKeyPtr->hfsPlus.keyLength = kHFSPlusExtentKeyMaximumLength; extentKeyPtr->hfsPlus.forkType = forkType; extentKeyPtr->hfsPlus.pad = 0; extentKeyPtr->hfsPlus.fileID = srcFileID; extentKeyPtr->hfsPlus.startBlock = 0; } #if CONFIG_HFS_STD else { btRecord.itemSize = sizeof(HFSExtentRecord); btKeySize = sizeof(HFSExtentKey); extentKeyPtr->hfs.keyLength = kHFSExtentKeyMaximumLength; extentKeyPtr->hfs.forkType = 0; extentKeyPtr->hfs.fileID = srcFileID; extentKeyPtr->hfs.startBlock = 0; } #else else { return cmBadNews;
OSErr LocateCatalogNodeByKey(const ExtendedVCB *volume, u_int32_t hint, CatalogKey *keyPtr, CatalogRecord *dataPtr, u_int32_t *newHint) { OSErr result; CatalogName *nodeName = NULL; HFSCatalogNodeID threadParentID; u_int16_t tempSize; FSBufferDescriptor btRecord; BTreeIterator searchIterator; FCB *fcb; bzero(&searchIterator, sizeof(searchIterator)); fcb = GetFileControlBlock(volume->catalogRefNum); btRecord.bufferAddress = dataPtr; btRecord.itemCount = 1; btRecord.itemSize = sizeof(CatalogRecord); searchIterator.hint.nodeNum = hint; bcopy(keyPtr, &searchIterator.key, sizeof(CatalogKey)); result = BTSearchRecord( fcb, &searchIterator, &btRecord, &tempSize, &searchIterator ); if (result == noErr) { *newHint = searchIterator.hint.nodeNum; BlockMoveData(&searchIterator.key, keyPtr, sizeof(CatalogKey)); } if (result == btNotFound) result = cmNotFound; ReturnIfError(result); // if we got a thread record, then go look up real record switch ( dataPtr->recordType ) { case kHFSFileThreadRecord: case kHFSFolderThreadRecord: threadParentID = dataPtr->hfsThread.parentID; nodeName = (CatalogName *) &dataPtr->hfsThread.nodeName; break; case kHFSPlusFileThreadRecord: case kHFSPlusFolderThreadRecord: threadParentID = dataPtr->hfsPlusThread.parentID; nodeName = (CatalogName *) &dataPtr->hfsPlusThread.nodeName; break; default: threadParentID = 0; break; } if ( threadParentID ) // found a thread result = LocateCatalogRecord(volume, threadParentID, nodeName, kNoHint, keyPtr, dataPtr, newHint); return result; }