#pragma segment Catalog
#include <sys/param.h>
#include <sys/utfconv.h>
#include "../../hfs_endian.h"
#include "../headers/FileMgrInternal.h"
#include "../headers/BTreesInternal.h"
#include "../headers/CatalogPrivate.h"
#include "../headers/HFSUnicodeWrappers.h"
#include "../headers/HFSInstrumentation.h"
extern SInt32 FastRelString( ConstStr255Param str1, ConstStr255Param str2 );
extern SInt16 RelString_Glue(StringPtr pStr1, StringPtr pStr2);
static OSErr IterateCatalogNode(ExtendedVCB *volume, CatalogIterator *catalogIterator,
UInt16 index, CatalogNodeData *nodeData,
HFSCatalogNodeID *nodeID, SInt16 *nodeType);
void InitCatalogThreadRecord(ExtendedVCB *volume, UInt32 nodeType, CatalogKey *nodeKey,
CatalogRecord *record, UInt32 *threadSize);
void InitCatalogRecord(ExtendedVCB *volume, UInt32 nodeType, UInt32 textEncoding,
CatalogRecord *record, UInt32 *recordSize, HFSCatalogNodeID catalogNodeID);
#if HFS_DIAGNOSTIC
#include <sys/systm.h>
#define PRINTIT(A) kprintf A;
#else
#define PRINTIT(A)
#endif
OSErr
CreateCatalogNode ( ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name,
UInt32 nodeType, HFSCatalogNodeID *catalogNodeID, UInt32 *catalogHint)
{
CatalogKey *nodeKey;
CatalogRecord nodeData; UInt32 nodeDataSize;
CatalogRecord parentThreadData; HFSCatalogNodeID parentsParentID;
CatalogName *parentNamePtr;
UInt32 tempHint;
UInt32 textEncoding;
UInt16 tempSize;
OSErr result;
Boolean isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
FCB *fcb;
FSBufferDescriptor btRecord;
BTreeIterator iterator;
BTreeIterator threadIter;
HFSCatalogNodeID nextCNID;
if (nodeType != kCatalogFolderNode && nodeType != kCatalogFileNode)
return paramErr;
fcb = GetFileControlBlock(volume->catalogRefNum);
nodeKey = (CatalogKey *) &iterator.key;
result = LocateCatalogThread(volume, parentID, &parentThreadData, &tempSize, &tempHint);
ReturnIfError(result);
TrashCatalogIterator(volume, parentID);
if (isHFSPlus)
{
if (parentThreadData.recordType != kHFSPlusFolderThreadRecord)
return dirNFErr;
parentsParentID = parentThreadData.hfsPlusThread.parentID;
parentNamePtr = (CatalogName*) &parentThreadData.hfsPlusThread.nodeName;
}
else
{
if (parentThreadData.recordType != kHFSFolderThreadRecord)
return dirNFErr;
parentsParentID = parentThreadData.hfsThread.parentID;
parentNamePtr = (CatalogName*) &parentThreadData.hfsThread.nodeName;
}
InvalidateCatalogNodeCache(volume, parentsParentID);
result = BuildCatalogKeyUTF8(volume, parentID, name, kUndefinedStrLen, nodeKey, &textEncoding);
ReturnIfError(result);
result = BTSearchRecord(fcb, &iterator, kInvalidMRUCacheKey, NULL, NULL, &iterator);
if (result != btNotFound)
return (cmExists);
nextCNID = volume->vcbNxtCNID;
if (!isHFSPlus && nextCNID == 0xFFFFFFFF)
return (dskFulErr);
if (isHFSPlus || nodeType == kCatalogFolderNode)
{
CatalogRecord threadData; UInt32 threadSize;
btRecord.bufferAddress = &threadData;
btRecord.itemSize = threadSize;
btRecord.itemCount = 1;
InitCatalogThreadRecord(volume, nodeType, nodeKey, &threadData, &threadSize);
TryNextID:
BuildCatalogKey(nextCNID, NULL, isHFSPlus, (CatalogKey*) &threadIter.key);
result = BTInsertRecord(fcb, &threadIter, &btRecord, threadSize);
if (result == btExists && isHFSPlus)
{
++nextCNID;
if (nextCNID < kHFSFirstUserCatalogNodeID)
{
volume->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
volume->vcbFlags |= 0xFF00;
nextCNID = kHFSFirstUserCatalogNodeID;
}
goto TryNextID;
}
ReturnIfError(result);
}
btRecord.bufferAddress = &nodeData;
btRecord.itemSize = nodeDataSize;
btRecord.itemCount = 1;
InitCatalogRecord(volume, nodeType, textEncoding, &nodeData, &nodeDataSize, nextCNID);
result = BTInsertRecord(fcb, &iterator, &btRecord, nodeDataSize );
if (result)
{
if (result == btExists)
result = cmExists;
if (isHFSPlus || nodeType == kCatalogFolderNode)
(void) BTDeleteRecord(fcb, &threadIter);
return result;
}
*catalogNodeID = nextCNID;
if (++nextCNID < kHFSFirstUserCatalogNodeID)
{
volume->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
volume->vcbFlags |= 0xFF00;
nextCNID = kHFSFirstUserCatalogNodeID;
}
volume->vcbNxtCNID = nextCNID;
result = UpdateFolderCount( volume, parentsParentID, parentNamePtr, nodeData.recordType, kNoHint, +1);
ReturnIfError(result);
AdjustVolumeCounts(volume, nodeData.recordType, +1);
result = FlushCatalog(volume);
return result;
}
void InitCatalogRecord(ExtendedVCB *volume, UInt32 nodeType, UInt32 textEncoding, CatalogRecord *record, UInt32 *recordSize, HFSCatalogNodeID nodeID)
{
UInt32 timeStamp;
ClearMemory(record, sizeof(CatalogRecord));
if (volume->vcbSigWord == kHFSPlusSigWord)
{
timeStamp = GetTimeUTC();
UpdateVolumeEncodings(volume, textEncoding);
if (nodeType == kCatalogFolderNode )
{
record->recordType = kHFSPlusFolderRecord;
record->hfsPlusFolder.folderID = nodeID;
record->hfsPlusFolder.createDate = timeStamp;
record->hfsPlusFolder.contentModDate = timeStamp;
record->hfsPlusFolder.accessDate = timeStamp;
record->hfsPlusFolder.attributeModDate = timeStamp;
record->hfsPlusFolder.textEncoding = textEncoding;
*recordSize = sizeof(HFSPlusCatalogFolder);
}
else if (nodeType == kCatalogFileNode )
{
record->recordType = kHFSPlusFileRecord;
record->hfsPlusFile.fileID = nodeID;
record->hfsPlusFile.createDate = timeStamp;
record->hfsPlusFile.contentModDate = timeStamp;
record->hfsPlusFile.accessDate = timeStamp;
record->hfsPlusFile.attributeModDate = timeStamp;
record->hfsPlusFile.flags |= kHFSThreadExistsMask;
record->hfsPlusFile.textEncoding = textEncoding;
*recordSize = sizeof(HFSPlusCatalogFile);
}
}
else
{
timeStamp = GetTimeLocal(true);
if (nodeType == kCatalogFolderNode )
{
record->recordType = kHFSFolderRecord;
record->hfsFolder.folderID = nodeID;
record->hfsFolder.createDate = timeStamp;
record->hfsFolder.modifyDate = timeStamp;
*recordSize = sizeof(HFSCatalogFolder);
}
else if (nodeType == kCatalogFileNode )
{
record->recordType = kHFSFileRecord;
record->hfsFile.fileID = nodeID;
record->hfsFile.createDate = timeStamp;
record->hfsFile.modifyDate = timeStamp;
*recordSize = sizeof(HFSCatalogFile);
}
}
}
void InitCatalogThreadRecord(ExtendedVCB *volume, UInt32 nodeType, CatalogKey *nodeKey,
CatalogRecord *record, UInt32 *threadSize)
{
ClearMemory(record, sizeof(CatalogRecord) );
if (volume->vcbSigWord == kHFSPlusSigWord)
{
if (nodeType == kCatalogFolderNode)
record->recordType = kHFSPlusFolderThreadRecord;
else
record->recordType = kHFSPlusFileThreadRecord;
record->hfsPlusThread.parentID = nodeKey->hfsPlus.parentID;
*threadSize = sizeof(record->hfsPlusThread);
*threadSize -= ( sizeof(record->hfsPlusThread.nodeName.unicode) -
(nodeKey->hfsPlus.nodeName.length * sizeof(UniChar)) );
BlockMoveData(&nodeKey->hfsPlus.nodeName, &record->hfsPlusThread.nodeName,
sizeof(UniChar) * (nodeKey->hfsPlus.nodeName.length + 1));
}
else {
if (nodeType == kCatalogFolderNode)
record->recordType = kHFSFolderThreadRecord;
else
record->recordType = kHFSFileThreadRecord;
record->hfsThread.parentID = nodeKey->hfs.parentID;
*threadSize = sizeof(record->hfsThread);
BlockMoveData(&nodeKey->hfs.nodeName, &record->hfsThread.nodeName,
nodeKey->hfs.nodeName[0] + 1);
}
}
OSErr
DeleteCatalogNode(ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name, UInt32 hint)
{
CatalogKey key; CatalogRecord data; UInt32 nodeHint;
HFSCatalogNodeID nodeID;
HFSCatalogNodeID nodeParentID;
UInt16 nodeType;
OSErr result;
Boolean isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
result = BuildCatalogKeyUTF8(volume, parentID, name, kUndefinedStrLen, &key, NULL);
ReturnIfError(result);
result = LocateCatalogNodeByKey(volume, hint, &key, &data, &nodeHint);
if ( (result == cmNotFound) && isHFSPlus )
result = LocateCatalogNodeByMangledName(volume, parentID, name, kUndefinedStrLen, &key, &data, &nodeHint);
ReturnIfError(result);
nodeParentID = isHFSPlus ? key.hfsPlus.parentID : key.hfs.parentID; nodeType = data.recordType; nodeID = 0;
switch (nodeType)
{
case kHFSFolderRecord:
if (data.hfsFolder.valence != 0) return cmNotEmpty;
nodeID = data.hfsFolder.folderID;
break;
case kHFSPlusFolderRecord:
if (data.hfsPlusFolder.valence != 0) return cmNotEmpty;
nodeID = data.hfsPlusFolder.folderID;
break;
case kHFSFileRecord:
if (data.hfsFile.flags & kHFSThreadExistsMask)
nodeID = data.hfsFile.fileID;
break;
case kHFSPlusFileRecord:
nodeID = data.hfsPlusFile.fileID; break;
default:
return cmNotFound;
}
if (nodeID == fsRtDirID) return cmRootCN;
TrashCatalogIterator(volume, nodeParentID); InvalidateCatalogNodeCache(volume, nodeParentID);
result = DeleteBTreeRecord(volume->catalogRefNum, &key);
ReturnIfError(result);
if ( nodeID )
{
CatalogKey threadKey;
BuildCatalogKey(nodeID, NULL, isHFSPlus, &threadKey);
(void) DeleteBTreeRecord(volume->catalogRefNum, &threadKey); }
result = UpdateFolderCount(volume, nodeParentID, NULL, nodeType, kNoHint, -1);
ReturnIfError(result);
AdjustVolumeCounts(volume, nodeType, -1);
result = FlushCatalog(volume);
return result;
}
OSErr
GetCatalogNode( ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name, UInt32 nameLen, UInt32 hint,
CatalogNodeData *nodeData, UInt32 *newHint)
{
CatalogKey *key;
CatalogRecord *record;
BTreeIterator searchIterator;
FSBufferDescriptor btRecord;
ByteCount utf8len;
UInt32 heuristicHint;
UInt32 *cachedHint;
FCB *fcb;
OSErr result = noErr;
UInt16 dataSize;
Boolean isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
if (isHFSPlus) {
btRecord.bufferAddress = nodeData;
btRecord.itemSize = sizeof(CatalogNodeData);
} else {
btRecord.bufferAddress = &nodeData->cnd_extra;
btRecord.itemSize = sizeof(HFSCatalogFile);
}
btRecord.itemCount = 1;
record = (CatalogRecord *) btRecord.bufferAddress;
key = (CatalogKey *) &searchIterator.key;
if (name && nameLen == kUndefinedStrLen)
nameLen = strlen(name);
result = BuildCatalogKeyUTF8(volume, parentID, name, nameLen, key, NULL);
ReturnIfError(result);
fcb = GetFileControlBlock(volume->catalogRefNum);
searchIterator.hint.nodeNum = *newHint;
searchIterator.hint.index = 0;
if (name != NULL && GetMRUCacheBlock(parentID, volume->hintCachePtr, (Ptr *)&cachedHint) == 0)
heuristicHint = *cachedHint;
else
heuristicHint = kInvalidMRUCacheKey;
result = BTSearchRecord(fcb, &searchIterator, heuristicHint, &btRecord, &dataSize, &searchIterator);
if (result == btNotFound)
result = cmNotFound;
if (name != NULL && result == noErr)
InsertMRUCacheBlock(volume->hintCachePtr, parentID, (Ptr) &(searchIterator.hint.nodeNum));
if (result == noErr) {
CatalogName *nodeName = NULL;
HFSCatalogNodeID threadParentID;
switch (record->recordType) {
case kHFSFileThreadRecord:
case kHFSFolderThreadRecord:
threadParentID = record->hfsThread.parentID;
nodeName = (CatalogName *) &record->hfsThread.nodeName;
break;
case kHFSPlusFileThreadRecord:
case kHFSPlusFolderThreadRecord:
threadParentID = record->hfsPlusThread.parentID;
nodeName = (CatalogName *) &record->hfsPlusThread.nodeName;
break;
default:
threadParentID = 0;
*newHint = searchIterator.hint.nodeNum;
break;
}
if (threadParentID) {
BuildCatalogKey(threadParentID, nodeName, isHFSPlus, key);
searchIterator.hint.nodeNum = kNoHint;
searchIterator.hint.index = 0;
result = BTSearchRecord(fcb, &searchIterator, kInvalidMRUCacheKey, &btRecord, &dataSize, &searchIterator);
if (result == btNotFound)
result = cmNotFound;
if (result == noErr)
*newHint = searchIterator.hint.nodeNum;
}
}
if ( result == cmNotFound && isHFSPlus)
result = LocateCatalogNodeByMangledName(volume, parentID, name, nameLen,
key, record, newHint);
if (result == cmNotFound && !isHFSPlus) {
Str31 hfsName;
utf8_to_mac_roman(nameLen, name, hfsName);
result = LocateCatalogNode(volume, parentID, (CatalogName*)hfsName,
0, key, record, newHint);
}
ReturnIfError(result);
nodeData->cnm_parID = isHFSPlus ? key->hfsPlus.parentID : key->hfs.parentID;
if (nodeData->cnm_flags & kCatNameNoCopyName) {
if (! isHFSPlus) {
if (record->recordType == kHFSFolderRecord || record->recordType == kHFSFileRecord)
CopyCatalogNodeData(volume, record, nodeData);
else
result = cmNotFound;
}
}
else {
nodeData->cnm_nameptr = nodeData->cnm_namespace;
if ( isHFSPlus ) {
result = utf8_encodestr(key->hfsPlus.nodeName.unicode,
key->hfsPlus.nodeName.length * sizeof(UniChar),
nodeData->cnm_namespace, (size_t *)&utf8len,
MAXHFSVNODELEN + 1, ':', 0);
if (result == ENAMETOOLONG) {
utf8len = utf8_encodelen(key->hfsPlus.nodeName.unicode,
key->hfsPlus.nodeName.length * sizeof(UniChar),
':', 0);
nodeData->cnm_nameptr = NewPtr(utf8len + 1);
nodeData->cnm_flags |= kCatNameIsAllocated;
result = utf8_encodestr(key->hfsPlus.nodeName.unicode,
key->hfsPlus.nodeName.length * sizeof(UniChar),
nodeData->cnm_nameptr, (size_t *)&utf8len,
utf8len + 1, ':', 0);
}
}
else {
if (record->recordType == kHFSFolderRecord || record->recordType == kHFSFileRecord) {
CopyCatalogNodeData(volume, record, nodeData);
result = hfs_to_utf8(volume, key->hfs.nodeName, MAXHFSVNODELEN + 1,
&utf8len, nodeData->cnm_namespace);
if (result == ENAMETOOLONG) {
nodeData->cnm_nameptr = NewPtr(utf8len + 1);
nodeData->cnm_flags |= kCatNameIsAllocated;
result = hfs_to_utf8(volume, key->hfs.nodeName, utf8len + 1,
&utf8len, nodeData->cnm_nameptr);
} else if (result) {
result = mac_roman_to_utf8(key->hfs.nodeName, MAXHFSVNODELEN + 1,
&utf8len, nodeData->cnm_namespace);
}
} else
result = cmNotFound;
}
nodeData->cnm_length = utf8len;
if (result && (nodeData->cnm_flags & kCatNameIsAllocated))
{
DisposePtr(nodeData->cnm_nameptr);
nodeData->cnm_flags &= ~kCatNameIsAllocated;
nodeData->cnm_nameptr = 0;
}
}
#if DEBUG_BUILD
if ( nodeData->cnd_nodeID > volume->vcbNxtCNID || nodeData->cnd_nodeID == 0)
DebugStr("\pGetCatalogNode bad file ID found!");
#endif
return result;
}
UInt32
GetDirEntrySize(BTreeIterator *bip, ExtendedVCB * vol)
{
CatalogKey * ckp;
CatalogName * cnp;
ByteCount utf8chars;
UInt8 name[kdirentMaxNameBytes + 1];
OSErr result;
ckp = (CatalogKey*) &bip->key;
if (vol->vcbSigWord == kHFSPlusSigWord) {
cnp = (CatalogName*) &ckp->hfsPlus.nodeName;
utf8chars = utf8_encodelen(cnp->ustr.unicode,
cnp->ustr.length * sizeof(UniChar), ':', 0);
if (utf8chars > kdirentMaxNameBytes)
utf8chars = kdirentMaxNameBytes;
utf8chars++;
} else {
cnp = (CatalogName*) ckp->hfs.nodeName;
result = hfs_to_utf8(vol, cnp->pstr, kdirentMaxNameBytes + 1,
&utf8chars, name);
}
return DIRENTRY_SIZE(utf8chars);
}
OSErr
PositionIterator(CatalogIterator *cip, UInt32 offset, BTreeIterator *bip, UInt16 *op)
{
#define CAT_START_OFFSET (2 * sizeof(struct hfsdotentry))
ExtendedVCB * vol;
FCB * fcb;
OSErr result = 0;
if (cip->folderID != cip->parentID)
return(cmNotFound);
vol = cip->volume;
fcb = GetFileControlBlock(vol->catalogRefNum);
UpdateBtreeIterator(cip, bip);
if (cip->currentOffset == offset) {
*op = kBTreeCurrentRecord;
} else if (cip->nextOffset == offset) {
*op = kBTreeNextRecord;
} else {
UInt32 heuristicHint;
UInt32 *cachedHint;
*op = kBTreeNextRecord;
result = GetMRUCacheBlock( cip->folderID, vol->hintCachePtr, (Ptr *)&cachedHint );
heuristicHint = (result == noErr) ? *cachedHint : kInvalidMRUCacheKey;
result = BTSearchRecord(fcb, bip, heuristicHint, NULL, NULL, bip);
if (result)
goto exit;
InsertMRUCacheBlock( vol->hintCachePtr, cip->folderID, (Ptr) &bip->hint.nodeNum );
if (offset > CAT_START_OFFSET) {
HFSCatalogNodeID pid, *idp;
UInt32 curOffset, nextOffset;
result = BTIterateRecord( fcb, kBTreeNextRecord, bip, NULL, NULL );
if (result)
goto exit;
if (vol->vcbSigWord == kHFSPlusSigWord)
idp = &((CatalogKey*) &bip->key)->hfsPlus.parentID;
else
idp = &((CatalogKey*) &bip->key)->hfs.parentID;
pid = *idp;
curOffset = CAT_START_OFFSET;
nextOffset = GetDirEntrySize(bip, vol);
while (nextOffset < offset) {
result = BTIterateRecord( fcb, kBTreeNextRecord, bip, NULL, NULL );
if (result)
goto exit;
if (pid != *idp) {
result = cmNotFound;
goto exit;
}
curOffset = nextOffset;
nextOffset += GetDirEntrySize(bip, vol);
};
if (nextOffset != offset) {
result = cmNotFound;
goto exit;
}
UpdateCatalogIterator(bip, cip);
cip->currentOffset = curOffset;
cip->nextOffset = nextOffset;
}
}
exit:
if (result == btNotFound)
result = cmNotFound;
return result;
}
OSErr
GetCatalogOffspring(ExtendedVCB *volume, HFSCatalogNodeID folderID, UInt16 index,
CatalogNodeData *nodeData,
HFSCatalogNodeID *nodeID, SInt16 *nodeType)
{
CatalogIterator * catalogIterator;
OSErr result;
if ( folderID == 0 )
return cmNotFound;
if ( index == 32767 )
return cmNotFound;
catalogIterator = oGetCatalogIterator(volume, folderID, index);
result = IterateCatalogNode(volume, catalogIterator, index,
nodeData, nodeID, nodeType);
(void) ReleaseCatalogIterator(catalogIterator);
return result;
}
static OSErr
IterateCatalogNode( ExtendedVCB *volume, CatalogIterator *catalogIterator, UInt16 index,
CatalogNodeData *nodeData, HFSCatalogNodeID *nodeID,
SInt16 *nodeType )
{
HFSCatalogNodeID offspringParentID;
CatalogKey * offspringKey;
CatalogName * offspringName;
BTreeIterator btreeIterator;
FSBufferDescriptor btRecord;
CatalogRecord * record;
UInt8 databuf[32];
FCB * fcb;
SInt16 selectionIndex;
UInt16 tempSize;
UInt16 operation;
OSErr result;
Boolean isHFSPlus;
ByteCount utf8len;
isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
fcb = GetFileControlBlock(volume->catalogRefNum);
UpdateBtreeIterator(catalogIterator, &btreeIterator);
if (nodeData == NULL) {
btRecord.bufferAddress = databuf;
btRecord.itemSize = sizeof(databuf);
} else if (isHFSPlus) {
btRecord.bufferAddress = nodeData;
btRecord.itemSize = sizeof(CatalogNodeData);
} else {
btRecord.bufferAddress = &nodeData->cnd_extra;
btRecord.itemSize = sizeof(HFSCatalogFile);
}
btRecord.itemCount = 1;
if ( catalogIterator->currentIndex == 0 ) {
UInt32 heuristicHint;
UInt32 *cachedHint;
result = GetMRUCacheBlock( catalogIterator->folderID, volume->hintCachePtr, (Ptr *)&cachedHint );
heuristicHint = (result == noErr) ? *cachedHint : kInvalidMRUCacheKey;
result = BTSearchRecord( fcb, &btreeIterator, heuristicHint, &btRecord, &tempSize, &btreeIterator );
ExitOnError(result);
UpdateCatalogIterator(&btreeIterator, catalogIterator);
InsertMRUCacheBlock( volume->hintCachePtr, catalogIterator->folderID, (Ptr) &btreeIterator.hint.nodeNum );
}
selectionIndex = index - catalogIterator->currentIndex;
if (selectionIndex == 1)
{
operation = kBTreeNextRecord;
}
else if (selectionIndex == -1)
{
operation = kBTreePrevRecord;
}
else if (selectionIndex == 0)
{
operation = kBTreeCurrentRecord;
}
else if (selectionIndex > 1)
{
UInt32 i;
for (i = 1; i < selectionIndex; ++i)
{
result = BTIterateRecord( fcb, kBTreeNextRecord, &btreeIterator, &btRecord, &tempSize );
ExitOnError(result);
}
operation = kBTreeNextRecord;
}
else {
SInt32 i;
for (i = -1; i > selectionIndex; --i)
{
result = BTIterateRecord( fcb, kBTreePrevRecord, &btreeIterator, &btRecord, &tempSize );
ExitOnError(result);
}
operation = kBTreePrevRecord;
}
result = BTIterateRecord( fcb, operation, &btreeIterator, &btRecord, &tempSize );
ExitOnError(result);
offspringKey = (CatalogKey*) &btreeIterator.key;
if (isHFSPlus)
{
offspringParentID = offspringKey->hfsPlus.parentID;
offspringName = (CatalogName*) &offspringKey->hfsPlus.nodeName;
}
else
{
offspringParentID = offspringKey->hfs.parentID;
offspringName = (CatalogName*) offspringKey->hfs.nodeName;
}
if (offspringParentID != catalogIterator->folderID) {
AgeCatalogIterator(catalogIterator);
result = cmNotFound; goto ErrorExit;
}
UpdateCatalogIterator(&btreeIterator, catalogIterator); catalogIterator->currentIndex = index;
record = (CatalogRecord *) btRecord.bufferAddress;
if (nodeData == NULL) {
if (isHFSPlus)
{
*nodeType = record->recordType;
*nodeID = record->hfsPlusFolder.folderID;
}
else
{
if (record->recordType == kHFSFileRecord) {
*nodeType = kCatalogFileNode;
*nodeID = record->hfsFile.fileID;
} else if (record->recordType == kHFSFolderRecord) {
*nodeType = kCatalogFolderNode;
*nodeID = record->hfsFolder.folderID;
} else
result = cmNotFound;
}
} else {
nodeData->cnm_parID = isHFSPlus ? offspringKey->hfsPlus.parentID : offspringKey->hfs.parentID;
nodeData->cnm_nameptr = nodeData->cnm_namespace;
if (isHFSPlus)
{
result = utf8_encodestr(offspringName->ustr.unicode,
offspringName->ustr.length * sizeof(UniChar),
nodeData->cnm_namespace, (size_t *)&utf8len,
MAXHFSVNODELEN + 1, ':', 0);
if (result == ENAMETOOLONG) {
utf8len = utf8_encodelen(offspringName->ustr.unicode,
offspringName->ustr.length * sizeof(UniChar),
':', 0);
nodeData->cnm_nameptr = NewPtr(utf8len + 1);
nodeData->cnm_flags |= kCatNameIsAllocated;
result = utf8_encodestr(offspringName->ustr.unicode,
offspringName->ustr.length * sizeof(UniChar),
nodeData->cnm_nameptr, (size_t *)&utf8len,
utf8len + 1, ':', 0);
}
}
else
{
if (record->recordType == kHFSFolderRecord || record->recordType == kHFSFileRecord) {
CopyCatalogNodeData(volume, record, nodeData);
result = hfs_to_utf8(volume, offspringName->pstr, MAXHFSVNODELEN + 1, &utf8len, nodeData->cnm_namespace);
if (result == ENAMETOOLONG) {
nodeData->cnm_nameptr = NewPtr(utf8len+1);
nodeData->cnm_flags |= kCatNameIsAllocated;
result = hfs_to_utf8(volume, offspringName->pstr, utf8len + 1, &utf8len, nodeData->cnm_nameptr);
}
} else {
result = cmNotFound;
}
}
}
return result;
ErrorExit:
if ( result == btNotFound )
result = cmNotFound;
return result;
}
OSErr
MoveRenameCatalogNode(ExtendedVCB *volume, HFSCatalogNodeID srcParentID, ConstUTF8Param srcName,
UInt32 srcHint, HFSCatalogNodeID dstParentID, ConstUTF8Param dstName, UInt32 *newHint)
{
CatalogKey srcKey; CatalogRecord srcRecord; CatalogKey dstKey; CatalogKey dstFolderKey; HFSCatalogNodeID dstFolderParentID = 0;
UInt32 dstFolderHint;
CatalogName *dstFolderNamePtr = NULL;
CatalogRecord tmpRecord; HFSCatalogNodeID threadID;
UInt32 textEncoding;
OSErr result;
Boolean isNewName;
Boolean isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
Boolean isOrigDeleted = false;
short srcNameLen;
short dstNameLen;
result = BuildCatalogKeyUTF8(volume, srcParentID, srcName, kUndefinedStrLen, &srcKey, &textEncoding);
ReturnIfError(result);
srcNameLen = strlen(srcName);
dstNameLen = strlen(dstName);
if ((srcNameLen == dstNameLen) && (bcmp(srcName, dstName, srcNameLen) == 0))
{
isNewName = false;
dstKey = srcKey;
if ( isHFSPlus ) {
dstKey.hfsPlus.parentID = dstParentID; }
else {
dstKey.hfs.parentID = dstParentID; }
}
else
{
isNewName = true;
result = BuildCatalogKeyUTF8(volume, dstParentID, dstName, kUndefinedStrLen, &dstKey, &textEncoding);
ReturnIfError(result);
}
result = LocateCatalogNodeByKey(volume, srcHint, &srcKey, &srcRecord, &srcHint);
if ( (result == cmNotFound) && isHFSPlus )
result = LocateCatalogNodeByMangledName(volume, srcParentID, srcName, kUndefinedStrLen, &srcKey, &srcRecord, &srcHint);
ReturnIfError(result);
srcParentID = (isHFSPlus ? srcKey.hfsPlus.parentID : srcKey.hfs.parentID);
if (srcParentID != dstParentID)
{
result = LocateCatalogNode(volume, dstParentID, NULL, kNoHint, &dstFolderKey, &tmpRecord, &dstFolderHint);
ReturnIfError(result);
if (tmpRecord.recordType == kHFSPlusFolderRecord)
{
dstParentID = tmpRecord.hfsPlusFolder.folderID;
dstFolderParentID = dstFolderKey.hfsPlus.parentID;
dstFolderNamePtr = (CatalogName*) &dstFolderKey.hfsPlus.nodeName;
}
else if (tmpRecord.recordType == kHFSFolderRecord)
{
dstParentID = tmpRecord.hfsFolder.folderID;
dstFolderParentID = dstFolderKey.hfs.parentID;
dstFolderNamePtr = (CatalogName*) &dstFolderKey.hfs.nodeName;
}
else
{
return badMovErr;
}
if (srcRecord.recordType == kHFSPlusFolderRecord || srcRecord.recordType == kHFSFolderRecord)
{
HFSCatalogNodeID srcFolderID;
HFSCatalogNodeID ancestorParentID;
CatalogKey tempKey; UInt32 tempHint;
if (isHFSPlus)
{
srcFolderID = srcRecord.hfsPlusFolder.folderID;
ancestorParentID = dstFolderKey.hfsPlus.parentID;
}
else
{
srcFolderID = srcRecord.hfsFolder.folderID;
ancestorParentID = dstFolderKey.hfs.parentID;
}
if ( srcFolderID == fsRtDirID || srcFolderID == dstParentID || srcFolderID == ancestorParentID ) {
return badMovErr;
}
while (ancestorParentID > fsRtDirID) {
result = LocateCatalogNode(volume, ancestorParentID, NULL, kNoHint, &tempKey, &tmpRecord, &tempHint);
ReturnIfError(result);
ancestorParentID = isHFSPlus ? tempKey.hfsPlus.parentID : tempKey.hfs.parentID;
if (srcFolderID == ancestorParentID) return badMovErr;
}
}
TrashCatalogIterator(volume, dstParentID); }
else
{
if ( !isNewName )
{
*newHint = srcHint; return noErr;
}
}
TrashCatalogIterator(volume, srcParentID); InvalidateCatalogNodeCache(volume, srcParentID);
if (isNewName && isHFSPlus)
{
srcRecord.hfsPlusFolder.textEncoding = textEncoding;
UpdateVolumeEncodings(volume, textEncoding);
}
result = InsertBTreeRecord(volume->catalogRefNum, &dstKey, &srcRecord, GetCatalogRecordSize(&srcRecord), newHint);
if (result == btExists)
{
UInt16 dataSize;
if (srcParentID != dstParentID || isNewName == false)
return cmExists;
result = SearchBTreeRecord(volume->catalogRefNum, &dstKey, srcHint,
&dstFolderKey, &tmpRecord, &dataSize, newHint);
if (result == btNotFound)
result = cmNotFound;
ReturnIfError(result);
if (srcRecord.recordType != tmpRecord.recordType)
return cmExists;
switch (srcRecord.recordType)
{
case kHFSPlusFileRecord:
case kHFSPlusFolderRecord:
if (srcRecord.hfsPlusFolder.folderID != tmpRecord.hfsPlusFolder.folderID)
return cmExists;
break;
case kHFSFolderRecord:
if (srcRecord.hfsFolder.folderID != tmpRecord.hfsFolder.folderID)
return cmExists;
break;
case kHFSFileRecord:
if (srcRecord.hfsFile.fileID != tmpRecord.hfsFile.fileID)
return cmExists;
break;
default:
return cmExists;
}
result = DeleteBTreeRecord(volume->catalogRefNum, &srcKey);
ReturnIfError(result);
isOrigDeleted = true;
result = InsertBTreeRecord(volume->catalogRefNum, &dstKey, &srcRecord, dataSize, newHint);
}
ReturnIfError(result);
switch (srcRecord.recordType)
{
case kHFSPlusFileRecord:
case kHFSPlusFolderRecord:
threadID = srcRecord.hfsPlusFolder.folderID;
break;
case kHFSFolderRecord:
threadID = srcRecord.hfsFolder.folderID;
break;
case kHFSFileRecord:
if (srcRecord.hfsFile.flags & kHFSThreadExistsMask)
{
threadID = srcRecord.hfsFile.fileID;
break;
}
default:
threadID = 0;
}
if (threadID)
{
UInt32 threadHint;
CatalogKey threadKey; CatalogRecord threadRecord; UInt16 threadSize;
result = LocateCatalogRecord(volume, threadID, NULL, kNoHint, &threadKey, &threadRecord, &threadHint);
if (result != noErr) goto Exit_Delete;
if (isHFSPlus)
{
if (srcParentID != dstParentID)
threadRecord.hfsPlusThread.parentID = dstParentID;
if (isNewName)
CopyCatalogName((CatalogName *)&dstKey.hfsPlus.nodeName, (CatalogName *) &threadRecord.hfsPlusThread.nodeName, isHFSPlus);
threadSize = sizeof(threadRecord.hfsPlusThread);
threadSize -= ( sizeof(threadRecord.hfsPlusThread.nodeName.unicode) - (threadRecord.hfsPlusThread.nodeName.length * sizeof(UniChar)) );
}
else
{
if (srcParentID != dstParentID)
threadRecord.hfsThread.parentID = dstParentID;
if (isNewName)
CopyCatalogName((CatalogName *)&dstKey.hfs.nodeName,(CatalogName *) threadRecord.hfsThread.nodeName, isHFSPlus);
threadSize = sizeof(threadRecord.hfsThread);
}
result = DeleteBTreeRecord(volume->catalogRefNum, &threadKey);
if (result != noErr) goto Exit_Delete;
result = InsertBTreeRecord(volume->catalogRefNum, &threadKey, &threadRecord, threadSize, &threadHint);
if (result != noErr) goto Exit_Delete; }
if (! isOrigDeleted) {
result = DeleteBTreeRecord(volume->catalogRefNum, &srcKey);
if (result)
{
(void) DeleteBTreeRecord(volume->catalogRefNum, &dstKey);
return result;
}
}
if (srcParentID != dstParentID)
{
result = UpdateFolderCount(volume, srcParentID, NULL, srcRecord.recordType, kNoHint, -1);
result = UpdateFolderCount(volume, dstFolderParentID, dstFolderNamePtr, srcRecord.recordType, dstFolderHint, +1);
}
VCB_LOCK(volume);
volume->vcbFlags |= 0xFF00; volume->vcbLsMod = GetTimeUTC(); VCB_UNLOCK(volume);
(void) FlushCatalog(volume);
return result;
Exit_Delete:
(void) DeleteBTreeRecord(volume->catalogRefNum, &dstKey);
return result;
}
OSErr
UpdateCatalogNode(ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name,
UInt32 catalogHint, const CatalogNodeData *nodeData)
{
CatalogKey *key;
CatalogRecord *record;
UInt32 hint;
UInt16 recordSize;
OSErr result;
CatalogKey catalogKey; CatalogRecord catalogRecord; Boolean isHFSPlus = volume->vcbSigWord == kHFSPlusSigWord;
key = &catalogKey;
record = &catalogRecord;
result = BuildCatalogKeyUTF8(volume, parentID, name, kUndefinedStrLen, key, NULL);
ReturnIfError(result);
result = LocateCatalogNodeByKey(volume, catalogHint, key, record, &hint);
if ( (result == cmNotFound) && isHFSPlus )
result = LocateCatalogNodeByMangledName(volume, parentID, name, kUndefinedStrLen, key, record, &hint);
if (result == btNotFound)
result = cmNotFound;
if (catalogHint != hint)
PRINTIT(("UpdateCatalogNode: catalogHint does not match (in: %ld, out: %ld)\n", catalogHint, hint));
ReturnIfError(result);
switch (record->recordType)
{
case kHFSFolderRecord:
{
#if DEBUG_BUILD
if (nodeData->cnd_type != kCatalogFolderNode)
DebugStr("\p UpdateCatalogNode: folder/file mismatch!");
#endif
record->hfsFolder.createDate = UTCToLocal(nodeData->cnd_createDate);
record->hfsFolder.modifyDate = UTCToLocal(nodeData->cnd_contentModDate);
record->hfsFolder.backupDate = UTCToLocal(nodeData->cnd_backupDate);
*(DInfo*) &record->hfsFolder.userInfo = *(DInfo*) &nodeData->cnd_finderInfo;
*(DXInfo*) &record->hfsFolder.finderInfo = *(DXInfo*) ((UInt32)&nodeData->cnd_finderInfo + 16);
recordSize = sizeof(HFSCatalogFolder);
break;
}
case kHFSFileRecord:
{
UInt32 i;
#if DEBUG_BUILD
if (nodeData->cnd_type != kCatalogFileNode)
DebugStr("UpdateCatalogNode: folder/file mismatch!");
if ((nodeData->cnd_datafork.totalBlocks > (0x7FFFFFFF/volume->blockSize)) ||
(nodeData->cnd_rsrcfork.totalBlocks > (0x7FFFFFFF/volume->blockSize)))
DebugStr("HFS file size is larger than 2Gig");
#endif
record->hfsFile.flags = (UInt8) nodeData->cnd_flags;
record->hfsFile.createDate = UTCToLocal(nodeData->cnd_createDate);
record->hfsFile.modifyDate = UTCToLocal(nodeData->cnd_contentModDate);
record->hfsFile.backupDate = UTCToLocal(nodeData->cnd_backupDate);
record->hfsFile.dataLogicalSize = nodeData->cnd_datafork.logicalSize;
record->hfsFile.dataPhysicalSize = nodeData->cnd_datafork.totalBlocks * volume->blockSize;
record->hfsFile.rsrcLogicalSize = nodeData->cnd_rsrcfork.logicalSize;
record->hfsFile.rsrcPhysicalSize = nodeData->cnd_rsrcfork.totalBlocks * volume->blockSize;
*(FInfo*) &record->hfsFile.userInfo = *(FInfo*) &nodeData->cnd_finderInfo;
*(FXInfo*) &record->hfsFile.finderInfo = *(FXInfo*) ((UInt32)&nodeData->cnd_finderInfo + 16);
for (i = 0; i < kHFSExtentDensity; ++i)
{
record->hfsFile.dataExtents[i].startBlock =
(UInt16) nodeData->cnd_datafork.extents[i].startBlock;
record->hfsFile.dataExtents[i].blockCount =
(UInt16) nodeData->cnd_datafork.extents[i].blockCount;
record->hfsFile.rsrcExtents[i].startBlock =
(UInt16) nodeData->cnd_rsrcfork.extents[i].startBlock;
record->hfsFile.rsrcExtents[i].blockCount =
(UInt16) nodeData->cnd_rsrcfork.extents[i].blockCount;
}
recordSize = sizeof(HFSCatalogFile);
break;
}
case kHFSPlusFolderRecord:
{
record->hfsPlusFolder.createDate = nodeData->cnd_createDate;
record->hfsPlusFolder.contentModDate = nodeData->cnd_contentModDate;
record->hfsPlusFolder.backupDate = nodeData->cnd_backupDate;
record->hfsPlusFolder.accessDate = nodeData->cnd_accessDate;
record->hfsPlusFolder.attributeModDate = nodeData->cnd_attributeModDate;
record->hfsPlusFolder.bsdInfo.ownerID = nodeData->cnd_ownerID;
record->hfsPlusFolder.bsdInfo.groupID = nodeData->cnd_groupID;
record->hfsPlusFolder.bsdInfo.ownerFlags = nodeData->cnd_ownerFlags;
record->hfsPlusFolder.bsdInfo.adminFlags = nodeData->cnd_adminFlags;
record->hfsPlusFolder.bsdInfo.fileMode = nodeData->cnd_mode;
record->hfsPlusFolder.textEncoding = nodeData->cnd_textEncoding;
BlockMoveData(&nodeData->cnd_finderInfo, &record->hfsPlusFolder.userInfo, 32);
recordSize = sizeof(HFSPlusCatalogFolder);
break;
}
case kHFSPlusFileRecord:
{
record->hfsPlusFile.flags = nodeData->cnd_flags;
record->hfsPlusFile.createDate = nodeData->cnd_createDate;
record->hfsPlusFile.contentModDate = nodeData->cnd_contentModDate;
record->hfsPlusFile.backupDate = nodeData->cnd_backupDate;
record->hfsPlusFile.accessDate = nodeData->cnd_accessDate;
record->hfsPlusFile.attributeModDate = nodeData->cnd_attributeModDate;
record->hfsPlusFile.bsdInfo.ownerID = nodeData->cnd_ownerID;
record->hfsPlusFile.bsdInfo.groupID = nodeData->cnd_groupID;
record->hfsPlusFile.bsdInfo.ownerFlags = nodeData->cnd_ownerFlags;
record->hfsPlusFile.bsdInfo.adminFlags = nodeData->cnd_adminFlags;
record->hfsPlusFile.bsdInfo.fileMode = nodeData->cnd_mode;
record->hfsPlusFile.bsdInfo.special.rawDevice = nodeData->cnd_rawDevice;
record->hfsPlusFile.textEncoding = nodeData->cnd_textEncoding;
record->hfsPlusFile.dataFork.logicalSize = nodeData->cnd_datafork.logicalSize;
record->hfsPlusFile.dataFork.totalBlocks = nodeData->cnd_datafork.totalBlocks;
BlockMoveData(&nodeData->cnd_datafork.extents,
&record->hfsPlusFile.dataFork.extents, sizeof(HFSPlusExtentRecord));
record->hfsPlusFile.resourceFork.logicalSize = nodeData->cnd_rsrcfork.logicalSize;
record->hfsPlusFile.resourceFork.totalBlocks = nodeData->cnd_rsrcfork.totalBlocks;
BlockMoveData(&nodeData->cnd_rsrcfork.extents,
&record->hfsPlusFile.resourceFork.extents, sizeof(HFSPlusExtentRecord));
BlockMoveData(&nodeData->cnd_finderInfo, &record->hfsPlusFile.userInfo, 32);
#if HFS_HARDLINKS && DEBUG_BUILD
if (SWAP_BE32 (record->hfsPlusFile.userInfo.fdType) == kHardLinkFileType &&
SWAP_BE32 (record->hfsPlusFile.userInfo.fdCreator) == kHardLinkCreator) {
if (record->hfsPlusFile.dataFork.logicalSize != 0)
DebugStr("UpdateCatalogNode: link has data fork!");
}
#endif
recordSize = sizeof(HFSPlusCatalogFile);
break;
}
default:
return cmNotFound;
}
result = ReplaceBTreeRecord(volume->catalogRefNum, key, catalogHint, record, recordSize, &hint);
if ( result == btNotFound )
{
result = cmNotFound;
}
else if ( result == noErr )
{
if (nodeData->cnd_contentModDate > volume->vcbLsMod ||
(isHFSPlus && nodeData->cnd_attributeModDate > volume->vcbLsMod))
{
VCB_LOCK(volume);
volume->vcbFlags |= 0xFF00; volume->vcbLsMod = GetTimeUTC(); VCB_UNLOCK(volume);
}
result = FlushCatalog(volume); }
return result;
}
OSErr
CreateFileIDRef(ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name, UInt32 hint, HFSCatalogNodeID *threadID)
{
CatalogKey nodeKey; CatalogRecord nodeData; HFSCatalogKey threadKey;
HFSCatalogThread threadData;
UInt32 nodeHint;
UInt32 tempHint;
OSErr result;
Boolean isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
*threadID = 0;
result = BuildCatalogKeyUTF8(volume, parentID, name, kUndefinedStrLen, &nodeKey, NULL);
ReturnIfError(result);
result = LocateCatalogNodeByKey(volume, hint, &nodeKey, &nodeData, &nodeHint);
if ( (result == cmNotFound) && isHFSPlus )
result = LocateCatalogNodeByMangledName(volume, parentID, name, kUndefinedStrLen, &nodeKey, &nodeData, &nodeHint);
ReturnIfError(result);
if (nodeData.recordType == kHFSPlusFileRecord)
{
*threadID = nodeData.hfsPlusFile.fileID;
return noErr; }
if (nodeData.recordType != kHFSFileRecord)
{
return notAFileErr;
}
if (nodeData.hfsFile.flags & kHFSThreadExistsMask)
{
*threadID = nodeData.hfsFile.fileID;
return noErr; }
result = VolumeWritable( volume );
if ( result != noErr ) return result;
BuildCatalogKey(nodeData.hfsFile.fileID, NULL, false, (CatalogKey *)&threadKey);
ClearMemory(&threadData, sizeof(HFSCatalogThread));
threadData.recordType = kHFSFileThreadRecord;
threadData.parentID = nodeKey.hfs.parentID;
BlockMoveData(&nodeKey.hfs.nodeName, &threadData.nodeName, nodeKey.hfs.nodeName[0] + 1);
result = InsertBTreeRecord(volume->catalogRefNum, &threadKey, &threadData, sizeof(HFSCatalogThread), &tempHint);
if (result == btExists) result = noErr; ReturnIfError(result);
nodeData.hfsFile.flags |= kHFSThreadExistsMask;
result = ReplaceBTreeRecord(volume->catalogRefNum, &nodeKey, nodeHint, &nodeData, sizeof(HFSCatalogFile), &nodeHint );
if (result == noErr) {
(void) FlushCatalog(volume);
*threadID = nodeData.hfsFile.fileID;
}
return result;
}
SInt32
CompareCatalogKeys(HFSCatalogKey *searchKey, HFSCatalogKey *trialKey)
{
HFSCatalogNodeID searchParentID, trialParentID;
SInt32 result;
searchParentID = searchKey->parentID;
trialParentID = trialKey->parentID;
if ( searchParentID > trialParentID ) result = 1;
else if ( searchParentID < trialParentID )
result = -1;
else {
#if ( ! FORDISKFIRSTAID )
LogStartTime(kTraceRelString);
result = FastRelString(searchKey->nodeName, trialKey->nodeName);
LogEndTime(kTraceRelString, noErr);
#else
result = (SInt32) RelString_Glue(searchKey->nodeName, trialKey->nodeName);
#endif
}
return result;
}
SInt32
CompareExtendedCatalogKeys(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
{
SInt32 result;
HFSCatalogNodeID searchParentID, trialParentID;
searchParentID = searchKey->parentID;
trialParentID = trialKey->parentID;
if ( searchParentID > trialParentID ) {
result = 1;
}
else if ( searchParentID < trialParentID )
{
result = -1;
}
else {
if ( searchKey->nodeName.length == 0 || trialKey->nodeName.length == 0 )
result = searchKey->nodeName.length - trialKey->nodeName.length;
else
result = FastUnicodeCompare(&searchKey->nodeName.unicode[0], searchKey->nodeName.length,
&trialKey->nodeName.unicode[0], trialKey->nodeName.length);
}
return result;
}