#include "../../hfs_macos_defs.h"
#include "../../hfs_format.h"
#include "../headers/FileMgrInternal.h"
#include "../headers/HFSUnicodeWrappers.h"
#include "../headers/CatalogPrivate.h"
struct ExtentsRecBuffer {
ExtentKey extentKey;
ExtentRecord extentData;
};
typedef struct ExtentsRecBuffer ExtentsRecBuffer;
UInt32 CheckExtents( void *extents, UInt32 blocks, Boolean isHFSPlus );
OSErr DeleteExtents( ExtendedVCB *vcb, UInt32 fileNumber, Boolean isHFSPlus );
OSErr MoveExtents( ExtendedVCB *vcb, UInt32 srcFileID, UInt32 destFileID, Boolean isHFSPlus );
void CopyCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest );
void CopyBigCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest );
void CopyExtentInfo( ExtentKey *key, ExtentRecord *data, ExtentsRecBuffer *buffer, UInt16 bufferCount );
OSErr ExchangeFileIDs( ExtendedVCB *vcb, ConstUTF8Param srcName, ConstUTF8Param destName, HFSCatalogNodeID srcID, HFSCatalogNodeID destID, UInt32 srcHint, UInt32 destHint )
{
CatalogKey srcKey; CatalogKey destKey; CatalogRecord srcData; CatalogRecord destData; CatalogRecord swapData; SInt16 numSrcExtentBlocks;
SInt16 numDestExtentBlocks;
OSErr err;
Boolean isHFSPlus = ( vcb->vcbSigWord == kHFSPlusSigWord );
TrashCatalogIterator(vcb, srcID); TrashCatalogIterator(vcb, destID);
err = BuildCatalogKeyUTF8(vcb, srcID, srcName, kUndefinedStrLen, &srcKey, NULL);
ReturnIfError(err);
err = BuildCatalogKeyUTF8(vcb, destID, destName, kUndefinedStrLen, &destKey, NULL);
ReturnIfError(err);
if ( isHFSPlus )
{
err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint );
ReturnIfError( err );
if ( srcData.recordType != kHFSPlusFileRecord )
return( cmFThdDirErr );
numSrcExtentBlocks = CheckExtents( srcData.hfsPlusFile.dataFork.extents, srcData.hfsPlusFile.dataFork.totalBlocks, isHFSPlus );
if ( numSrcExtentBlocks == 0 ) numSrcExtentBlocks = CheckExtents( srcData.hfsPlusFile.resourceFork.extents, srcData.hfsPlusFile.resourceFork.totalBlocks, isHFSPlus );
err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
ReturnIfError( err );
if ( destData.recordType != kHFSPlusFileRecord )
return( cmFThdDirErr );
numDestExtentBlocks = CheckExtents( destData.hfsPlusFile.dataFork.extents, destData.hfsPlusFile.dataFork.totalBlocks, isHFSPlus );
if ( numDestExtentBlocks == 0 ) numDestExtentBlocks = CheckExtents( destData.hfsPlusFile.resourceFork.extents, destData.hfsPlusFile.resourceFork.totalBlocks, isHFSPlus );
err = DeleteExtents( vcb, kHFSBogusExtentFileID, isHFSPlus );
ReturnIfError( err );
if ( numSrcExtentBlocks && numDestExtentBlocks ) {
err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, kHFSBogusExtentFileID, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr )
return( err );
else
goto ExUndo1a;
}
err = MoveExtents( vcb, destData.hfsPlusFile.fileID, srcData.hfsPlusFile.fileID, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr )
return( err );
ExUndo2aPlus: err = DeleteExtents( vcb, srcData.hfsPlusFile.fileID, isHFSPlus );
ReturnIfError( err );
err = MoveExtents( vcb, kHFSBogusExtentFileID, srcData.hfsPlusFile.fileID, isHFSPlus ); ReturnIfError( err );
goto ExUndo1a;
}
err = MoveExtents( vcb, kHFSBogusExtentFileID, destData.hfsPlusFile.fileID, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr )
return( err );
err = DeleteExtents( vcb, destData.hfsPlusFile.fileID, isHFSPlus );
ReturnIfError( err );
err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, destData.hfsPlusFile.fileID, isHFSPlus ); ReturnIfError( err );
goto ExUndo2aPlus;
}
}
else if ( numSrcExtentBlocks ) {
err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, destData.hfsPlusFile.fileID, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr )
return( err );
err = DeleteExtents( vcb, srcData.hfsPlusFile.fileID, isHFSPlus );
ReturnIfError( err );
goto FlushAndReturn;
}
}
else if ( numDestExtentBlocks ) {
err = MoveExtents( vcb, destData.hfsPlusFile.fileID, srcData.hfsPlusFile.fileID, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr )
return( err );
err = DeleteExtents( vcb, destData.hfsPlusFile.fileID, isHFSPlus );
ReturnIfError( err );
goto FlushAndReturn;
}
}
err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint );
if ( err != noErr )
return( cmBadNews );
BlockMoveData( &srcData, &swapData, sizeof(CatalogRecord) );
CopyBigCatalogNodeInfo( &destData, &srcData );
err = ReplaceBTreeRecord( vcb->catalogRefNum, &srcKey, srcHint, &srcData, sizeof(HFSPlusCatalogFile), &srcHint );
ReturnIfError( err );
err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
if ( err != noErr )
return( cmBadNews );
CopyBigCatalogNodeInfo( &swapData, &destData );
err = ReplaceBTreeRecord( vcb->catalogRefNum, &destKey, destHint, &destData, sizeof(HFSPlusCatalogFile), &destHint );
ReturnIfError( err );
}
else {
err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint );
ReturnIfError( err );
if ( srcData.recordType != kHFSFileRecord )
return( cmFThdDirErr );
numSrcExtentBlocks = CheckExtents( srcData.hfsFile.dataExtents, srcData.hfsFile.dataPhysicalSize / vcb->blockSize, isHFSPlus );
if ( numSrcExtentBlocks == 0 ) numSrcExtentBlocks = CheckExtents( srcData.hfsFile.rsrcExtents, srcData.hfsFile.rsrcPhysicalSize / vcb->blockSize, isHFSPlus );
err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
ReturnIfError( err );
if ( destData.recordType != kHFSFileRecord )
return( cmFThdDirErr );
numDestExtentBlocks = CheckExtents( destData.hfsFile.dataExtents, destData.hfsFile.dataPhysicalSize / vcb->blockSize, isHFSPlus );
if ( numDestExtentBlocks == 0 ) numDestExtentBlocks = CheckExtents( destData.hfsFile.rsrcExtents, destData.hfsFile.rsrcPhysicalSize / vcb->blockSize, isHFSPlus );
err = DeleteExtents( vcb, kHFSBogusExtentFileID, isHFSPlus );
ReturnIfError( err );
if ( numSrcExtentBlocks && numDestExtentBlocks ) {
err = MoveExtents( vcb, srcData.hfsFile.fileID, kHFSBogusExtentFileID, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr )
return( err );
ExUndo1a: err = DeleteExtents( vcb, kHFSBogusExtentFileID, isHFSPlus );
ReturnIfError( err );
err = FlushCatalog( vcb ); err = FlushExtentFile( vcb ); return( dskFulErr );
}
err = MoveExtents( vcb, destData.hfsFile.fileID, srcData.hfsFile.fileID, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr )
return( err );
ExUndo2a: err = DeleteExtents( vcb, srcData.hfsFile.fileID, isHFSPlus );
ReturnIfError( err );
err = MoveExtents( vcb, kHFSBogusExtentFileID, srcData.hfsFile.fileID, isHFSPlus ); ReturnIfError( err );
goto ExUndo1a;
}
err = MoveExtents( vcb, kHFSBogusExtentFileID, destData.hfsFile.fileID, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr )
return( err );
err = DeleteExtents( vcb, destData.hfsFile.fileID, isHFSPlus );
ReturnIfError( err );
err = MoveExtents( vcb, srcData.hfsFile.fileID, destData.hfsFile.fileID, isHFSPlus ); ReturnIfError( err );
goto ExUndo2a;
}
}
else if ( numSrcExtentBlocks ) {
err = MoveExtents( vcb, srcData.hfsFile.fileID, destData.hfsFile.fileID, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr )
return( err );
err = DeleteExtents( vcb, srcData.hfsFile.fileID, isHFSPlus );
ReturnIfError( err );
goto FlushAndReturn;
}
}
else if ( numDestExtentBlocks ) {
err = MoveExtents( vcb, destData.hfsFile.fileID, srcData.hfsFile.fileID, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr )
return( err );
err = DeleteExtents( vcb, destData.hfsFile.fileID, isHFSPlus );
ReturnIfError( err );
goto FlushAndReturn;
}
}
err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint );
if ( err != noErr )
return( cmBadNews );
BlockMoveData( &srcData, &swapData, sizeof(CatalogRecord) );
CopyCatalogNodeInfo( &destData, &srcData );
err = ReplaceBTreeRecord( vcb->catalogRefNum, &srcKey, srcHint, &srcData, sizeof(HFSCatalogFile), &srcHint );
ReturnIfError( err );
err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
if ( err != noErr )
return( cmBadNews );
CopyCatalogNodeInfo( &swapData, &destData );
err = ReplaceBTreeRecord( vcb->catalogRefNum, &destKey, destHint, &destData, sizeof(HFSCatalogFile), &destHint );
ReturnIfError( err );
}
err = noErr;
FlushAndReturn:
err = FlushCatalog( vcb ); err = FlushExtentFile( vcb ); return( err );
}
void CopyCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest )
{
dest->hfsFile.dataLogicalSize = src->hfsFile.dataLogicalSize;
dest->hfsFile.dataPhysicalSize = src->hfsFile.dataPhysicalSize;
dest->hfsFile.rsrcLogicalSize = src->hfsFile.rsrcLogicalSize;
dest->hfsFile.rsrcPhysicalSize = src->hfsFile.rsrcPhysicalSize;
dest->hfsFile.modifyDate = src->hfsFile.modifyDate;
BlockMoveData( src->hfsFile.dataExtents, dest->hfsFile.dataExtents, sizeof(HFSExtentRecord) );
BlockMoveData( src->hfsFile.rsrcExtents, dest->hfsFile.rsrcExtents, sizeof(HFSExtentRecord) );
}
void CopyBigCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest )
{
BlockMoveData( &src->hfsPlusFile.dataFork, &dest->hfsPlusFile.dataFork, sizeof(HFSPlusForkData) );
BlockMoveData( &src->hfsPlusFile.resourceFork, &dest->hfsPlusFile.resourceFork, sizeof(HFSPlusForkData) );
dest->hfsPlusFile.contentModDate = src->hfsPlusFile.contentModDate;
}
OSErr MoveExtents( ExtendedVCB *vcb, UInt32 srcFileID, UInt32 destFileID, Boolean isHFSPlus )
{
FCB * fcb;
ExtentsRecBuffer extentsBuffer[kNumExtentsToCache];
ExtentKey * extentKeyPtr;
ExtentRecord extentData;
BTreeIterator btIterator;
FSBufferDescriptor btRecord;
UInt16 btKeySize;
UInt16 btRecordSize;
SInt16 i, j;
OSErr err;
fcb = GetFileControlBlock(vcb->extentsRefNum);
(void) BTInvalidateHint(&btIterator);
extentKeyPtr = (ExtentKey*) &btIterator.key;
btRecord.bufferAddress = &extentData;
btRecord.itemCount = 1;
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;
}
err = BTSearchRecord(fcb, &btIterator, &btRecord, &btRecordSize, &btIterator);
if (err != btNotFound)
{
if ( DEBUG_BUILD )
DebugStr("\pUnexpected error from SearchBTreeRecord");
if (err == noErr) err = cmBadNews;
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 ) break; else if ( err != noErr )
return( err );
foundFileID = isHFSPlus ? extentKeyPtr->hfsPlus.fileID : extentKeyPtr->hfs.fileID;
if ( foundFileID == srcFileID )
{
CopyExtentInfo(extentKeyPtr, &extentData, extentsBuffer, i);
}
else
{
break;
}
}
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; else
extentsBuffer[j].extentKey.hfs.fileID = destFileID;
(void) BTInvalidateHint(&tmpIterator);
BlockMoveData(&(extentsBuffer[j].extentKey), &tmpIterator.key, btKeySize);
btRecord.bufferAddress = &(extentsBuffer[j].extentData);
err = BTInsertRecord(fcb, &tmpIterator, &btRecord, btRecordSize);
if ( err != noErr )
{ if ( err == btExists )
{
if ( DEBUG_BUILD )
DebugStr("\pCan't insert record -- already exists");
return( cmBadNews );
}
else
return( err );
}
}
if ( i != kNumExtentsToCache ) {
err = DeleteExtents( vcb, srcFileID, isHFSPlus ); if ( DEBUG_BUILD && err != noErr )
DebugStr("\pError from DeleteExtents");
break; }
} while ( true );
return( err );
}
void CopyExtentInfo( ExtentKey *key, ExtentRecord *data, ExtentsRecBuffer *buffer, UInt16 bufferCount )
{
BlockMoveData( key, &(buffer[bufferCount].extentKey), sizeof( ExtentKey ) );
BlockMoveData( data, &(buffer[bufferCount].extentData), sizeof( ExtentRecord ) );
}
OSErr DeleteExtents( ExtendedVCB *vcb, UInt32 fileID, Boolean isHFSPlus )
{
FCB * fcb;
ExtentKey * extentKeyPtr;
ExtentRecord extentData;
BTreeIterator btIterator;
FSBufferDescriptor btRecord;
UInt16 btRecordSize;
OSErr err;
fcb = GetFileControlBlock(vcb->extentsRefNum);
(void) BTInvalidateHint(&btIterator);
extentKeyPtr = (ExtentKey*) &btIterator.key;
btRecord.bufferAddress = &extentData;
btRecord.itemCount = 1;
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) { err = cmBadNews; }
return err; }
do
{
BTreeIterator tmpIterator;
HFSCatalogNodeID foundFileID;
err = BTIterateRecord(fcb, kBTreeNextRecord, &btIterator, &btRecord, &btRecordSize);
if ( err != noErr )
{
if (err == btNotFound) err = noErr;
break; }
foundFileID = isHFSPlus ? extentKeyPtr->hfsPlus.fileID : extentKeyPtr->hfs.fileID;
if ( foundFileID != fileID )
break;
tmpIterator = btIterator;
err = BTDeleteRecord( fcb, &tmpIterator );
if (err != noErr)
break;
} while ( true );
return( err );
}
UInt32 CheckExtents( void *extents, UInt32 totalBlocks, Boolean isHFSPlus )
{
UInt32 extentAllocationBlocks;
UInt16 i;
if ( totalBlocks == 0 )
return( 0 );
extentAllocationBlocks = 0;
if ( isHFSPlus )
{
for ( i = 0 ; i < kHFSPlusExtentDensity ; i++ )
{
extentAllocationBlocks += ((HFSPlusExtentDescriptor *)extents)[i].blockCount;
if ( extentAllocationBlocks >= totalBlocks ) return( 0 );
}
}
else
{
for ( i = 0 ; i < kHFSExtentDensity ; i++ )
{
extentAllocationBlocks += ((HFSExtentDescriptor *)extents)[i].blockCount;
if ( extentAllocationBlocks >= totalBlocks ) return( 0 );
}
}
return( extentAllocationBlocks );
}