CatalogUtilities.c [plain text]
#include <sys/param.h>
#include <sys/utfconv.h>
#include "../headers/FileMgrInternal.h"
#include "../headers/BTreesInternal.h"
#include "../headers/CatalogPrivate.h"
#include "../headers/HFSUnicodeWrappers.h"
#include <string.h>
static void ExtractTextEncoding (ItemCount length, ConstUniCharArrayPtr string, UInt32 * textEncoding);
OSErr
LocateCatalogNode(const ExtendedVCB *volume, HFSCatalogNodeID folderID, const CatalogName *name,
UInt32 hint, CatalogKey *keyPtr, CatalogRecord *dataPtr, UInt32 *newHint)
{
OSErr result;
CatalogName *nodeName = NULL;
HFSCatalogNodeID threadParentID;
result = LocateCatalogRecord(volume, folderID, name, hint, keyPtr, dataPtr, newHint);
ReturnIfError(result);
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 ) result = LocateCatalogRecord(volume, threadParentID, nodeName, kNoHint, keyPtr, dataPtr, newHint);
return result;
}
OSErr
LocateCatalogNodeByKey(const ExtendedVCB *volume, UInt32 hint, CatalogKey *keyPtr,
CatalogRecord *dataPtr, UInt32 *newHint)
{
OSErr result;
CatalogName *nodeName = NULL;
HFSCatalogNodeID threadParentID;
UInt16 tempSize;
result = SearchBTreeRecord(volume->catalogRefNum, keyPtr, hint, keyPtr,
dataPtr, &tempSize, newHint);
if (result == btNotFound)
result = cmNotFound;
ReturnIfError(result);
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 ) result = LocateCatalogRecord(volume, threadParentID, nodeName, kNoHint, keyPtr, dataPtr, newHint);
return result;
}
#if 0
#define VolumeHasEncodings(v) \
( ((v)->encodingsBitmap != 0 )
#define EncodingInstalled(i) \
( (fsVars)->gConversionContext[(i)].toUnicode != 0 )
#define EncodingUsedByVolume(v,i) \
( ((v)->encodingsBitmap & (1 << (i))) )
OSErr
LocateCatalogNodeWithRetry (const ExtendedVCB *volume, HFSCatalogNodeID folderID, ConstStr31Param pascalName, CatalogName *unicodeName,
UInt32 hint, CatalogKey *keyPtr, CatalogRecord *dataPtr, UInt32 *newHint)
{
TextEncoding defaultEncoding;
TextEncoding encoding;
ItemCount encodingsToTry;
FSVarsRec *fsVars;
OSErr result = cmNotFound;
fsVars = (FSVarsRec*) LMGetFSMVars();
defaultEncoding = GetDefaultTextEncoding();
encodingsToTry = CountInstalledEncodings();
{
--encodingsToTry;
result = PrepareInputName(pascalName, true, defaultEncoding, unicodeName);
if (result == noErr)
result = LocateCatalogNode(volume, folderID, unicodeName, hint, keyPtr, dataPtr, newHint);
else
result = cmNotFound;
if ( result != cmNotFound || encodingsToTry == 0)
return result;
}
if ( defaultEncoding != kTextEncodingMacRoman )
{
--encodingsToTry;
result = PrepareInputName(pascalName, true, kTextEncodingMacRoman, unicodeName);
if (result == noErr)
result = LocateCatalogNode(volume, folderID, unicodeName, hint, keyPtr, dataPtr, newHint);
else
result = cmNotFound;
if ( result != cmNotFound || encodingsToTry == 0 )
return result;
}
if ( VolumeHasEncodings(volume) ) {
UInt32 index;
index = 0;
while ( index < kMacBaseEncodingCount )
{
++index;
encoding = MapIndexToEncoding(index);
if ( encoding == defaultEncoding )
continue;
if ( EncodingInstalled(index) && EncodingUsedByVolume(volume, index) )
{
--encodingsToTry;
result = PrepareInputName(pascalName, true, encoding, unicodeName);
if (result == noErr)
result = LocateCatalogNode(volume, folderID, unicodeName, hint, keyPtr, dataPtr, newHint);
else
result = cmNotFound;
if ( result != cmNotFound || encodingsToTry == 0 )
return result;
}
}
}
{
UInt32 index;
index = 0;
while ( (encodingsToTry > 0) && (index < kMacBaseEncodingCount) )
{
++index;
encoding = MapIndexToEncoding(index);
if ( encoding == defaultEncoding )
continue;
if ( EncodingInstalled(index) && EncodingUsedByVolume(volume, index) == false )
{
--encodingsToTry;
result = PrepareInputName(pascalName, true, encoding, unicodeName);
if (result == noErr)
result = LocateCatalogNode(volume, folderID, unicodeName, hint, keyPtr, dataPtr, newHint);
else
result = cmNotFound;
if ( result != cmNotFound || encodingsToTry == 0 )
return result;
}
}
}
return cmNotFound;
}
#endif
#define kMaxCompareLen 64
OSErr
LocateCatalogNodeByMangledName( const ExtendedVCB *volume, HFSCatalogNodeID folderID,
const unsigned char * name, UInt32 length, CatalogKey *keyPtr,
CatalogRecord *dataPtr, UInt32 *hintPtr )
{
HFSCatalogNodeID fileID;
unsigned char nodeName[kMaxCompareLen+1];
OSErr result;
size_t actualDstLen;
ByteCount prefixlen;
if (name == NULL || name[0] == '\0')
return cmNotFound;
fileID = GetEmbeddedFileID(name, length, &prefixlen);
if ( fileID < kHFSFirstUserCatalogNodeID )
return cmNotFound;
result = LocateCatalogNode(volume, fileID, NULL, kNoHint, keyPtr, dataPtr, hintPtr);
if ( result == cmParentNotFound ) result = cmNotFound; ReturnIfError(result);
if ( folderID != keyPtr->hfsPlus.parentID )
return cmNotFound;
(void) utf8_encodestr(keyPtr->hfsPlus.nodeName.unicode,
keyPtr->hfsPlus.nodeName.length * sizeof (UniChar),
nodeName, &actualDstLen, kMaxCompareLen+1, ':', 0);
prefixlen = min(prefixlen, kMaxCompareLen);
if ((prefixlen - actualDstLen) < 6)
prefixlen = actualDstLen;
if ( (actualDstLen < prefixlen) || bcmp(nodeName, name, prefixlen-6) != 0)
return cmNotFound;
return noErr; }
OSErr
LocateCatalogRecord(const ExtendedVCB *volume, HFSCatalogNodeID folderID, const CatalogName *name,
UInt32 hint, CatalogKey *keyPtr, CatalogRecord *dataPtr, UInt32 *newHint)
{
OSErr result;
CatalogKey tempKey; UInt16 tempSize;
BuildCatalogKey(folderID, name, (volume->vcbSigWord == kHFSPlusSigWord), &tempKey);
if ( name == NULL )
hint = kNoHint;
result = SearchBTreeRecord(volume->catalogRefNum, &tempKey, hint, keyPtr, dataPtr, &tempSize, newHint);
return (result == btNotFound ? cmNotFound : result);
}
OSErr
LocateCatalogThread(const ExtendedVCB *volume, HFSCatalogNodeID nodeID, CatalogRecord *threadData, UInt16 *threadSize, UInt32 *threadHint)
{
CatalogKey threadKey; OSErr result;
BuildCatalogKey(nodeID, NULL, (volume->vcbSigWord == kHFSPlusSigWord), &threadKey);
result = SearchBTreeRecord( volume->catalogRefNum, &threadKey, kNoHint, &threadKey,
threadData, threadSize, threadHint);
return (result == btNotFound ? cmNotFound : result);
}
void
BuildCatalogKey(HFSCatalogNodeID parentID, const CatalogName *cName, Boolean isHFSPlus, CatalogKey *key)
{
if ( isHFSPlus )
{
key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength; key->hfsPlus.parentID = parentID; key->hfsPlus.nodeName.length = 0; if ( cName != NULL )
{
CopyCatalogName(cName, (CatalogName *) &key->hfsPlus.nodeName, isHFSPlus);
key->hfsPlus.keyLength += sizeof(UniChar) * cName->ustr.length; }
}
else
{
key->hfs.keyLength = kHFSCatalogKeyMinimumLength; key->hfs.reserved = 0; key->hfs.parentID = parentID; key->hfs.nodeName[0] = 0; if ( cName != NULL )
{
UpdateCatalogName(cName->pstr, key->hfs.nodeName);
key->hfs.keyLength += key->hfs.nodeName[0]; }
}
}
OSErr
BuildCatalogKeyUTF8(ExtendedVCB *volume, HFSCatalogNodeID parentID, const char *name, UInt32 nameLength,
CatalogKey *key, UInt32 *textEncoding)
{
OSErr err = 0;
if ( name == NULL)
nameLength = 0;
else if (nameLength == kUndefinedStrLen)
nameLength = strlen(name);
if ( volume->vcbSigWord == kHFSPlusSigWord ) {
size_t unicodeBytes = 0;
key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength; key->hfsPlus.parentID = parentID; key->hfsPlus.nodeName.length = 0; if ( nameLength > 0 ) {
err = utf8_decodestr(name, nameLength, key->hfsPlus.nodeName.unicode,
&unicodeBytes, sizeof(key->hfsPlus.nodeName.unicode), ':', UTF_DECOMPOSED);
key->hfsPlus.nodeName.length = unicodeBytes / sizeof(UniChar);
key->hfsPlus.keyLength += unicodeBytes;
}
if (textEncoding)
ExtractTextEncoding(key->hfsPlus.nodeName.length, key->hfsPlus.nodeName.unicode, textEncoding);
}
else {
key->hfs.keyLength = kHFSCatalogKeyMinimumLength; key->hfs.reserved = 0; key->hfs.parentID = parentID; key->hfs.nodeName[0] = 0; if ( nameLength > 0 ) {
err = utf8_to_hfs(volume, nameLength, name, &key->hfs.nodeName[0]);
if (err && (textEncoding == NULL))
err = utf8_to_mac_roman(nameLength, name, &key->hfs.nodeName[0]);
key->hfs.keyLength += key->hfs.nodeName[0]; }
if (textEncoding)
*textEncoding = 0;
}
if (err) {
if (err == ENAMETOOLONG)
err = bdNamErr;
else
err = paramErr;
}
return err;
}
static void
ExtractTextEncoding(ItemCount length, ConstUniCharArrayPtr string, UInt32 * textEncoding)
{
int i;
UniChar ch;
*textEncoding = 0;
for (i = 0; i < length; ++i) {
ch = string[i];
if (ch >= 0x3000) {
if (ch < 0xa000) {
*textEncoding = kTextEncodingMacJapanese;
break;
}
if (ch >= 0xff00 && ch <= 0xffef) {
*textEncoding = kTextEncodingMacJapanese;
break;
}
}
}
}
OSErr
FlushCatalog(ExtendedVCB *volume)
{
FCB * fcb;
OSErr result;
fcb = GetFileControlBlock(volume->catalogRefNum);
result = BTFlushPath(fcb);
if (result == noErr)
{
if ( fcb->fcbFlags & fcbModifiedMask )
{
VCB_LOCK(volume);
volume->vcbFlags |= 0xFF00; volume->vcbLsMod = GetTimeUTC(); VCB_UNLOCK(volume);
result = FlushVolumeControlBlock(volume);
}
}
return result;
}
void
UpdateCatalogName(ConstStr31Param srcName, Str31 destName)
{
Size length = srcName[0];
if (length > CMMaxCName)
length = CMMaxCName;
destName[0] = length;
BlockMoveData(&srcName[1], &destName[1], length);
}
void
AdjustVolumeCounts(ExtendedVCB *volume, SInt16 type, SInt16 delta)
{
VCB_LOCK(volume);
if (type == kHFSFolderRecord || type == kHFSPlusFolderRecord)
volume->vcbDirCnt += delta; else
volume->vcbFilCnt += delta;
volume->vcbFlags |= 0xFF00; volume->vcbLsMod = GetTimeUTC();
VCB_UNLOCK(volume);
}
void
UpdateVolumeEncodings(ExtendedVCB *volume, TextEncoding encoding)
{
UInt32 index;
encoding &= 0x7F;
index = MapEncodingToIndex(encoding);
VCB_LOCK(volume);
volume->encodingsBitmap |= (1 << index);
VCB_UNLOCK(volume);
}
OSErr
UpdateFolderCount( ExtendedVCB *volume, HFSCatalogNodeID parentID, const CatalogName *name, SInt16 newType,
UInt32 hint, SInt16 valenceDelta)
{
CatalogKey tempKey; CatalogRecord tempData; UInt32 tempHint;
HFSCatalogNodeID folderID;
UInt16 recordSize;
OSErr result;
#if 0
result = SearchBTreeRecord(volume->catalogRefNum, parentKey, hint,
&tempKey, &tempData, &recordSize, &tempHint);
if (result)
return (result == btNotFound ? cmNotFound : result);
#else
result = LocateCatalogNode(volume, parentID, name, hint, &tempKey, &tempData, &tempHint);
ReturnIfError(result);
#endif
if ( volume->vcbSigWord == kHFSPlusSigWord ) {
UInt32 timeStamp;
if ( DEBUG_BUILD && tempData.recordType != kHFSPlusFolderRecord )
DebugStr("\p UpdateFolder: found HFS folder on HFS+ volume!");
timeStamp = GetTimeUTC();
if (valenceDelta > 0)
tempData.hfsPlusFolder.valence += valenceDelta;
else if (tempData.hfsPlusFolder.valence != 0)
tempData.hfsPlusFolder.valence += valenceDelta;
else
volume->vcbFlags |= kHFS_DamagedVolume;
tempData.hfsPlusFolder.contentModDate = timeStamp; folderID = tempData.hfsPlusFolder.folderID;
recordSize = sizeof(tempData.hfsPlusFolder);
}
else {
if ( DEBUG_BUILD && tempData.recordType != kHFSFolderRecord )
DebugStr("\p UpdateFolder: found HFS+ folder on HFS volume!");
if (valenceDelta > 0)
tempData.hfsFolder.valence += valenceDelta;
else if (tempData.hfsFolder.valence != 0)
tempData.hfsFolder.valence += valenceDelta;
else
volume->vcbFlags |= kHFS_DamagedVolume;
tempData.hfsFolder.modifyDate = GetTimeLocal(true); folderID = tempData.hfsFolder.folderID;
recordSize = sizeof(tempData.hfsFolder);
}
result = ReplaceBTreeRecord(volume->catalogRefNum, &tempKey, tempHint,
&tempData, recordSize, &tempHint);
ReturnIfError(result);
if ( folderID == kHFSRootFolderID )
{
if (newType == kHFSFolderRecord || newType == kHFSPlusFolderRecord)
{
VCB_LOCK(volume);
volume->vcbNmRtDirs += valenceDelta; VCB_UNLOCK(volume);
}
else
{
VCB_LOCK(volume);
volume->vcbNmFls += valenceDelta; VCB_UNLOCK(volume);
}
}
return result;
}
UInt16
GetCatalogRecordSize(const CatalogRecord *dataRecord)
{
switch (dataRecord->recordType)
{
case kHFSFileRecord:
return sizeof(HFSCatalogFile);
case kHFSFolderRecord:
return sizeof(HFSCatalogFolder);
case kHFSPlusFileRecord:
return sizeof(HFSPlusCatalogFile);
case kHFSPlusFolderRecord:
return sizeof(HFSPlusCatalogFolder);
case kHFSFolderThreadRecord:
case kHFSFileThreadRecord:
return sizeof(HFSCatalogThread);
case kHFSPlusFolderThreadRecord:
case kHFSPlusFileThreadRecord:
return sizeof(HFSPlusCatalogThread);
default:
return 0;
}
}
void
CopyCatalogNodeData(const ExtendedVCB *volume, const CatalogRecord *dataPtr, CatalogNodeData *nodeData)
{
if (dataPtr->recordType == kHFSFolderRecord) {
nodeData->cnd_type = kCatalogFolderNode;
nodeData->cnd_flags = dataPtr->hfsFolder.flags;
nodeData->cnd_nodeID = dataPtr->hfsFolder.folderID;
nodeData->cnd_createDate = LocalToUTC(dataPtr->hfsFolder.createDate);
nodeData->cnd_contentModDate = LocalToUTC(dataPtr->hfsFolder.modifyDate);
nodeData->cnd_backupDate = LocalToUTC(dataPtr->hfsFolder.backupDate);
nodeData->cnd_valence = dataPtr->hfsFolder.valence;
BlockMoveData(&dataPtr->hfsFolder.userInfo, &nodeData->cnd_finderInfo, 32);
} else if (dataPtr->recordType == kHFSFileRecord) {
UInt32 i;
nodeData->cnd_type = kCatalogFileNode;
nodeData->cnd_flags = dataPtr->hfsFile.flags;
nodeData->cnd_nodeID = dataPtr->hfsFile.fileID;
nodeData->cnd_createDate = LocalToUTC(dataPtr->hfsFile.createDate);
nodeData->cnd_contentModDate = LocalToUTC(dataPtr->hfsFile.modifyDate);
nodeData->cnd_backupDate = LocalToUTC(dataPtr->hfsFile.backupDate);
nodeData->cnd_linkCount = 0;
BlockMoveData(&dataPtr->hfsFile.userInfo, &nodeData->cnd_finderInfo, 16);
BlockMoveData(&dataPtr->hfsFile.finderInfo, (void*)((UInt32)&nodeData->cnd_finderInfo + 16), 16);
nodeData->cnd_datafork.logicalSize = dataPtr->hfsFile.dataLogicalSize;
nodeData->cnd_datafork.totalBlocks =
dataPtr->hfsFile.dataPhysicalSize / volume->blockSize;
nodeData->cnd_rsrcfork.logicalSize = dataPtr->hfsFile.rsrcLogicalSize;
nodeData->cnd_rsrcfork.totalBlocks =
dataPtr->hfsFile.rsrcPhysicalSize / volume->blockSize;
for (i = 0; i < kHFSExtentDensity; ++i) {
nodeData->cnd_datafork.extents[i].startBlock =
(UInt32) (dataPtr->hfsFile.dataExtents[i].startBlock);
nodeData->cnd_datafork.extents[i].blockCount =
(UInt32) (dataPtr->hfsFile.dataExtents[i].blockCount);
nodeData->cnd_rsrcfork.extents[i].startBlock =
(UInt32) (dataPtr->hfsFile.rsrcExtents[i].startBlock);
nodeData->cnd_rsrcfork.extents[i].blockCount =
(UInt32) (dataPtr->hfsFile.rsrcExtents[i].blockCount);
}
for (i = kHFSExtentDensity; i < kHFSPlusExtentDensity; ++i) {
nodeData->cnd_datafork.extents[i].startBlock = 0;
nodeData->cnd_datafork.extents[i].blockCount = 0;
nodeData->cnd_rsrcfork.extents[i].startBlock = 0;
nodeData->cnd_rsrcfork.extents[i].blockCount = 0;
}
} else {
nodeData->cnd_type = 0;
}
}
void
CopyCatalogName(const CatalogName *srcName, CatalogName *dstName, Boolean isHFSPLus)
{
UInt32 length;
if ( srcName == NULL )
{
if ( dstName != NULL )
dstName->ustr.length = 0; return;
}
if (isHFSPLus)
length = sizeof(UniChar) * (srcName->ustr.length + 1);
else
length = sizeof(UInt8) + srcName->pstr[0];
if ( length > 1 )
BlockMoveData(srcName, dstName, length);
else
dstName->ustr.length = 0; }
UInt32
CatalogNameLength(const CatalogName *name, Boolean isHFSPlus)
{
if (isHFSPlus)
return name->ustr.length;
else
return name->pstr[0];
}