#include "../../hfs_macos_defs.h"
#include "../../hfs_format.h"
#include "../headers/FileMgrInternal.h"
#include "../headers/HFSUnicodeWrappers.h"
#include "../headers/CatalogPrivate.h"
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <libkern/libkern.h>
struct ExtentsRecBuffer {
ExtentKey extentKey;
ExtentRecord extentData;
};
typedef struct ExtentsRecBuffer ExtentsRecBuffer;
static u_int32_t CheckExtents( void *extents, u_int32_t blocks, Boolean isHFSPlus );
static OSErr DeleteExtents( ExtendedVCB *vcb, u_int32_t fileNumber, int quitEarly, u_int8_t forkType, Boolean isHFSPlus );
static OSErr MoveExtents( ExtendedVCB *vcb, u_int32_t srcFileID, u_int32_t destFileID, int quitEarly, u_int8_t forkType, Boolean isHFSPlus );
#if CONFIG_HFS_STD
static void CopyCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest );
#endif
static void CopyBigCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest );
static void CopyExtentInfo( ExtentKey *key, ExtentRecord *data, ExtentsRecBuffer *buffer, u_int16_t bufferCount );
OSErr MoveData( ExtendedVCB *vcb, HFSCatalogNodeID srcID, HFSCatalogNodeID destID, int rsrc) {
OSErr err;
if (rsrc) {
err = MoveExtents( vcb, srcID, destID, 1, (u_int8_t)0xff, 1);
if ( err != noErr ) {
if ( err != dskFulErr ) {
return( err );
}
err = DeleteExtents( vcb, destID, 1, (u_int8_t)0xff, 1);
ReturnIfError( err ); goto FlushAndReturn;
}
}
else {
err = MoveExtents( vcb, srcID, destID, 1, 0, 1);
if ( err != noErr ) {
if ( err != dskFulErr ) {
return( err );
}
err = DeleteExtents( vcb, destID, 1, 0, 1);
ReturnIfError( err ); goto FlushAndReturn;
}
}
FlushAndReturn:
err = FlushCatalog( vcb );
err = FlushExtentFile( vcb );
return( err );
}
OSErr ExchangeFileIDs( ExtendedVCB *vcb, ConstUTF8Param srcName, ConstUTF8Param destName, HFSCatalogNodeID srcID, HFSCatalogNodeID destID, u_int32_t srcHint, u_int32_t destHint )
{
CatalogKey srcKey; CatalogKey destKey; CatalogRecord srcData; CatalogRecord destData; CatalogRecord swapData; int16_t numSrcExtentBlocks;
int16_t numDestExtentBlocks;
OSErr err;
Boolean isHFSPlus = ( vcb->vcbSigWord == kHFSPlusSigWord );
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, 0, 0, isHFSPlus );
ReturnIfError( err );
if ( numSrcExtentBlocks && numDestExtentBlocks ) {
err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, kHFSBogusExtentFileID, 0,0, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr ) {
return( err );
}
else {
err = DeleteExtents( vcb, kHFSBogusExtentFileID, 0, 0, isHFSPlus );
ReturnIfError( err );
err = FlushCatalog( vcb ); err = FlushExtentFile( vcb ); return( dskFulErr );
}
}
err = MoveExtents( vcb, destData.hfsPlusFile.fileID, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr )
return( err );
ExUndo2aPlus: err = DeleteExtents( vcb, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
ReturnIfError( err );
err = MoveExtents( vcb, kHFSBogusExtentFileID, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); ReturnIfError( err );
err = DeleteExtents( vcb, kHFSBogusExtentFileID, 0, 0, isHFSPlus );
ReturnIfError( err );
err = FlushCatalog( vcb ); err = FlushExtentFile( vcb ); return( dskFulErr );
}
err = MoveExtents( vcb, kHFSBogusExtentFileID, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr )
return( err );
err = DeleteExtents( vcb, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
ReturnIfError( err );
err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); ReturnIfError( err );
goto ExUndo2aPlus;
}
}
else if ( numSrcExtentBlocks ) {
err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr )
return( err );
err = DeleteExtents( vcb, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
ReturnIfError( err );
goto FlushAndReturn;
}
}
else if ( numDestExtentBlocks ) {
err = MoveExtents( vcb, destData.hfsPlusFile.fileID, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr )
return( err );
err = DeleteExtents( vcb, destData.hfsPlusFile.fileID, 0, 0, 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 );
}
#if CONFIG_HFS_STD
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, 0, 0, isHFSPlus );
ReturnIfError( err );
if ( numSrcExtentBlocks && numDestExtentBlocks ) {
err = MoveExtents( vcb, srcData.hfsFile.fileID, kHFSBogusExtentFileID, 0, 0, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr )
return( err );
ExUndo1a: err = DeleteExtents( vcb, kHFSBogusExtentFileID, 0, 0, isHFSPlus );
ReturnIfError( err );
err = FlushCatalog( vcb ); err = FlushExtentFile( vcb ); return( dskFulErr );
}
err = MoveExtents( vcb, destData.hfsFile.fileID, srcData.hfsFile.fileID, 0, 0, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr )
return( err );
ExUndo2a: err = DeleteExtents( vcb, srcData.hfsFile.fileID, 0, 0, isHFSPlus );
ReturnIfError( err );
err = MoveExtents( vcb, kHFSBogusExtentFileID, srcData.hfsFile.fileID, 0, 0, isHFSPlus ); ReturnIfError( err );
goto ExUndo1a;
}
err = MoveExtents( vcb, kHFSBogusExtentFileID, destData.hfsFile.fileID, 0, 0, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr )
return( err );
err = DeleteExtents( vcb, destData.hfsFile.fileID, 0, 0, isHFSPlus );
ReturnIfError( err );
err = MoveExtents( vcb, srcData.hfsFile.fileID, destData.hfsFile.fileID, 0, 0, isHFSPlus ); ReturnIfError( err );
goto ExUndo2a;
}
}
else if ( numSrcExtentBlocks ) {
err = MoveExtents( vcb, srcData.hfsFile.fileID, destData.hfsFile.fileID, 0, 0, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr )
return( err );
err = DeleteExtents( vcb, srcData.hfsFile.fileID, 0, 0, isHFSPlus );
ReturnIfError( err );
goto FlushAndReturn;
}
}
else if ( numDestExtentBlocks ) {
err = MoveExtents( vcb, destData.hfsFile.fileID, srcData.hfsFile.fileID, 0, 0, isHFSPlus );
if ( err != noErr )
{
if ( err != dskFulErr )
return( err );
err = DeleteExtents( vcb, destData.hfsFile.fileID, 0, 0, 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 );
}
#endif
err = noErr;
FlushAndReturn:
err = FlushCatalog( vcb ); err = FlushExtentFile( vcb ); return( err );
}
#if CONFIG_HFS_STD
static 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) );
}
#endif
static 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;
}
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; }
MALLOC (tmpIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK);
if (tmpIterator == NULL) {
FREE (btIterator, M_TEMP);
return memFullErr; }
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;
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;
}
#endif
err = BTSearchRecord(fcb, btIterator, &btRecord, &btRecordSize, btIterator);
if (err != btNotFound)
{
if ( DEBUG_BUILD )
DebugStr("Unexpected error from SearchBTreeRecord");
if (err == noErr) err = cmBadNews;
FREE (tmpIterator, M_TEMP);
FREE (btIterator, M_TEMP);
return err;
}
do
{
btRecord.bufferAddress = &extentData;
btRecord.itemCount = 1;
for ( i=0 ; i<kNumExtentsToCache ; i++ )
{
HFSCatalogNodeID foundFileID = 0;
err = BTIterateRecord(fcb, kBTreeNextRecord, btIterator, &btRecord, &btRecordSize);
if ( err == btNotFound ) break; else if ( err != noErr ) {
FREE (btIterator, M_TEMP);
FREE (tmpIterator, M_TEMP);
return( err ); }
if (isHFSPlus) {
foundFileID = extentKeyPtr->hfsPlus.fileID;
}
#if CONFIG_HFS_STD
else {
foundFileID = extentKeyPtr->hfs.fileID;
}
#endif
if ( foundFileID == srcFileID ) {
if (quitEarly && isHFSPlus) {
if (extentKeyPtr->hfsPlus.forkType != forkType) {
break;
}
}
CopyExtentInfo(extentKeyPtr, &extentData, extentsBuffer, i);
}
else{
break;
}
}
if (isHFSPlus)
btRecordSize = sizeof(HFSPlusExtentRecord);
#if CONFIG_HFS_STD
else
btRecordSize = sizeof(HFSExtentRecord);
#endif
for ( j=0 ; j<i ; j++ )
{
if (isHFSPlus)
extentsBuffer[j].extentKey.hfsPlus.fileID = destFileID; #if CONFIG_HFS_STD
else
extentsBuffer[j].extentKey.hfs.fileID = destFileID; #endif
(void) BTInvalidateHint(tmpIterator);
BlockMoveData(&(extentsBuffer[j].extentKey), &tmpIterator->key, btKeySize);
btRecord.bufferAddress = &(extentsBuffer[j].extentData);
err = BTInsertRecord(fcb, tmpIterator, &btRecord, btRecordSize);
if ( err != noErr ) {
FREE (btIterator, M_TEMP);
FREE (tmpIterator, M_TEMP);
if ( err == btExists )
{
if ( DEBUG_BUILD ) {
DebugStr("Can't insert record -- already exists");
}
return( cmBadNews );
}
else {
return( err );
}
}
}
if ( i != kNumExtentsToCache ) {
err = DeleteExtents( vcb, srcFileID, quitEarly, forkType, isHFSPlus ); if ( DEBUG_BUILD && err != noErr )
DebugStr("Error from DeleteExtents");
break; }
} while ( true );
FREE (tmpIterator, M_TEMP);
FREE (btIterator, M_TEMP);
return( err );
}
static void CopyExtentInfo( ExtentKey *key, ExtentRecord *data, ExtentsRecBuffer *buffer, u_int16_t bufferCount )
{
BlockMoveData( key, &(buffer[bufferCount].extentKey), sizeof( ExtentKey ) );
BlockMoveData( data, &(buffer[bufferCount].extentData), sizeof( ExtentRecord ) );
}
static OSErr DeleteExtents( ExtendedVCB *vcb, u_int32_t fileID, int quitEarly, u_int8_t forkType, Boolean isHFSPlus )
{
FCB * fcb;
ExtentKey * extentKeyPtr;
ExtentRecord extentData;
struct BTreeIterator *btIterator = NULL;
struct BTreeIterator *tmpIterator = NULL;
FSBufferDescriptor btRecord;
u_int16_t btRecordSize;
OSErr err;
MALLOC (btIterator, struct BTreeIterator*, sizeof(struct BTreeIterator),
M_TEMP, M_WAITOK | M_ZERO);
MALLOC (tmpIterator, struct BTreeIterator*, sizeof(struct BTreeIterator),
M_TEMP, M_WAITOK | M_ZERO);
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 = forkType;
extentKeyPtr->hfsPlus.pad = 0;
extentKeyPtr->hfsPlus.fileID = fileID;
extentKeyPtr->hfsPlus.startBlock = 0;
}
#if CONFIG_HFS_STD
else {
btRecord.itemSize = sizeof(HFSExtentRecord);
extentKeyPtr->hfs.keyLength = kHFSExtentKeyMaximumLength;
extentKeyPtr->hfs.forkType = forkType;
extentKeyPtr->hfs.fileID = fileID;
extentKeyPtr->hfs.startBlock = 0;
}
#else
else {
err = cmBadNews;
goto exit;
}
#endif
err = BTSearchRecord(fcb, btIterator, &btRecord, &btRecordSize, btIterator);
if ( err != btNotFound )
{
if (err == noErr) { err = cmBadNews; }
goto exit;
}
do
{
HFSCatalogNodeID foundFileID = 0;
err = BTIterateRecord(fcb, kBTreeNextRecord, btIterator, &btRecord, &btRecordSize);
if ( err != noErr )
{
if (err == btNotFound) err = noErr;
break; }
if (isHFSPlus) {
foundFileID = extentKeyPtr->hfsPlus.fileID;
}
#if CONFIG_HFS_STD
else {
foundFileID = extentKeyPtr->hfs.fileID;
}
#endif
if ( foundFileID != fileID ) {
break; }
if (quitEarly && isHFSPlus) {
if (extentKeyPtr->hfsPlus.forkType != forkType) {
break;
}
}
*tmpIterator = *btIterator;
err = BTDeleteRecord( fcb, tmpIterator );
if (err != noErr)
break;
} while ( true );
exit:
FREE (tmpIterator, M_TEMP);
FREE (btIterator, M_TEMP);
return( err );
}
static u_int32_t CheckExtents( void *extents, u_int32_t totalBlocks, Boolean isHFSPlus )
{
u_int32_t extentAllocationBlocks;
u_int16_t 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 );
}
}
#if CONFIG_HFS_STD
else
{
for ( i = 0 ; i < kHFSExtentDensity ; i++ )
{
extentAllocationBlocks += ((HFSExtentDescriptor *)extents)[i].blockCount;
if ( extentAllocationBlocks >= totalBlocks ) return( 0 );
}
}
#endif
return( extentAllocationBlocks );
}