#include #include #include static inline void flipAttrData(HFSPlusAttrData* data) { FLIPENDIAN(data->recordType); FLIPENDIAN(data->size); } static inline void flipAttrForkData(HFSPlusAttrForkData* data) { FLIPENDIAN(data->recordType); flipForkData(&data->theFork); } static inline void flipAttrExtents(HFSPlusAttrExtents* data) { FLIPENDIAN(data->recordType); flipExtentRecord(&data->extents); } static int attrCompare(BTKey* vLeft, BTKey* vRight) { HFSPlusAttrKey* left; HFSPlusAttrKey* right; uint16_t i; uint16_t cLeft; uint16_t cRight; left = (HFSPlusAttrKey*) vLeft; right =(HFSPlusAttrKey*) vRight; if(left->fileID < right->fileID) { return -1; } else if(left->fileID > right->fileID) { return 1; } else { for(i = 0; i < left->name.length; i++) { if(i >= right->name.length) { return 1; } else { cLeft = left->name.unicode[i]; cRight = right->name.unicode[i]; if(cLeft < cRight) return -1; else if(cLeft > cRight) return 1; } } if(i < right->name.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; } } } #define UNICODE_START (sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t)) static BTKey* attrKeyRead(off_t offset, io_func* io) { int i; HFSPlusAttrKey* key; key = (HFSPlusAttrKey*) malloc(sizeof(HFSPlusAttrKey)); if(!READ(io, offset, UNICODE_START, key)) return NULL; FLIPENDIAN(key->keyLength); FLIPENDIAN(key->fileID); FLIPENDIAN(key->startBlock); FLIPENDIAN(key->name.length); if(key->name.length > 254) { printf("Invalid xattr key at offset %x\n", offset); free(key); return NULL; } if(!READ(io, offset + UNICODE_START, key->name.length * sizeof(uint16_t), ((unsigned char *)key) + UNICODE_START)) return NULL; for(i = 0; i < key->name.length; i++) { FLIPENDIAN(key->name.unicode[i]); } return (BTKey*)key; } static int attrKeyWrite(off_t offset, BTKey* toWrite, io_func* io) { HFSPlusAttrKey* key; uint16_t keyLength; uint16_t nodeNameLength; int i; keyLength = toWrite->keyLength; key = (HFSPlusAttrKey*) malloc(keyLength); memcpy(key, toWrite, keyLength); nodeNameLength = key->name.length; FLIPENDIAN(key->keyLength); FLIPENDIAN(key->fileID); FLIPENDIAN(key->startBlock); FLIPENDIAN(key->name.length); for(i = 0; i < nodeNameLength; i++) { FLIPENDIAN(key->name.unicode[i]); } if(!WRITE(io, offset, keyLength, key)) return FALSE; free(key); return TRUE; } static void attrKeyPrint(BTKey* toPrint) { HFSPlusAttrKey* key; key = (HFSPlusAttrKey*)toPrint; printf("attribute%d:%d:", key->fileID, key->startBlock); printUnicode(&key->name); } static BTKey* attrDataRead(off_t offset, io_func* io) { HFSPlusAttrRecord* record; record = (HFSPlusAttrRecord*) malloc(sizeof(HFSPlusAttrRecord)); if(!READ(io, offset, sizeof(uint32_t), record)) return NULL; FLIPENDIAN(record->recordType); switch(record->recordType) { case kHFSPlusAttrInlineData: if(!READ(io, offset, sizeof(HFSPlusAttrData), record)) return NULL; flipAttrData((HFSPlusAttrData*) record); record = realloc(record, sizeof(HFSPlusAttrData) + ((HFSPlusAttrData*) record)->size); if(!READ(io, offset + sizeof(HFSPlusAttrData), ((HFSPlusAttrData*) record)->size, ((HFSPlusAttrData*) record)->data)) return NULL; break; case kHFSPlusAttrForkData: if(!READ(io, offset, sizeof(HFSPlusAttrForkData), record)) return NULL; flipAttrForkData((HFSPlusAttrForkData*) record); break; case kHFSPlusAttrExtents: if(!READ(io, offset, sizeof(HFSPlusAttrExtents), record)) return NULL; flipAttrExtents((HFSPlusAttrExtents*) record); break; } return (BTKey*)record; } static int updateAttributes(Volume* volume, HFSPlusAttrKey* skey, HFSPlusAttrRecord* srecord) { HFSPlusAttrKey key; HFSPlusAttrRecord* record; int ret, len; memcpy(&key, skey, skey->keyLength); switch(srecord->recordType) { case kHFSPlusAttrInlineData: len = srecord->attrData.size + sizeof(HFSPlusAttrData); record = (HFSPlusAttrRecord*) malloc(len); memcpy(record, srecord, len); flipAttrData((HFSPlusAttrData*) record); removeFromBTree(volume->attrTree, (BTKey*)(&key)); ret = addToBTree(volume->attrTree, (BTKey*)(&key), len, (unsigned char *)record); free(record); break; case kHFSPlusAttrForkData: record = (HFSPlusAttrRecord*) malloc(sizeof(HFSPlusAttrForkData)); memcpy(record, srecord, sizeof(HFSPlusAttrForkData)); flipAttrForkData((HFSPlusAttrForkData*) record); removeFromBTree(volume->attrTree, (BTKey*)(&key)); ret = addToBTree(volume->attrTree, (BTKey*)(&key), sizeof(HFSPlusAttrForkData), (unsigned char *)record); free(record); break; case kHFSPlusAttrExtents: record = (HFSPlusAttrRecord*) malloc(sizeof(HFSPlusAttrExtents)); memcpy(record, srecord, sizeof(HFSPlusAttrExtents)); flipAttrExtents((HFSPlusAttrExtents*) record); removeFromBTree(volume->attrTree, (BTKey*)(&key)); ret = addToBTree(volume->attrTree, (BTKey*)(&key), sizeof(HFSPlusAttrExtents), (unsigned char *)record); free(record); break; } return ret; } size_t getAttribute(Volume* volume, uint32_t fileID, const char* name, uint8_t** data) { HFSPlusAttrKey key; HFSPlusAttrRecord* record; size_t size; int exact; if(!volume->attrTree) return FALSE; memset(&key, 0 , sizeof(HFSPlusAttrKey)); key.fileID = fileID; key.startBlock = 0; ASCIIToUnicode(name, &key.name); key.keyLength = sizeof(HFSPlusAttrKey) - sizeof(HFSUniStr255) + sizeof(key.name.length) + (sizeof(uint16_t) * key.name.length); *data = NULL; record = (HFSPlusAttrRecord*) search(volume->attrTree, (BTKey*)(&key), &exact, NULL, NULL); if(exact == FALSE) { if(record) free(record); return 0; } switch(record->recordType) { case kHFSPlusAttrInlineData: size = record->attrData.size; *data = (uint8_t*) malloc(size); memcpy(*data, record->attrData.data, size); free(record); return size; default: fprintf(stderr, "unsupported attribute node format\n"); return 0; } } int setAttribute(Volume* volume, uint32_t fileID, const char* name, uint8_t* data, size_t size) { HFSPlusAttrKey key; HFSPlusAttrData* record; int ret, exact; if(!volume->attrTree) return FALSE; memset(&key, 0 , sizeof(HFSPlusAttrKey)); key.fileID = fileID; key.startBlock = 0; ASCIIToUnicode(name, &key.name); key.keyLength = sizeof(HFSPlusAttrKey) - sizeof(HFSUniStr255) + sizeof(key.name.length) + (sizeof(uint16_t) * key.name.length); record = (HFSPlusAttrData*) malloc(sizeof(HFSPlusAttrData) + size); memset(record, 0, sizeof(HFSPlusAttrData)); record->recordType = kHFSPlusAttrInlineData; record->size = size; memcpy(record->data, data, size); ret = updateAttributes(volume, &key, (HFSPlusAttrRecord*) record); free(record); return ret; } int unsetAttribute(Volume* volume, uint32_t fileID, const char* name) { HFSPlusAttrKey key; if(!volume->attrTree) return FALSE; memset(&key, 0 , sizeof(HFSPlusAttrKey)); key.fileID = fileID; key.startBlock = 0; ASCIIToUnicode(name, &key.name); key.keyLength = sizeof(HFSPlusAttrKey) - sizeof(HFSUniStr255) + sizeof(key.name.length) + (sizeof(uint16_t) * key.name.length); return removeFromBTree(volume->attrTree, (BTKey*)(&key)); } XAttrList* getAllExtendedAttributes(HFSCatalogNodeID CNID, Volume* volume) { BTree* tree; HFSPlusAttrKey key; HFSPlusAttrRecord* record; uint32_t nodeNumber; int recordNumber; BTNodeDescriptor* descriptor; HFSPlusAttrKey* currentKey; off_t recordOffset; XAttrList* list = NULL; XAttrList* lastItem = NULL; XAttrList* item = NULL; if(!volume->attrTree) return NULL; memset(&key, 0 , sizeof(HFSPlusAttrKey)); key.fileID = CNID; key.startBlock = 0; key.name.length = 0; key.keyLength = sizeof(HFSPlusAttrKey) - sizeof(HFSUniStr255) + sizeof(key.name.length) + (sizeof(uint16_t) * key.name.length); tree = volume->attrTree; record = (HFSPlusAttrRecord*) search(tree, (BTKey*)(&key), NULL, &nodeNumber, &recordNumber); if(record == NULL) return NULL; free(record); while(nodeNumber != 0) { descriptor = readBTNodeDescriptor(nodeNumber, tree); while(recordNumber < descriptor->numRecords) { recordOffset = getRecordOffset(recordNumber, nodeNumber, tree); currentKey = (HFSPlusAttrKey*) READ_KEY(tree, recordOffset, tree->io); if(currentKey->fileID == CNID) { item = (XAttrList*) malloc(sizeof(XAttrList)); item->name = (char*) malloc(currentKey->name.length + 1); int i; for(i = 0; i < currentKey->name.length; i++) { item->name[i] = currentKey->name.unicode[i]; } item->name[currentKey->name.length] = '\0'; item->next = NULL; if(lastItem != NULL) { lastItem->next = item; } else { list = item; } lastItem = item; free(currentKey); } else { free(currentKey); free(descriptor); return list; } recordNumber++; } nodeNumber = descriptor->fLink; recordNumber = 0; free(descriptor); } return list; } BTree* openAttributesTree(io_func* file) { return openBTree(file, &attrCompare, &attrKeyRead, &attrKeyWrite, &attrKeyPrint, &attrDataRead); }