#include #include #include #include static inline void flipBSDInfo(HFSPlusBSDInfo* info) { FLIPENDIAN(info->ownerID); FLIPENDIAN(info->groupID); FLIPENDIAN(info->fileMode); FLIPENDIAN(info->special); } static inline void flipPoint(Point* point) { FLIPENDIAN(point->v); FLIPENDIAN(point->h); } static inline void flipRect(Rect* rect) { FLIPENDIAN(rect->top); FLIPENDIAN(rect->left); FLIPENDIAN(rect->bottom); FLIPENDIAN(rect->right); } static inline void flipFolderInfo(FolderInfo* info) { flipRect(&info->windowBounds); FLIPENDIAN(info->finderFlags); flipPoint(&info->location); } static inline void flipExtendedFolderInfo(ExtendedFolderInfo* info) { flipPoint(&info->scrollPosition); FLIPENDIAN(info->extendedFinderFlags); FLIPENDIAN(info->putAwayFolderID); } static inline void flipFileInfo(FileInfo* info) { FLIPENDIAN(info->fileType); FLIPENDIAN(info->fileCreator); FLIPENDIAN(info->finderFlags); flipPoint(&info->location); } static inline void flipExtendedFileInfo(ExtendedFileInfo* info) { FLIPENDIAN(info->extendedFinderFlags); FLIPENDIAN(info->putAwayFolderID); } void flipCatalogFolder(HFSPlusCatalogFolder* record) { FLIPENDIAN(record->recordType); FLIPENDIAN(record->flags); FLIPENDIAN(record->valence); FLIPENDIAN(record->folderID); FLIPENDIAN(record->createDate); FLIPENDIAN(record->contentModDate); FLIPENDIAN(record->attributeModDate); FLIPENDIAN(record->accessDate); FLIPENDIAN(record->backupDate); flipBSDInfo(&record->permissions); flipFolderInfo(&record->userInfo); flipExtendedFolderInfo(&record->finderInfo); FLIPENDIAN(record->textEncoding); FLIPENDIAN(record->folderCount); } void flipCatalogFile(HFSPlusCatalogFile* record) { FLIPENDIAN(record->recordType); FLIPENDIAN(record->flags); FLIPENDIAN(record->fileID); FLIPENDIAN(record->createDate); FLIPENDIAN(record->contentModDate); FLIPENDIAN(record->attributeModDate); FLIPENDIAN(record->accessDate); FLIPENDIAN(record->backupDate); flipBSDInfo(&record->permissions); flipFileInfo(&record->userInfo); flipExtendedFileInfo(&record->finderInfo); FLIPENDIAN(record->textEncoding); flipForkData(&record->dataFork); flipForkData(&record->resourceFork); } void flipCatalogThread(HFSPlusCatalogThread* record, int out) { int i; int nameLength; FLIPENDIAN(record->recordType); FLIPENDIAN(record->parentID); if(out) { nameLength = record->nodeName.length; FLIPENDIAN(record->nodeName.length); } else { FLIPENDIAN(record->nodeName.length); nameLength = record->nodeName.length; } for(i = 0; i < nameLength; i++) { if(out) { if(record->nodeName.unicode[i] == ':') { record->nodeName.unicode[i] = '/'; } FLIPENDIAN(record->nodeName.unicode[i]); } else { FLIPENDIAN(record->nodeName.unicode[i]); if(record->nodeName.unicode[i] == '/') { record->nodeName.unicode[i] = ':'; } } } } #define UNICODE_START (sizeof(uint16_t) + sizeof(HFSCatalogNodeID) + sizeof(uint16_t)) static void catalogKeyPrint(BTKey* toPrint) { HFSPlusCatalogKey* key; key = (HFSPlusCatalogKey*) toPrint; printf("%d:", key->parentID); printUnicode(&key->nodeName); } static int catalogCompare(BTKey* vLeft, BTKey* vRight) { HFSPlusCatalogKey* left; HFSPlusCatalogKey* right; uint16_t i; uint16_t cLeft; uint16_t cRight; left = (HFSPlusCatalogKey*) vLeft; right =(HFSPlusCatalogKey*) vRight; if(left->parentID < right->parentID) { return -1; } else if(left->parentID > right->parentID) { return 1; } else { for(i = 0; i < left->nodeName.length; i++) { if(i >= right->nodeName.length) { return 1; } else { /* ugly hack to support weird : to / conversion on iPhone */ if(left->nodeName.unicode[i] == ':') { cLeft = '/'; } else { cLeft = left->nodeName.unicode[i] ; } if(right->nodeName.unicode[i] == ':') { cRight = '/'; } else { cRight = right->nodeName.unicode[i]; } if(cLeft < cRight) return -1; else if(cLeft > cRight) return 1; } } if(i < right->nodeName.length) { return -1; } else { /* do a safety check on key length. Otherwise, bad things may happen later on when we try to add or remove with this key */ /*if(left->keyLength == right->keyLength) { return 0; } else if(left->keyLength < right->keyLength) { return -1; } else { return 1; }*/ return 0; } } } static int catalogCompareCS(BTKey* vLeft, BTKey* vRight) { HFSPlusCatalogKey* left; HFSPlusCatalogKey* right; left = (HFSPlusCatalogKey*) vLeft; right =(HFSPlusCatalogKey*) vRight; if(left->parentID < right->parentID) { return -1; } else if(left->parentID > right->parentID) { return 1; } else { return FastUnicodeCompare(left->nodeName.unicode, left->nodeName.length, right->nodeName.unicode, right->nodeName.length); } } static BTKey* catalogKeyRead(off_t offset, io_func* io) { HFSPlusCatalogKey* key; uint16_t i; key = (HFSPlusCatalogKey*) malloc(sizeof(HFSPlusCatalogKey)); if(!READ(io, offset, UNICODE_START, key)) return NULL; FLIPENDIAN(key->keyLength); FLIPENDIAN(key->parentID); FLIPENDIAN(key->nodeName.length); if(!READ(io, offset + UNICODE_START, key->nodeName.length * sizeof(uint16_t), ((unsigned char *)key) + UNICODE_START)) return NULL; for(i = 0; i < key->nodeName.length; i++) { FLIPENDIAN(key->nodeName.unicode[i]); if(key->nodeName.unicode[i] == '/') /* ugly hack that iPhone seems to do */ key->nodeName.unicode[i] = ':'; } return (BTKey*)key; } static int catalogKeyWrite(off_t offset, BTKey* toWrite, io_func* io) { HFSPlusCatalogKey* key; uint16_t i; uint16_t keyLength; uint16_t nodeNameLength; keyLength = toWrite->keyLength + sizeof(uint16_t); key = (HFSPlusCatalogKey*) malloc(keyLength); memcpy(key, toWrite, keyLength); nodeNameLength = key->nodeName.length; FLIPENDIAN(key->keyLength); FLIPENDIAN(key->parentID); FLIPENDIAN(key->nodeName.length); for(i = 0; i < nodeNameLength; i++) { if(key->nodeName.unicode[i] == ':') /* ugly hack that iPhone seems to do */ key->nodeName.unicode[i] = '/'; FLIPENDIAN(key->nodeName.unicode[i]); } if(!WRITE(io, offset, keyLength, key)) return FALSE; free(key); return TRUE; } static BTKey* catalogDataRead(off_t offset, io_func* io) { int16_t recordType; HFSPlusCatalogRecord* record; uint16_t nameLength; if(!READ(io, offset, sizeof(int16_t), &recordType)) return NULL; FLIPENDIAN(recordType); fflush(stdout); switch(recordType) { case kHFSPlusFolderRecord: record = (HFSPlusCatalogRecord*) malloc(sizeof(HFSPlusCatalogFolder)); if(!READ(io, offset, sizeof(HFSPlusCatalogFolder), record)) return NULL; flipCatalogFolder((HFSPlusCatalogFolder*)record); break; case kHFSPlusFileRecord: record = (HFSPlusCatalogRecord*) malloc(sizeof(HFSPlusCatalogFile)); if(!READ(io, offset, sizeof(HFSPlusCatalogFile), record)) return NULL; flipCatalogFile((HFSPlusCatalogFile*)record); break; case kHFSPlusFolderThreadRecord: case kHFSPlusFileThreadRecord: record = (HFSPlusCatalogRecord*) malloc(sizeof(HFSPlusCatalogThread)); if(!READ(io, offset + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t), sizeof(uint16_t), &nameLength)) return NULL; FLIPENDIAN(nameLength); if(!READ(io, offset, sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint16_t) + (sizeof(uint16_t) * nameLength), record)) return NULL; flipCatalogThread((HFSPlusCatalogThread*)record, FALSE); break; } return (BTKey*)record; } void ASCIIToUnicode(const char* ascii, HFSUniStr255* unistr) { int count; count = 0; while(ascii[count] != '\0') { unistr->unicode[count] = ascii[count]; count++; } unistr->length = count; } HFSPlusCatalogRecord* getRecordByCNID(HFSCatalogNodeID CNID, Volume* volume) { HFSPlusCatalogKey key; HFSPlusCatalogThread* thread; HFSPlusCatalogRecord* record; int exact; key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length); key.parentID = CNID; key.nodeName.length = 0; thread = (HFSPlusCatalogThread*) search(volume->catalogTree, (BTKey*)(&key), &exact, NULL, NULL); if(thread == NULL) { return NULL; } if(exact == FALSE) { free(thread); return NULL; } key.parentID = thread->parentID; key.nodeName = thread->nodeName; free(thread); record = (HFSPlusCatalogRecord*) search(volume->catalogTree, (BTKey*)(&key), &exact, NULL, NULL); if(record == NULL || exact == FALSE) return NULL; else return record; } CatalogRecordList* getFolderContents(HFSCatalogNodeID CNID, Volume* volume) { BTree* tree; HFSPlusCatalogThread* record; HFSPlusCatalogKey key; uint32_t nodeNumber; int recordNumber; BTNodeDescriptor* descriptor; off_t recordOffset; off_t recordDataOffset; HFSPlusCatalogKey* currentKey; CatalogRecordList* list; CatalogRecordList* lastItem; CatalogRecordList* item; char pathBuffer[1024]; HFSPlusCatalogRecord* toReturn; HFSPlusCatalogKey nkey; int exact; tree = volume->catalogTree; key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length); key.parentID = CNID; key.nodeName.length = 0; list = NULL; record = (HFSPlusCatalogThread*) search(tree, (BTKey*)(&key), NULL, &nodeNumber, &recordNumber); if(record == NULL) return NULL; free(record); ++recordNumber; while(nodeNumber != 0) { descriptor = readBTNodeDescriptor(nodeNumber, tree); while(recordNumber < descriptor->numRecords) { recordOffset = getRecordOffset(recordNumber, nodeNumber, tree); currentKey = (HFSPlusCatalogKey*) READ_KEY(tree, recordOffset, tree->io); recordDataOffset = recordOffset + currentKey->keyLength + sizeof(currentKey->keyLength); if(currentKey->parentID == CNID) { item = (CatalogRecordList*) malloc(sizeof(CatalogRecordList)); item->name = currentKey->nodeName; item->record = (HFSPlusCatalogRecord*) READ_DATA(tree, recordDataOffset, tree->io); if(item->record->recordType == kHFSPlusFileRecord && (((HFSPlusCatalogFile*)item->record)->userInfo.fileType) == kHardLinkFileType) { sprintf(pathBuffer, "iNode%d", ((HFSPlusCatalogFile*)item->record)->permissions.special.iNodeNum); nkey.parentID = volume->metadataDir; ASCIIToUnicode(pathBuffer, &nkey.nodeName); nkey.keyLength = sizeof(nkey.parentID) + sizeof(nkey.nodeName.length) + (sizeof(uint16_t) * nkey.nodeName.length); toReturn = (HFSPlusCatalogRecord*) search(volume->catalogTree, (BTKey*)(&nkey), &exact, NULL, NULL); free(item->record); item->record = toReturn; } item->next = NULL; if(list == NULL) { list = item; } else { lastItem->next = item; } lastItem = item; free(currentKey); } else { free(currentKey); free(descriptor); return list; } recordNumber++; } nodeNumber = descriptor->fLink; recordNumber = 0; free(descriptor); } return list; } void releaseCatalogRecordList(CatalogRecordList* list) { CatalogRecordList* next; while(list) { next = list->next; free(list->record); free(list); list = next; } } HFSPlusCatalogRecord* getLinkTarget(HFSPlusCatalogRecord* record, HFSCatalogNodeID parentID, HFSPlusCatalogKey *key, Volume* volume) { io_func* io; char pathBuffer[1024]; HFSPlusCatalogRecord* toReturn; HFSPlusCatalogKey nkey; int exact; if(record->recordType == kHFSPlusFileRecord && (((HFSPlusCatalogFile*)record)->permissions.fileMode & S_IFLNK) == S_IFLNK) { io = openRawFile(((HFSPlusCatalogFile*)record)->fileID, &(((HFSPlusCatalogFile*)record)->dataFork), record, volume); READ(io, 0, (((HFSPlusCatalogFile*)record)->dataFork).logicalSize, pathBuffer); CLOSE(io); pathBuffer[(((HFSPlusCatalogFile*)record)->dataFork).logicalSize] = '\0'; toReturn = getRecordFromPath3(pathBuffer, volume, NULL, key, TRUE, TRUE, parentID); free(record); return toReturn; } else if(record->recordType == kHFSPlusFileRecord && (((HFSPlusCatalogFile*)record)->userInfo.fileType) == kHardLinkFileType) { sprintf(pathBuffer, "iNode%d", ((HFSPlusCatalogFile*)record)->permissions.special.iNodeNum); nkey.parentID = volume->metadataDir; ASCIIToUnicode(pathBuffer, &nkey.nodeName); nkey.keyLength = sizeof(nkey.parentID) + sizeof(nkey.nodeName.length) + (sizeof(uint16_t) * nkey.nodeName.length); toReturn = (HFSPlusCatalogRecord*) search(volume->catalogTree, (BTKey*)(&nkey), &exact, NULL, NULL); free(record); return toReturn; } else { return record; } } static const uint16_t METADATA_DIR[] = {0, 0, 0, 0, 'H', 'F', 'S', '+', ' ', 'P', 'r', 'i', 'v', 'a', 't', 'e', ' ', 'D', 'a', 't', 'a'}; HFSCatalogNodeID getMetadataDirectoryID(Volume* volume) { HFSPlusCatalogKey key; HFSPlusCatalogFolder* record; int exact; HFSCatalogNodeID id; key.nodeName.length = sizeof(METADATA_DIR) / sizeof(uint16_t); key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length) + sizeof(METADATA_DIR); key.parentID = kHFSRootFolderID; memcpy(key.nodeName.unicode, METADATA_DIR, sizeof(METADATA_DIR)); record = (HFSPlusCatalogFolder*) search(volume->catalogTree, (BTKey*)(&key), &exact, NULL, NULL); id = record->folderID; free(record); return id; } HFSPlusCatalogRecord* getRecordFromPath(const char* path, Volume* volume, char **name, HFSPlusCatalogKey* retKey) { return getRecordFromPath2(path, volume, name, retKey, TRUE); } HFSPlusCatalogRecord* getRecordFromPath2(const char* path, Volume* volume, char **name, HFSPlusCatalogKey* retKey, char traverse) { return getRecordFromPath3(path, volume, name, retKey, TRUE, TRUE, kHFSRootFolderID); } HFSPlusCatalogRecord* getRecordFromPath3(const char* path, Volume* volume, char **name, HFSPlusCatalogKey* retKey, char traverse, char returnLink, HFSCatalogNodeID parentID) { HFSPlusCatalogKey key; HFSPlusCatalogRecord* record; char* origPath; char* myPath; char* word; char* pathLimit; uint32_t realParent; int exact; if(path[0] == '\0' || (path[0] == '/' && path[1] == '\0')) { if(name != NULL) *name = (char*)path; key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length); key.parentID = kHFSRootFolderID; key.nodeName.length = 0; record = (HFSPlusCatalogRecord*) search(volume->catalogTree, (BTKey*)(&key), &exact, NULL, NULL); key.parentID = ((HFSPlusCatalogThread*)record)->parentID; key.nodeName = ((HFSPlusCatalogThread*)record)->nodeName; free(record); record = (HFSPlusCatalogRecord*) search(volume->catalogTree, (BTKey*)(&key), &exact, NULL, NULL); return record; } myPath = strdup(path); origPath = myPath; record = NULL; if(path[0] == '/') { key.parentID = kHFSRootFolderID; } else { key.parentID = parentID; } pathLimit = myPath + strlen(myPath); for(word = (char*)strtok(myPath, "/"); word && (word < pathLimit); word = ((word + strlen(word) + 1) < pathLimit) ? (char*)strtok(word + strlen(word) + 1, "/") : NULL) { if(name != NULL) *name = (char*)(path + (word - origPath)); if(record != NULL) { free(record); record = NULL; } if(word[0] == '\0') { continue; } ASCIIToUnicode(word, &key.nodeName); key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length) + (sizeof(uint16_t) * key.nodeName.length); record = (HFSPlusCatalogRecord*) search(volume->catalogTree, (BTKey*)(&key), &exact, NULL, NULL); if(record == NULL || exact == FALSE) { free(origPath); if(record != NULL) { free(record); } return NULL; } if(traverse) { if(((word + strlen(word) + 1) < pathLimit) || returnLink) { record = getLinkTarget(record, key.parentID, &key, volume); if(record == NULL || exact == FALSE) { free(origPath); return NULL; } } } if(record->recordType == kHFSPlusFileRecord) { if((word + strlen(word) + 1) >= pathLimit) { free(origPath); if(retKey != NULL) { memcpy(retKey, &key, sizeof(HFSPlusCatalogKey)); } return record; } else { free(origPath); free(record); return NULL; } } if(record->recordType != kHFSPlusFolderRecord) hfs_panic("inconsistent catalog tree!"); realParent = key.parentID; key.parentID = ((HFSPlusCatalogFolder*)record)->folderID; } if(retKey != NULL) { memcpy(retKey, &key, sizeof(HFSPlusCatalogKey)); retKey->parentID = realParent; } free(origPath); return record; } int updateCatalog(Volume* volume, HFSPlusCatalogRecord* catalogRecord) { HFSPlusCatalogKey key; HFSPlusCatalogRecord* record; HFSPlusCatalogFile file; HFSPlusCatalogFolder folder; int exact; key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length); if(catalogRecord->recordType == kHFSPlusFolderRecord) { key.parentID = ((HFSPlusCatalogFolder*)catalogRecord)->folderID; } else if(catalogRecord->recordType == kHFSPlusFileRecord) { key.parentID = ((HFSPlusCatalogFile*)catalogRecord)->fileID; } else { /* unexpected */ return FALSE; } key.nodeName.length = 0; record = (HFSPlusCatalogRecord*) search(volume->catalogTree, (BTKey*)(&key), &exact, NULL, NULL); key.parentID = ((HFSPlusCatalogThread*)record)->parentID; key.nodeName = ((HFSPlusCatalogThread*)record)->nodeName; key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length) + (sizeof(uint16_t) * key.nodeName.length); free(record); record = (HFSPlusCatalogRecord*) search(volume->catalogTree, (BTKey*)(&key), &exact, NULL, NULL); removeFromBTree(volume->catalogTree, (BTKey*)(&key)); switch(record->recordType) { case kHFSPlusFolderRecord: memcpy(&folder, catalogRecord, sizeof(HFSPlusCatalogFolder)); flipCatalogFolder(&folder); free(record); return addToBTree(volume->catalogTree, (BTKey*)(&key), sizeof(HFSPlusCatalogFolder), (unsigned char *)(&folder)); break; case kHFSPlusFileRecord: memcpy(&file, catalogRecord, sizeof(HFSPlusCatalogFile)); flipCatalogFile(&file); free(record); return addToBTree(volume->catalogTree, (BTKey*)(&key), sizeof(HFSPlusCatalogFile), (unsigned char *)(&file)); break; } return TRUE; } int move(const char* source, const char* dest, Volume* volume) { HFSPlusCatalogRecord* srcRec; HFSPlusCatalogFolder* srcFolderRec; HFSPlusCatalogFolder* destRec; char* destPath; char* destName; char* curChar; char* lastSeparator; int i; int threadLength; HFSPlusCatalogKey srcKey; HFSPlusCatalogKey destKey; HFSPlusCatalogThread* thread; srcRec = getRecordFromPath3(source, volume, NULL, &srcKey, TRUE, FALSE, kHFSRootFolderID); if(srcRec == NULL) { free(srcRec); return FALSE; } srcFolderRec = (HFSPlusCatalogFolder*) getRecordByCNID(srcKey.parentID, volume); if(srcFolderRec == NULL || srcFolderRec->recordType != kHFSPlusFolderRecord) { free(srcRec); free(srcFolderRec); return FALSE; } destPath = strdup(dest); curChar = destPath; lastSeparator = NULL; while((*curChar) != '\0') { if((*curChar) == '/') lastSeparator = curChar; curChar++; } if(lastSeparator == NULL) { destRec = (HFSPlusCatalogFolder*) getRecordFromPath("/", volume, NULL, NULL); destName = destPath; } else { destName = lastSeparator + 1; *lastSeparator = '\0'; destRec = (HFSPlusCatalogFolder*) getRecordFromPath(destPath, volume, NULL, NULL); if(destRec == NULL || destRec->recordType != kHFSPlusFolderRecord) { free(destPath); free(srcRec); free(destRec); free(srcFolderRec); return FALSE; } } removeFromBTree(volume->catalogTree, (BTKey*)(&srcKey)); srcKey.nodeName.length = 0; if(srcRec->recordType == kHFSPlusFolderRecord) { srcKey.parentID = ((HFSPlusCatalogFolder*)srcRec)->folderID; } else if(srcRec->recordType == kHFSPlusFileRecord) { srcKey.parentID = ((HFSPlusCatalogFile*)srcRec)->fileID; } else { /* unexpected */ return FALSE; } srcKey.keyLength = sizeof(srcKey.parentID) + sizeof(srcKey.nodeName.length); removeFromBTree(volume->catalogTree, (BTKey*)(&srcKey)); destKey.nodeName.length = strlen(destName); threadLength = sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint16_t) + (sizeof(uint16_t) * destKey.nodeName.length); thread = (HFSPlusCatalogThread*) malloc(threadLength); thread->reserved = 0; destKey.parentID = destRec->folderID; thread->parentID = destKey.parentID; thread->nodeName.length = destKey.nodeName.length; for(i = 0; i < destKey.nodeName.length; i++) { destKey.nodeName.unicode[i] = destName[i]; thread->nodeName.unicode[i] = destName[i]; } destKey.keyLength = sizeof(uint32_t) + sizeof(uint16_t) + (sizeof(uint16_t) * destKey.nodeName.length); switch(srcRec->recordType) { case kHFSPlusFolderRecord: thread->recordType = kHFSPlusFolderThreadRecord; flipCatalogFolder((HFSPlusCatalogFolder*)srcRec); addToBTree(volume->catalogTree, (BTKey*)(&destKey), sizeof(HFSPlusCatalogFolder), (unsigned char *)(srcRec)); break; case kHFSPlusFileRecord: thread->recordType = kHFSPlusFileThreadRecord; flipCatalogFile((HFSPlusCatalogFile*)srcRec); addToBTree(volume->catalogTree, (BTKey*)(&destKey), sizeof(HFSPlusCatalogFile), (unsigned char *)(srcRec)); break; } destKey.nodeName.length = 0; destKey.parentID = srcKey.parentID; destKey.keyLength = sizeof(destKey.parentID) + sizeof(destKey.nodeName.length); flipCatalogThread(thread, TRUE); addToBTree(volume->catalogTree, (BTKey*)(&destKey), threadLength, (unsigned char *)(thread)); /* adjust valence */ srcFolderRec->valence--; updateCatalog(volume, (HFSPlusCatalogRecord*) srcFolderRec); destRec->valence++; updateCatalog(volume, (HFSPlusCatalogRecord*) destRec); free(thread); free(destPath); free(srcRec); free(destRec); free(srcFolderRec); return TRUE; } int removeFile(const char* fileName, Volume* volume) { HFSPlusCatalogRecord* record; HFSPlusCatalogKey key; io_func* io; HFSPlusCatalogFolder* parentFolder; record = getRecordFromPath3(fileName, volume, NULL, &key, TRUE, FALSE, kHFSRootFolderID); if(record != NULL) { parentFolder = (HFSPlusCatalogFolder*) getRecordByCNID(key.parentID, volume); if(parentFolder != NULL) { if(parentFolder->recordType != kHFSPlusFolderRecord) { ASSERT(FALSE, "parent not folder"); free(parentFolder); return FALSE; } } else { ASSERT(FALSE, "can't find parent"); return FALSE; } if(record->recordType == kHFSPlusFileRecord) { io = openRawFile(((HFSPlusCatalogFile*)record)->fileID, &((HFSPlusCatalogFile*)record)->dataFork, record, volume); allocate((RawFile*)io->data, 0); CLOSE(io); removeFromBTree(volume->catalogTree, (BTKey*)(&key)); XAttrList* next; XAttrList* attrs = getAllExtendedAttributes(((HFSPlusCatalogFile*)record)->fileID, volume); if(attrs != NULL) { while(attrs != NULL) { next = attrs->next; unsetAttribute(volume, ((HFSPlusCatalogFile*)record)->fileID, attrs->name); free(attrs->name); free(attrs); attrs = next; } } key.nodeName.length = 0; key.parentID = ((HFSPlusCatalogFile*)record)->fileID; key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length); removeFromBTree(volume->catalogTree, (BTKey*)(&key)); volume->volumeHeader->fileCount--; } else { if(((HFSPlusCatalogFolder*)record)->valence > 0) { free(record); free(parentFolder); ASSERT(FALSE, "folder not empty"); return FALSE; } else { removeFromBTree(volume->catalogTree, (BTKey*)(&key)); XAttrList* next; XAttrList* attrs = getAllExtendedAttributes(((HFSPlusCatalogFolder*)record)->folderID, volume); if(attrs != NULL) { while(attrs != NULL) { next = attrs->next; unsetAttribute(volume, ((HFSPlusCatalogFolder*)record)->folderID, attrs->name); free(attrs->name); free(attrs); attrs = next; } } key.nodeName.length = 0; key.parentID = ((HFSPlusCatalogFolder*)record)->folderID; key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length); removeFromBTree(volume->catalogTree, (BTKey*)(&key)); } parentFolder->folderCount--; volume->volumeHeader->folderCount--; } parentFolder->valence--; updateCatalog(volume, (HFSPlusCatalogRecord*) parentFolder); updateVolume(volume); free(record); free(parentFolder); return TRUE; } else { free(parentFolder); ASSERT(FALSE, "cannot find record"); return FALSE; } } int makeSymlink(const char* pathName, const char* target, Volume* volume) { io_func* io; HFSPlusCatalogFile* record; record = (HFSPlusCatalogFile*) getRecordFromPath3(pathName, volume, NULL, NULL, TRUE, FALSE, kHFSRootFolderID); if(!record) { newFile(pathName, volume); record = (HFSPlusCatalogFile*) getRecordFromPath(pathName, volume, NULL, NULL); if(!record) { return FALSE; } record->permissions.fileMode |= S_IFLNK; record->userInfo.fileType = kSymLinkFileType; record->userInfo.fileCreator = kSymLinkCreator; updateCatalog(volume, (HFSPlusCatalogRecord*) record); } else { if(record->recordType != kHFSPlusFileRecord || (((HFSPlusCatalogFile*)record)->permissions.fileMode & S_IFLNK) != S_IFLNK) { free(record); return FALSE; } } io = openRawFile(record->fileID, &record->dataFork, (HFSPlusCatalogRecord*) record, volume); WRITE(io, 0, strlen(target), (void*) target); CLOSE(io); free(record); return TRUE; } HFSCatalogNodeID newFolder(const char* pathName, Volume* volume) { HFSPlusCatalogFolder* parentFolder; HFSPlusCatalogFolder folder; HFSPlusCatalogKey key; HFSPlusCatalogThread thread; uint32_t newFolderID; int threadLength; char* path; char* name; char* curChar; char* lastSeparator; path = strdup(pathName); curChar = path; lastSeparator = NULL; while((*curChar) != '\0') { if((*curChar) == '/') lastSeparator = curChar; curChar++; } if(lastSeparator == NULL) { parentFolder = (HFSPlusCatalogFolder*) getRecordFromPath("/", volume, NULL, NULL); name = path; } else { name = lastSeparator + 1; *lastSeparator = '\0'; parentFolder = (HFSPlusCatalogFolder*) getRecordFromPath(path, volume, NULL, NULL); } if(parentFolder == NULL || parentFolder->recordType != kHFSPlusFolderRecord) { free(path); free(parentFolder); return FALSE; } newFolderID = volume->volumeHeader->nextCatalogID++; volume->volumeHeader->folderCount++; folder.recordType = kHFSPlusFolderRecord; folder.flags = kHFSHasFolderCountMask; folder.valence = 0; folder.folderID = newFolderID; folder.createDate = UNIX_TO_APPLE_TIME(time(NULL)); folder.contentModDate = folder.createDate; folder.attributeModDate = folder.createDate; folder.accessDate = folder.createDate; folder.backupDate = folder.createDate; folder.permissions.ownerID = parentFolder->permissions.ownerID; folder.permissions.groupID = parentFolder->permissions.groupID; folder.permissions.adminFlags = 0; folder.permissions.ownerFlags = 0; folder.permissions.fileMode = S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; folder.permissions.special.iNodeNum = 0; memset(&folder.userInfo, 0, sizeof(folder.userInfo)); memset(&folder.finderInfo, 0, sizeof(folder.finderInfo)); folder.textEncoding = 0; folder.folderCount = 0; key.parentID = parentFolder->folderID; ASCIIToUnicode(name, &key.nodeName); key.keyLength = sizeof(key.parentID) + STR_SIZE(key.nodeName); thread.recordType = kHFSPlusFolderThreadRecord; thread.reserved = 0; thread.parentID = parentFolder->folderID; ASCIIToUnicode(name, &thread.nodeName); threadLength = sizeof(thread.recordType) + sizeof(thread.reserved) + sizeof(thread.parentID) + STR_SIZE(thread.nodeName); flipCatalogThread(&thread, TRUE); flipCatalogFolder(&folder); ASSERT(addToBTree(volume->catalogTree, (BTKey*)(&key), sizeof(HFSPlusCatalogFolder), (unsigned char *)(&folder)), "addToBTree"); key.nodeName.length = 0; key.parentID = newFolderID; key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length); ASSERT(addToBTree(volume->catalogTree, (BTKey*)(&key), threadLength, (unsigned char *)(&thread)), "addToBTree"); parentFolder->folderCount++; parentFolder->valence++; updateCatalog(volume, (HFSPlusCatalogRecord*) parentFolder); updateVolume(volume); free(parentFolder); free(path); return newFolderID; } HFSCatalogNodeID newFile(const char* pathName, Volume* volume) { HFSPlusCatalogFolder* parentFolder; HFSPlusCatalogFile file; HFSPlusCatalogKey key; HFSPlusCatalogThread thread; uint32_t newFileID; int threadLength; char* path; char* name; char* curChar; char* lastSeparator; path = strdup(pathName); curChar = path; lastSeparator = NULL; while((*curChar) != '\0') { if((*curChar) == '/') lastSeparator = curChar; curChar++; } if(lastSeparator == NULL) { parentFolder = (HFSPlusCatalogFolder*) getRecordFromPath("/", volume, NULL, NULL); name = path; } else { name = lastSeparator + 1; *lastSeparator = '\0'; parentFolder = (HFSPlusCatalogFolder*) getRecordFromPath(path, volume, NULL, NULL); } if(parentFolder == NULL || parentFolder->recordType != kHFSPlusFolderRecord) { free(path); free(parentFolder); return FALSE; } newFileID = volume->volumeHeader->nextCatalogID++; volume->volumeHeader->fileCount++; file.recordType = kHFSPlusFileRecord; file.flags = kHFSThreadExistsMask; file.reserved1 = 0; file.fileID = newFileID; file.createDate = UNIX_TO_APPLE_TIME(time(NULL)); file.contentModDate = file.createDate; file.attributeModDate = file.createDate; file.accessDate = file.createDate; file.backupDate = file.createDate; file.permissions.ownerID = parentFolder->permissions.ownerID; file.permissions.groupID = parentFolder->permissions.groupID; file.permissions.adminFlags = 0; file.permissions.ownerFlags = 0; file.permissions.fileMode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; file.permissions.special.iNodeNum = 0; memset(&file.userInfo, 0, sizeof(file.userInfo)); memset(&file.finderInfo, 0, sizeof(file.finderInfo)); file.textEncoding = 0; file.reserved2 = 0; memset(&file.dataFork, 0, sizeof(file.dataFork)); memset(&file.resourceFork, 0, sizeof(file.resourceFork)); key.parentID = parentFolder->folderID; ASCIIToUnicode(name, &key.nodeName); key.keyLength = sizeof(key.parentID) + STR_SIZE(key.nodeName); thread.recordType = kHFSPlusFileThreadRecord; thread.reserved = 0; thread.parentID = parentFolder->folderID; ASCIIToUnicode(name, &thread.nodeName); threadLength = sizeof(thread.recordType) + sizeof(thread.reserved) + sizeof(thread.parentID) + STR_SIZE(thread.nodeName); flipCatalogThread(&thread, TRUE); flipCatalogFile(&file); ASSERT(addToBTree(volume->catalogTree, (BTKey*)(&key), sizeof(HFSPlusCatalogFile), (unsigned char *)(&file)), "addToBTree"); key.nodeName.length = 0; key.parentID = newFileID; key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length); ASSERT(addToBTree(volume->catalogTree, (BTKey*)(&key), threadLength, (unsigned char *)(&thread)), "addToBTree"); parentFolder->valence++; updateCatalog(volume, (HFSPlusCatalogRecord*) parentFolder); updateVolume(volume); free(parentFolder); free(path); return newFileID; } int chmodFile(const char* pathName, int mode, Volume* volume) { HFSPlusCatalogRecord* record; record = getRecordFromPath(pathName, volume, NULL, NULL); if(record == NULL) { return FALSE; } if(record->recordType == kHFSPlusFolderRecord) { ((HFSPlusCatalogFolder*)record)->permissions.fileMode = (((HFSPlusCatalogFolder*)record)->permissions.fileMode & 0770000) | mode; } else if(record->recordType == kHFSPlusFileRecord) { ((HFSPlusCatalogFile*)record)->permissions.fileMode = (((HFSPlusCatalogFolder*)record)->permissions.fileMode & 0770000) | mode; } else { return FALSE; } updateCatalog(volume, record); free(record); return TRUE; } int chownFile(const char* pathName, uint32_t owner, uint32_t group, Volume* volume) { HFSPlusCatalogRecord* record; record = getRecordFromPath(pathName, volume, NULL, NULL); if(record == NULL) { return FALSE; } if(record->recordType == kHFSPlusFolderRecord) { ((HFSPlusCatalogFolder*)record)->permissions.ownerID = owner; ((HFSPlusCatalogFolder*)record)->permissions.groupID = group; } else if(record->recordType == kHFSPlusFileRecord) { ((HFSPlusCatalogFile*)record)->permissions.ownerID = owner; ((HFSPlusCatalogFile*)record)->permissions.groupID = group; } else { return FALSE; } updateCatalog(volume, record); free(record); return TRUE; } BTree* openCatalogTree(io_func* file) { BTree* btree; btree = openBTree(file, &catalogCompare, &catalogKeyRead, &catalogKeyWrite, &catalogKeyPrint, &catalogDataRead); if(btree->headerRec->keyCompareType == kHFSCaseFolding) { btree->compare = &catalogCompareCS; } return btree; }