/* skip the indicated number of blocks */ int blockiter_skip(blockiter *b, UInt32 skip) { while (skip > 0) { // Skip to skip or end of current extent UInt32 diff = b->e->block_count - b->block; if (skip < diff) { diff = skip; skip = 0; } else skip -= diff; b->curr_block += diff; b->block += diff; if (b->curr_block >= b->max_block) return -1; // end of Blocks, but no error if (b->block >= b->e->block_count) { b->index++; b->block = 0; // reset relative position b->e++; if (b -> index >= 8) // need to fetch another extent { if (blockiter_next_extent(b)) HFSP_ERROR(ENOENT, "Extends record not found."); } } } // we are here when skip was null, thats ok return 0; fail: return -1; }
/* Find out wether the volume is wrapped and unwrap it eventually */ static int volume_read_wrapper(volume * vol, hfsp_vh* vh) { UInt16 signature; char buf[vol->blksize]; char *p = buf; if( volume_readinbuf(vol, buf, 2) ) // Wrapper or volume header starts here return -1; signature = bswabU16_inc(p); if( signature == HFS_VOLHEAD_SIG) { /* Wrapper */ UInt32 drAlBlkSiz; /* size (in bytes) of allocation blocks */ UInt32 sect_per_block; /* how may block build an hfs sector */ UInt16 drAlBlSt; /* first allocation block in volume */ UInt16 embeds, embedl; /* Start/lenght of embedded area in blocks */ p += 0x12; /* skip unneded HFS vol fields */ drAlBlkSiz = bswabU32_inc(p); /* offset 0x14 */ p += 0x4; /* skip unneded HFS vol fields */ drAlBlSt = bswabU16_inc(p); /* offset 0x1C */ p += 0x5E; /* skip unneded HFS vol fields */ signature = bswabU16_inc(p); /* offset 0x7C, drEmbedSigWord */ if( signature != HFSP_VOLHEAD_SIG) HFSP_ERROR(-1, "This looks like a normal HFS volume"); embeds = bswabU16_inc(p); embedl = bswabU16_inc(p); sect_per_block = (drAlBlkSiz / HFSP_BLOCKSZ); // end is absolute (not relative to HFS+ start) vol->maxblocks = embedl * sect_per_block; vol->startblock = drAlBlSt + embeds * sect_per_block; /* Now we can try to read the embedded HFS+ volume header */ return volume_read(vol,vh,2); } else if( signature == HFSP_VOLHEAD_SIG) { /* Native HFS+ volume */ p = buf; // Restore to begin of block return volume_readbuf(vh, p); } else HFSP_ERROR(-1, "Neither Wrapper nor native HFS+ volume header found"); fail: return -1; }
/* search for the given key in the btree. * * returns pointer to memory just after key or NULL * In any case *keyind recives the index where the * key was found (or could be inserted.) */ static void * record_find_key(btree* tree, void* key, int* keyind, UInt16* node_index) { node_buf* buf = record_find_node(tree, key); if (buf) { int comp = -1; int start = 0; // components of a binary search int end = buf->desc.num_rec; int mid = -1; void *p = NULL; char curr_key[tree->head.max_key_len]; hfsp_key_read readkey = tree->kread; hfsp_key_compare key_compare = tree->kcomp; while (start < end) { mid = (start + end) >> 1; p = btree_key_by_index(tree, buf, mid); if (!p) HFSP_ERROR(-1, "record_init_key: unexpected error"); p = readkey (p, curr_key); if (!p) HFSP_ERROR(-1, "record_init_cat_key: unexpected error"); comp = key_compare(curr_key, key); if (comp > 0) start = mid + 1; else if (comp < 0) end = mid; else break; } if (!p) // Empty tree, fascinating ... HFSP_ERROR(ENOENT, "record_init_key: unexpected empty node"); *keyind = mid; *node_index = buf->index; if (!comp) // found something ... return p; } HFSP_ERROR(ENOENT, NULL); fail: return NULL; }
/* move record up in folder hierarchy (if possible) */ int record_up(record* r) { if (r->record.type == HFSP_FOLDER) { // locate folder thread if (record_init_cnid(r, r->tree, r->record.u.folder.id)) return -1; } else if(r->record.type == HFSP_FOLDER_THREAD) { // do nothing were are already where we want to be } else HFSP_ERROR(-1, "record_up: record is neither folder nor folder thread."); if(r->record.type != HFSP_FOLDER_THREAD) HFSP_ERROR(-1, "record_up: unable to locate parent"); return record_init_cnid(r, r->tree, r->record.u.thread.parentID); fail: return -1; }
/* Position node in btree so that key might be inside */ static node_buf* record_find_node(btree* tree, void *key) { int start, end, mid, comp; // components of a binary search void *p = NULL; char curr_key[tree->head.max_key_len]; // The current key under examination hfsp_key_read readkey = tree->kread; hfsp_key_compare key_compare = tree->kcomp; UInt32 index; node_buf* node = btree_node_by_index(tree, tree->head.root); if (!node) HFSP_ERROR(-1, "record_find_node: Cant position to root node"); while (node->desc.kind == HFSP_NODE_NDX) { mid = start = 0; end = node->desc.num_rec; comp = -1; while (start < end) { mid = (start + end) >> 1; p = btree_key_by_index(tree, node, mid); if (!p) HFSP_ERROR(-1, "record_find_node: unexpected error"); p = readkey (p, curr_key); if (!p) HFSP_ERROR(-1, "record_find_node: unexpected error"); comp = key_compare(curr_key, key); if (comp > 0) start = mid + 1; else if (comp < 0) end = mid; else break; } if (!p) // Empty tree, fascinating ... HFSP_ERROR(-1, "record_find_node: unexpected empty node"); if (comp < 0) // mmh interesting key is before this key ... { if (mid == 0) return NULL; // nothing before this key .. p = btree_key_by_index(tree, node, mid-1); if (!p) HFSP_ERROR(-1, "record_find_node: unexpected error"); p = readkey (p, curr_key); if (!p) HFSP_ERROR(-1, "record_find_node: unexpected error"); } index = bswabU32_inc(p); node = btree_node_by_index(tree, index); } return node; // go on and use the found node fail: return NULL; }
/* read a hfsp_extent_key from memory */ void* record_extent_readkey(void* p, void* buf) { hfsp_extent_key* key = (hfsp_extent_key*) buf; UInt16 key_length; key->key_length = key_length = bswabU16_inc(p); key->fork_type = bswabU8_inc(p); key->filler = bswabU8_inc(p); if (key_length != 10) HFSP_ERROR(-1, "Invalid key length in record_extent_readkey"); key->file_id = bswabU32_inc(p); key->start_block = bswabU32_inc(p); return p; fail: return NULL; }
/* intialize the record to the first record of the parent. */ int record_init_parent(record* r, record* parent) { if (parent->record.type == HFSP_FOLDER) return record_init_cnid(r, parent->tree, parent->record.u.folder.id); else if(parent->record.type == HFSP_FOLDER_THREAD) { if (r != parent) *r = *parent; // The folder thread is in fact the first entry, like '.' return 0; } HFSP_ERROR(EINVAL, "record_init_parent: parent is neither folder nor folder thread."); fail: return EINVAL; }
/* write a hfsp_extent_key to memory */ char* record_extent_writekey(char* p, void* buf) { hfsp_extent_key* key = (hfsp_extent_key*) buf; UInt16 key_length = key->key_length; if (key_length != 10) HFSP_ERROR(-1, "Invalid key length in record_extent_writekey"); bstoreU16_inc (&p, key_length); bstoreU8_inc (&p, key->fork_type); bstoreU8_inc (&p, key->filler); bstoreU32_inc (&p, key->file_id); bstoreU32_inc (&p, key->start_block); return p; fail: return NULL; }
/* intialize the record by searching for the given string in the given folder. * * parent and r may be the same. */ int record_init_string_parent(record* r, record* parent, char* name) { hfsp_cat_key key; if (parent->record.type == HFSP_FOLDER) key.parent_cnid = parent->record.u.folder.id; else if(parent->record.type == HFSP_FOLDER_THREAD) key.parent_cnid = parent->key.parent_cnid; else HFSP_ERROR(-1, "record_init_string_parent: parent is not a folder."); key.key_length = 6 + unicode_asc2uni(&key.name,name); // 6 for minumum size return record_init_key(r, parent->tree, &key); fail: return -1; }
/* read a hfsp_cat_thread from memory */ static void* record_readthread(void *p, hfsp_cat_thread* entry) { int i; UInt16 len; UInt16* cp; entry-> reserved = bswabU16_inc(p); entry-> parentID = bswabU32_inc(p); entry->nodeName.strlen = len= bswabU16_inc(p); cp = entry->nodeName.name; if (len > 255) HFSP_ERROR(-1, "Invalid key length in record thread"); for (i=0; i < len; i++, cp++) *cp = bswabU16_inc(p); return p; fail: return NULL; }
/* read a hfsp_cat_entry from memory */ static void* record_readentry(void *p, hfsp_cat_entry* entry) { UInt16 type = bswabU16_inc(p); entry->type = type; switch (type) { case HFSP_FOLDER: return record_readfolder(p, &entry->u.folder); case HFSP_FILE: return record_readfile (p, &entry->u.file); case HFSP_FOLDER_THREAD: case HFSP_FILE_THREAD: return record_readthread(p, &entry->u.thread); default: HFSP_ERROR(-1, "Unexpected record type in record_readentry"); } ; fail: return NULL; }
/* write a hfsp_cat_entry to memory */ char* record_writeentry(char *p, hfsp_cat_entry* entry) { UInt16 type = entry->type; bstoreU16_inc(&p, type); switch (type) { case HFSP_FOLDER: return record_writefolder(p, &entry->u.folder); case HFSP_FILE: return record_writefile (p, &entry->u.file); case HFSP_FOLDER_THREAD: case HFSP_FILE_THREAD: return record_writethread(p, &entry->u.thread); default: HFSP_ERROR(-1, "Unexpected record type in record_writeentry"); } ; fail: return NULL; }
/* write a hfsp_cat_thread to memory */ static char* record_writethread(char *p, hfsp_cat_thread* entry) { int i; UInt16 len; UInt16* cp; bstoreU16_inc(&p, entry->reserved); bstoreU32_inc(&p, entry->parentID); /* this is bad style, friends... (SW) */ /* bstoreU16_inc(&p, len = entry->nodeName.strlen);*/ len = entry->nodeName.strlen; bstoreU16_inc(&p, len); cp = entry->nodeName.name; if (len > 255) HFSP_ERROR(-1, "Invalid key length in record_thread"); for (i=0; i < len; i++, cp++) bstoreU16_inc(&p, *cp); return p; fail: return NULL; }
/* intialize the record by searching for the given key in the btree. * * r is umodified on error. */ static int record_init_key(record* r, btree* tree, hfsp_cat_key* key) { int keyind; UInt16 node_index; void *p = record_find_key(tree, key, &keyind, &node_index); if (p) { r -> tree = tree; r -> node_index= node_index; r -> keyind = keyind; r -> key = *key; // Better use a record_key_copy ... p = record_readentry(p, &r->record); if (!p) HFSP_ERROR(-1, "record_init_key: unexpected error"); return 0; } fail: return -1; }
/* write a hfsp_cat_key back to memory, check for correct length. * * @param p buffer in memory to write to * @param buf memory containing the (swapped) key. * * @return pointer to byte after the structure or NULL on failure. * */ char* record_writekey(char* p, void* buf) { hfsp_cat_key* key = (hfsp_cat_key*) buf; UInt16 key_length, len,i; UInt16* cp; key_length = key->key_length; len = key->name.strlen; cp = key->name.name; if (key_length != (6 + len * 2)) HFSP_ERROR(EINVAL, "Invalid key length in record_writekey"); bstoreU16_inc(&p, key_length); bstoreU32_inc(&p, key->parent_cnid); bstoreU16_inc(&p, len); for (i=0; i < len; i++, cp++) bstoreU16_inc(&p, *cp); return p; fail: return NULL; }
/* read a hfsp_cat_key from memory */ void* record_readkey(void* p, void* buf) { hfsp_cat_key* key = (hfsp_cat_key*) buf; const void* check; UInt16 key_length, len,i; UInt16* cp; key->key_length = key_length = bswabU16_inc(p); check = p; key->parent_cnid = bswabU32_inc(p); key->name.strlen = len = bswabU16_inc(p); cp = key->name.name; for (i=0; i < len; i++, cp++) *cp = bswabU16_inc(p); /* check if keylenght was correct */ if (key_length != ((char*) p) - ((char*) check)) HFSP_ERROR(EINVAL, "Invalid key length in record_readkey"); return p; fail: return NULL; }
/* intialize the extent_record to the extent identified by the * (first) blockindex. * * forktype: either HFSP_EXTEND_DATA or HFSP_EXTEND_RSRC */ int record_init_file(extent_record* r, btree* tree, UInt8 forktype, UInt32 fileId, UInt32 blockindex) { int keyind; UInt16 node_index; hfsp_extent_key key = { 10, forktype, 0, fileId, blockindex }; void *p = record_find_key(tree, &key, &keyind, &node_index); if (p) { r -> tree = tree; r -> node_index= node_index; r -> keyind = keyind; r -> key = key; // Better use a record_key_copy ... p = volume_readextent(p, r->extent); if (!p) HFSP_ERROR(-1, "record_init_file: unexpected error"); return 0; } fail: return -1; }
/* find next block of the fork iterating over */ int blockiter_next(blockiter *b) { b->curr_block ++; b->block ++; if (b->curr_block >= b->max_block) return -1; // end of Blocks, but no error // in current part of extent ? if (b->block >= b->e->block_count) { b->index++; b->block = 0; // reset relative position b->e++; if (b -> index >= 8) // need to fetch another extent { if (blockiter_next_extent(b)) HFSP_ERROR(ENOENT, "Extends record not found."); } } return 0; fail: return -1; }
/* Read the volume from the given buffer and swap the bytes. * * ToDo: add more consitency checks. */ static int volume_readbuf(hfsp_vh* vh, char * p) { if( (vh->signature = bswabU16_inc(p)) != HFSP_VOLHEAD_SIG) HFSP_ERROR(-1, "This is not a HFS+ volume"); vh->version = bswabU16_inc(p); vh->attributes = bswabU32_inc(p); vh->last_mount_vers = bswabU32_inc(p); vh->reserved = bswabU32_inc(p); vh->create_date = bswabU32_inc(p); vh->modify_date = bswabU32_inc(p); vh->backup_date = bswabU32_inc(p); vh->checked_date = bswabU32_inc(p); vh->file_count = bswabU32_inc(p); vh->folder_count = bswabU32_inc(p); vh->blocksize = bswabU32_inc(p); vh->total_blocks = bswabU32_inc(p); vh->free_blocks = bswabU32_inc(p); vh->next_alloc = bswabU32_inc(p); vh->rsrc_clump_sz = bswabU32_inc(p); vh->data_clump_sz = bswabU32_inc(p); vh->next_cnid = bswabU32_inc(p); vh->write_count = bswabU32_inc(p); vh->encodings_bmp = bswabU64_inc(p); memcpy(vh->finder_info, p, 32); p += 32; // So finderinfo must be swapped later, *** p = volume_readfork(p, &vh->alloc_file ); p = volume_readfork(p, &vh->ext_file ); p = volume_readfork(p, &vh->cat_file ); p = volume_readfork(p, &vh->attr_file ); p = volume_readfork(p, &vh->start_file ); return 0; fail: return -1; }