initial code for dumping imessages in a reasonable format

This commit is contained in:
Jeffrey Paul
2014-02-09 00:30:49 +01:00
parent c0021efb13
commit 9dd7628f04
157 changed files with 24178 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
INCLUDE(FindZLIB)
IF(NOT ZLIB_FOUND)
message(FATAL_ERROR "zlib is required for hfs!")
ENDIF(NOT ZLIB_FOUND)
include_directories(${ZLIB_INCLUDE_DIR})
link_directories(${ZLIB_LIBRARIES})
link_directories (${PROJECT_BINARY_DIR}/common)
add_library(hfs btree.c catalog.c extents.c xattr.c fastunicodecompare.c flatfile.c hfslib.c rawfile.c utility.c volume.c hfscompress.c)
target_link_libraries(hfs common z)
add_executable(hfsplus hfs.c)
target_link_libraries (hfsplus hfs)
install(TARGETS hfsplus DESTINATION .)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,119 @@
#include <stdlib.h>
#include <string.h>
#include <hfs/hfsplus.h>
static inline void flipExtentDescriptor(HFSPlusExtentDescriptor* extentDescriptor) {
FLIPENDIAN(extentDescriptor->startBlock);
FLIPENDIAN(extentDescriptor->blockCount);
}
void flipExtentRecord(HFSPlusExtentRecord* extentRecord) {
HFSPlusExtentDescriptor *extentDescriptor;
extentDescriptor = (HFSPlusExtentDescriptor*)extentRecord;
flipExtentDescriptor(&extentDescriptor[0]);
flipExtentDescriptor(&extentDescriptor[1]);
flipExtentDescriptor(&extentDescriptor[2]);
flipExtentDescriptor(&extentDescriptor[3]);
flipExtentDescriptor(&extentDescriptor[4]);
flipExtentDescriptor(&extentDescriptor[5]);
flipExtentDescriptor(&extentDescriptor[6]);
flipExtentDescriptor(&extentDescriptor[7]);
}
static int extentCompare(BTKey* vLeft, BTKey* vRight) {
HFSPlusExtentKey* left;
HFSPlusExtentKey* right;
left = (HFSPlusExtentKey*) vLeft;
right =(HFSPlusExtentKey*) vRight;
if(left->forkType < right->forkType) {
return -1;
} else if(left->forkType > right->forkType) {
return 1;
} else {
if(left->fileID < right->fileID) {
return -1;
} else if(left->fileID > right->fileID) {
return 1;
} else {
if(left->startBlock < right->startBlock) {
return -1;
} else if(left->startBlock > right->startBlock) {
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 BTKey* extentKeyRead(off_t offset, io_func* io) {
HFSPlusExtentKey* key;
key = (HFSPlusExtentKey*) malloc(sizeof(HFSPlusExtentKey));
if(!READ(io, offset, sizeof(HFSPlusExtentKey), key))
return NULL;
FLIPENDIAN(key->keyLength);
FLIPENDIAN(key->forkType);
FLIPENDIAN(key->fileID);
FLIPENDIAN(key->startBlock);
return (BTKey*)key;
}
static int extentKeyWrite(off_t offset, BTKey* toWrite, io_func* io) {
HFSPlusExtentKey* key;
key = (HFSPlusExtentKey*) malloc(sizeof(HFSPlusExtentKey));
memcpy(key, toWrite, sizeof(HFSPlusExtentKey));
FLIPENDIAN(key->keyLength);
FLIPENDIAN(key->forkType);
FLIPENDIAN(key->fileID);
FLIPENDIAN(key->startBlock);
if(!WRITE(io, offset, sizeof(HFSPlusExtentKey), key))
return FALSE;
free(key);
return TRUE;
}
static void extentKeyPrint(BTKey* toPrint) {
HFSPlusExtentKey* key;
key = (HFSPlusExtentKey*)toPrint;
printf("extent%d:%d:%d", key->forkType, key->fileID, key->startBlock);
}
static BTKey* extentDataRead(off_t offset, io_func* io) {
HFSPlusExtentRecord* record;
record = (HFSPlusExtentRecord*) malloc(sizeof(HFSPlusExtentRecord));
if(!READ(io, offset, sizeof(HFSPlusExtentRecord), record))
return NULL;
flipExtentRecord(record);
return (BTKey*)record;
}
BTree* openExtentsTree(io_func* file) {
return openBTree(file, &extentCompare, &extentKeyRead, &extentKeyWrite, &extentKeyPrint, &extentDataRead);
}

View File

@@ -0,0 +1,418 @@
#include <stdint.h>
#include <hfs/hfsplus.h>
/* This routine is taken from Apple's TN 1150, with adaptations for C */
/* The lower case table consists of a 256-entry high-byte table followed by
some number of 256-entry subtables. The high-byte table contains either an
offset to the subtable for characters with that high byte or zero, which
means that there are no case mappings or ignored characters in that block.
Ignored characters are mapped to zero.
*/
uint16_t gLowerCaseTable[] = {
/* 0 */ 0x0100, 0x0200, 0x0000, 0x0300, 0x0400, 0x0500, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* 1 */ 0x0600, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* 2 */ 0x0700, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* 3 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* 4 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* 5 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* 6 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* 7 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* 8 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* 9 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* A */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* B */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* C */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* D */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* E */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* F */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0900, 0x0A00,
/* 0 */ 0xFFFF, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
/* 1 */ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
/* 2 */ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
/* 3 */ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
/* 4 */ 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
/* 5 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
0x0078, 0x0079, 0x007A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
/* 6 */ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
/* 7 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
/* 8 */ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
/* 9 */ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
/* A */ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
/* B */ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
/* C */ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00E6, 0x00C7,
0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
/* D */ 0x00F0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
0x00F8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00FE, 0x00DF,
/* E */ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
/* F */ 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF,
/* 0 */ 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107,
0x0108, 0x0109, 0x010A, 0x010B, 0x010C, 0x010D, 0x010E, 0x010F,
/* 1 */ 0x0111, 0x0111, 0x0112, 0x0113, 0x0114, 0x0115, 0x0116, 0x0117,
0x0118, 0x0119, 0x011A, 0x011B, 0x011C, 0x011D, 0x011E, 0x011F,
/* 2 */ 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, 0x0125, 0x0127, 0x0127,
0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, 0x012F,
/* 3 */ 0x0130, 0x0131, 0x0133, 0x0133, 0x0134, 0x0135, 0x0136, 0x0137,
0x0138, 0x0139, 0x013A, 0x013B, 0x013C, 0x013D, 0x013E, 0x0140,
/* 4 */ 0x0140, 0x0142, 0x0142, 0x0143, 0x0144, 0x0145, 0x0146, 0x0147,
0x0148, 0x0149, 0x014B, 0x014B, 0x014C, 0x014D, 0x014E, 0x014F,
/* 5 */ 0x0150, 0x0151, 0x0153, 0x0153, 0x0154, 0x0155, 0x0156, 0x0157,
0x0158, 0x0159, 0x015A, 0x015B, 0x015C, 0x015D, 0x015E, 0x015F,
/* 6 */ 0x0160, 0x0161, 0x0162, 0x0163, 0x0164, 0x0165, 0x0167, 0x0167,
0x0168, 0x0169, 0x016A, 0x016B, 0x016C, 0x016D, 0x016E, 0x016F,
/* 7 */ 0x0170, 0x0171, 0x0172, 0x0173, 0x0174, 0x0175, 0x0176, 0x0177,
0x0178, 0x0179, 0x017A, 0x017B, 0x017C, 0x017D, 0x017E, 0x017F,
/* 8 */ 0x0180, 0x0253, 0x0183, 0x0183, 0x0185, 0x0185, 0x0254, 0x0188,
0x0188, 0x0256, 0x0257, 0x018C, 0x018C, 0x018D, 0x01DD, 0x0259,
/* 9 */ 0x025B, 0x0192, 0x0192, 0x0260, 0x0263, 0x0195, 0x0269, 0x0268,
0x0199, 0x0199, 0x019A, 0x019B, 0x026F, 0x0272, 0x019E, 0x0275,
/* A */ 0x01A0, 0x01A1, 0x01A3, 0x01A3, 0x01A5, 0x01A5, 0x01A6, 0x01A8,
0x01A8, 0x0283, 0x01AA, 0x01AB, 0x01AD, 0x01AD, 0x0288, 0x01AF,
/* B */ 0x01B0, 0x028A, 0x028B, 0x01B4, 0x01B4, 0x01B6, 0x01B6, 0x0292,
0x01B9, 0x01B9, 0x01BA, 0x01BB, 0x01BD, 0x01BD, 0x01BE, 0x01BF,
/* C */ 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C6, 0x01C6, 0x01C6, 0x01C9,
0x01C9, 0x01C9, 0x01CC, 0x01CC, 0x01CC, 0x01CD, 0x01CE, 0x01CF,
/* D */ 0x01D0, 0x01D1, 0x01D2, 0x01D3, 0x01D4, 0x01D5, 0x01D6, 0x01D7,
0x01D8, 0x01D9, 0x01DA, 0x01DB, 0x01DC, 0x01DD, 0x01DE, 0x01DF,
/* E */ 0x01E0, 0x01E1, 0x01E2, 0x01E3, 0x01E5, 0x01E5, 0x01E6, 0x01E7,
0x01E8, 0x01E9, 0x01EA, 0x01EB, 0x01EC, 0x01ED, 0x01EE, 0x01EF,
/* F */ 0x01F0, 0x01F3, 0x01F3, 0x01F3, 0x01F4, 0x01F5, 0x01F6, 0x01F7,
0x01F8, 0x01F9, 0x01FA, 0x01FB, 0x01FC, 0x01FD, 0x01FE, 0x01FF,
/* 0 */ 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307,
0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F,
/* 1 */ 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317,
0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F,
/* 2 */ 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327,
0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F,
/* 3 */ 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337,
0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F,
/* 4 */ 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347,
0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F,
/* 5 */ 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357,
0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F,
/* 6 */ 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367,
0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F,
/* 7 */ 0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377,
0x0378, 0x0379, 0x037A, 0x037B, 0x037C, 0x037D, 0x037E, 0x037F,
/* 8 */ 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0386, 0x0387,
0x0388, 0x0389, 0x038A, 0x038B, 0x038C, 0x038D, 0x038E, 0x038F,
/* 9 */ 0x0390, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
/* A */ 0x03C0, 0x03C1, 0x03A2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,
0x03C8, 0x03C9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
/* B */ 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
/* C */ 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,
0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x03CF,
/* D */ 0x03D0, 0x03D1, 0x03D2, 0x03D3, 0x03D4, 0x03D5, 0x03D6, 0x03D7,
0x03D8, 0x03D9, 0x03DA, 0x03DB, 0x03DC, 0x03DD, 0x03DE, 0x03DF,
/* E */ 0x03E0, 0x03E1, 0x03E3, 0x03E3, 0x03E5, 0x03E5, 0x03E7, 0x03E7,
0x03E9, 0x03E9, 0x03EB, 0x03EB, 0x03ED, 0x03ED, 0x03EF, 0x03EF,
/* F */ 0x03F0, 0x03F1, 0x03F2, 0x03F3, 0x03F4, 0x03F5, 0x03F6, 0x03F7,
0x03F8, 0x03F9, 0x03FA, 0x03FB, 0x03FC, 0x03FD, 0x03FE, 0x03FF,
/* 0 */ 0x0400, 0x0401, 0x0452, 0x0403, 0x0454, 0x0455, 0x0456, 0x0407,
0x0458, 0x0459, 0x045A, 0x045B, 0x040C, 0x040D, 0x040E, 0x045F,
/* 1 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
0x0438, 0x0419, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
/* 2 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
/* 3 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
/* 4 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
/* 5 */ 0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457,
0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x045D, 0x045E, 0x045F,
/* 6 */ 0x0461, 0x0461, 0x0463, 0x0463, 0x0465, 0x0465, 0x0467, 0x0467,
0x0469, 0x0469, 0x046B, 0x046B, 0x046D, 0x046D, 0x046F, 0x046F,
/* 7 */ 0x0471, 0x0471, 0x0473, 0x0473, 0x0475, 0x0475, 0x0476, 0x0477,
0x0479, 0x0479, 0x047B, 0x047B, 0x047D, 0x047D, 0x047F, 0x047F,
/* 8 */ 0x0481, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487,
0x0488, 0x0489, 0x048A, 0x048B, 0x048C, 0x048D, 0x048E, 0x048F,
/* 9 */ 0x0491, 0x0491, 0x0493, 0x0493, 0x0495, 0x0495, 0x0497, 0x0497,
0x0499, 0x0499, 0x049B, 0x049B, 0x049D, 0x049D, 0x049F, 0x049F,
/* A */ 0x04A1, 0x04A1, 0x04A3, 0x04A3, 0x04A5, 0x04A5, 0x04A7, 0x04A7,
0x04A9, 0x04A9, 0x04AB, 0x04AB, 0x04AD, 0x04AD, 0x04AF, 0x04AF,
/* B */ 0x04B1, 0x04B1, 0x04B3, 0x04B3, 0x04B5, 0x04B5, 0x04B7, 0x04B7,
0x04B9, 0x04B9, 0x04BB, 0x04BB, 0x04BD, 0x04BD, 0x04BF, 0x04BF,
/* C */ 0x04C0, 0x04C1, 0x04C2, 0x04C4, 0x04C4, 0x04C5, 0x04C6, 0x04C8,
0x04C8, 0x04C9, 0x04CA, 0x04CC, 0x04CC, 0x04CD, 0x04CE, 0x04CF,
/* D */ 0x04D0, 0x04D1, 0x04D2, 0x04D3, 0x04D4, 0x04D5, 0x04D6, 0x04D7,
0x04D8, 0x04D9, 0x04DA, 0x04DB, 0x04DC, 0x04DD, 0x04DE, 0x04DF,
/* E */ 0x04E0, 0x04E1, 0x04E2, 0x04E3, 0x04E4, 0x04E5, 0x04E6, 0x04E7,
0x04E8, 0x04E9, 0x04EA, 0x04EB, 0x04EC, 0x04ED, 0x04EE, 0x04EF,
/* F */ 0x04F0, 0x04F1, 0x04F2, 0x04F3, 0x04F4, 0x04F5, 0x04F6, 0x04F7,
0x04F8, 0x04F9, 0x04FA, 0x04FB, 0x04FC, 0x04FD, 0x04FE, 0x04FF,
/* 0 */ 0x0500, 0x0501, 0x0502, 0x0503, 0x0504, 0x0505, 0x0506, 0x0507,
0x0508, 0x0509, 0x050A, 0x050B, 0x050C, 0x050D, 0x050E, 0x050F,
/* 1 */ 0x0510, 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0516, 0x0517,
0x0518, 0x0519, 0x051A, 0x051B, 0x051C, 0x051D, 0x051E, 0x051F,
/* 2 */ 0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527,
0x0528, 0x0529, 0x052A, 0x052B, 0x052C, 0x052D, 0x052E, 0x052F,
/* 3 */ 0x0530, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,
0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F,
/* 4 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577,
0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F,
/* 5 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0557,
0x0558, 0x0559, 0x055A, 0x055B, 0x055C, 0x055D, 0x055E, 0x055F,
/* 6 */ 0x0560, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,
0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F,
/* 7 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577,
0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F,
/* 8 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0587,
0x0588, 0x0589, 0x058A, 0x058B, 0x058C, 0x058D, 0x058E, 0x058F,
/* 9 */ 0x0590, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597,
0x0598, 0x0599, 0x059A, 0x059B, 0x059C, 0x059D, 0x059E, 0x059F,
/* A */ 0x05A0, 0x05A1, 0x05A2, 0x05A3, 0x05A4, 0x05A5, 0x05A6, 0x05A7,
0x05A8, 0x05A9, 0x05AA, 0x05AB, 0x05AC, 0x05AD, 0x05AE, 0x05AF,
/* B */ 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7,
0x05B8, 0x05B9, 0x05BA, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF,
/* C */ 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05C4, 0x05C5, 0x05C6, 0x05C7,
0x05C8, 0x05C9, 0x05CA, 0x05CB, 0x05CC, 0x05CD, 0x05CE, 0x05CF,
/* D */ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,
0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
/* E */ 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,
0x05E8, 0x05E9, 0x05EA, 0x05EB, 0x05EC, 0x05ED, 0x05EE, 0x05EF,
/* F */ 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0x05F5, 0x05F6, 0x05F7,
0x05F8, 0x05F9, 0x05FA, 0x05FB, 0x05FC, 0x05FD, 0x05FE, 0x05FF,
/* 0 */ 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007,
0x1008, 0x1009, 0x100A, 0x100B, 0x100C, 0x100D, 0x100E, 0x100F,
/* 1 */ 0x1010, 0x1011, 0x1012, 0x1013, 0x1014, 0x1015, 0x1016, 0x1017,
0x1018, 0x1019, 0x101A, 0x101B, 0x101C, 0x101D, 0x101E, 0x101F,
/* 2 */ 0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027,
0x1028, 0x1029, 0x102A, 0x102B, 0x102C, 0x102D, 0x102E, 0x102F,
/* 3 */ 0x1030, 0x1031, 0x1032, 0x1033, 0x1034, 0x1035, 0x1036, 0x1037,
0x1038, 0x1039, 0x103A, 0x103B, 0x103C, 0x103D, 0x103E, 0x103F,
/* 4 */ 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047,
0x1048, 0x1049, 0x104A, 0x104B, 0x104C, 0x104D, 0x104E, 0x104F,
/* 5 */ 0x1050, 0x1051, 0x1052, 0x1053, 0x1054, 0x1055, 0x1056, 0x1057,
0x1058, 0x1059, 0x105A, 0x105B, 0x105C, 0x105D, 0x105E, 0x105F,
/* 6 */ 0x1060, 0x1061, 0x1062, 0x1063, 0x1064, 0x1065, 0x1066, 0x1067,
0x1068, 0x1069, 0x106A, 0x106B, 0x106C, 0x106D, 0x106E, 0x106F,
/* 7 */ 0x1070, 0x1071, 0x1072, 0x1073, 0x1074, 0x1075, 0x1076, 0x1077,
0x1078, 0x1079, 0x107A, 0x107B, 0x107C, 0x107D, 0x107E, 0x107F,
/* 8 */ 0x1080, 0x1081, 0x1082, 0x1083, 0x1084, 0x1085, 0x1086, 0x1087,
0x1088, 0x1089, 0x108A, 0x108B, 0x108C, 0x108D, 0x108E, 0x108F,
/* 9 */ 0x1090, 0x1091, 0x1092, 0x1093, 0x1094, 0x1095, 0x1096, 0x1097,
0x1098, 0x1099, 0x109A, 0x109B, 0x109C, 0x109D, 0x109E, 0x109F,
/* A */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7,
0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF,
/* B */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7,
0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF,
/* C */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10C6, 0x10C7,
0x10C8, 0x10C9, 0x10CA, 0x10CB, 0x10CC, 0x10CD, 0x10CE, 0x10CF,
/* D */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7,
0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF,
/* E */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7,
0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF,
/* F */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10F6, 0x10F7,
0x10F8, 0x10F9, 0x10FA, 0x10FB, 0x10FC, 0x10FD, 0x10FE, 0x10FF,
/* 0 */ 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007,
0x2008, 0x2009, 0x200A, 0x200B, 0x0000, 0x0000, 0x0000, 0x0000,
/* 1 */ 0x2010, 0x2011, 0x2012, 0x2013, 0x2014, 0x2015, 0x2016, 0x2017,
0x2018, 0x2019, 0x201A, 0x201B, 0x201C, 0x201D, 0x201E, 0x201F,
/* 2 */ 0x2020, 0x2021, 0x2022, 0x2023, 0x2024, 0x2025, 0x2026, 0x2027,
0x2028, 0x2029, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x202F,
/* 3 */ 0x2030, 0x2031, 0x2032, 0x2033, 0x2034, 0x2035, 0x2036, 0x2037,
0x2038, 0x2039, 0x203A, 0x203B, 0x203C, 0x203D, 0x203E, 0x203F,
/* 4 */ 0x2040, 0x2041, 0x2042, 0x2043, 0x2044, 0x2045, 0x2046, 0x2047,
0x2048, 0x2049, 0x204A, 0x204B, 0x204C, 0x204D, 0x204E, 0x204F,
/* 5 */ 0x2050, 0x2051, 0x2052, 0x2053, 0x2054, 0x2055, 0x2056, 0x2057,
0x2058, 0x2059, 0x205A, 0x205B, 0x205C, 0x205D, 0x205E, 0x205F,
/* 6 */ 0x2060, 0x2061, 0x2062, 0x2063, 0x2064, 0x2065, 0x2066, 0x2067,
0x2068, 0x2069, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* 7 */ 0x2070, 0x2071, 0x2072, 0x2073, 0x2074, 0x2075, 0x2076, 0x2077,
0x2078, 0x2079, 0x207A, 0x207B, 0x207C, 0x207D, 0x207E, 0x207F,
/* 8 */ 0x2080, 0x2081, 0x2082, 0x2083, 0x2084, 0x2085, 0x2086, 0x2087,
0x2088, 0x2089, 0x208A, 0x208B, 0x208C, 0x208D, 0x208E, 0x208F,
/* 9 */ 0x2090, 0x2091, 0x2092, 0x2093, 0x2094, 0x2095, 0x2096, 0x2097,
0x2098, 0x2099, 0x209A, 0x209B, 0x209C, 0x209D, 0x209E, 0x209F,
/* A */ 0x20A0, 0x20A1, 0x20A2, 0x20A3, 0x20A4, 0x20A5, 0x20A6, 0x20A7,
0x20A8, 0x20A9, 0x20AA, 0x20AB, 0x20AC, 0x20AD, 0x20AE, 0x20AF,
/* B */ 0x20B0, 0x20B1, 0x20B2, 0x20B3, 0x20B4, 0x20B5, 0x20B6, 0x20B7,
0x20B8, 0x20B9, 0x20BA, 0x20BB, 0x20BC, 0x20BD, 0x20BE, 0x20BF,
/* C */ 0x20C0, 0x20C1, 0x20C2, 0x20C3, 0x20C4, 0x20C5, 0x20C6, 0x20C7,
0x20C8, 0x20C9, 0x20CA, 0x20CB, 0x20CC, 0x20CD, 0x20CE, 0x20CF,
/* D */ 0x20D0, 0x20D1, 0x20D2, 0x20D3, 0x20D4, 0x20D5, 0x20D6, 0x20D7,
0x20D8, 0x20D9, 0x20DA, 0x20DB, 0x20DC, 0x20DD, 0x20DE, 0x20DF,
/* E */ 0x20E0, 0x20E1, 0x20E2, 0x20E3, 0x20E4, 0x20E5, 0x20E6, 0x20E7,
0x20E8, 0x20E9, 0x20EA, 0x20EB, 0x20EC, 0x20ED, 0x20EE, 0x20EF,
/* F */ 0x20F0, 0x20F1, 0x20F2, 0x20F3, 0x20F4, 0x20F5, 0x20F6, 0x20F7,
0x20F8, 0x20F9, 0x20FA, 0x20FB, 0x20FC, 0x20FD, 0x20FE, 0x20FF,
/* 0 */ 0x2100, 0x2101, 0x2102, 0x2103, 0x2104, 0x2105, 0x2106, 0x2107,
0x2108, 0x2109, 0x210A, 0x210B, 0x210C, 0x210D, 0x210E, 0x210F,
/* 1 */ 0x2110, 0x2111, 0x2112, 0x2113, 0x2114, 0x2115, 0x2116, 0x2117,
0x2118, 0x2119, 0x211A, 0x211B, 0x211C, 0x211D, 0x211E, 0x211F,
/* 2 */ 0x2120, 0x2121, 0x2122, 0x2123, 0x2124, 0x2125, 0x2126, 0x2127,
0x2128, 0x2129, 0x212A, 0x212B, 0x212C, 0x212D, 0x212E, 0x212F,
/* 3 */ 0x2130, 0x2131, 0x2132, 0x2133, 0x2134, 0x2135, 0x2136, 0x2137,
0x2138, 0x2139, 0x213A, 0x213B, 0x213C, 0x213D, 0x213E, 0x213F,
/* 4 */ 0x2140, 0x2141, 0x2142, 0x2143, 0x2144, 0x2145, 0x2146, 0x2147,
0x2148, 0x2149, 0x214A, 0x214B, 0x214C, 0x214D, 0x214E, 0x214F,
/* 5 */ 0x2150, 0x2151, 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157,
0x2158, 0x2159, 0x215A, 0x215B, 0x215C, 0x215D, 0x215E, 0x215F,
/* 6 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177,
0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F,
/* 7 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177,
0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F,
/* 8 */ 0x2180, 0x2181, 0x2182, 0x2183, 0x2184, 0x2185, 0x2186, 0x2187,
0x2188, 0x2189, 0x218A, 0x218B, 0x218C, 0x218D, 0x218E, 0x218F,
/* 9 */ 0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x2196, 0x2197,
0x2198, 0x2199, 0x219A, 0x219B, 0x219C, 0x219D, 0x219E, 0x219F,
/* A */ 0x21A0, 0x21A1, 0x21A2, 0x21A3, 0x21A4, 0x21A5, 0x21A6, 0x21A7,
0x21A8, 0x21A9, 0x21AA, 0x21AB, 0x21AC, 0x21AD, 0x21AE, 0x21AF,
/* B */ 0x21B0, 0x21B1, 0x21B2, 0x21B3, 0x21B4, 0x21B5, 0x21B6, 0x21B7,
0x21B8, 0x21B9, 0x21BA, 0x21BB, 0x21BC, 0x21BD, 0x21BE, 0x21BF,
/* C */ 0x21C0, 0x21C1, 0x21C2, 0x21C3, 0x21C4, 0x21C5, 0x21C6, 0x21C7,
0x21C8, 0x21C9, 0x21CA, 0x21CB, 0x21CC, 0x21CD, 0x21CE, 0x21CF,
/* D */ 0x21D0, 0x21D1, 0x21D2, 0x21D3, 0x21D4, 0x21D5, 0x21D6, 0x21D7,
0x21D8, 0x21D9, 0x21DA, 0x21DB, 0x21DC, 0x21DD, 0x21DE, 0x21DF,
/* E */ 0x21E0, 0x21E1, 0x21E2, 0x21E3, 0x21E4, 0x21E5, 0x21E6, 0x21E7,
0x21E8, 0x21E9, 0x21EA, 0x21EB, 0x21EC, 0x21ED, 0x21EE, 0x21EF,
/* F */ 0x21F0, 0x21F1, 0x21F2, 0x21F3, 0x21F4, 0x21F5, 0x21F6, 0x21F7,
0x21F8, 0x21F9, 0x21FA, 0x21FB, 0x21FC, 0x21FD, 0x21FE, 0x21FF,
/* 0 */ 0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05, 0xFE06, 0xFE07,
0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D, 0xFE0E, 0xFE0F,
/* 1 */ 0xFE10, 0xFE11, 0xFE12, 0xFE13, 0xFE14, 0xFE15, 0xFE16, 0xFE17,
0xFE18, 0xFE19, 0xFE1A, 0xFE1B, 0xFE1C, 0xFE1D, 0xFE1E, 0xFE1F,
/* 2 */ 0xFE20, 0xFE21, 0xFE22, 0xFE23, 0xFE24, 0xFE25, 0xFE26, 0xFE27,
0xFE28, 0xFE29, 0xFE2A, 0xFE2B, 0xFE2C, 0xFE2D, 0xFE2E, 0xFE2F,
/* 3 */ 0xFE30, 0xFE31, 0xFE32, 0xFE33, 0xFE34, 0xFE35, 0xFE36, 0xFE37,
0xFE38, 0xFE39, 0xFE3A, 0xFE3B, 0xFE3C, 0xFE3D, 0xFE3E, 0xFE3F,
/* 4 */ 0xFE40, 0xFE41, 0xFE42, 0xFE43, 0xFE44, 0xFE45, 0xFE46, 0xFE47,
0xFE48, 0xFE49, 0xFE4A, 0xFE4B, 0xFE4C, 0xFE4D, 0xFE4E, 0xFE4F,
/* 5 */ 0xFE50, 0xFE51, 0xFE52, 0xFE53, 0xFE54, 0xFE55, 0xFE56, 0xFE57,
0xFE58, 0xFE59, 0xFE5A, 0xFE5B, 0xFE5C, 0xFE5D, 0xFE5E, 0xFE5F,
/* 6 */ 0xFE60, 0xFE61, 0xFE62, 0xFE63, 0xFE64, 0xFE65, 0xFE66, 0xFE67,
0xFE68, 0xFE69, 0xFE6A, 0xFE6B, 0xFE6C, 0xFE6D, 0xFE6E, 0xFE6F,
/* 7 */ 0xFE70, 0xFE71, 0xFE72, 0xFE73, 0xFE74, 0xFE75, 0xFE76, 0xFE77,
0xFE78, 0xFE79, 0xFE7A, 0xFE7B, 0xFE7C, 0xFE7D, 0xFE7E, 0xFE7F,
/* 8 */ 0xFE80, 0xFE81, 0xFE82, 0xFE83, 0xFE84, 0xFE85, 0xFE86, 0xFE87,
0xFE88, 0xFE89, 0xFE8A, 0xFE8B, 0xFE8C, 0xFE8D, 0xFE8E, 0xFE8F,
/* 9 */ 0xFE90, 0xFE91, 0xFE92, 0xFE93, 0xFE94, 0xFE95, 0xFE96, 0xFE97,
0xFE98, 0xFE99, 0xFE9A, 0xFE9B, 0xFE9C, 0xFE9D, 0xFE9E, 0xFE9F,
/* A */ 0xFEA0, 0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4, 0xFEA5, 0xFEA6, 0xFEA7,
0xFEA8, 0xFEA9, 0xFEAA, 0xFEAB, 0xFEAC, 0xFEAD, 0xFEAE, 0xFEAF,
/* B */ 0xFEB0, 0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4, 0xFEB5, 0xFEB6, 0xFEB7,
0xFEB8, 0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC, 0xFEBD, 0xFEBE, 0xFEBF,
/* C */ 0xFEC0, 0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4, 0xFEC5, 0xFEC6, 0xFEC7,
0xFEC8, 0xFEC9, 0xFECA, 0xFECB, 0xFECC, 0xFECD, 0xFECE, 0xFECF,
/* D */ 0xFED0, 0xFED1, 0xFED2, 0xFED3, 0xFED4, 0xFED5, 0xFED6, 0xFED7,
0xFED8, 0xFED9, 0xFEDA, 0xFEDB, 0xFEDC, 0xFEDD, 0xFEDE, 0xFEDF,
/* E */ 0xFEE0, 0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4, 0xFEE5, 0xFEE6, 0xFEE7,
0xFEE8, 0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC, 0xFEED, 0xFEEE, 0xFEEF,
/* F */ 0xFEF0, 0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4, 0xFEF5, 0xFEF6, 0xFEF7,
0xFEF8, 0xFEF9, 0xFEFA, 0xFEFB, 0xFEFC, 0xFEFD, 0xFEFE, 0x0000,
/* 0 */ 0xFF00, 0xFF01, 0xFF02, 0xFF03, 0xFF04, 0xFF05, 0xFF06, 0xFF07,
0xFF08, 0xFF09, 0xFF0A, 0xFF0B, 0xFF0C, 0xFF0D, 0xFF0E, 0xFF0F,
/* 1 */ 0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17,
0xFF18, 0xFF19, 0xFF1A, 0xFF1B, 0xFF1C, 0xFF1D, 0xFF1E, 0xFF1F,
/* 2 */ 0xFF20, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47,
0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F,
/* 3 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57,
0xFF58, 0xFF59, 0xFF5A, 0xFF3B, 0xFF3C, 0xFF3D, 0xFF3E, 0xFF3F,
/* 4 */ 0xFF40, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47,
0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F,
/* 5 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57,
0xFF58, 0xFF59, 0xFF5A, 0xFF5B, 0xFF5C, 0xFF5D, 0xFF5E, 0xFF5F,
/* 6 */ 0xFF60, 0xFF61, 0xFF62, 0xFF63, 0xFF64, 0xFF65, 0xFF66, 0xFF67,
0xFF68, 0xFF69, 0xFF6A, 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F,
/* 7 */ 0xFF70, 0xFF71, 0xFF72, 0xFF73, 0xFF74, 0xFF75, 0xFF76, 0xFF77,
0xFF78, 0xFF79, 0xFF7A, 0xFF7B, 0xFF7C, 0xFF7D, 0xFF7E, 0xFF7F,
/* 8 */ 0xFF80, 0xFF81, 0xFF82, 0xFF83, 0xFF84, 0xFF85, 0xFF86, 0xFF87,
0xFF88, 0xFF89, 0xFF8A, 0xFF8B, 0xFF8C, 0xFF8D, 0xFF8E, 0xFF8F,
/* 9 */ 0xFF90, 0xFF91, 0xFF92, 0xFF93, 0xFF94, 0xFF95, 0xFF96, 0xFF97,
0xFF98, 0xFF99, 0xFF9A, 0xFF9B, 0xFF9C, 0xFF9D, 0xFF9E, 0xFF9F,
/* A */ 0xFFA0, 0xFFA1, 0xFFA2, 0xFFA3, 0xFFA4, 0xFFA5, 0xFFA6, 0xFFA7,
0xFFA8, 0xFFA9, 0xFFAA, 0xFFAB, 0xFFAC, 0xFFAD, 0xFFAE, 0xFFAF,
/* B */ 0xFFB0, 0xFFB1, 0xFFB2, 0xFFB3, 0xFFB4, 0xFFB5, 0xFFB6, 0xFFB7,
0xFFB8, 0xFFB9, 0xFFBA, 0xFFBB, 0xFFBC, 0xFFBD, 0xFFBE, 0xFFBF,
/* C */ 0xFFC0, 0xFFC1, 0xFFC2, 0xFFC3, 0xFFC4, 0xFFC5, 0xFFC6, 0xFFC7,
0xFFC8, 0xFFC9, 0xFFCA, 0xFFCB, 0xFFCC, 0xFFCD, 0xFFCE, 0xFFCF,
/* D */ 0xFFD0, 0xFFD1, 0xFFD2, 0xFFD3, 0xFFD4, 0xFFD5, 0xFFD6, 0xFFD7,
0xFFD8, 0xFFD9, 0xFFDA, 0xFFDB, 0xFFDC, 0xFFDD, 0xFFDE, 0xFFDF,
/* E */ 0xFFE0, 0xFFE1, 0xFFE2, 0xFFE3, 0xFFE4, 0xFFE5, 0xFFE6, 0xFFE7,
0xFFE8, 0xFFE9, 0xFFEA, 0xFFEB, 0xFFEC, 0xFFED, 0xFFEE, 0xFFEF,
/* F */ 0xFFF0, 0xFFF1, 0xFFF2, 0xFFF3, 0xFFF4, 0xFFF5, 0xFFF6, 0xFFF7,
0xFFF8, 0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF,
};
int32_t FastUnicodeCompare ( register uint16_t str1[], register uint16_t length1,
register uint16_t str2[], register uint16_t length2)
{
register uint16_t c1,c2;
register uint16_t temp;
register uint16_t* lowerCaseTable;
lowerCaseTable = gLowerCaseTable;
while (1) {
c1 = 0;
c2 = 0;
while (length1 && c1 == 0) {
c1 = *(str1++);
--length1;
if ((temp = lowerCaseTable[c1>>8]) != 0)
c1 = lowerCaseTable[temp + (c1 & 0x00FF)];
}
while (length2 && c2 == 0) {
c2 = *(str2++);
--length2;
if ((temp = lowerCaseTable[c2>>8]) != 0)
c2 = lowerCaseTable[temp + (c2 & 0x00FF)];
}
if (c1 == ':') {
c1 = '/';
}
if (c2 == ':') {
c2 = '/';
}
if (c1 != c2)
break;
if (c1 == 0)
return 0;
}
if (c1 < c2)
return -1;
else
return 1;
}

View File

@@ -0,0 +1,104 @@
#include <stdlib.h>
#include <hfs/hfsplus.h>
static int flatFileRead(io_func* io, off_t location, size_t size, void *buffer) {
FILE* file;
file = (FILE*) io->data;
if(size == 0) {
return TRUE;
}
//printf("%d %d\n", location, size); fflush(stdout);
if(fseeko(file, location, SEEK_SET) != 0) {
perror("fseek");
return FALSE;
}
if(fread(buffer, size, 1, file) != 1) {
perror("fread");
return FALSE;
} else {
return TRUE;
}
}
static int flatFileWrite(io_func* io, off_t location, size_t size, void *buffer) {
FILE* file;
/*int i;
printf("write: %lld %d - ", location, size); fflush(stdout);
for(i = 0; i < size; i++) {
printf("%x ", ((unsigned char*)buffer)[i]);
fflush(stdout);
}
printf("\n"); fflush(stdout);*/
if(size == 0) {
return TRUE;
}
file = (FILE*) io->data;
if(fseeko(file, location, SEEK_SET) != 0) {
perror("fseek");
return FALSE;
}
if(fwrite(buffer, size, 1, file) != 1) {
perror("fwrite");
return FALSE;
} else {
return TRUE;
}
return TRUE;
}
static void closeFlatFile(io_func* io) {
FILE* file;
file = (FILE*) io->data;
fclose(file);
free(io);
}
io_func* openFlatFile(const char* fileName) {
io_func* io;
io = (io_func*) malloc(sizeof(io_func));
io->data = fopen(fileName, "rb+");
if(io->data == NULL) {
perror("fopen");
return NULL;
}
io->read = &flatFileRead;
io->write = &flatFileWrite;
io->close = &closeFlatFile;
return io;
}
io_func* openFlatFileRO(const char* fileName) {
io_func* io;
io = (io_func*) malloc(sizeof(io_func));
io->data = fopen(fileName, "rb");
if(io->data == NULL) {
perror("fopen");
return NULL;
}
io->read = &flatFileRead;
io->write = &flatFileWrite;
io->close = &closeFlatFile;
return io;
}

View File

@@ -0,0 +1,333 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <hfs/hfsplus.h>
#include <dirent.h>
#include <hfs/hfslib.h>
#include "abstractfile.h"
#include <inttypes.h>
char endianness;
void cmd_ls(Volume* volume, int argc, const char *argv[]) {
if(argc > 1)
hfs_ls(volume, argv[1]);
else
hfs_ls(volume, "/");
}
void cmd_cat(Volume* volume, int argc, const char *argv[]) {
HFSPlusCatalogRecord* record;
AbstractFile* stdoutFile;
record = getRecordFromPath(argv[1], volume, NULL, NULL);
stdoutFile = createAbstractFileFromFile(stdout);
if(record != NULL) {
if(record->recordType == kHFSPlusFileRecord)
writeToFile((HFSPlusCatalogFile*)record, stdoutFile, volume);
else
printf("Not a file\n");
} else {
printf("No such file or directory\n");
}
free(record);
free(stdoutFile);
}
void cmd_extract(Volume* volume, int argc, const char *argv[]) {
HFSPlusCatalogRecord* record;
AbstractFile *outFile;
if(argc < 3) {
printf("Not enough arguments");
return;
}
outFile = createAbstractFileFromFile(fopen(argv[2], "wb"));
if(outFile == NULL) {
printf("cannot create file");
}
record = getRecordFromPath(argv[1], volume, NULL, NULL);
if(record != NULL) {
if(record->recordType == kHFSPlusFileRecord)
writeToFile((HFSPlusCatalogFile*)record, outFile, volume);
else
printf("Not a file\n");
} else {
printf("No such file or directory\n");
}
outFile->close(outFile);
free(record);
}
void cmd_mv(Volume* volume, int argc, const char *argv[]) {
if(argc > 2) {
move(argv[1], argv[2], volume);
} else {
printf("Not enough arguments");
}
}
void cmd_symlink(Volume* volume, int argc, const char *argv[]) {
if(argc > 2) {
makeSymlink(argv[1], argv[2], volume);
} else {
printf("Not enough arguments");
}
}
void cmd_mkdir(Volume* volume, int argc, const char *argv[]) {
if(argc > 1) {
newFolder(argv[1], volume);
} else {
printf("Not enough arguments");
}
}
void cmd_add(Volume* volume, int argc, const char *argv[]) {
AbstractFile *inFile;
if(argc < 3) {
printf("Not enough arguments");
return;
}
inFile = createAbstractFileFromFile(fopen(argv[1], "rb"));
if(inFile == NULL) {
printf("file to add not found");
}
add_hfs(volume, inFile, argv[2]);
}
void cmd_rm(Volume* volume, int argc, const char *argv[]) {
if(argc > 1) {
removeFile(argv[1], volume);
} else {
printf("Not enough arguments");
}
}
void cmd_chmod(Volume* volume, int argc, const char *argv[]) {
int mode;
if(argc > 2) {
sscanf(argv[1], "%o", &mode);
chmodFile(argv[2], mode, volume);
} else {
printf("Not enough arguments");
}
}
void cmd_extractall(Volume* volume, int argc, const char *argv[]) {
HFSPlusCatalogRecord* record;
char cwd[1024];
char* name;
ASSERT(getcwd(cwd, 1024) != NULL, "cannot get current working directory");
if(argc > 1)
record = getRecordFromPath(argv[1], volume, &name, NULL);
else
record = getRecordFromPath("/", volume, &name, NULL);
if(argc > 2) {
ASSERT(chdir(argv[2]) == 0, "chdir");
}
if(record != NULL) {
if(record->recordType == kHFSPlusFolderRecord)
extractAllInFolder(((HFSPlusCatalogFolder*)record)->folderID, volume);
else
printf("Not a folder\n");
} else {
printf("No such file or directory\n");
}
free(record);
ASSERT(chdir(cwd) == 0, "chdir");
}
void cmd_rmall(Volume* volume, int argc, const char *argv[]) {
HFSPlusCatalogRecord* record;
char* name;
char initPath[1024];
int lastCharOfPath;
if(argc > 1) {
record = getRecordFromPath(argv[1], volume, &name, NULL);
strcpy(initPath, argv[1]);
lastCharOfPath = strlen(argv[1]) - 1;
if(argv[1][lastCharOfPath] != '/') {
initPath[lastCharOfPath + 1] = '/';
initPath[lastCharOfPath + 2] = '\0';
}
} else {
record = getRecordFromPath("/", volume, &name, NULL);
initPath[0] = '/';
initPath[1] = '\0';
}
if(record != NULL) {
if(record->recordType == kHFSPlusFolderRecord) {
removeAllInFolder(((HFSPlusCatalogFolder*)record)->folderID, volume, initPath);
} else {
printf("Not a folder\n");
}
} else {
printf("No such file or directory\n");
}
free(record);
}
void cmd_addall(Volume* volume, int argc, const char *argv[]) {
if(argc < 2) {
printf("Not enough arguments");
return;
}
if(argc > 2) {
addall_hfs(volume, argv[1], argv[2]);
} else {
addall_hfs(volume, argv[1], "/");
}
}
void cmd_grow(Volume* volume, int argc, const char *argv[]) {
uint64_t newSize;
if(argc < 2) {
printf("Not enough arguments\n");
return;
}
newSize = 0;
sscanf(argv[1], "%" PRId64, &newSize);
grow_hfs(volume, newSize);
printf("grew volume: %" PRId64 "\n", newSize);
}
void cmd_getattr(Volume* volume, int argc, const char *argv[]) {
HFSPlusCatalogRecord* record;
if(argc < 3) {
printf("Not enough arguments");
return;
}
record = getRecordFromPath(argv[1], volume, NULL, NULL);
if(record != NULL) {
HFSCatalogNodeID id;
uint8_t* data;
size_t size;
if(record->recordType == kHFSPlusFileRecord)
id = ((HFSPlusCatalogFile*)record)->fileID;
else
id = ((HFSPlusCatalogFolder*)record)->folderID;
size = getAttribute(volume, id, argv[2], &data);
if(size > 0) {
fwrite(data, size, 1, stdout);
free(data);
} else {
printf("No such attribute\n");
}
} else {
printf("No such file or directory\n");
}
free(record);
}
void TestByteOrder()
{
short int word = 0x0001;
char *byte = (char *) &word;
endianness = byte[0] ? IS_LITTLE_ENDIAN : IS_BIG_ENDIAN;
}
int main(int argc, const char *argv[]) {
io_func* io;
Volume* volume;
TestByteOrder();
if(argc < 3) {
printf("usage: %s <image-file> <ls|cat|mv|mkdir|add|rm|chmod|extract|extractall|rmall|addall|debug> <arguments>\n", argv[0]);
return 0;
}
io = openFlatFile(argv[1]);
if(io == NULL) {
fprintf(stderr, "error: Cannot open image-file.\n");
return 1;
}
volume = openVolume(io);
if(volume == NULL) {
fprintf(stderr, "error: Cannot open volume.\n");
CLOSE(io);
return 1;
}
if(argc > 1) {
if(strcmp(argv[2], "ls") == 0) {
cmd_ls(volume, argc - 2, argv + 2);
} else if(strcmp(argv[2], "cat") == 0) {
cmd_cat(volume, argc - 2, argv + 2);
} else if(strcmp(argv[2], "mv") == 0) {
cmd_mv(volume, argc - 2, argv + 2);
} else if(strcmp(argv[2], "symlink") == 0) {
cmd_symlink(volume, argc - 2, argv + 2);
} else if(strcmp(argv[2], "mkdir") == 0) {
cmd_mkdir(volume, argc - 2, argv + 2);
} else if(strcmp(argv[2], "add") == 0) {
cmd_add(volume, argc - 2, argv + 2);
} else if(strcmp(argv[2], "rm") == 0) {
cmd_rm(volume, argc - 2, argv + 2);
} else if(strcmp(argv[2], "chmod") == 0) {
cmd_chmod(volume, argc - 2, argv + 2);
} else if(strcmp(argv[2], "extract") == 0) {
cmd_extract(volume, argc - 2, argv + 2);
} else if(strcmp(argv[2], "extractall") == 0) {
cmd_extractall(volume, argc - 2, argv + 2);
} else if(strcmp(argv[2], "rmall") == 0) {
cmd_rmall(volume, argc - 2, argv + 2);
} else if(strcmp(argv[2], "addall") == 0) {
cmd_addall(volume, argc - 2, argv + 2);
} else if(strcmp(argv[2], "grow") == 0) {
cmd_grow(volume, argc - 2, argv + 2);
} else if(strcmp(argv[2], "getattr") == 0) {
cmd_getattr(volume, argc - 2, argv + 2);
} else if(strcmp(argv[2], "debug") == 0) {
if(argc > 3 && strcmp(argv[3], "verbose") == 0) {
debugBTree(volume->catalogTree, TRUE);
} else {
debugBTree(volume->catalogTree, FALSE);
}
}
}
closeVolume(volume);
CLOSE(io);
return 0;
}

View File

@@ -0,0 +1,292 @@
#include <zlib.h>
#include "common.h"
#include <hfs/hfsplus.h>
#include <hfs/hfscompress.h>
void flipHFSPlusDecmpfs(HFSPlusDecmpfs* compressData) {
FLIPENDIANLE(compressData->magic);
FLIPENDIANLE(compressData->flags);
FLIPENDIANLE(compressData->size);
}
void flipRsrcHead(HFSPlusCmpfRsrcHead* data) {
FLIPENDIAN(data->headerSize);
FLIPENDIAN(data->totalSize);
FLIPENDIAN(data->dataSize);
FLIPENDIAN(data->flags);
}
void flipRsrcBlockHead(HFSPlusCmpfRsrcBlockHead* data) {
FLIPENDIAN(data->dataSize);
FLIPENDIANLE(data->numBlocks);
}
void flipRsrcBlock(HFSPlusCmpfRsrcBlock* data) {
FLIPENDIANLE(data->offset);
FLIPENDIANLE(data->size);
}
void flipHFSPlusCmpfEnd(HFSPlusCmpfEnd* data) {
FLIPENDIAN(data->unk1);
FLIPENDIAN(data->unk2);
FLIPENDIAN(data->unk3);
FLIPENDIAN(data->magic);
FLIPENDIAN(data->flags);
FLIPENDIANLE(data->size);
FLIPENDIANLE(data->unk4);
}
static int compressedRead(io_func* io, off_t location, size_t size, void *buffer) {
HFSPlusCompressed* data = (HFSPlusCompressed*) io->data;
size_t toRead;
while(size > 0) {
if(data->cached && location >= data->cachedStart && location < data->cachedEnd) {
if((data->cachedEnd - location) < size)
toRead = data->cachedEnd - location;
else
toRead = size;
memcpy(buffer, data->cached + (location - data->cachedStart), toRead);
size -= toRead;
location += toRead;
buffer = ((uint8_t*) buffer) + toRead;
}
if(size == 0)
break;
// Try to cache
uLongf actualSize;
uint32_t block = location / 0x10000;
uint8_t* compressed = (uint8_t*) malloc(data->blocks->blocks[block].size);
if(!READ(data->io, data->rsrcHead.headerSize + sizeof(uint32_t) + data->blocks->blocks[block].offset, data->blocks->blocks[block].size, compressed)) {
hfs_panic("error reading");
}
if(data->cached)
free(data->cached);
data->cached = (uint8_t*) malloc(0x10000);
actualSize = 0x10000;
uncompress(data->cached, &actualSize, compressed, data->blocks->blocks[block].size);
data->cachedStart = block * 0x10000;
data->cachedEnd = data->cachedStart + actualSize;
free(compressed);
}
return TRUE;
}
static int compressedWrite(io_func* io, off_t location, size_t size, void *buffer) {
HFSPlusCompressed* data = (HFSPlusCompressed*) io->data;
if(data->cachedStart != 0 || data->cachedEnd != data->decmpfs->size) {
// Cache entire file
uint8_t* newCache = (uint8_t*) malloc(data->decmpfs->size);
compressedRead(io, 0, data->decmpfs->size, newCache);
if(data->cached)
free(data->cached);
data->cached = newCache;
data->cachedStart = 0;
data->cachedEnd = data->decmpfs->size;
}
if((location + size) > data->decmpfs->size) {
data->decmpfs->size = location + size;
data->cached = (uint8_t*) realloc(data->cached, data->decmpfs->size);
data->cachedEnd = data->decmpfs->size;
}
memcpy(data->cached + location, buffer, size);
data->dirty = TRUE;
return TRUE;
}
static void closeHFSPlusCompressed(io_func* io) {
HFSPlusCompressed* data = (HFSPlusCompressed*) io->data;
if(data->io)
CLOSE(data->io);
if(data->dirty) {
if(data->blocks)
free(data->blocks);
data->decmpfs->magic = CMPFS_MAGIC;
data->decmpfs->flags = 0x4;
data->decmpfsSize = sizeof(HFSPlusDecmpfs);
uint32_t numBlocks = (data->decmpfs->size + 0xFFFF) / 0x10000;
uint32_t blocksSize = sizeof(HFSPlusCmpfRsrcBlockHead) + (numBlocks * sizeof(HFSPlusCmpfRsrcBlock));
data->blocks = (HFSPlusCmpfRsrcBlockHead*) malloc(sizeof(HFSPlusCmpfRsrcBlockHead) + (numBlocks * sizeof(HFSPlusCmpfRsrcBlock)));
data->blocks->numBlocks = numBlocks;
data->blocks->dataSize = blocksSize - sizeof(uint32_t); // without the front dataSize in BlockHead.
data->rsrcHead.headerSize = 0x100;
data->rsrcHead.dataSize = blocksSize;
data->rsrcHead.totalSize = data->rsrcHead.headerSize + data->rsrcHead.dataSize;
data->rsrcHead.flags = 0x32;
uint8_t* buffer = (uint8_t*) malloc((0x10000 * 1.1) + 12);
uint32_t curFileOffset = data->blocks->dataSize;
uint32_t i;
for(i = 0; i < numBlocks; i++) {
data->blocks->blocks[i].offset = curFileOffset;
uLongf actualSize = (0x10000 * 1.1) + 12;
compress(buffer, &actualSize, data->cached + (0x10000 * i),
(data->decmpfs->size - (0x10000 * i)) > 0x10000 ? 0x10000 : (data->decmpfs->size - (0x10000 * i)));
data->blocks->blocks[i].size = actualSize;
// check if we can fit the whole thing into an inline extended attribute
// a little fudge factor here since sizeof(HFSPlusAttrKey) is bigger than it ought to be, since only 127 characters are strictly allowed
if(numBlocks <= 1 && (actualSize + sizeof(HFSPlusDecmpfs) + sizeof(HFSPlusAttrKey)) <= 0x1000) {
data->decmpfs->flags = 0x3;
memcpy(data->decmpfs->data, buffer, actualSize);
data->decmpfsSize = sizeof(HFSPlusDecmpfs) + actualSize;
printf("inline data\n");
break;
} else {
if(i == 0) {
data->io = openRawFile(data->file->fileID, &data->file->resourceFork, (HFSPlusCatalogRecord*)data->file, data->volume);
if(!data->io) {
hfs_panic("error opening resource fork");
}
}
WRITE(data->io, data->rsrcHead.headerSize + sizeof(uint32_t) + data->blocks->blocks[i].offset, data->blocks->blocks[i].size, buffer);
curFileOffset += data->blocks->blocks[i].size;
data->blocks->dataSize += data->blocks->blocks[i].size;
data->rsrcHead.dataSize += data->blocks->blocks[i].size;
data->rsrcHead.totalSize += data->blocks->blocks[i].size;
}
}
free(buffer);
if(data->decmpfs->flags == 0x4) {
flipRsrcHead(&data->rsrcHead);
WRITE(data->io, 0, sizeof(HFSPlusCmpfRsrcHead), &data->rsrcHead);
flipRsrcHead(&data->rsrcHead);
for(i = 0; i < data->blocks->numBlocks; i++) {
flipRsrcBlock(&data->blocks->blocks[i]);
}
flipRsrcBlockHead(data->blocks);
WRITE(data->io, data->rsrcHead.headerSize, blocksSize, data->blocks);
flipRsrcBlockHead(data->blocks);
for(i = 0; i < data->blocks->numBlocks; i++) {
flipRsrcBlock(&data->blocks->blocks[i]);
}
HFSPlusCmpfEnd end;
memset(&end, 0, sizeof(HFSPlusCmpfEnd));
end.unk1 = 0x1C;
end.unk2 = 0x32;
end.unk3 = 0x0;
end.magic = CMPFS_MAGIC;
end.flags = 0xA;
end.size = 0xFFFF01;
end.unk4 = 0x0;
flipHFSPlusCmpfEnd(&end);
WRITE(data->io, data->rsrcHead.totalSize, sizeof(HFSPlusCmpfEnd), &end);
flipHFSPlusCmpfEnd(&end);
CLOSE(data->io);
}
flipHFSPlusDecmpfs(data->decmpfs);
setAttribute(data->volume, data->file->fileID, "com.apple.decmpfs", (uint8_t*)(data->decmpfs), data->decmpfsSize);
flipHFSPlusDecmpfs(data->decmpfs);
}
if(data->cached)
free(data->cached);
if(data->blocks)
free(data->blocks);
free(data->decmpfs);
free(data);
free(io);
}
io_func* openHFSPlusCompressed(Volume* volume, HFSPlusCatalogFile* file) {
io_func* io;
HFSPlusCompressed* data;
uLongf actualSize;
io = (io_func*) malloc(sizeof(io_func));
data = (HFSPlusCompressed*) malloc(sizeof(HFSPlusCompressed));
data->volume = volume;
data->file = file;
io->data = data;
io->read = &compressedRead;
io->write = &compressedWrite;
io->close = &closeHFSPlusCompressed;
data->cached = NULL;
data->cachedStart = 0;
data->cachedEnd = 0;
data->io = NULL;
data->blocks = NULL;
data->dirty = FALSE;
data->decmpfsSize = getAttribute(volume, file->fileID, "com.apple.decmpfs", (uint8_t**)(&data->decmpfs));
if(data->decmpfsSize == 0) {
data->decmpfs = (HFSPlusDecmpfs*) malloc(0x1000);
data->decmpfs->size = 0;
return io; // previously not compressed file
}
flipHFSPlusDecmpfs(data->decmpfs);
if(data->decmpfs->flags == 0x3) {
data->cached = (uint8_t*) malloc(data->decmpfs->size);
actualSize = data->decmpfs->size;
uncompress(data->cached, &actualSize, data->decmpfs->data, data->decmpfsSize - sizeof(HFSPlusDecmpfs));
if(actualSize != data->decmpfs->size) {
fprintf(stderr, "decmpfs: size mismatch\n");
}
data->cachedStart = 0;
data->cachedEnd = actualSize;
} else {
data->io = openRawFile(file->fileID, &file->resourceFork, (HFSPlusCatalogRecord*)file, volume);
if(!data->io) {
hfs_panic("error opening resource fork");
}
if(!READ(data->io, 0, sizeof(HFSPlusCmpfRsrcHead), &data->rsrcHead)) {
hfs_panic("error reading");
}
flipRsrcHead(&data->rsrcHead);
data->blocks = (HFSPlusCmpfRsrcBlockHead*) malloc(sizeof(HFSPlusCmpfRsrcBlockHead));
if(!READ(data->io, data->rsrcHead.headerSize, sizeof(HFSPlusCmpfRsrcBlockHead), data->blocks)) {
hfs_panic("error reading");
}
flipRsrcBlockHead(data->blocks);
data->blocks = (HFSPlusCmpfRsrcBlockHead*) realloc(data->blocks, sizeof(HFSPlusCmpfRsrcBlockHead) + (sizeof(HFSPlusCmpfRsrcBlock) * data->blocks->numBlocks));
if(!READ(data->io, data->rsrcHead.headerSize + sizeof(HFSPlusCmpfRsrcBlockHead), sizeof(HFSPlusCmpfRsrcBlock) * data->blocks->numBlocks, data->blocks->blocks)) {
hfs_panic("error reading");
}
int i;
for(i = 0; i < data->blocks->numBlocks; i++) {
flipRsrcBlock(&data->blocks->blocks[i]);
}
}
return io;
}

View File

@@ -0,0 +1,723 @@
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <time.h>
#include <sys/types.h>
#include "common.h"
#include <hfs/hfsplus.h>
#include <hfs/hfscompress.h>
#include "abstractfile.h"
#include <sys/stat.h>
#include <inttypes.h>
#define BUFSIZE 1024*1024
static int silence = 0;
void hfs_setsilence(int s) {
silence = s;
}
void writeToFile(HFSPlusCatalogFile* file, AbstractFile* output, Volume* volume) {
unsigned char* buffer;
io_func* io;
off_t curPosition;
size_t bytesLeft;
buffer = (unsigned char*) malloc(BUFSIZE);
if(file->permissions.ownerFlags & UF_COMPRESSED) {
io = openHFSPlusCompressed(volume, file);
if(io == NULL) {
hfs_panic("error opening file");
free(buffer);
return;
}
curPosition = 0;
bytesLeft = ((HFSPlusCompressed*) io->data)->decmpfs->size;
} else {
io = openRawFile(file->fileID, &file->dataFork, (HFSPlusCatalogRecord*)file, volume);
if(io == NULL) {
hfs_panic("error opening file");
free(buffer);
return;
}
curPosition = 0;
bytesLeft = file->dataFork.logicalSize;
}
while(bytesLeft > 0) {
if(bytesLeft > BUFSIZE) {
if(!READ(io, curPosition, BUFSIZE, buffer)) {
hfs_panic("error reading");
}
if(output->write(output, buffer, BUFSIZE) != BUFSIZE) {
hfs_panic("error writing");
}
curPosition += BUFSIZE;
bytesLeft -= BUFSIZE;
} else {
if(!READ(io, curPosition, bytesLeft, buffer)) {
hfs_panic("error reading");
}
if(output->write(output, buffer, bytesLeft) != bytesLeft) {
hfs_panic("error writing");
}
curPosition += bytesLeft;
bytesLeft -= bytesLeft;
}
}
CLOSE(io);
free(buffer);
}
void writeToHFSFile(HFSPlusCatalogFile* file, AbstractFile* input, Volume* volume) {
unsigned char *buffer;
io_func* io;
off_t curPosition;
off_t bytesLeft;
buffer = (unsigned char*) malloc(BUFSIZE);
bytesLeft = input->getLength(input);
if(file->permissions.ownerFlags & UF_COMPRESSED) {
io = openHFSPlusCompressed(volume, file);
if(io == NULL) {
hfs_panic("error opening file");
free(buffer);
return;
}
} else {
io = openRawFile(file->fileID, &file->dataFork, (HFSPlusCatalogRecord*)file, volume);
if(io == NULL) {
hfs_panic("error opening file");
free(buffer);
return;
}
allocate((RawFile*)io->data, bytesLeft);
}
curPosition = 0;
while(bytesLeft > 0) {
if(bytesLeft > BUFSIZE) {
if(input->read(input, buffer, BUFSIZE) != BUFSIZE) {
hfs_panic("error reading");
}
if(!WRITE(io, curPosition, BUFSIZE, buffer)) {
hfs_panic("error writing");
}
curPosition += BUFSIZE;
bytesLeft -= BUFSIZE;
} else {
if(input->read(input, buffer, (size_t)bytesLeft) != (size_t)bytesLeft) {
hfs_panic("error reading");
}
if(!WRITE(io, curPosition, (size_t)bytesLeft, buffer)) {
hfs_panic("error reading");
}
curPosition += bytesLeft;
bytesLeft -= bytesLeft;
}
}
CLOSE(io);
free(buffer);
}
void get_hfs(Volume* volume, const char* inFileName, AbstractFile* output) {
HFSPlusCatalogRecord* record;
record = getRecordFromPath(inFileName, volume, NULL, NULL);
if(record != NULL) {
if(record->recordType == kHFSPlusFileRecord)
writeToFile((HFSPlusCatalogFile*)record, output, volume);
else {
printf("Not a file\n");
exit(0);
}
} else {
printf("No such file or directory\n");
exit(0);
}
free(record);
}
int add_hfs(Volume* volume, AbstractFile* inFile, const char* outFileName) {
HFSPlusCatalogRecord* record;
int ret;
record = getRecordFromPath(outFileName, volume, NULL, NULL);
if(record != NULL) {
if(record->recordType == kHFSPlusFileRecord) {
writeToHFSFile((HFSPlusCatalogFile*)record, inFile, volume);
ret = TRUE;
} else {
printf("Not a file\n");
exit(0);
}
} else {
if(newFile(outFileName, volume)) {
record = getRecordFromPath(outFileName, volume, NULL, NULL);
writeToHFSFile((HFSPlusCatalogFile*)record, inFile, volume);
ret = TRUE;
} else {
ret = FALSE;
}
}
inFile->close(inFile);
if(record != NULL) {
free(record);
}
return ret;
}
void grow_hfs(Volume* volume, uint64_t newSize) {
uint32_t newBlocks;
uint32_t blocksToGrow;
uint64_t newMapSize;
uint64_t i;
unsigned char zero;
zero = 0;
newBlocks = newSize / volume->volumeHeader->blockSize;
if(newBlocks <= volume->volumeHeader->totalBlocks) {
printf("Cannot shrink volume\n");
return;
}
blocksToGrow = newBlocks - volume->volumeHeader->totalBlocks;
newMapSize = newBlocks / 8;
if(volume->volumeHeader->allocationFile.logicalSize < newMapSize) {
if(volume->volumeHeader->freeBlocks
< ((newMapSize - volume->volumeHeader->allocationFile.logicalSize) / volume->volumeHeader->blockSize)) {
printf("Not enough room to allocate new allocation map blocks\n");
exit(0);
}
allocate((RawFile*) (volume->allocationFile->data), newMapSize);
}
/* unreserve last block */
setBlockUsed(volume, volume->volumeHeader->totalBlocks - 1, 0);
/* don't need to increment freeBlocks because we will allocate another alternate volume header later on */
/* "unallocate" the new blocks */
for(i = ((volume->volumeHeader->totalBlocks / 8) + 1); i < newMapSize; i++) {
ASSERT(WRITE(volume->allocationFile, i, 1, &zero), "WRITE");
}
/* grow backing store size */
ASSERT(WRITE(volume->image, newSize - 1, 1, &zero), "WRITE");
/* write new volume information */
volume->volumeHeader->totalBlocks = newBlocks;
volume->volumeHeader->freeBlocks += blocksToGrow;
/* reserve last block */
setBlockUsed(volume, volume->volumeHeader->totalBlocks - 1, 1);
updateVolume(volume);
}
void removeAllInFolder(HFSCatalogNodeID folderID, Volume* volume, const char* parentName) {
CatalogRecordList* list;
CatalogRecordList* theList;
char fullName[1024];
char* name;
char* pathComponent;
int pathLen;
char isRoot;
HFSPlusCatalogFolder* folder;
theList = list = getFolderContents(folderID, volume);
strcpy(fullName, parentName);
pathComponent = fullName + strlen(fullName);
isRoot = FALSE;
if(strcmp(fullName, "/") == 0) {
isRoot = TRUE;
}
while(list != NULL) {
name = unicodeToAscii(&list->name);
if(isRoot && (name[0] == '\0' || strncmp(name, ".HFS+ Private Directory Data", sizeof(".HFS+ Private Directory Data") - 1) == 0)) {
free(name);
list = list->next;
continue;
}
strcpy(pathComponent, name);
pathLen = strlen(fullName);
if(list->record->recordType == kHFSPlusFolderRecord) {
folder = (HFSPlusCatalogFolder*)list->record;
fullName[pathLen] = '/';
fullName[pathLen + 1] = '\0';
removeAllInFolder(folder->folderID, volume, fullName);
} else {
printf("%s\n", fullName);
removeFile(fullName, volume);
}
free(name);
list = list->next;
}
releaseCatalogRecordList(theList);
if(!isRoot) {
*(pathComponent - 1) = '\0';
printf("%s\n", fullName);
removeFile(fullName, volume);
}
}
void addAllInFolder(HFSCatalogNodeID folderID, Volume* volume, const char* parentName) {
CatalogRecordList* list;
CatalogRecordList* theList;
char cwd[1024];
char fullName[1024];
char testBuffer[1024];
char* pathComponent;
int pathLen;
char* name;
DIR* dir;
DIR* tmp;
HFSCatalogNodeID cnid;
struct dirent* ent;
AbstractFile* file;
HFSPlusCatalogFile* outFile;
strcpy(fullName, parentName);
pathComponent = fullName + strlen(fullName);
ASSERT(getcwd(cwd, 1024) != NULL, "cannot get current working directory");
theList = list = getFolderContents(folderID, volume);
ASSERT((dir = opendir(cwd)) != NULL, "opendir");
while((ent = readdir(dir)) != NULL) {
if(ent->d_name[0] == '.' && (ent->d_name[1] == '\0' || (ent->d_name[1] == '.' && ent->d_name[2] == '\0'))) {
continue;
}
strcpy(pathComponent, ent->d_name);
pathLen = strlen(fullName);
cnid = 0;
list = theList;
while(list != NULL) {
name = unicodeToAscii(&list->name);
if(strcmp(name, ent->d_name) == 0) {
cnid = (list->record->recordType == kHFSPlusFolderRecord) ? (((HFSPlusCatalogFolder*)list->record)->folderID)
: (((HFSPlusCatalogFile*)list->record)->fileID);
free(name);
break;
}
free(name);
list = list->next;
}
if((tmp = opendir(ent->d_name)) != NULL) {
closedir(tmp);
printf("folder: %s\n", fullName); fflush(stdout);
if(cnid == 0) {
cnid = newFolder(fullName, volume);
}
fullName[pathLen] = '/';
fullName[pathLen + 1] = '\0';
ASSERT(chdir(ent->d_name) == 0, "chdir");
addAllInFolder(cnid, volume, fullName);
ASSERT(chdir(cwd) == 0, "chdir");
} else {
printf("file: %s\n", fullName); fflush(stdout);
if(cnid == 0) {
cnid = newFile(fullName, volume);
}
file = createAbstractFileFromFile(fopen(ent->d_name, "rb"));
ASSERT(file != NULL, "fopen");
outFile = (HFSPlusCatalogFile*)getRecordByCNID(cnid, volume);
writeToHFSFile(outFile, file, volume);
file->close(file);
free(outFile);
if(strncmp(fullName, "/Applications/", sizeof("/Applications/") - 1) == 0) {
testBuffer[0] = '\0';
strcpy(testBuffer, "/Applications/");
strcat(testBuffer, ent->d_name);
strcat(testBuffer, ".app/");
strcat(testBuffer, ent->d_name);
if(strcmp(testBuffer, fullName) == 0) {
if(strcmp(ent->d_name, "Installer") == 0
|| strcmp(ent->d_name, "BootNeuter") == 0
) {
printf("Giving setuid permissions to %s...\n", fullName); fflush(stdout);
chmodFile(fullName, 04755, volume);
} else {
printf("Giving permissions to %s\n", fullName); fflush(stdout);
chmodFile(fullName, 0755, volume);
}
}
} else if(strncmp(fullName, "/bin/", sizeof("/bin/") - 1) == 0
|| strncmp(fullName, "/Applications/BootNeuter.app/bin/", sizeof("/Applications/BootNeuter.app/bin/") - 1) == 0
|| strncmp(fullName, "/sbin/", sizeof("/sbin/") - 1) == 0
|| strncmp(fullName, "/usr/sbin/", sizeof("/usr/sbin/") - 1) == 0
|| strncmp(fullName, "/usr/bin/", sizeof("/usr/bin/") - 1) == 0
|| strncmp(fullName, "/usr/libexec/", sizeof("/usr/libexec/") - 1) == 0
|| strncmp(fullName, "/usr/local/bin/", sizeof("/usr/local/bin/") - 1) == 0
|| strncmp(fullName, "/usr/local/sbin/", sizeof("/usr/local/sbin/") - 1) == 0
|| strncmp(fullName, "/usr/local/libexec/", sizeof("/usr/local/libexec/") - 1) == 0
) {
chmodFile(fullName, 0755, volume);
printf("Giving permissions to %s\n", fullName); fflush(stdout);
}
}
}
closedir(dir);
releaseCatalogRecordList(theList);
}
void extractAllInFolder(HFSCatalogNodeID folderID, Volume* volume) {
CatalogRecordList* list;
CatalogRecordList* theList;
char cwd[1024];
char* name;
HFSPlusCatalogFolder* folder;
HFSPlusCatalogFile* file;
AbstractFile* outFile;
struct stat status;
ASSERT(getcwd(cwd, 1024) != NULL, "cannot get current working directory");
theList = list = getFolderContents(folderID, volume);
while(list != NULL) {
name = unicodeToAscii(&list->name);
if(strncmp(name, ".HFS+ Private Directory Data", sizeof(".HFS+ Private Directory Data") - 1) == 0 || name[0] == '\0') {
free(name);
list = list->next;
continue;
}
if(list->record->recordType == kHFSPlusFolderRecord) {
folder = (HFSPlusCatalogFolder*)list->record;
printf("folder: %s\n", name);
if(stat(name, &status) != 0) {
ASSERT(mkdir(name, 0755) == 0, "mkdir");
}
ASSERT(chdir(name) == 0, "chdir");
extractAllInFolder(folder->folderID, volume);
ASSERT(chdir(cwd) == 0, "chdir");
} else if(list->record->recordType == kHFSPlusFileRecord) {
printf("file: %s\n", name);
file = (HFSPlusCatalogFile*)list->record;
outFile = createAbstractFileFromFile(fopen(name, "wb"));
if(outFile != NULL) {
writeToFile(file, outFile, volume);
outFile->close(outFile);
} else {
printf("WARNING: cannot fopen %s\n", name);
}
}
free(name);
list = list->next;
}
releaseCatalogRecordList(theList);
}
void addall_hfs(Volume* volume, const char* dirToMerge, const char* dest) {
HFSPlusCatalogRecord* record;
char* name;
char cwd[1024];
char initPath[1024];
int lastCharOfPath;
ASSERT(getcwd(cwd, 1024) != NULL, "cannot get current working directory");
if(chdir(dirToMerge) != 0) {
printf("Cannot open that directory: %s\n", dirToMerge);
exit(0);
}
record = getRecordFromPath(dest, volume, &name, NULL);
strcpy(initPath, dest);
lastCharOfPath = strlen(dest) - 1;
if(dest[lastCharOfPath] != '/') {
initPath[lastCharOfPath + 1] = '/';
initPath[lastCharOfPath + 2] = '\0';
}
if(record != NULL) {
if(record->recordType == kHFSPlusFolderRecord)
addAllInFolder(((HFSPlusCatalogFolder*)record)->folderID, volume, initPath);
else {
printf("Not a folder\n");
exit(0);
}
} else {
printf("No such file or directory\n");
exit(0);
}
ASSERT(chdir(cwd) == 0, "chdir");
free(record);
}
int copyAcrossVolumes(Volume* volume1, Volume* volume2, char* path1, char* path2) {
void* buffer;
size_t bufferSize;
AbstractFile* tmpFile;
int ret;
buffer = malloc(1);
bufferSize = 0;
tmpFile = createAbstractFileFromMemoryFile((void**)&buffer, &bufferSize);
if(!silence)
{
printf("retrieving... "); fflush(stdout);
}
get_hfs(volume1, path1, tmpFile);
tmpFile->seek(tmpFile, 0);
if(!silence)
{
printf("writing (%ld)... ", (long) tmpFile->getLength(tmpFile)); fflush(stdout);
}
ret = add_hfs(volume2, tmpFile, path2);
if(!silence)
{
printf("done\n");
}
free(buffer);
return ret;
}
void displayFolder(HFSCatalogNodeID folderID, Volume* volume) {
CatalogRecordList* list;
CatalogRecordList* theList;
HFSPlusCatalogFolder* folder;
HFSPlusCatalogFile* file;
time_t fileTime;
struct tm *date;
HFSPlusDecmpfs* compressData;
size_t attrSize;
theList = list = getFolderContents(folderID, volume);
while(list != NULL) {
if(list->record->recordType == kHFSPlusFolderRecord) {
folder = (HFSPlusCatalogFolder*)list->record;
printf("%06o ", folder->permissions.fileMode);
printf("%3d ", folder->permissions.ownerID);
printf("%3d ", folder->permissions.groupID);
printf("%12d ", folder->valence);
fileTime = APPLE_TO_UNIX_TIME(folder->contentModDate);
} else if(list->record->recordType == kHFSPlusFileRecord) {
file = (HFSPlusCatalogFile*)list->record;
printf("%06o ", file->permissions.fileMode);
printf("%3d ", file->permissions.ownerID);
printf("%3d ", file->permissions.groupID);
if(file->permissions.ownerFlags & UF_COMPRESSED) {
attrSize = getAttribute(volume, file->fileID, "com.apple.decmpfs", (uint8_t**)(&compressData));
flipHFSPlusDecmpfs(compressData);
printf("%12" PRId64 " ", compressData->size);
free(compressData);
} else {
printf("%12" PRId64 " ", file->dataFork.logicalSize);
}
fileTime = APPLE_TO_UNIX_TIME(file->contentModDate);
}
date = localtime(&fileTime);
if(date != NULL) {
printf("%2d/%2d/%4d %02d:%02d ", date->tm_mon, date->tm_mday, date->tm_year + 1900, date->tm_hour, date->tm_min);
} else {
printf(" ");
}
printUnicode(&list->name);
printf("\n");
list = list->next;
}
releaseCatalogRecordList(theList);
}
void displayFileLSLine(Volume* volume, HFSPlusCatalogFile* file, const char* name) {
time_t fileTime;
struct tm *date;
HFSPlusDecmpfs* compressData;
printf("%06o ", file->permissions.fileMode);
printf("%3d ", file->permissions.ownerID);
printf("%3d ", file->permissions.groupID);
if(file->permissions.ownerFlags & UF_COMPRESSED) {
getAttribute(volume, file->fileID, "com.apple.decmpfs", (uint8_t**)(&compressData));
flipHFSPlusDecmpfs(compressData);
printf("%12" PRId64 " ", compressData->size);
free(compressData);
} else {
printf("%12" PRId64 " ", file->dataFork.logicalSize);
}
fileTime = APPLE_TO_UNIX_TIME(file->contentModDate);
date = localtime(&fileTime);
if(date != NULL) {
printf("%2d/%2d/%4d %2d:%02d ", date->tm_mon, date->tm_mday, date->tm_year + 1900, date->tm_hour, date->tm_min);
} else {
printf(" ");
}
printf("%s\n", name);
XAttrList* next;
XAttrList* attrs = getAllExtendedAttributes(file->fileID, volume);
if(attrs != NULL) {
printf("Extended attributes\n");
while(attrs != NULL) {
next = attrs->next;
printf("\t%s\n", attrs->name);
free(attrs->name);
free(attrs);
attrs = next;
}
}
}
void hfs_ls(Volume* volume, const char* path) {
HFSPlusCatalogRecord* record;
char* name;
record = getRecordFromPath(path, volume, &name, NULL);
printf("%s: \n", name);
if(record != NULL) {
if(record->recordType == kHFSPlusFolderRecord)
displayFolder(((HFSPlusCatalogFolder*)record)->folderID, volume);
else
displayFileLSLine(volume, (HFSPlusCatalogFile*)record, name);
} else {
printf("No such file or directory\n");
}
printf("Total filesystem size: %d, free: %d\n", (volume->volumeHeader->totalBlocks - volume->volumeHeader->freeBlocks) * volume->volumeHeader->blockSize, volume->volumeHeader->freeBlocks * volume->volumeHeader->blockSize);
free(record);
}
void hfs_untar(Volume* volume, AbstractFile* tarFile) {
size_t tarSize = tarFile->getLength(tarFile);
size_t curRecord = 0;
char block[512];
while(curRecord < tarSize) {
tarFile->seek(tarFile, curRecord);
tarFile->read(tarFile, block, 512);
uint32_t mode = 0;
char* fileName = NULL;
const char* target = NULL;
uint32_t type = 0;
uint32_t size;
uint32_t uid;
uint32_t gid;
sscanf(&block[100], "%o", &mode);
fileName = &block[0];
sscanf(&block[156], "%o", &type);
target = &block[157];
sscanf(&block[124], "%o", &size);
sscanf(&block[108], "%o", &uid);
sscanf(&block[116], "%o", &gid);
if(fileName[0] == '\0')
break;
if(fileName[0] == '.' && fileName[1] == '/') {
fileName += 2;
}
if(fileName[0] == '\0')
goto loop;
if(fileName[strlen(fileName) - 1] == '/')
fileName[strlen(fileName) - 1] = '\0';
HFSPlusCatalogRecord* record = getRecordFromPath3(fileName, volume, NULL, NULL, TRUE, FALSE, kHFSRootFolderID);
if(record) {
if(record->recordType == kHFSPlusFolderRecord || type == 5) {
if(!silence)
printf("ignoring %s, type = %d\n", fileName, type);
free(record);
goto loop;
} else {
printf("replacing %s\n", fileName);
free(record);
removeFile(fileName, volume);
}
}
if(type == 0) {
if(!silence)
printf("file: %s (%04o), size = %d\n", fileName, mode, size);
void* buffer = malloc(size);
tarFile->seek(tarFile, curRecord + 512);
tarFile->read(tarFile, buffer, size);
AbstractFile* inFile = createAbstractFileFromMemory(&buffer, size);
add_hfs(volume, inFile, fileName);
free(buffer);
} else if(type == 5) {
if(!silence)
printf("directory: %s (%04o)\n", fileName, mode);
newFolder(fileName, volume);
} else if(type == 2) {
if(!silence)
printf("symlink: %s (%04o) -> %s\n", fileName, mode, target);
makeSymlink(fileName, target, volume);
}
chmodFile(fileName, mode, volume);
chownFile(fileName, uid, gid, volume);
loop:
curRecord = (curRecord + 512) + ((size + 511) / 512 * 512);
}
}

View File

@@ -0,0 +1,502 @@
#include <stdlib.h>
#include <string.h>
#include <hfs/hfsplus.h>
int writeExtents(RawFile* rawFile);
int isBlockUsed(Volume* volume, uint32_t block)
{
unsigned char byte;
READ(volume->allocationFile, block / 8, 1, &byte);
return (byte & (1 << (7 - (block % 8)))) != 0;
}
int setBlockUsed(Volume* volume, uint32_t block, int used) {
unsigned char byte;
READ(volume->allocationFile, block / 8, 1, &byte);
if(used) {
byte |= (1 << (7 - (block % 8)));
} else {
byte &= ~(1 << (7 - (block % 8)));
}
ASSERT(WRITE(volume->allocationFile, block / 8, 1, &byte), "WRITE");
return TRUE;
}
int allocate(RawFile* rawFile, off_t size) {
unsigned char* zeros;
Volume* volume;
HFSPlusForkData* forkData;
uint32_t blocksNeeded;
uint32_t blocksToAllocate;
Extent* extent;
Extent* lastExtent;
uint32_t curBlock;
volume = rawFile->volume;
forkData = rawFile->forkData;
extent = rawFile->extents;
blocksNeeded = ((uint64_t)size / (uint64_t)volume->volumeHeader->blockSize) + (((size % volume->volumeHeader->blockSize) == 0) ? 0 : 1);
if(blocksNeeded > forkData->totalBlocks) {
zeros = (unsigned char*) malloc(volume->volumeHeader->blockSize);
memset(zeros, 0, volume->volumeHeader->blockSize);
blocksToAllocate = blocksNeeded - forkData->totalBlocks;
if(blocksToAllocate > volume->volumeHeader->freeBlocks) {
return FALSE;
}
lastExtent = NULL;
while(extent != NULL) {
lastExtent = extent;
extent = extent->next;
}
if(lastExtent == NULL) {
rawFile->extents = (Extent*) malloc(sizeof(Extent));
lastExtent = rawFile->extents;
lastExtent->blockCount = 0;
lastExtent->next = NULL;
curBlock = volume->volumeHeader->nextAllocation;
} else {
curBlock = lastExtent->startBlock + lastExtent->blockCount;
}
while(blocksToAllocate > 0) {
if(isBlockUsed(volume, curBlock)) {
if(lastExtent->blockCount > 0) {
lastExtent->next = (Extent*) malloc(sizeof(Extent));
lastExtent = lastExtent->next;
lastExtent->blockCount = 0;
lastExtent->next = NULL;
}
curBlock = volume->volumeHeader->nextAllocation;
volume->volumeHeader->nextAllocation++;
if(volume->volumeHeader->nextAllocation >= volume->volumeHeader->totalBlocks) {
volume->volumeHeader->nextAllocation = 0;
}
} else {
if(lastExtent->blockCount == 0) {
lastExtent->startBlock = curBlock;
}
/* zero out allocated block */
ASSERT(WRITE(volume->image, curBlock * volume->volumeHeader->blockSize, volume->volumeHeader->blockSize, zeros), "WRITE");
setBlockUsed(volume, curBlock, TRUE);
volume->volumeHeader->freeBlocks--;
blocksToAllocate--;
curBlock++;
lastExtent->blockCount++;
if(curBlock >= volume->volumeHeader->totalBlocks) {
curBlock = volume->volumeHeader->nextAllocation;
}
}
}
free(zeros);
} else if(blocksNeeded < forkData->totalBlocks) {
blocksToAllocate = blocksNeeded;
lastExtent = NULL;
while(blocksToAllocate > 0) {
if(blocksToAllocate > extent->blockCount) {
blocksToAllocate -= extent->blockCount;
lastExtent = extent;
extent = extent->next;
} else {
break;
}
}
if(blocksToAllocate == 0 && lastExtent != NULL) {
// snip the extent list here, since we don't need the rest
lastExtent->next = NULL;
} else if(blocksNeeded == 0) {
rawFile->extents = NULL;
}
do {
for(curBlock = (extent->startBlock + blocksToAllocate); curBlock < (extent->startBlock + extent->blockCount); curBlock++) {
setBlockUsed(volume, curBlock, FALSE);
volume->volumeHeader->freeBlocks++;
}
lastExtent = extent;
extent = extent->next;
if(blocksToAllocate == 0)
{
free(lastExtent);
} else {
lastExtent->next = NULL;
lastExtent->blockCount = blocksToAllocate;
}
blocksToAllocate = 0;
} while(extent != NULL);
}
writeExtents(rawFile);
forkData->logicalSize = size;
forkData->totalBlocks = blocksNeeded;
updateVolume(rawFile->volume);
if(rawFile->catalogRecord != NULL) {
updateCatalog(rawFile->volume, rawFile->catalogRecord);
}
return TRUE;
}
static int rawFileRead(io_func* io,off_t location, size_t size, void *buffer) {
RawFile* rawFile;
Volume* volume;
Extent* extent;
size_t blockSize;
off_t fileLoc;
off_t locationInBlock;
size_t possible;
rawFile = (RawFile*) io->data;
volume = rawFile->volume;
blockSize = volume->volumeHeader->blockSize;
if(!rawFile->extents)
return FALSE;
extent = rawFile->extents;
fileLoc = 0;
locationInBlock = location;
while(TRUE) {
fileLoc += extent->blockCount * blockSize;
if(fileLoc <= location) {
locationInBlock -= extent->blockCount * blockSize;
extent = extent->next;
if(extent == NULL)
break;
} else {
break;
}
}
while(size > 0) {
if(extent == NULL)
return FALSE;
possible = extent->blockCount * blockSize - locationInBlock;
if(size > possible) {
ASSERT(READ(volume->image, extent->startBlock * blockSize + locationInBlock, possible, buffer), "READ");
size -= possible;
buffer = (void*)(((size_t)buffer) + possible);
extent = extent->next;
} else {
ASSERT(READ(volume->image, extent->startBlock * blockSize + locationInBlock, size, buffer), "READ");
break;
}
locationInBlock = 0;
}
return TRUE;
}
static int rawFileWrite(io_func* io,off_t location, size_t size, void *buffer) {
RawFile* rawFile;
Volume* volume;
Extent* extent;
size_t blockSize;
off_t fileLoc;
off_t locationInBlock;
size_t possible;
rawFile = (RawFile*) io->data;
volume = rawFile->volume;
blockSize = volume->volumeHeader->blockSize;
if(rawFile->forkData->logicalSize < (location + size)) {
ASSERT(allocate(rawFile, location + size), "allocate");
}
extent = rawFile->extents;
fileLoc = 0;
locationInBlock = location;
while(TRUE) {
fileLoc += extent->blockCount * blockSize;
if(fileLoc <= location) {
locationInBlock -= extent->blockCount * blockSize;
extent = extent->next;
if(extent == NULL)
break;
} else {
break;
}
}
while(size > 0) {
if(extent == NULL)
return FALSE;
possible = extent->blockCount * blockSize - locationInBlock;
if(size > possible) {
ASSERT(WRITE(volume->image, extent->startBlock * blockSize + locationInBlock, possible, buffer), "WRITE");
size -= possible;
buffer = (void*)(((size_t)buffer) + possible);
extent = extent->next;
} else {
ASSERT(WRITE(volume->image, extent->startBlock * blockSize + locationInBlock, size, buffer), "WRITE");
break;
}
locationInBlock = 0;
}
return TRUE;
}
static void closeRawFile(io_func* io) {
RawFile* rawFile;
Extent* extent;
Extent* toRemove;
rawFile = (RawFile*) io->data;
extent = rawFile->extents;
while(extent != NULL) {
toRemove = extent;
extent = extent->next;
free(toRemove);
}
free(rawFile);
free(io);
}
int removeExtents(RawFile* rawFile) {
uint32_t blocksLeft;
HFSPlusForkData* forkData;
uint32_t currentBlock;
uint32_t startBlock;
uint32_t blockCount;
HFSPlusExtentDescriptor* descriptor;
int currentExtent;
HFSPlusExtentKey extentKey;
int exact;
extentKey.keyLength = sizeof(HFSPlusExtentKey) - sizeof(extentKey.keyLength);
extentKey.forkType = 0;
extentKey.fileID = rawFile->id;
forkData = rawFile->forkData;
blocksLeft = forkData->totalBlocks;
currentExtent = 0;
currentBlock = 0;
descriptor = (HFSPlusExtentDescriptor*) forkData->extents;
while(blocksLeft > 0) {
if(currentExtent == 8) {
if(rawFile->volume->extentsTree == NULL) {
hfs_panic("no extents overflow file loaded yet!");
return FALSE;
}
if(descriptor != ((HFSPlusExtentDescriptor*) forkData->extents)) {
free(descriptor);
}
extentKey.startBlock = currentBlock;
descriptor = (HFSPlusExtentDescriptor*) search(rawFile->volume->extentsTree, (BTKey*)(&extentKey), &exact, NULL, NULL);
if(descriptor == NULL || exact == FALSE) {
hfs_panic("inconsistent extents information!");
return FALSE;
} else {
removeFromBTree(rawFile->volume->extentsTree, (BTKey*)(&extentKey));
currentExtent = 0;
continue;
}
}
startBlock = descriptor[currentExtent].startBlock;
blockCount = descriptor[currentExtent].blockCount;
currentBlock += blockCount;
blocksLeft -= blockCount;
currentExtent++;
}
if(descriptor != ((HFSPlusExtentDescriptor*) forkData->extents)) {
free(descriptor);
}
return TRUE;
}
int writeExtents(RawFile* rawFile) {
Extent* extent;
int currentExtent;
HFSPlusExtentKey extentKey;
HFSPlusExtentDescriptor descriptor[8];
HFSPlusForkData* forkData;
removeExtents(rawFile);
forkData = rawFile->forkData;
currentExtent = 0;
extent = rawFile->extents;
memset(forkData->extents, 0, sizeof(HFSPlusExtentRecord));
while(extent != NULL && currentExtent < 8) {
((HFSPlusExtentDescriptor*)forkData->extents)[currentExtent].startBlock = extent->startBlock;
((HFSPlusExtentDescriptor*)forkData->extents)[currentExtent].blockCount = extent->blockCount;
extent = extent->next;
currentExtent++;
}
if(extent != NULL) {
extentKey.keyLength = sizeof(HFSPlusExtentKey) - sizeof(extentKey.keyLength);
extentKey.forkType = 0;
extentKey.fileID = rawFile->id;
currentExtent = 0;
while(extent != NULL) {
if(currentExtent == 0) {
memset(descriptor, 0, sizeof(HFSPlusExtentRecord));
}
if(currentExtent == 8) {
extentKey.startBlock = descriptor[0].startBlock;
addToBTree(rawFile->volume->extentsTree, (BTKey*)(&extentKey), sizeof(HFSPlusExtentRecord), (unsigned char *)(&(descriptor[0])));
currentExtent = 0;
}
descriptor[currentExtent].startBlock = extent->startBlock;
descriptor[currentExtent].blockCount = extent->blockCount;
currentExtent++;
extent = extent->next;
}
extentKey.startBlock = descriptor[0].startBlock;
addToBTree(rawFile->volume->extentsTree, (BTKey*)(&extentKey), sizeof(HFSPlusExtentRecord), (unsigned char *)(&(descriptor[0])));
}
return TRUE;
}
int readExtents(RawFile* rawFile) {
uint32_t blocksLeft;
HFSPlusForkData* forkData;
uint32_t currentBlock;
Extent* extent;
Extent* lastExtent;
HFSPlusExtentDescriptor* descriptor;
int currentExtent;
HFSPlusExtentKey extentKey;
int exact;
extentKey.keyLength = sizeof(HFSPlusExtentKey) - sizeof(extentKey.keyLength);
extentKey.forkType = 0;
extentKey.fileID = rawFile->id;
forkData = rawFile->forkData;
blocksLeft = forkData->totalBlocks;
currentExtent = 0;
currentBlock = 0;
descriptor = (HFSPlusExtentDescriptor*) forkData->extents;
lastExtent = NULL;
while(blocksLeft > 0) {
extent = (Extent*) malloc(sizeof(Extent));
if(currentExtent == 8) {
if(rawFile->volume->extentsTree == NULL) {
hfs_panic("no extents overflow file loaded yet!");
return FALSE;
}
if(descriptor != ((HFSPlusExtentDescriptor*) forkData->extents)) {
free(descriptor);
}
extentKey.startBlock = currentBlock;
descriptor = (HFSPlusExtentDescriptor*) search(rawFile->volume->extentsTree, (BTKey*)(&extentKey), &exact, NULL, NULL);
if(descriptor == NULL || exact == FALSE) {
hfs_panic("inconsistent extents information!");
return FALSE;
} else {
currentExtent = 0;
continue;
}
}
extent->startBlock = descriptor[currentExtent].startBlock;
extent->blockCount = descriptor[currentExtent].blockCount;
extent->next = NULL;
currentBlock += extent->blockCount;
blocksLeft -= extent->blockCount;
currentExtent++;
if(lastExtent == NULL) {
rawFile->extents = extent;
} else {
lastExtent->next = extent;
}
lastExtent = extent;
}
if(descriptor != ((HFSPlusExtentDescriptor*) forkData->extents)) {
free(descriptor);
}
return TRUE;
}
io_func* openRawFile(HFSCatalogNodeID id, HFSPlusForkData* forkData, HFSPlusCatalogRecord* catalogRecord, Volume* volume) {
io_func* io;
RawFile* rawFile;
io = (io_func*) malloc(sizeof(io_func));
rawFile = (RawFile*) malloc(sizeof(RawFile));
rawFile->id = id;
rawFile->volume = volume;
rawFile->forkData = forkData;
rawFile->catalogRecord = catalogRecord;
rawFile->extents = NULL;
io->data = rawFile;
io->read = &rawFileRead;
io->write = &rawFileWrite;
io->close = &closeRawFile;
if(!readExtents(rawFile)) {
return NULL;
}
return io;
}

View File

@@ -0,0 +1,30 @@
#include <stdlib.h>
#include <stdio.h>
#include <hfs/hfsplus.h>
void hfs_panic(const char* hfs_panicString) {
fprintf(stderr, "%s\n", hfs_panicString);
exit(1);
}
void printUnicode(HFSUniStr255* str) {
int i;
for(i = 0; i < str->length; i++) {
printf("%c", (char)(str->unicode[i] & 0xff));
}
}
char* unicodeToAscii(HFSUniStr255* str) {
int i;
char* toReturn;
toReturn = (char*) malloc(sizeof(char) * (str->length + 1));
for(i = 0; i < str->length; i++) {
toReturn[i] = (char)(str->unicode[i] & 0xff);
}
toReturn[i] = '\0';
return toReturn;
}

View File

@@ -0,0 +1,176 @@
#include <stdlib.h>
#include <string.h>
#include <hfs/hfsplus.h>
void flipForkData(HFSPlusForkData* forkData) {
FLIPENDIAN(forkData->logicalSize);
FLIPENDIAN(forkData->clumpSize);
FLIPENDIAN(forkData->totalBlocks);
flipExtentRecord(&forkData->extents);
}
static HFSPlusVolumeHeader* readVolumeHeader(io_func* io, off_t offset) {
HFSPlusVolumeHeader* volumeHeader;
volumeHeader = (HFSPlusVolumeHeader*) malloc(sizeof(HFSPlusVolumeHeader));
if(!(READ(io, offset, sizeof(HFSPlusVolumeHeader), volumeHeader)))
return NULL;
FLIPENDIAN(volumeHeader->signature);
FLIPENDIAN(volumeHeader->version);
FLIPENDIAN(volumeHeader->attributes);
FLIPENDIAN(volumeHeader->lastMountedVersion);
FLIPENDIAN(volumeHeader->journalInfoBlock);
FLIPENDIAN(volumeHeader->createDate);
FLIPENDIAN(volumeHeader->modifyDate);
FLIPENDIAN(volumeHeader->backupDate);
FLIPENDIAN(volumeHeader->checkedDate);
FLIPENDIAN(volumeHeader->fileCount);
FLIPENDIAN(volumeHeader->folderCount);
FLIPENDIAN(volumeHeader->blockSize);
FLIPENDIAN(volumeHeader->totalBlocks);
FLIPENDIAN(volumeHeader->freeBlocks);
FLIPENDIAN(volumeHeader->nextAllocation);
FLIPENDIAN(volumeHeader->rsrcClumpSize);
FLIPENDIAN(volumeHeader->dataClumpSize);
FLIPENDIAN(volumeHeader->nextCatalogID);
FLIPENDIAN(volumeHeader->writeCount);
FLIPENDIAN(volumeHeader->encodingsBitmap);
flipForkData(&volumeHeader->allocationFile);
flipForkData(&volumeHeader->extentsFile);
flipForkData(&volumeHeader->catalogFile);
flipForkData(&volumeHeader->attributesFile);
flipForkData(&volumeHeader->startupFile);
return volumeHeader;
}
static int writeVolumeHeader(io_func* io, HFSPlusVolumeHeader* volumeHeaderToWrite, off_t offset) {
HFSPlusVolumeHeader* volumeHeader;
volumeHeader = (HFSPlusVolumeHeader*) malloc(sizeof(HFSPlusVolumeHeader));
memcpy(volumeHeader, volumeHeaderToWrite, sizeof(HFSPlusVolumeHeader));
FLIPENDIAN(volumeHeader->signature);
FLIPENDIAN(volumeHeader->version);
FLIPENDIAN(volumeHeader->attributes);
FLIPENDIAN(volumeHeader->lastMountedVersion);
FLIPENDIAN(volumeHeader->journalInfoBlock);
FLIPENDIAN(volumeHeader->createDate);
FLIPENDIAN(volumeHeader->modifyDate);
FLIPENDIAN(volumeHeader->backupDate);
FLIPENDIAN(volumeHeader->checkedDate);
FLIPENDIAN(volumeHeader->fileCount);
FLIPENDIAN(volumeHeader->folderCount);
FLIPENDIAN(volumeHeader->blockSize);
FLIPENDIAN(volumeHeader->totalBlocks);
FLIPENDIAN(volumeHeader->freeBlocks);
FLIPENDIAN(volumeHeader->nextAllocation);
FLIPENDIAN(volumeHeader->rsrcClumpSize);
FLIPENDIAN(volumeHeader->dataClumpSize);
FLIPENDIAN(volumeHeader->nextCatalogID);
FLIPENDIAN(volumeHeader->writeCount);
FLIPENDIAN(volumeHeader->encodingsBitmap);
flipForkData(&volumeHeader->allocationFile);
flipForkData(&volumeHeader->extentsFile);
flipForkData(&volumeHeader->catalogFile);
flipForkData(&volumeHeader->attributesFile);
flipForkData(&volumeHeader->startupFile);
if(!(WRITE(io, offset, sizeof(HFSPlusVolumeHeader), volumeHeader)))
return FALSE;
free(volumeHeader);
return TRUE;
}
int updateVolume(Volume* volume) {
ASSERT(writeVolumeHeader(volume->image, volume->volumeHeader,
((off_t)volume->volumeHeader->totalBlocks * (off_t)volume->volumeHeader->blockSize) - 1024), "writeVolumeHeader");
return writeVolumeHeader(volume->image, volume->volumeHeader, 1024);
}
Volume* openVolume(io_func* io) {
Volume* volume;
io_func* file;
volume = (Volume*) malloc(sizeof(Volume));
volume->image = io;
volume->extentsTree = NULL;
volume->volumeHeader = readVolumeHeader(io, 1024);
if(volume->volumeHeader == NULL) {
free(volume);
return NULL;
}
file = openRawFile(kHFSExtentsFileID, &volume->volumeHeader->extentsFile, NULL, volume);
if(file == NULL) {
free(volume->volumeHeader);
free(volume);
return NULL;
}
volume->extentsTree = openExtentsTree(file);
if(volume->extentsTree == NULL) {
free(volume->volumeHeader);
free(volume);
return NULL;
}
file = openRawFile(kHFSCatalogFileID, &volume->volumeHeader->catalogFile, NULL, volume);
if(file == NULL) {
closeBTree(volume->extentsTree);
free(volume->volumeHeader);
free(volume);
return NULL;
}
volume->catalogTree = openCatalogTree(file);
if(volume->catalogTree == NULL) {
closeBTree(volume->extentsTree);
free(volume->volumeHeader);
free(volume);
return NULL;
}
volume->allocationFile = openRawFile(kHFSAllocationFileID, &volume->volumeHeader->allocationFile, NULL, volume);
if(volume->allocationFile == NULL) {
closeBTree(volume->catalogTree);
closeBTree(volume->extentsTree);
free(volume->volumeHeader);
free(volume);
return NULL;
}
volume->attrTree = NULL;
file = openRawFile(kHFSAttributesFileID, &volume->volumeHeader->attributesFile, NULL, volume);
if(file != NULL) {
volume->attrTree = openAttributesTree(file);
if(!volume->attrTree) {
CLOSE(file);
}
}
volume->metadataDir = getMetadataDirectoryID(volume);
return volume;
}
void closeVolume(Volume *volume) {
if(volume->attrTree)
closeBTree(volume->attrTree);
CLOSE(volume->allocationFile);
closeBTree(volume->catalogTree);
closeBTree(volume->extentsTree);
free(volume->volumeHeader);
free(volume);
}

View File

@@ -0,0 +1,374 @@
#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);
}