//-- 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 ); }
/* * This is a helper function that counts the total number of valid * extents in all the overflow extent records for given fileID * in overflow extents btree */ static errno_t hfs_count_overflow_extents(struct hfsmount *hfsmp, uint32_t fileID, uint32_t *num_extents) { int error; FCB *fcb; struct BTreeIterator *iterator = NULL; FSBufferDescriptor btdata; HFSPlusExtentKey *extentKey; HFSPlusExtentRecord extentData; uint32_t extent_count = 0; int i; fcb = VTOF(hfsmp->hfs_extents_vp); MALLOC(iterator, struct BTreeIterator *, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK | M_ZERO); extentKey = (HFSPlusExtentKey *) &iterator->key; extentKey->keyLength = kHFSPlusExtentKeyMaximumLength; extentKey->forkType = kHFSDataForkType; extentKey->fileID = fileID; extentKey->startBlock = 0; btdata.bufferAddress = &extentData; btdata.itemSize = sizeof(HFSPlusExtentRecord); btdata.itemCount = 1; /* Search for overflow extent record */ error = BTSearchRecord(fcb, iterator, &btdata, NULL, iterator); /* * We used startBlock of zero, so we will not find any records and errors * are expected. It will also position the iterator just before the first * overflow extent record for given fileID (if any). */ if (error && error != fsBTRecordNotFoundErr && error != fsBTEndOfIterationErr) goto out; error = 0; for (;;) { if (msleep(NULL, NULL, PINOD | PCATCH, "hfs_fsinfo", NULL) == EINTR) { error = EINTR; break; } error = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL); if (error != 0) { /* These are expected errors, so mask them */ if (error == fsBTRecordNotFoundErr || error == fsBTEndOfIterationErr) { error = 0; } break; } /* If we encounter different fileID, stop the iteration */ if (extentKey->fileID != fileID) { break; } if (extentKey->forkType != kHFSDataForkType) break; /* This is our record of interest; only count the datafork extents. */ for (i = 0; i < kHFSPlusExtentDensity; i++) { if (extentData[i].blockCount == 0) { break; } extent_count++; } } out: FREE(iterator, M_TEMP); if (error == 0) { *num_extents = extent_count; } return MacToVFSError(error); }
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 ); }
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; }