375 lines
9.3 KiB
C
375 lines
9.3 KiB
C
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <hfs/hfsplus.h>
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|