diff -Nru parted-1.6.10/AUTHORS parted-1.6.10-hfs/AUTHORS --- parted-1.6.10/AUTHORS 2004-04-15 20:24:37.000000000 -0400 +++ parted-1.6.10-hfs/AUTHORS 2004-04-23 22:43:33.406374728 -0400 @@ -149,3 +149,7 @@ Giuseppe Sacco * Italian translations +Guillaume Knispel + * nearly all hfs and hfs+ code (libparted/fs_hfs) + * hfs+ support for mac partitions (libparted/disk_mac.c) + diff -Nru parted-1.6.10/libparted/disk_mac.c parted-1.6.10-hfs/libparted/disk_mac.c --- parted-1.6.10/libparted/disk_mac.c 2004-04-15 19:41:40.000000000 -0400 +++ parted-1.6.10-hfs/libparted/disk_mac.c 2004-04-23 22:44:10.838684152 -0400 @@ -1068,7 +1068,8 @@ return 1; } - if (fs_type && !strcmp (fs_type->name, "hfs")) { + if (fs_type && (!strcmp (fs_type->name, "hfs") + || !strcmp (fs_type->name, "hfs+"))) { strcpy (mac_data->system_name, "Apple_HFS"); mac_data->status |= 0x7f; } else { diff -Nru parted-1.6.10/libparted/fs_hfs/hfs.c parted-1.6.10-hfs/libparted/fs_hfs/hfs.c --- parted-1.6.10/libparted/fs_hfs/hfs.c 2002-04-08 06:10:25.000000000 -0400 +++ parted-1.6.10-hfs/libparted/fs_hfs/hfs.c 2004-04-23 22:44:10.949667280 -0400 @@ -1,6 +1,6 @@ /* libparted - a library for manipulating disk partitions - Copyright (C) 2000 Free Software Foundation, Inc. + Copyright (C) 2000, 2003 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,10 +17,67 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + Author : Guillaume Knispel + Report bug to +*/ + +/* + Technical doc about Apple HFS and HFS+ file systems is available at : + * For HFS, section "Data Organization on Volumes", + "Chapter 2 - File Manager" + of the book "Inside Macintosh: Files" + http://developer.apple.com/techpubs/mac/Files/Files-99.html#HEADING99-0 + * For HFS+, "Technical Note TN1150", "HFS Plus Volume Format" + http://developer.apple.com/technotes/tn/tn1150.html + + Some useful HFS precisions concerning alignement and bit ordering + are in the HFS+ TN +*/ + +/* HISTORY : +## modifications dd-mm-yyyy +---------------------- PATCH FOR PARTED 1.6.5 ---------------------------- + 1 initial revision 07-04-2003 + 2 one pass resizing, removal of debug info 08-04-2003 + 3 safe abort if resize failed, code cleanups, timer, 10-04-2003 + source file split, won't resize if not unmounted, + only relocate data if needed, minimize disk operations + 4 memory leaks removal, code cleanups, resize hfs+ code, 17-04-2003 + more checks, minor hfs resize bugfix, probe code + returns real geometry + 5 hfs+ resize bugfixes : 19-04-2003 + * fragmented fs could be corrupted + * VH wasn't written on error during alloc map writing + * attributes file could be corrupted + 6 Use PedSector to be able to use > 2 To HD 23-04-2003 + Minor probe bugfix, Cleanups, HFS+ Timer tuning, + 7 80 columns indentation 23-04-2003 + 8 Bugfix free blocks calculation in wrapper + (makes Mac OS boot !) 28-04-2003 +---------------------- PATCH FOR PARTED 1.6.6 ---------------------------- + 9 Don't destroy the file being worked on in case of + interruption of Parted 28-10-2003 +*/ + +/* TODO : + * Think about a mean to do more allocation bitmap + saves when shrinking hfs+ -- not too hard, necessary ? + * what's said in #9 :) -- DONE, but may sometime makes fail the process + * write lots of error message display to be able to have + usefull bug reports -- Urgent, put in 10 maximum ! + * rewrite *_search_move_* because they're becoming unmaintainable + (use of goto, etc...) -- Can wait patch 10 or later + * Change the relocation algorithm for a better one -- #42 :) */ + #include "config.h" +#include +#include #include #include +#include +#include #if ENABLE_NLS # include @@ -29,34 +86,3145 @@ # define _(String) (String) #endif /* ENABLE_NLS */ -#include +#include "hfs.h" + +#define MAX_BUFF 256 /* in blocks */ -#define HFS_SIGNATURE 0x4244 +/* -------------------- */ +/* -- CODE -- */ +/* -------------------- */ + +/* Probe an HFS volume, detecting it even if +it is in fact a wrapper to an HFS+ volume */ +/* Used by hfsplus_probe and hfs_probe */ +static PedGeometry* +hfs_and_wrapper_probe (PedGeometry* geom) +{ + uint8_t buf[PED_SECTOR_SIZE]; + HfsMasterDirectoryBlock *mdb; + PedGeometry* geom_ret; + PedSector search, max; + + mdb = (HfsMasterDirectoryBlock *) buf; + + if ((geom->length < 5) + || (!ped_geometry_read (geom, buf, 2, 1)) + || (PED_BE16_TO_CPU (mdb->signature) != HFS_SIGNATURE) ) + return NULL; + + search = ((PedSector) PED_BE16_TO_CPU (mdb->start_block) + + ((PedSector) PED_BE16_TO_CPU (mdb->total_blocks) + * (PED_BE32_TO_CPU (mdb->block_size) / PED_SECTOR_SIZE ))); + max = search + ( PED_BE32_TO_CPU (mdb->block_size) / PED_SECTOR_SIZE ); + if (!(geom_ret = ped_geometry_new (geom->dev, geom->start, search + 2))) + return NULL; + + for (; search < max; search++) { + if (!ped_geometry_set (geom_ret, geom_ret->start, search + 2) + || !ped_geometry_read (geom_ret, buf, search, 1)) + break; + if (PED_BE16_TO_CPU (mdb->signature) == HFS_SIGNATURE) + return geom_ret; + } + + ped_geometry_destroy (geom_ret); + return NULL; +} + +static PedGeometry* +hfsplus_probe (PedGeometry* geom) +{ + PedGeometry* geom_ret; + uint8_t buf[PED_SECTOR_SIZE]; + + if ((geom_ret = hfs_and_wrapper_probe(geom))) { + /* HFS+ is embedded in an HFS volume ? */ + HfsMasterDirectoryBlock *mdb; + mdb = (HfsMasterDirectoryBlock *) buf; + + if (!ped_geometry_read (geom, buf, 2, 1) + || (PED_BE16_TO_CPU (mdb->old_new.embedded.signature) + != HFSP_SIGNATURE)) { + ped_geometry_destroy (geom_ret); + return NULL; + } else + return geom_ret; + } else { + /* This is a standalone HFS+ volume ? */ + PedSector search, max; + HfsPVolumeHeader *vh; + vh = (HfsPVolumeHeader *) buf; + + if (!ped_geometry_read (geom, buf, 2, 1) + || (PED_BE16_TO_CPU (vh->signature) != HFSP_SIGNATURE)) + return NULL; + + max = (PedSector) PED_BE32_TO_CPU (vh->total_blocks) + * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE ); + search = max - ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE ) - 1; + if (!(geom_ret = ped_geometry_new (geom->dev, geom->start, + search + 2))) + return NULL; + + for (; search < max; search++) { + if (!ped_geometry_set (geom_ret, geom_ret->start, + search + 2) + || !ped_geometry_read (geom_ret, buf, search, 1)) + break; + if (PED_BE16_TO_CPU (vh->signature) == HFSP_SIGNATURE) + return geom_ret; + } + + ped_geometry_destroy (geom_ret); + return NULL; + } +} static PedGeometry* hfs_probe (PedGeometry* geom) { - char buf[512]; + PedGeometry* geom_base; + PedGeometry* geom_plus = NULL; + + if ((geom_base = hfs_and_wrapper_probe(geom)) + && (!(geom_plus = hfsplus_probe(geom_base)))) + return geom_base; + else { + if (geom_base) ped_geometry_destroy (geom_base); + if (geom_plus) ped_geometry_destroy (geom_plus); + return NULL; + } +} + +#ifndef DISCOVER_ONLY +/* declaration used by hfs_btree_search (indirect recursion) */ +static int +hfs_file_read_sector (HfsPrivateFile* file, void *buf, PedSector sector); + +/* This is a very unundoable operation :p */ +/* Maybe I shouldn't touch the alternate MDB ? */ +/* Anyway clobber is call before other fs creation */ +/* So this must be a non-issue */ +static int +hfs_clobber (PedGeometry* geom) +{ + uint8_t buf[PED_SECTOR_SIZE]; + + memset (buf, 0, PED_SECTOR_SIZE); + + /* destroy boot blocks, mdb, alternate mdb ... */ + return ped_geometry_write (geom, buf, 0, 1) & + ped_geometry_write (geom, buf, 1, 1) & + ped_geometry_write (geom, buf, 2, 1) & + ped_geometry_write (geom, buf, geom->length - 2, 1) & + ped_geometry_write (geom, buf, geom->length - 1, 1); +} + +static int +hfsplus_clobber (PedGeometry* geom) +{ + unsigned int i = 1; + uint8_t buf[PED_SECTOR_SIZE]; + HfsMasterDirectoryBlock *mdb; + + mdb = (HfsMasterDirectoryBlock *) buf; + + if (!ped_geometry_read (geom, buf, 2, 1)) + return 0; + + + if (PED_BE16_TO_CPU (mdb->signature) == HFS_SIGNATURE) { + /* embedded hfs+ */ + PedGeometry *embedded; + + i = (PED_BE32_TO_CPU(mdb->block_size) / PED_SECTOR_SIZE); + embedded = ped_geometry_new ( + geom->dev, + (PedSector) geom->start + + PED_BE16_TO_CPU (mdb->start_block) + + (PedSector) PED_BE16_TO_CPU ( + mdb->old_new.embedded.location.start_block ) * i, + (PedSector) PED_BE16_TO_CPU ( + mdb->old_new.embedded.location.block_count ) * i ); + i = hfs_clobber (embedded); + ped_geometry_destroy (embedded); + } + + /* non-embedded or envelop destroy as hfs */ + return ( hfs_clobber (geom) && i ); +} + +/* Open the data fork of a file with its first three extents and its CNID */ +static HfsPrivateFile* +hfs_file_open (PedFileSystem *fs, uint32_t CNID, + HfsExtDataRec ext_desc, PedSector sect_nb) +{ + HfsPrivateFile* file; + + file = (HfsPrivateFile*) ped_malloc (sizeof (HfsPrivateFile)); + + file->fs = fs; + file->sect_nb = sect_nb; + file->CNID = CNID; + memcpy(file->first, ext_desc, sizeof (HfsExtDataRec)); + memcpy(file->cache, ext_desc, sizeof (HfsExtDataRec)); + file->start_cache = 0; + + return file; +} + +/* Close an HFS file */ +static void +hfs_file_close (HfsPrivateFile* file) +{ + ped_free (file); +} + +/* do a B-Tree lookup */ +/* read the first record immediatly inferior or egal to the given key */ +/* return 0 on error */ +/* record_out _must_ be large enough to receive record_size bytes */ +static int +hfs_btree_search (HfsPrivateFile* b_tree_file, HfsPrivateGenericKey* key, + void *record_out, unsigned int record_size, + HfsCPrivateLeafRec* record_ref) +{ + uint8_t node[PED_SECTOR_SIZE]; + HfsHeaderRecord* header; + HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node; + HfsPrivateGenericKey* record_key = NULL; + unsigned int node_number, record_number; + int i; + + /* Read the header node */ + if (!hfs_file_read_sector(b_tree_file, node, 0)) + return 0; + header = ((HfsHeaderRecord*) (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(PED_SECTOR_SIZE-2)))))); + + /* Get the node number of the root */ + node_number = PED_BE32_TO_CPU(header->root_node); + + /* Read the root node */ + if (!hfs_file_read_sector(b_tree_file, node, node_number)) + return 0; + + /* Follow the white rabbit */ + while (1) { + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = record_number; i; i--) { + int cmp, min_length; + + record_key = (HfsPrivateGenericKey*) + (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(PED_SECTOR_SIZE - 2*i))))); + /* check for obvious error in FS */ + if (((uint8_t*)record_key - node < HFS_FIRST_REC) + || ((uint8_t*)record_key - node + >= PED_SECTOR_SIZE + - 2 * (signed)(record_number+1))) + return 0; + min_length = ( (key->key_length + > record_key->key_length) ? + (record_key->key_length) : + (key->key_length) ); + cmp = memcmp (record_key->key_content, + key->key_content, min_length); + if (cmp < 0 || ( cmp == 0 && record_key->key_length + <= key->key_length)) + break; + } + if (!i) return 0; + if (desc->type == HFS_IDX_NODE) { + unsigned int skip; + + skip = (1 + record_key->key_length + 1) & ~1; + node_number = PED_BE32_TO_CPU (*((uint32_t *) + (((uint8_t *) record_key) + skip))); + if (!hfs_file_read_sector(b_tree_file, node, + node_number)) + return 0; + } else + break; + } + + /* copy the result if needed */ + if (record_size) + memcpy (record_out, record_key, record_size); + + /* send record reference if needed */ + if (record_ref) { + record_ref->node_size = 1; /* in sectors */ + record_ref->node_number = node_number; + record_ref->record_pos = (uint8_t*)record_key - node; + record_ref->record_number = i; + } + + /* success */ + return 1; +} + +static int +hfs_get_extent_containing (HfsPrivateFile* file, unsigned int block, + HfsExtDataRec cache, uint16_t* ptr_start_cache) +{ + uint8_t record[sizeof (HfsExtentKey) + + sizeof (HfsExtDataRec)]; + HfsExtentKey search; + HfsExtentKey* ret_key = (HfsExtentKey*) record; + HfsExtDescriptor* ret_cache = (HfsExtDescriptor*) + (record + sizeof (HfsExtentKey)); + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + file->fs->type_specific; + + + search.key_length = sizeof (HfsExtentKey) - 1; + search.type = HFS_DATA_FORK; + search.file_ID = file->CNID; + search.start = PED_CPU_TO_BE16 (block); + + if (!hfs_btree_search (priv_data->extent_file, + (HfsPrivateGenericKey*) &search, + record, sizeof (record), NULL)) + return 0; + + if (ret_key->file_ID != search.file_ID || ret_key->type != search.type) + return 0; + + memcpy (cache, ret_cache, sizeof(HfsExtDataRec)); + *ptr_start_cache = PED_BE16_TO_CPU (ret_key->start); + + return 1; +} + +/* find and return the nth sector of a file */ +/* return 0 on error */ +static PedSector +hfs_file_find_sector (HfsPrivateFile* file, PedSector sector) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + file->fs->type_specific; + HfsMasterDirectoryBlock* mdb = priv_data->mdb; + unsigned int i, s, vol_block; + unsigned int block = sector + / (PED_BE32_TO_CPU (mdb->block_size) + / PED_SECTOR_SIZE); + unsigned int offset = sector + % (PED_BE32_TO_CPU (mdb->block_size) + / PED_SECTOR_SIZE); + + /* in the three first extent */ + for (s = 0, i = 0; i < HFS_EXT_NB; i++, + s += PED_BE16_TO_CPU (file->first[i].block_count)) { + if ((block >= s) && ( block < s + PED_BE16_TO_CPU ( + file->first[i].block_count))) { + vol_block = (block - s) + PED_BE16_TO_CPU ( + file->first[i].start_block); + goto sector_found; + } + } + + /* in the three cached extent */ + for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++, + s += PED_BE16_TO_CPU (file->cache[i].block_count)) { + if ((block >= s) && (block < s + PED_BE16_TO_CPU ( + file->cache[i].block_count))) { + vol_block = (block - s) + PED_BE16_TO_CPU ( + file->cache[i].start_block); + goto sector_found; + } + } + + /* update cache */ + if (!hfs_get_extent_containing (file, block, file->cache, + &(file->start_cache))) + return 0; + + /* in the three cached extent */ + for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++, + s += PED_BE16_TO_CPU (file->cache[i].block_count)) { + if ((block >= s) && (block < s + PED_BE16_TO_CPU ( + file->cache[i].block_count))) { + vol_block = (block - s) + PED_BE16_TO_CPU ( + file->cache[i].start_block); + goto sector_found; + } + } + + return 0; + + sector_found: + return (PedSector) PED_BE16_TO_CPU (mdb->start_block) + + (PedSector) vol_block * (PED_BE32_TO_CPU (mdb->block_size) + / PED_SECTOR_SIZE) + + offset; +} + +/* Read the nth sector of a file */ +/* return 0 on error */ +static int +hfs_file_read_sector (HfsPrivateFile* file, void *buf, PedSector sector) +{ + PedSector abs_sector; + + if (sector >= file->sect_nb) + return 0; + + abs_sector = hfs_file_find_sector (file, sector); + return abs_sector && ped_geometry_read (file->fs->geom, buf, + abs_sector, 1); +} + +/* Write the nth sector of a file */ +/* return 0 on error */ +static int +hfs_file_write_sector (HfsPrivateFile* file, void *buf, PedSector sector) +{ + PedSector abs_sector; + + if (sector >= file->sect_nb) + return 0; + + abs_sector = hfs_file_find_sector (file, sector); + return abs_sector && ped_geometry_write (file->fs->geom, buf, + abs_sector, 1); +} + +/* This function reads bad blocks extents in the extents file + and store it in f.s. specific data of fs */ +static int +hfs_read_bad_blocks (const PedFileSystem *fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + + if (!priv_data->bad_blocks_loaded) { + uint8_t record[sizeof (HfsExtentKey) + + sizeof (HfsExtDataRec)]; + HfsExtentKey search; + HfsExtentKey* ret_key = (HfsExtentKey*) record; + HfsExtDescriptor* ret_cache = (HfsExtDescriptor*) + (record + + sizeof (HfsExtentKey)); + unsigned int block, last_start, first_pass = 1; + + search.key_length = sizeof (HfsExtentKey) - 1; + search.type = HFS_DATA_FORK; + search.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID); + + last_start = -1; block = 0; + while (1) { + int i; + + search.start = PED_CPU_TO_BE16 (block); + if (!hfs_btree_search (priv_data->extent_file, + (HfsPrivateGenericKey*) &search, + record, sizeof (record), NULL)) { + if (first_pass) + break; + else + return 0; + } + if (ret_key->file_ID != search.file_ID + || ret_key->type != search.type) + break; + if (PED_BE16_TO_CPU (ret_key->start) == last_start) + break; + + last_start = PED_BE16_TO_CPU (ret_key->start); + for (i = 0; i < HFS_EXT_NB; i++) { + if (ret_cache[i].block_count) { + HfsPrivateLinkExtent* new_xt = + (HfsPrivateLinkExtent*) ped_malloc ( + sizeof (HfsPrivateLinkExtent)); + new_xt->next = + priv_data->bad_blocks_xtent_list; + memcpy(&(new_xt->extent), ret_cache+i, + sizeof (HfsExtDescriptor)); + priv_data->bad_blocks_xtent_list = + new_xt; + priv_data->bad_blocks_xtent_nb++; + block += PED_BE16_TO_CPU ( + ret_cache[i].block_count); + } + } + first_pass = 0; + } + + priv_data->bad_blocks_loaded = 1; + } + + return 1; +} + +/* free the bad blocks linked list */ +static void +hfs_free_bad_blocks_list(HfsPrivateLinkExtent* first) +{ + HfsPrivateLinkExtent* next; + + while (first) { + next = first->next; + ped_free (first); + first = next; + } +} + +/* This function check if fblock is a bad block */ +static int +hfs_is_bad_block (const PedFileSystem *fs, unsigned int fblock) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsPrivateLinkExtent* walk; + + for (walk = priv_data->bad_blocks_xtent_list; walk; walk = walk->next) { + /* Won't compile without the strange cast ! gcc bug ? */ + /* or maybe C subtilties... */ + if ((fblock >= PED_BE16_TO_CPU (walk->extent.start_block)) && + (fblock < (unsigned int) (PED_BE16_TO_CPU ( + walk->extent.start_block) + + PED_BE16_TO_CPU ( + walk->extent.block_count)))) + return 1; + } + + return 0; +} + +/* This function moves data of size blocks starting + at block *ptr_fblock to block *ptr_to_fblock */ +/* return new start or -1 on failure */ +static int +hfs_effect_move_extent (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock, unsigned int size) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + uint8_t* block; + unsigned int i; + unsigned int new_start; + unsigned int next_to_fblock = 0; + unsigned int start = 0, stop = 0; + int ok = 0; + +#if 0 + + /* try to find enough room to fit the extent */ + for (i = *ptr_to_fblock; + i < *ptr_to_fblock + size && i < *ptr_fblock; + i++) { + if (hfs_is_bad_block (fs, i)) + *ptr_to_fblock = i+1; + } + +#else + + /* try to find enough room to fit the extent in a non overlapping way */ + + /* special case of next if, keep it only if it increases speed in a + significant way */ + if (*ptr_to_fblock + size <= *ptr_fblock) { + ok = 1; + for (i = *ptr_to_fblock; ok && i < *ptr_to_fblock; i++) + if ((priv_data->alloc_map[i/8] & (1<<(7-(i&7))))) ok = 0; + next_to_fblock = *ptr_to_fblock + size; + start = *ptr_to_fblock; + } + + /* back search */ + if (!ok && *ptr_fblock != *ptr_to_fblock) { + start = stop = *ptr_fblock; + while (start && stop-start != size) { + --start; + if ((priv_data->alloc_map[start/8] & (1<<(7-(start&7))))) + stop = start; + } + if (stop-start == size) { + if (stop > *ptr_to_fblock) + next_to_fblock = stop; + else + next_to_fblock = *ptr_to_fblock; + ok = 1; + } else ok = 0; + } + + /* forward search for 2 pass relocation */ + if (!ok && *ptr_fblock != *ptr_to_fblock) { + start = stop = *ptr_fblock; + while (stop < PED_BE16_TO_CPU(priv_data->mdb->total_blocks) + && stop-start != size) { + if ((priv_data->alloc_map[stop/8] & (1<<(7-(stop&7))))) + start = stop + 1; + ++stop; + } + ok = (stop-start == size); + next_to_fblock = *ptr_to_fblock; + } + +#endif + + /* new non overlapping room has been found ? */ + if (ok) { + /* enough room */ + unsigned int j; + + block = (uint8_t*) ped_malloc(PED_BE32_TO_CPU ( + priv_data->mdb->block_size) + * ((sizemdb->start_block) + + (PedSector) (*ptr_fblock + i) + * (PED_BE32_TO_CPU ( + priv_data->mdb->block_size) + / PED_SECTOR_SIZE); + if (!ped_geometry_read (fs->geom, block, abs_sector, + PED_BE32_TO_CPU ( + priv_data->mdb->block_size) + / PED_SECTOR_SIZE * j)) + return -1; + + abs_sector = (PedSector) PED_BE16_TO_CPU ( + priv_data->mdb->start_block) + + (PedSector) (new_start + i) + * (PED_BE32_TO_CPU ( + priv_data->mdb->block_size) + / PED_SECTOR_SIZE); + if (!ped_geometry_write (fs->geom, block, abs_sector, + PED_BE32_TO_CPU ( + priv_data->mdb->block_size) + / PED_SECTOR_SIZE * j)) + return -1; + + for (ai = i+j; i < ai; i++) { + bit = 7 - ((*ptr_fblock + i) & 7); + byte = (*ptr_fblock + i) / 8; + + to_bit = 7 - ((new_start + i) & 7); + to_byte = (new_start + i) / 8; + + /* free source block */ + priv_data->alloc_map[byte] &= ~(1 << bit); + + /* set dest block */ + priv_data->alloc_map[to_byte] |= (1 << to_bit); + } + } + + /* save the allocation map */ + if (!ped_geometry_write(fs->geom, priv_data->alloc_map, + PED_BE16_TO_CPU ( + priv_data->mdb->volume_bitmap_block), + ( PED_BE16_TO_CPU ( + priv_data->mdb->total_blocks) + + PED_SECTOR_SIZE * 8 - 1) + / (PED_SECTOR_SIZE * 8))) + return -1; + + ped_free (block); + + *ptr_fblock += size; + *ptr_to_fblock = next_to_fblock; + } else { + /* not enough room, but try to continue */ + new_start = *ptr_fblock; + *ptr_fblock = *ptr_to_fblock = new_start + size; + } + + return new_start; +} + +/* Search an extent in the catalog file and move it if found */ +/* Return 1 if everything was fine */ +/* Return -1 if an error occured */ +/* Return 0 if no extent was found */ +static int +hfs_search_move_catalog (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + uint8_t node[PED_SECTOR_SIZE]; + HfsHeaderRecord* header; + HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node; + HfsCatalogKey* catalog_key; + HfsCatalog* catalog_data; + unsigned int leaf_node, record_number; + unsigned int i, j; + unsigned int* src_fblock = ptr_fblock; + int new_start, pass2 = 0; + + /* Search the extent starting at *ptr_block in the catalog file */ + if (!hfs_file_read_sector (priv_data->catalog_file, node, 0)) + return -1; + header = ((HfsHeaderRecord*) (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(PED_SECTOR_SIZE-2)))))); + leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + + for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfs_file_read_sector (priv_data->catalog_file, node, + leaf_node)) + return -1; + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; i++) { + /* fucking undocumented alignement powered by apple :p */ + unsigned int skip; + catalog_key = (HfsCatalogKey*) (node + PED_BE16_TO_CPU( + *((uint16_t *)(node+(PED_SECTOR_SIZE - 2*i))))); + skip = (1 + catalog_key->key_length + 1) & ~1; + catalog_data = (HfsCatalog*)(((uint8_t*)catalog_key) + + skip); + /* check for obvious error in FS */ + if (((uint8_t*)catalog_key - node < HFS_FIRST_REC) + || ((uint8_t*)catalog_data - node + >= PED_SECTOR_SIZE + - 2 * (signed)(record_number+1))) + return -1; + if (catalog_data->type != HFS_CAT_FILE) continue; + for (j = 0; j < HFS_EXT_NB; j++) { + if (catalog_data + ->sel.file.extents_data[j].block_count + && PED_BE16_TO_CPU (catalog_data + ->sel.file.extents_data[j].start_block) + == (*ptr_fblock)) + goto catalog_data_found; + if (catalog_data + ->sel.file.extents_res[j].block_count + && PED_BE16_TO_CPU (catalog_data + ->sel.file.extents_res[j].start_block) + == (*ptr_fblock)) + goto catalog_res_found; + } + } + } + /* no extent starting a *ptr_block has been found in the catalog file */ + return 0; + + + /* an extent part of a data fork has been found in the catalog file */ + catalog_data_found: + new_start = hfs_effect_move_extent (fs, src_fblock, ptr_to_fblock, + PED_BE16_TO_CPU (catalog_data + ->sel.file.extents_data[j] + .block_count)); + if (new_start != -1) { + int old_start; + old_start = PED_BE16_TO_CPU(catalog_data->sel.file + .extents_data[j].start_block); + catalog_data->sel.file.extents_data[j].start_block = + PED_CPU_TO_BE16 (new_start); + /* write if any changes */ + if ((old_start != new_start) + && !hfs_file_write_sector (priv_data->catalog_file, + node, leaf_node)) + return -1; + /* !!!!!!!!!!!!!!!!! */ + /* This "if" by itself makes the need to rewrite the whole function */ + /* !!!!!!!!!!!!!!!!! */ + if (new_start > old_start) { + if (pass2) return -1; + pass2 = 1; src_fblock = &new_start; + goto catalog_data_found; + } + return 1; + } else + return -1; + + /* an extent part of a resource fork has been found in the catalog file */ + catalog_res_found: + new_start = hfs_effect_move_extent (fs, src_fblock, ptr_to_fblock, + PED_BE16_TO_CPU (catalog_data + ->sel.file.extents_res[j] + .block_count)); + if (new_start != -1) { + int old_start; + old_start = PED_BE16_TO_CPU(catalog_data->sel.file + .extents_res[j].start_block); + catalog_data->sel.file.extents_res[j].start_block = + PED_CPU_TO_BE16 (new_start); + /* write if any changes */ + if ((old_start != new_start) + && !hfs_file_write_sector (priv_data->catalog_file, + node, leaf_node)) + return -1; + /* !!!!!!!!!!!!!!!!! */ + /* This "if" by itself makes the need to rewrite the whole function */ + /* !!!!!!!!!!!!!!!!! */ + if (new_start > old_start) { + if (pass2) return -1; + pass2 = 1; src_fblock = &new_start; + goto catalog_res_found; + } + return 1; + } else + return -1; +} + +/* Search an extent in the extent file and move it if found */ +/* Return 1 if everything was fine */ +/* Return -1 if an error occured */ +/* Return 0 if no extent was found */ +static int +hfs_search_move_extent (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + uint8_t node[PED_SECTOR_SIZE]; + HfsHeaderRecord* header; + HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node; + HfsExtentKey* extent_key; + HfsExtDescriptor* extent_data; + unsigned int leaf_node, record_number; + unsigned int i, j; + unsigned int* src_fblock = ptr_fblock; + int new_start, pass2 = 0; + + /* Search the extent in the extent file */ + if (!hfs_file_read_sector (priv_data->extent_file, node, 0)) + return -1; + header = ((HfsHeaderRecord*) (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(PED_SECTOR_SIZE-2)))))); + leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + + for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfs_file_read_sector (priv_data->extent_file, node, + leaf_node)) + return -1; + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; i++) { + extent_key = (HfsExtentKey*) + (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(PED_SECTOR_SIZE - 2*i))))); + extent_data = (HfsExtDescriptor*)(((uint8_t*)extent_key) + + sizeof (HfsExtentKey)); + /* check for obvious error in FS */ + if (((uint8_t*)extent_key - node < HFS_FIRST_REC) + || ((uint8_t*)extent_data - node + >= PED_SECTOR_SIZE + - 2 * (signed)(record_number+1))) + return -1; + for (j = 0; j < HFS_EXT_NB; j++) { + if (extent_data[j].block_count + && PED_BE16_TO_CPU ( + extent_data[j].start_block) + == (*ptr_fblock)) + goto extent_found; + } + } + } + /* no extent starting a *ptr_block has been found in the extents file */ + return 0; + + /* an extent has been found in the extents file */ + extent_found: + new_start = hfs_effect_move_extent (fs, src_fblock, ptr_to_fblock, + PED_BE16_TO_CPU (extent_data[j] + .block_count)); + if (new_start != -1) { + int old_start; + old_start = PED_BE16_TO_CPU(extent_data[j].start_block); + extent_data[j].start_block = PED_CPU_TO_BE16 (new_start); + /* This extent might have been cached into the file structure + of the extent or catalog file */ + if (priv_data->catalog_file->cache[j].start_block == + PED_CPU_TO_BE16(old_start)) + memcpy (priv_data->catalog_file->cache, extent_data, + sizeof (HfsExtDataRec)); + if (priv_data->extent_file->cache[j].start_block == + PED_CPU_TO_BE16(old_start)) + memcpy (priv_data->extent_file->cache, extent_data, + sizeof (HfsExtDataRec)); + /* write if any changes */ + if ((old_start != new_start) + && !hfs_file_write_sector (priv_data->extent_file, node, + leaf_node)) + return -1; + /* !!!!!!!!!!!!!!!!! */ + /* This "if" by itself makes the need to rewrite the whole function */ + /* !!!!!!!!!!!!!!!!! */ + if (new_start > old_start) { + if (pass2) return -1; + pass2 = 1; src_fblock = &new_start; + goto extent_found; + } + return 1; + } else + return -1; +} + +/* Search an extent in the mdb and move it if found */ +/* Return 1 if everything was fine */ +/* Return -1 if an error occured */ +/* Return 0 if no extent was found */ +static int +hfs_search_move_primary (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + uint8_t node[PED_SECTOR_SIZE]; + unsigned int j; + unsigned int* src_fblock = ptr_fblock; + int new_start, pass2 = 0, from; + int old_start; - if (geom->length < 2) + /* Search an extent in the MDB */ + for (j = 0; j < HFS_EXT_NB; j++) { + if (priv_data->mdb->extents_file_rec[j].block_count + && PED_BE16_TO_CPU (priv_data->mdb->extents_file_rec[j] + .start_block) + == (*ptr_fblock)) + goto ext_file_found; + if (priv_data->mdb->catalog_file_rec[j].block_count + && PED_BE16_TO_CPU (priv_data->mdb->catalog_file_rec[j] + .start_block) + == (*ptr_fblock)) + goto cat_file_found; + } + return 0; + + ext_file_found: + new_start = hfs_effect_move_extent (fs, src_fblock, ptr_to_fblock, + PED_BE16_TO_CPU (priv_data->mdb + ->extents_file_rec[j] + .block_count)); + if (new_start != -1) { + old_start = PED_BE16_TO_CPU(priv_data->mdb + ->extents_file_rec[j].start_block); + priv_data->mdb->extents_file_rec[j].start_block = + PED_CPU_TO_BE16 (new_start); + memcpy (priv_data->extent_file->first, + priv_data->mdb->extents_file_rec, + sizeof (HfsExtDataRec)); + if (!priv_data->extent_file->start_cache) + memcpy (priv_data->extent_file->cache, + priv_data->extent_file->first, + sizeof (HfsExtDataRec)); + from = 0; + goto update_mdb; + } else + return -1; + + + cat_file_found: + new_start = hfs_effect_move_extent (fs, src_fblock, ptr_to_fblock, + PED_BE16_TO_CPU (priv_data->mdb + ->catalog_file_rec[j] + .block_count)); + if (new_start != -1) { + old_start = PED_BE16_TO_CPU(priv_data->mdb + ->catalog_file_rec[j].start_block); + priv_data->mdb->catalog_file_rec[j].start_block = + PED_CPU_TO_BE16 (new_start); + memcpy (priv_data->catalog_file->first, + priv_data->mdb->catalog_file_rec, + sizeof (HfsExtDataRec)); + if (!priv_data->catalog_file->start_cache) + memcpy (priv_data->catalog_file->cache, + priv_data->catalog_file->first, + sizeof (HfsExtDataRec)); + from = 1; + goto update_mdb; + } else + return -1; + + update_mdb: + if (!ped_geometry_read (fs->geom, node, 2, 1)) + return -1; + memcpy (node, priv_data->mdb, sizeof (HfsMasterDirectoryBlock)); + if (!ped_geometry_write (fs->geom, node, 2, 1)) + return -1; + if (!ped_geometry_write (fs->geom, node, fs->geom->length - 2, 1)) + return -1; + /* !!!!!!!!!!!!!!!!! */ + /* This "if" by itself makes the need to rewrite the whole function */ + /* !!!!!!!!!!!!!!!!! */ + if (new_start > old_start) { + if (pass2) return -1; + pass2 = 1; src_fblock = &new_start; + if (from) goto cat_file_found; else goto ext_file_found; + } + return 1; +} + +/* This function moves an extent starting at block fblock to block to_fblock + if there's enough room */ +/* Return 1 if everything was fine */ +/* Return -1 if an error occured */ +/* Return 0 if no extent was found */ +static int +hfs_move_extent_starting_at (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock) +{ + int ret; + + /* order = decreasing probability to be found */ + if ((ret = hfs_search_move_catalog (fs, ptr_fblock, ptr_to_fblock)) + || (ret = hfs_search_move_extent (fs, ptr_fblock, ptr_to_fblock)) + || (ret = hfs_search_move_primary (fs, ptr_fblock, ptr_to_fblock))) { + return ret; + } + + return 0; +} + +/* This function moves file's data to compact used and free space, + starting at fblock block */ +/* return 0 on error */ +static int +hfs_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock, + PedTimer* timer, unsigned int to_free) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsMasterDirectoryBlock* mdb = priv_data->mdb; + unsigned int to_fblock = fblock; + unsigned int start = fblock; + unsigned int div = PED_BE16_TO_CPU (mdb->total_blocks) + + 1 - start - to_free; + int byte, bit, ret; + int to_byte, to_bit; + + to_byte = byte = fblock / 8; + to_bit = bit = 7 - (fblock & 7); + + if (!hfs_read_bad_blocks (fs)) + return 0; + + while (fblock < PED_BE16_TO_CPU (mdb->total_blocks)) { + if (((priv_data->alloc_map[byte] >> bit) & 1) + && (!hfs_is_bad_block (fs, fblock))) { + if (!(ret = hfs_move_extent_starting_at (fs, &fblock, + &to_fblock))) + to_fblock = ++fblock; + else if (ret == -1) + return 0; + } else { + fblock++; + } + + byte = fblock / 8; + bit = 7 - (fblock & 7); + + to_byte = to_fblock / 8; + to_bit = 7 - (to_fblock & 7); + + ped_timer_update(timer, (float)(to_fblock - start)/div); + } + + return 1; +} + +/* This function returns the first sector of the last free block of an + HFS volume we can get after a hfs_pack_free_space_from_block call */ +static PedSector +hfs_get_empty_end (const PedFileSystem *fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsMasterDirectoryBlock* mdb = priv_data->mdb; + HfsPrivateLinkExtent* link; + unsigned int block, last_bad, end_free_blocks; + int byte, bit; + + /* find the next block to the last bad block of the volume */ + if (!hfs_read_bad_blocks (fs)) return 0; + + last_bad = 0; + for (link = priv_data->bad_blocks_xtent_list; link; link = link->next) { + if ((unsigned int) PED_BE16_TO_CPU (link->extent.start_block) + + PED_BE16_TO_CPU (link->extent.block_count) > last_bad) + last_bad = PED_BE16_TO_CPU (link->extent.start_block) + + PED_BE16_TO_CPU (link->extent.block_count); + } + + /* Count the free blocks from last_bad to the end of the volume */ + end_free_blocks = 0; + for (block = last_bad; + block < PED_BE16_TO_CPU (mdb->total_blocks); + block++) { + byte = block / 8; + bit = 7 - (block & 7); + if (!((priv_data->alloc_map[byte]>>bit)&1)) + end_free_blocks++; + } + + /* Calculate the block that will by the first free at the + end of the volume */ + block = PED_BE16_TO_CPU (mdb->total_blocks) - end_free_blocks; + + return (PedSector) PED_BE16_TO_CPU (mdb->start_block) + + (PedSector) block * (PED_BE32_TO_CPU (mdb->block_size) + / PED_SECTOR_SIZE); +} + +/* return the block which should be used to pack data to have at + least free fblock blocks at the end of the volume */ +static unsigned int +hfs_find_start_pack (const PedFileSystem *fs, unsigned int fblock) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + unsigned int block; + + for (block = PED_BE16_TO_CPU (priv_data->mdb->total_blocks) - 1; + block && fblock; + block--) { + if (!((priv_data->alloc_map[block / 8] + >> (7 - (block & 7))) & 1)) + fblock--; + } + + while (block && !((priv_data->alloc_map[block / 8] + >> (7 - (block & 7))) & 1)) + block--; + if ((priv_data->alloc_map[block / 8] >> (7 - (block & 7))) & 1) + block++; + + return block; +} + +static PedFileSystem* +hfs_open (PedGeometry* geom) +{ + uint8_t buf[PED_SECTOR_SIZE]; + PedFileSystem* fs; + HfsMasterDirectoryBlock* mdb; + HfsPrivateFSData* priv_data; + + /* Read MDB */ if (!ped_geometry_read (geom, buf, 2, 1)) + return NULL; + + /* Allocate memory */ + fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); + mdb = (HfsMasterDirectoryBlock*) ped_malloc ( + sizeof (HfsMasterDirectoryBlock)); + priv_data = (HfsPrivateFSData*) ped_malloc ( + sizeof (HfsPrivateFSData)); + + memcpy (mdb, buf, sizeof (HfsMasterDirectoryBlock)); + + /* init structures */ + priv_data->mdb = mdb; + priv_data->bad_blocks_loaded = 0; + priv_data->bad_blocks_xtent_nb = 0; + priv_data->bad_blocks_xtent_list = NULL; + priv_data->extent_file = + hfs_file_open (fs, PED_CPU_TO_BE32 (HFS_XTENT_ID), + mdb->extents_file_rec, + PED_CPU_TO_BE32 (mdb->extents_file_size) + / PED_SECTOR_SIZE); + priv_data->catalog_file = + hfs_file_open (fs, PED_CPU_TO_BE32 (HFS_CATALOG_ID), + mdb->catalog_file_rec, + PED_CPU_TO_BE32 (mdb->catalog_file_size) + / PED_SECTOR_SIZE); + + /* Read allocation blocks */ + if (!ped_geometry_read(geom, priv_data->alloc_map, + PED_BE16_TO_CPU (mdb->volume_bitmap_block), + ( PED_BE16_TO_CPU (mdb->total_blocks) + + PED_SECTOR_SIZE * 8 - 1 ) + / (PED_SECTOR_SIZE * 8) ) ) { + hfs_file_close (priv_data->extent_file); + hfs_file_close (priv_data->catalog_file); + ped_free(fs); ped_free(mdb); ped_free(priv_data); + return NULL; + } + + fs->type = &hfs_type; + fs->geom = ped_geometry_duplicate (geom); + fs->type_specific = (void*) priv_data; + fs->checked = ( PED_BE16_TO_CPU (mdb->volume_attributes) + >> HFS_UNMOUNTED ) & 1; + + return fs; +} + +static int +hfs_close (PedFileSystem *fs) +{ + hfs_file_close (((HfsPrivateFSData*)(fs->type_specific))->extent_file); + hfs_file_close (((HfsPrivateFSData*)(fs->type_specific))->catalog_file); + if (((HfsPrivateFSData*)(fs->type_specific))->bad_blocks_loaded) + hfs_free_bad_blocks_list ( + ((HfsPrivateFSData*)(fs->type_specific)) + ->bad_blocks_xtent_list); + ped_free (((HfsPrivateFSData*)(fs->type_specific))->mdb); + ped_free (fs->type_specific); + ped_geometry_destroy (fs->geom); + ped_free (fs); + + return 1; +} + +static PedConstraint* +hfs_get_resize_constraint (const PedFileSystem *fs) +{ + PedDevice* dev = fs->geom->dev; + PedAlignment start_align; + PedGeometry start_sector; + PedGeometry full_dev; + PedSector min_size; + + if (!ped_alignment_init (&start_align, fs->geom->start, 0)) + return NULL; + if (!ped_geometry_init (&start_sector, dev, fs->geom->start, 1)) + return NULL; + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + /* 2 = last two sectors (alternate MDB and unused sector) */ + min_size = hfs_get_empty_end(fs) + 2; + + return ped_constraint_new (&start_align, ped_alignment_any, + &start_sector, &full_dev, min_size, + fs->geom->length); +} + +static int +hfs_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + uint8_t buf[PED_SECTOR_SIZE]; + unsigned int nblock, nfree; + unsigned int block, to_free; + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsMasterDirectoryBlock* mdb = priv_data->mdb; + int resize = 1; + unsigned int hfs_sect_block = ( PED_BE32_TO_CPU ( + mdb->block_size) + / PED_SECTOR_SIZE ); + + + /* check preconditions */ + PED_ASSERT (fs->geom->dev == geom->dev, return 0); + + if (fs->geom->start != geom->start) + { + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Sorry, can't move the start of hfs partitions yet!")); + return 0; + } + + if (geom->length > fs->geom->length + || geom->length < hfs_get_empty_end(fs) + 2) + return 0; + + /* Clear the unmounted bit */ + mdb->volume_attributes &= PED_CPU_TO_BE16 (~( 1 << HFS_UNMOUNTED )); + if (!ped_geometry_read (fs->geom, buf, 2, 1)) + return 0; + memcpy (buf, mdb, sizeof (HfsMasterDirectoryBlock)); + if (!ped_geometry_write (fs->geom, buf, 2, 1)) return 0; - if (PED_BE16_TO_CPU (*(uint16_t*) buf) == HFS_SIGNATURE) - return ped_geometry_duplicate (geom); - else + ped_timer_reset (timer); + ped_timer_set_state_name(timer, _("shrinking")); + ped_timer_update(timer, 0.0); + /* relocate data */ + block = hfs_find_start_pack (fs, to_free = + ( fs->geom->length - geom->length + + hfs_sect_block - 1 ) + / hfs_sect_block ); + if (!hfs_pack_free_space_from_block (fs, block, timer, to_free)) { + resize = 0; + goto write_MDB; + } + + /* Calculate new block number and other MDB field */ + nblock = ( geom->length - (PED_BE16_TO_CPU (mdb->start_block) + 2) ) + / hfs_sect_block; + nfree = PED_BE16_TO_CPU (mdb->free_blocks) + - ( PED_BE16_TO_CPU (mdb->total_blocks) - nblock ); + + /* Check that all block after future end are really free */ + for (block = nblock; + block < PED_BE16_TO_CPU (mdb->total_blocks); + block++) { + int byte, bit; + byte = block / 8; + bit = 7 - (block & 7); + if ((priv_data->alloc_map[byte]>>bit)&1) { + resize = 0; + goto write_MDB; + } + } + + /* Update geometry */ + if (resize) { + /* update in fs structure */ + if (PED_BE16_TO_CPU (mdb->next_allocation) >= nblock) + mdb->next_allocation = PED_CPU_TO_BE16 (0); + mdb->total_blocks = PED_CPU_TO_BE16 (nblock); + mdb->free_blocks = PED_CPU_TO_BE16 (nfree); + /* update parted structure */ + fs->geom->length = geom->length; + fs->geom->end = fs->geom->start + geom->length - 1; + } + + /* Set the unmounted bit */ + mdb->volume_attributes |= PED_CPU_TO_BE16 ( 1 << HFS_UNMOUNTED ); + + /* Effective write */ + write_MDB: + ped_timer_set_state_name(timer,_("writing HFS Master Directory Block")); + if (!ped_geometry_read (fs->geom, buf, 2, 1)) + return 0; + memcpy (buf, mdb, sizeof (HfsMasterDirectoryBlock)); + if (!ped_geometry_write (fs->geom, buf, 2, 1)) + return 0; + if (!ped_geometry_write (fs->geom, buf, fs->geom->length - 2, 1)) + return 0; + ped_timer_update(timer, 1.0); + + return (resize); +} + +/* ----- HFS+ ----- */ + +/* Open the data fork of a file with its first eight extents and its CNID */ +static HfsPPrivateFile* +hfsplus_file_open (PedFileSystem *fs, HfsPNodeID CNID, + HfsPExtDataRec ext_desc, PedSector sect_nb) +{ + HfsPPrivateFile* file; + + file = (HfsPPrivateFile*) ped_malloc (sizeof (HfsPPrivateFile)); + + file->fs = fs; + file->sect_nb = sect_nb; + file->CNID = CNID; + memcpy(file->first, ext_desc, sizeof (HfsPExtDataRec)); + memcpy(file->cache, ext_desc, sizeof (HfsPExtDataRec)); + file->start_cache = 0; + + return file; +} + +/* Close an HFS+ file */ +static void +hfsplus_file_close (HfsPPrivateFile* file) +{ + ped_free (file); +} + +/* declaration of hfsplus_file_read(_sector) + because it's used by hfsplus_btree_search (indirect recursion) */ +static int +hfsplus_file_read_sector (HfsPPrivateFile* file, void *buf, PedSector sector); +static int +hfsplus_file_read(HfsPPrivateFile* file, void *buf, + PedSector sector, unsigned int nb); + +/* do a B-Tree lookup */ +/* read the first record immediatly inferior or egal to the given key */ +/* return 0 on error */ +/* record_out _must_ be large enough to receive the whole record (key + data) */ +static int +hfsplus_btree_search (HfsPPrivateFile* b_tree_file, HfsPPrivateGenericKey* key, + void *record_out, unsigned int record_size, + HfsCPrivateLeafRec* record_ref) +{ + uint8_t node_1[PED_SECTOR_SIZE]; + uint8_t* node; + HfsPHeaderRecord* header; + HfsPNodeDescriptor* desc = (HfsPNodeDescriptor*) node_1; + HfsPPrivateGenericKey* record_key = NULL; + unsigned int node_number, record_number, size, bsize; + int i; + + /* Read the header node */ + if (!hfsplus_file_read_sector(b_tree_file, node_1, 0)) + return 0; + header = (HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC); + + /* Get the node number of the root */ + node_number = PED_BE32_TO_CPU (header->root_node); + + /* Get the size of a node in sectors and allocate buffer */ + size = (bsize = PED_BE16_TO_CPU (header->node_size)) / PED_SECTOR_SIZE; + node = (uint8_t*) ped_malloc (bsize); + desc = (HfsPNodeDescriptor*) node; + + /* Read the root node */ + if (!hfsplus_file_read (b_tree_file, node, + (PedSector) node_number * size, size)) + return 0; + + /* Follow the white rabbit */ + while (1) { + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = record_number; i; i--) { + int cmp, min_length; + + record_key = (HfsPPrivateGenericKey*) + (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(bsize - 2*i))))); + /* check for obvious error in FS */ + if (((uint8_t*)record_key - node < HFS_FIRST_REC) + || ((uint8_t*)record_key - node + >= (signed)bsize + - 2 * (signed)(record_number+1))) { + ped_free (node); + return 0; + } + min_length = ((PED_BE16_TO_CPU(key->key_length) + >PED_BE16_TO_CPU(record_key->key_length))? + PED_BE16_TO_CPU(record_key->key_length) : + PED_BE16_TO_CPU(key->key_length) ); + cmp = memcmp (record_key->key_content, key->key_content, + min_length); + if (cmp < 0 || ( cmp == 0 && PED_BE16_TO_CPU ( + record_key->key_length) + <= PED_BE16_TO_CPU ( + key->key_length))) + break; + } + if (!i) { ped_free (node); return 0; } + if (desc->type == HFS_IDX_NODE) { + unsigned int skip; + + skip = ( 2 + PED_BE16_TO_CPU (record_key->key_length) + + 1 ) & ~1; + node_number = PED_BE32_TO_CPU (*((uint32_t *) + (((uint8_t *) record_key) + skip))); + if (!hfsplus_file_read(b_tree_file, node, + (PedSector) node_number * size, + size)) { + ped_free (node); + return 0; + } + } else + break; + } + + /* copy the result if needed */ + if (record_size) + memcpy (record_out, record_key, record_size); + + /* send record reference if needed */ + if (record_ref) { + record_ref->node_size = size; /* in sectors */ + record_ref->node_number = node_number; + record_ref->record_pos = (uint8_t*)record_key - node; + record_ref->record_number = i; + } + + /* success */ + ped_free (node); + return 1; +} + +static int +hfsplus_get_extent_containing (HfsPPrivateFile* file, unsigned int block, + HfsPExtDataRec cache, uint32_t* ptr_start_cache) +{ + uint8_t record[sizeof (HfsPExtentKey) + + sizeof (HfsPExtDataRec)]; + HfsPExtentKey search; + HfsPExtentKey* ret_key = (HfsPExtentKey*) record; + HfsPExtDescriptor* ret_cache = (HfsPExtDescriptor*) + (record + sizeof (HfsPExtentKey)); + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + file->fs->type_specific; + + + search.key_length = PED_CPU_TO_BE16 (sizeof (HfsPExtentKey) - 2); + search.type = HFS_DATA_FORK; + search.pad = 0; + search.file_ID = file->CNID; + search.start = PED_CPU_TO_BE32 (block); + + if (!hfsplus_btree_search (priv_data->extents_file, + (HfsPPrivateGenericKey*) &search, + record, sizeof (record), NULL)) + return 0; + + if (ret_key->file_ID != search.file_ID || ret_key->type != search.type) + return 0; + + memcpy (cache, ret_cache, sizeof(HfsPExtDataRec)); + *ptr_start_cache = PED_BE32_TO_CPU (ret_key->start); + + return 1; +} + +/* find the nth sector of a file */ +/* return 0 on error */ +static PedSector +hfsplus_file_find_sector (HfsPPrivateFile* file, PedSector sector) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + file->fs->type_specific; + HfsPVolumeHeader* vh = priv_data-> vh; + unsigned int i, s, vol_block; + unsigned int block = sector + / ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE ); + unsigned int offset = sector + % ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE ); + + /* in the three first extent */ + for (s = 0, i = 0; + i < HFSP_EXT_NB; + i++, s += PED_BE32_TO_CPU (file->first[i].block_count)) { + if ((block >= s) && (block < s + PED_BE32_TO_CPU ( + file->first[i].block_count))) { + vol_block = (block - s) + + PED_BE32_TO_CPU (file->first[i] + .start_block); + goto plus_sector_found; + } + } + + /* in the three cached extent */ + for (s = file->start_cache, i = 0; + i < HFSP_EXT_NB; + i++, s += PED_BE32_TO_CPU (file->cache[i].block_count)) { + if ((block >= s) && (block < s + PED_BE32_TO_CPU ( + file->cache[i].block_count))) { + vol_block = (block - s) + + PED_BE32_TO_CPU (file->cache[i] + .start_block); + goto plus_sector_found; + } + } + + /* update cache */ + if (!hfsplus_get_extent_containing (file, block, file->cache, + &(file->start_cache))) + return 0; + + /* in the three cached extent */ + for (s = file->start_cache, i = 0; + i < HFSP_EXT_NB; + i++, s += PED_BE32_TO_CPU (file->cache[i].block_count)) { + if ((block >= s) && (block < s + PED_BE32_TO_CPU ( + file->cache[i].block_count))) { + vol_block = (block - s) + + PED_BE32_TO_CPU (file->cache[i] + .start_block); + goto plus_sector_found; + } + } + + return 0; + + plus_sector_found: + return (PedSector) vol_block * ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE ) + offset; +} + +/* Read the nth sector of a file */ +/* return 0 on error */ +static int +hfsplus_file_read_sector (HfsPPrivateFile* file, void *buf, PedSector sector) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + file->fs->type_specific; + PedSector abs_sector; + + if (sector >= file->sect_nb) + return 0; + + abs_sector = hfsplus_file_find_sector (file, sector); + return abs_sector && ped_geometry_read (priv_data->plus_geom, buf, + abs_sector, 1); +} + +static int +hfsplus_file_read(HfsPPrivateFile* file, void *buf, PedSector sector, + unsigned int nb) +{ + while (nb) { + if (!hfsplus_file_read_sector (file, buf, sector)) + return 0; + + buf += PED_SECTOR_SIZE; + nb--; + sector++; + } + + return 1; +} + +/* Write the nth sector of a file */ +/* return 0 on error */ +static int +hfsplus_file_write_sector (HfsPPrivateFile* file, void *buf, PedSector sector) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + file->fs->type_specific; + PedSector abs_sector; + + if (sector >= file->sect_nb) + return 0; + + abs_sector = hfsplus_file_find_sector (file, sector); + return abs_sector && ped_geometry_write (priv_data->plus_geom, buf, + abs_sector, 1); +} + +static int +hfsplus_file_write (HfsPPrivateFile* file, void *buf, PedSector sector, + unsigned int nb) +{ + while (nb) { + if (!hfsplus_file_write_sector (file, buf, sector)) + return 0; + + buf += PED_SECTOR_SIZE; + nb--; + sector++; + } + + return 1; +} + +/* This function reads bad blocks extents in the extents file + and store it in f.s. specific data of fs */ +static int +hfsplus_read_bad_blocks (const PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + + if (!priv_data->bad_blocks_loaded) { + uint8_t record[sizeof (HfsPExtentKey) + + sizeof (HfsPExtDataRec)]; + HfsPExtentKey search; + HfsPExtentKey* ret_key = (HfsPExtentKey*) record; + HfsPExtDescriptor* ret_cache = (HfsPExtDescriptor*) + (record + sizeof (HfsPExtentKey)); + int block, first_pass = 1; + unsigned int last_start; + + search.key_length = sizeof (HfsExtentKey) - 2; + search.type = HFS_DATA_FORK; + search.pad = 0; + search.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID); + + last_start = -1; block = 0; + while (1) { + int i; + + search.start = PED_CPU_TO_BE32 (block); + if (!hfsplus_btree_search (priv_data->extents_file, + (HfsPPrivateGenericKey*) + &search, + record, sizeof (record), + NULL)) { + if (first_pass) + break; + else + return 0; + } + if (ret_key->file_ID != search.file_ID + || ret_key->type != search.type) + break; + if (PED_BE32_TO_CPU (ret_key->start) == last_start) + break; + + last_start = PED_BE32_TO_CPU (ret_key->start); + for (i = 0; i < HFSP_EXT_NB; i++) { + if (ret_cache[i].block_count) { + HfsPPrivateLinkExtent* new_xt = + (HfsPPrivateLinkExtent*) ped_malloc ( + sizeof (HfsPPrivateLinkExtent)); + new_xt->next = + priv_data->bad_blocks_xtent_list; + memcpy (&(new_xt->extent), ret_cache+i, + sizeof (HfsPExtDescriptor)); + priv_data->bad_blocks_xtent_list = + new_xt; + priv_data->bad_blocks_xtent_nb++; + block += PED_BE32_TO_CPU (ret_cache[i] + .block_count); + } + } + first_pass = 0; + } + + priv_data->bad_blocks_loaded = 1; + } + + return 1; +} + +/* free the bad blocks linked list */ +static void +hfsplus_free_bad_blocks_list(HfsPPrivateLinkExtent* first) +{ + HfsPPrivateLinkExtent* next; + + while (first) { + next = first->next; + ped_free (first); + first = next; + } +} + +/* This function check if fblock is a bad block */ +static int +hfsplus_is_bad_block (const PedFileSystem *fs, unsigned int fblock) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPPrivateLinkExtent* walk; + + for (walk = priv_data->bad_blocks_xtent_list; walk; walk = walk->next) { + /* Won't compile without the strange cast ! gcc bug ? */ + /* or maybe C subtilties... */ + if ((fblock >= PED_BE32_TO_CPU (walk->extent.start_block)) && + (fblock < (unsigned int)(PED_BE32_TO_CPU ( + walk->extent.start_block) + + PED_BE32_TO_CPU (walk->extent.block_count)))) + return 1; + } + + return 0; +} + +static int +hfsplus_close (PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + + if (priv_data->wrapper) hfs_close(priv_data->wrapper); + if (priv_data->free_geom) ped_geometry_destroy (priv_data->plus_geom); + ped_free(priv_data->alloc_map); + hfsplus_file_close (priv_data->extents_file); + hfsplus_file_close (priv_data->catalog_file); + hfsplus_file_close (priv_data->attributes_file); + if (priv_data->bad_blocks_loaded) + hfsplus_free_bad_blocks_list(priv_data->bad_blocks_xtent_list); + ped_free (priv_data->vh); + ped_free (priv_data); + ped_geometry_destroy (fs->geom); + ped_free (fs); + + return 1; +} + +static PedFileSystem* +hfsplus_open (PedGeometry* geom) +{ + uint8_t buf[PED_SECTOR_SIZE]; + PedFileSystem* fs; + HfsPVolumeHeader* vh; + HfsPPrivateFSData* priv_data; + PedGeometry* wrapper_geom; + HfsPPrivateFile* allocation_file; + unsigned int map_sectors; + + fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); + vh = (HfsPVolumeHeader*) ped_malloc (sizeof (HfsPVolumeHeader)); + priv_data = (HfsPPrivateFSData*)ped_malloc (sizeof (HfsPPrivateFSData)); + + fs->geom = ped_geometry_duplicate (geom); + fs->type_specific = (void*) priv_data; + + if ((wrapper_geom = hfs_and_wrapper_probe (geom))) { + HfsPrivateFSData* hfs_priv_data; + PedSector abs_sect, length; + unsigned int bs; + + ped_geometry_destroy (wrapper_geom); + priv_data->wrapper = hfs_open(geom); + hfs_priv_data = (HfsPrivateFSData*) + priv_data->wrapper->type_specific; + bs = PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size) + / PED_SECTOR_SIZE; + abs_sect = (PedSector) geom->start + + (PedSector) PED_BE16_TO_CPU ( + hfs_priv_data->mdb->start_block) + + (PedSector) PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new + .embedded.location.start_block ) + * bs; + length = (PedSector) PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new + .embedded.location.block_count) + * bs; + priv_data->plus_geom = ped_geometry_new (geom->dev, abs_sect, + length); + priv_data->free_geom = 1; + } else { + priv_data->wrapper = NULL; + priv_data->plus_geom = fs->geom; + priv_data->free_geom = 0; + } + + if (!ped_geometry_read (priv_data->plus_geom, buf, 2, 1)) { + if (priv_data->wrapper) + hfs_close(priv_data->wrapper); + if (priv_data->free_geom) + ped_geometry_destroy (priv_data->plus_geom); + ped_free (vh); + ped_free (priv_data); + ped_geometry_destroy (fs->geom); + ped_free (fs); + return NULL; + } + + memcpy (vh, buf, sizeof (HfsPVolumeHeader)); + + priv_data->vh = vh; + priv_data->bad_blocks_loaded = 0; + priv_data->bad_blocks_xtent_nb = 0; + priv_data->bad_blocks_xtent_list = NULL; + priv_data->extents_file = + hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFS_XTENT_ID), + vh->extents_file.extents, + PED_BE64_TO_CPU ( + vh->extents_file.logical_size ) + / PED_SECTOR_SIZE); + priv_data->catalog_file = + hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFS_CATALOG_ID), + vh->catalog_file.extents, + PED_BE64_TO_CPU ( + vh->catalog_file.logical_size ) + / PED_SECTOR_SIZE); + priv_data->attributes_file = + hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFSP_ATTRIB_ID), + vh->attributes_file.extents, + PED_BE64_TO_CPU ( + vh->attributes_file.logical_size) + / PED_SECTOR_SIZE); + map_sectors = ( PED_BE32_TO_CPU (vh->total_blocks) + + PED_SECTOR_SIZE * 8 - 1 ) / (PED_SECTOR_SIZE * 8); + priv_data->alloc_map = (uint8_t*) + ped_malloc (map_sectors * PED_SECTOR_SIZE); + + allocation_file = + hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFSP_ALLOC_ID), + vh->allocation_file.extents, + PED_BE64_TO_CPU ( + vh->allocation_file.logical_size) + / PED_SECTOR_SIZE); + if (!hfsplus_file_read (allocation_file, priv_data->alloc_map, 0, + map_sectors)) { + hfsplus_file_close (allocation_file); + hfsplus_close(fs); return NULL; + } + hfsplus_file_close (allocation_file); + + fs->type = &hfsplus_type; + fs->checked = ((PED_BE32_TO_CPU (vh->attributes) >> HFS_UNMOUNTED) & 1) + && !((PED_BE32_TO_CPU (vh->attributes) >> HFSP_INCONSISTENT) & 1); + + return fs; } -#ifndef DISCOVER_ONLY +/* This function moves data of size blocks starting at block *ptr_fblock + to block *ptr_to_fblock */ +/* return new start or -1 on failure */ static int -hfs_clobber (PedGeometry* geom) +hfsplus_effect_move_extent (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock, unsigned int size) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + uint8_t* block; + unsigned int i; + unsigned int new_start; + unsigned int next_to_fblock = 0; + unsigned int start = 0, stop = 0; + int ok = 0; + +#if 0 + + /* try to find enough room to fit the extent */ + /* TODO : without overwriting some data */ + for (i = *ptr_to_fblock; + i < *ptr_to_fblock + size && i < *ptr_fblock; + i++) { + if (hfsplus_is_bad_block (fs, i)) + *ptr_to_fblock = i+1; + } + +#else + + /* try to find enough room to fit the extent in a non overlapping way */ + + /* special case of next if, keep it only if it increases speed in a + significant way */ + if (*ptr_to_fblock + size <= *ptr_fblock) { + ok = 1; + for (i = *ptr_to_fblock; ok && i < *ptr_to_fblock; i++) + if ((priv_data->alloc_map[i/8] & (1<<(7-(i&7))))) ok = 0; + next_to_fblock = *ptr_to_fblock + size; + start = *ptr_to_fblock; + } + + /* back search */ + if (!ok && *ptr_fblock != *ptr_to_fblock) { + start = stop = *ptr_fblock; + while (start && stop-start != size) { + --start; + if ((priv_data->alloc_map[start/8] & (1<<(7-(start&7))))) + stop = start; + } + if (stop-start == size) { + if (stop > *ptr_to_fblock) + next_to_fblock = stop; + else + next_to_fblock = *ptr_to_fblock; + ok = 1; + } else ok = 0; + } + + /* forward search for 2 pass relocation */ + if (!ok && *ptr_fblock != *ptr_to_fblock) { + start = stop = *ptr_fblock; + while (stop < PED_BE32_TO_CPU(priv_data->vh->total_blocks) + && stop-start != size) { + if ((priv_data->alloc_map[stop/8] & (1<<(7-(stop&7))))) + start = stop + 1; + ++stop; + } + ok = (stop-start == size); + next_to_fblock = *ptr_to_fblock; + } + +#endif + + if (ok) { + /* enough room */ + unsigned int j; + + block = (uint8_t*) ped_malloc(PED_BE32_TO_CPU ( + priv_data->vh->block_size) + *((sizevh->block_size) + / PED_SECTOR_SIZE ); + if (!ped_geometry_read (priv_data->plus_geom, block, + abs_sector, + PED_BE32_TO_CPU ( + priv_data->vh->block_size ) + / PED_SECTOR_SIZE * j)) + return -1; + + abs_sector = (PedSector) (new_start + i) + * ( PED_BE32_TO_CPU (priv_data->vh->block_size) + / PED_SECTOR_SIZE ); + if (!ped_geometry_write (priv_data->plus_geom, block, + abs_sector, + PED_BE32_TO_CPU ( + priv_data->vh->block_size ) + / PED_SECTOR_SIZE * j)) + return -1; + + for (ai = i+j; i < ai; i++) { + bit = 7 - ((*ptr_fblock + i) & 7); + byte = (*ptr_fblock + i) / 8; + + to_bit = 7 - ((new_start + i) & 7); + to_byte = (new_start + i) / 8; + + /* free source block */ + priv_data->alloc_map[byte] &= ~(1 << bit); + + /* set dest block */ + priv_data->alloc_map[to_byte] |= (1 << to_bit); + } + } + +/* TODO : a better handling of allocation map saving process */ +#if 0 + /* save the allocation map */ + if (!ped_geometry_write(fs->geom, priv_data->alloc_map, PED_BE16_TO_CPU (priv_data->mdb->volume_bitmap_block), + (PED_BE16_TO_CPU (priv_data->mdb->total_blocks) + PED_SECTOR_SIZE * 8 - 1) / (PED_SECTOR_SIZE * 8))) + return -1; +#endif + + ped_free (block); + + *ptr_fblock += size; + *ptr_to_fblock = next_to_fblock; + } else { + /* not enough room */ + new_start = *ptr_fblock; + *ptr_fblock = *ptr_to_fblock = new_start + size; + } + + return new_start; +} + +/* Search an extent in the catalog file and move it if found */ +/* Return 1 if everything was fine */ +/* Return -1 if an error occured */ +/* Return 0 if no extent was found */ +static int +hfsplus_search_move_catalog (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + uint8_t node_1[PED_SECTOR_SIZE]; + uint8_t* node; + HfsPHeaderRecord* header; + HfsPNodeDescriptor* desc = (HfsPNodeDescriptor*) node_1; + HfsPCatalogKey* catalog_key; + HfsPCatalog* catalog_data; + unsigned int leaf_node, record_number; + unsigned int i, j, size, bsize; + unsigned int* src_fblock = ptr_fblock; + int new_start, pass2 = 0; + + /* Search the extent starting at *ptr_block in the catalog file */ + if (!hfsplus_file_read_sector (priv_data->catalog_file, node_1, 0)) + return -1; + header = ((HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC)); + leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + bsize = PED_BE16_TO_CPU (header->node_size); + size = bsize / PED_SECTOR_SIZE; + + node = (uint8_t*) ped_malloc (bsize); + desc = (HfsPNodeDescriptor*) node; + + for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfsplus_file_read (priv_data->catalog_file, node, + (PedSector) leaf_node * size, size)) { + ped_free (node); + return -1; + } + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; i++) { + unsigned int skip; + catalog_key = (HfsPCatalogKey*) + ( node + PED_BE16_TO_CPU (*((uint16_t *) + (node+(bsize - 2*i)))) ); + skip = ( 2 + PED_BE16_TO_CPU (catalog_key->key_length) + + 1) & ~1; + catalog_data = (HfsPCatalog*) + (((uint8_t*)catalog_key) + skip); + /* check for obvious error in FS */ + if (((uint8_t*)catalog_key - node < HFS_FIRST_REC) + || ((uint8_t*)catalog_data - node + >= (signed) bsize + - 2 * (signed)(record_number+1))) { + ped_free (node); + return -1; + } + if (PED_BE16_TO_CPU(catalog_data->type)!=HFS_CAT_FILE) + continue; + for (j = 0; j < HFSP_EXT_NB; j++) { + if ( catalog_data->sel.file + .data_fork.extents[j].block_count + && PED_BE32_TO_CPU (catalog_data->sel + .file.data_fork.extents[j].start_block) + == (*ptr_fblock) ) + goto plus_catalog_data_found; + if ( catalog_data->sel.file.res_fork + .extents[j].block_count + && PED_BE32_TO_CPU (catalog_data->sel + .file.res_fork.extents[j].start_block) + == (*ptr_fblock) ) + goto plus_catalog_res_found; + } + } + } + /* no extent starting a *ptr_block has been found in the catalog file */ + ped_free (node); + return 0; + + + /* an extent part of a data fork has been found in the catalog file */ + plus_catalog_data_found: + new_start = hfsplus_effect_move_extent (fs, src_fblock, ptr_to_fblock, + PED_BE32_TO_CPU ( + catalog_data->sel.file + .data_fork.extents[j] + .block_count )); + if (new_start != -1) { + unsigned int old_start; + old_start = PED_BE32_TO_CPU (catalog_data->sel.file.data_fork + .extents[j].start_block); + catalog_data->sel.file.data_fork.extents[j].start_block = + PED_CPU_TO_BE32 (new_start); + /* write if any changes */ + if ((old_start != (unsigned int) new_start) + && !hfsplus_file_write (priv_data->catalog_file, node, + (PedSector) leaf_node * size, + size)) { + ped_free (node); + return -1; + } + /* !!!!!!!!!!!!!!!!! */ + /* This "if" by itself makes the need to rewrite the whole function */ + /* !!!!!!!!!!!!!!!!! */ + if ((unsigned) new_start > old_start) { + if (pass2) return -1; + pass2 = 1; src_fblock = &new_start; + goto plus_catalog_data_found; + } + ped_free (node); + return 1; + } else { + ped_free (node); + return -1; + } + + /* an extent part of a resource fork has been found + in the catalog file */ + plus_catalog_res_found: + new_start = hfsplus_effect_move_extent (fs, src_fblock, ptr_to_fblock, + PED_BE32_TO_CPU ( + catalog_data->sel.file + .res_fork.extents[j] + .block_count )); + if (new_start != -1) { + unsigned int old_start; + old_start = PED_BE32_TO_CPU (catalog_data->sel.file.res_fork + .extents[j].start_block); + catalog_data->sel.file.res_fork.extents[j].start_block = + PED_CPU_TO_BE32 (new_start); + /* write if any changes */ + if ((old_start != (unsigned int) new_start) + && !hfsplus_file_write (priv_data->catalog_file, node, + (PedSector) leaf_node * size, + size)) { + ped_free (node); + return -1; + } + /* !!!!!!!!!!!!!!!!! */ + /* This "if" by itself makes the need to rewrite the whole function */ + /* !!!!!!!!!!!!!!!!! */ + if ((unsigned) new_start > old_start) { + if (pass2) return -1; + pass2 = 1; src_fblock = &new_start; + goto plus_catalog_res_found; + } + ped_free (node); + return 1; + } else { + ped_free (node); + return -1; + } +} + +/* Search an extent in the attributes file and move it if found */ +/* Return 1 if everything was fine */ +/* Return -1 if an error occured */ +/* Return 0 if no extent was found */ +static int +hfsplus_search_move_attributes (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + uint8_t node_1[PED_SECTOR_SIZE]; + uint8_t* node; + HfsPHeaderRecord* header; + HfsPNodeDescriptor* desc = (HfsPNodeDescriptor*) node_1; + HfsPPrivateGenericKey* generic_key; + HfsPForkDataAttr* fork_ext_data; + unsigned int leaf_node, record_number; + unsigned int i, j, size, bsize; + unsigned int* src_fblock = ptr_fblock; + int new_start, pass2 = 0; + + /* attributes file is facultative */ + if (!priv_data->attributes_file->sect_nb) + return 0; + + /* Search the extent starting at *ptr_block in the catalog file */ + if (!hfsplus_file_read_sector (priv_data->attributes_file, node_1, 0)) + return -1; + header = ((HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC)); + leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + bsize = PED_BE16_TO_CPU (header->node_size); + size = bsize / PED_SECTOR_SIZE; + + node = (uint8_t*) ped_malloc (bsize); + desc = (HfsPNodeDescriptor*) node; + + for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfsplus_file_read (priv_data->attributes_file, node, + (PedSector) leaf_node * size, size)) { + ped_free (node); + return -1; + } + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; i++) { + unsigned int skip; + generic_key = (HfsPPrivateGenericKey*) + (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(bsize - 2*i))))); + skip = ( 2 + PED_BE16_TO_CPU (generic_key->key_length) + + 1 ) & ~1; + fork_ext_data = (HfsPForkDataAttr*) + (((uint8_t*)generic_key) + skip); + /* check for obvious error in FS */ + if (((uint8_t*)generic_key - node < HFS_FIRST_REC) + || ((uint8_t*)fork_ext_data - node + >= (signed) bsize + - 2 * (signed)(record_number+1))) { + ped_free (node); + return -1; + } + if ( PED_BE32_TO_CPU (fork_ext_data->record_type ) + == HFSP_ATTR_FORK) { + for (j = 0; j < HFSP_EXT_NB; j++) { + if ( fork_ext_data->fork_res.fork + .extents[j].block_count + && PED_BE32_TO_CPU ( + fork_ext_data->fork_res + .fork.extents[j].start_block ) + == (*ptr_fblock) ) + goto plus_attr_fork_found; + } + } else if ( PED_BE32_TO_CPU (fork_ext_data->record_type) + == HFSP_ATTR_EXTENTS ) { + for (j = 0; j < HFSP_EXT_NB; j++) { + if ( fork_ext_data->fork_res + .extents[j].block_count + && PED_BE32_TO_CPU ( + fork_ext_data->fork_res + .extents[j].start_block) + == (*ptr_fblock) ) + goto plus_attr_ext_found; + } + } else continue; + } + } + /* no extent starting a *ptr_block has been found in the catalog file */ + ped_free (node); + return 0; + + + /* an extent part of a attr fork has been found in the catalog file */ + plus_attr_fork_found: + new_start = hfsplus_effect_move_extent (fs, ptr_fblock, ptr_to_fblock, + PED_BE32_TO_CPU ( + fork_ext_data->fork_res + .fork.extents[j] + .block_count)); + if (new_start != -1) { + unsigned int old_start; + old_start = PED_BE32_TO_CPU (fork_ext_data->fork_res.fork. + extents[j].start_block); + fork_ext_data->fork_res.fork.extents[j].start_block = + PED_CPU_TO_BE32 (new_start); + /* write if any changes */ + if ((old_start != (unsigned) new_start) + && !hfsplus_file_write (priv_data->attributes_file, node, + (PedSector) leaf_node * size, + size)) { + ped_free (node); + return -1; + } + /* !!!!!!!!!!!!!!!!! */ + /* This "if" by itself makes the need to rewrite the whole function */ + /* !!!!!!!!!!!!!!!!! */ + if ((unsigned) new_start > old_start) { + if (pass2) return -1; + pass2 = 1; src_fblock = &new_start; + goto plus_attr_fork_found; + } + ped_free (node); + return 1; + } else { + ped_free (node); + return -1; + } + + /* an extent part of a attr ext has been found in the catalog file */ + plus_attr_ext_found: + new_start = hfsplus_effect_move_extent (fs, ptr_fblock, ptr_to_fblock, + PED_BE32_TO_CPU ( + fork_ext_data->fork_res + .extents[j].block_count)); + if (new_start != -1) { + unsigned int old_start; + old_start = PED_BE32_TO_CPU(fork_ext_data->fork_res + .extents[j].start_block); + fork_ext_data->fork_res.extents[j].start_block = + PED_CPU_TO_BE32 (new_start); + /* write if any changes */ + if ((old_start != (unsigned) new_start) + && !hfsplus_file_write (priv_data->attributes_file, node, + (PedSector) leaf_node * size, + size)) { + ped_free (node); + return -1; + } + /* !!!!!!!!!!!!!!!!! */ + /* This "if" by itself makes the need to rewrite the whole function */ + /* !!!!!!!!!!!!!!!!! */ + if ((unsigned) new_start > old_start) { + if (pass2) return -1; + pass2 = 1; src_fblock = &new_start; + goto plus_attr_ext_found; + } + ped_free (node); + return 1; + } else { + ped_free (node); + return -1; + } +} + +/* Search an extent in the extent file and move it if found */ +/* Return 1 if everything was fine */ +/* Return -1 if an error occured */ +/* Return 0 if no extent was found */ +static int +hfsplus_search_move_extent (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock) { - char buf[512]; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + uint8_t node_1[PED_SECTOR_SIZE]; + uint8_t* node; + HfsPHeaderRecord* header; + HfsPNodeDescriptor* desc = (HfsPNodeDescriptor*) node; + HfsPExtentKey* extent_key; + HfsPExtDescriptor* extent_data; + unsigned int leaf_node, record_number; + unsigned int i, j, size, bsize; + unsigned int* src_fblock = ptr_fblock; + int new_start, pass2 = 0; + + /* Search the extent in the extent file */ + if (!hfsplus_file_read_sector (priv_data->extents_file, node_1, 0)) + return -1; + header = ((HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC)); + leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + bsize = PED_BE16_TO_CPU (header->node_size); + size = bsize / PED_SECTOR_SIZE; - memset (buf, 0, 512); - return ped_geometry_write (geom, buf, 2, 1); + node = (uint8_t*) ped_malloc (bsize); + desc = (HfsPNodeDescriptor*) node; + + for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfsplus_file_read (priv_data->extents_file, node, + (PedSector) leaf_node * size, size)) { + ped_free (node); + return -1; + } + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; i++) { + extent_key = (HfsPExtentKey*) + (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(bsize - 2*i))))); + extent_data = (HfsPExtDescriptor*) + (((uint8_t*)extent_key) + sizeof (HfsPExtentKey)); + /* check for obvious error in FS */ + if (((uint8_t*)extent_key - node < HFS_FIRST_REC) + || ((uint8_t*)extent_data - node + >= (signed)bsize + - 2 * (signed)(record_number+1))) { + ped_free (node); + return -1; + } + for (j = 0; j < HFSP_EXT_NB; j++) { + if ( extent_data[j].block_count + && PED_BE32_TO_CPU ( + extent_data[j].start_block) + == (*ptr_fblock) ) + goto plus_extent_found; + } + } + } + /* no extent starting a *ptr_block has been found in the extents file */ + ped_free (node); + return 0; + + /* an extent has been found in the extents file */ + plus_extent_found: + new_start = hfsplus_effect_move_extent (fs, ptr_fblock, ptr_to_fblock, + PED_BE32_TO_CPU ( + extent_data[j] + .block_count)); + if (new_start != -1) { + unsigned int old_start; + old_start = PED_BE32_TO_CPU(extent_data[j].start_block); + extent_data[j].start_block = PED_CPU_TO_BE32 (new_start); + /* This extent might have been cached into the file structure + of the extent or catalog file */ + if (priv_data->catalog_file->cache[j].start_block + == PED_CPU_TO_BE32(old_start)) + memcpy (priv_data->catalog_file->cache, extent_data, + sizeof (HfsPExtDataRec)); + if (priv_data->extents_file->cache[j].start_block + == PED_CPU_TO_BE32(old_start)) + memcpy (priv_data->extents_file->cache, extent_data, + sizeof (HfsPExtDataRec)); + if (priv_data->attributes_file->cache[j].start_block + == PED_CPU_TO_BE32(old_start)) + memcpy (priv_data->attributes_file->cache, + extent_data, sizeof (HfsPExtDataRec)); + /* write if any changes */ + if ((old_start != (unsigned) new_start) + && !hfsplus_file_write (priv_data->extents_file, node, + (PedSector) leaf_node * size, + size)) { + ped_free (node); + return -1; + } + /* !!!!!!!!!!!!!!!!! */ + /* This "if" by itself makes the need to rewrite the whole function */ + /* !!!!!!!!!!!!!!!!! */ + if ((unsigned) new_start > old_start) { + if (pass2) return -1; + pass2 = 1; src_fblock = &new_start; + goto plus_extent_found; + } + ped_free (node); + return 1; + } else { + ped_free (node); + return -1; + } +} + +/* Search an extent in the VH and move it if found */ +/* Return 1 if everything was fine */ +/* Return -1 if an error occured */ +/* Return 0 if no extent was found */ +static int +hfsplus_search_move_primary (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + uint8_t node[PED_SECTOR_SIZE]; + unsigned int j; + unsigned int* src_fblock = ptr_fblock; + int new_start, pass2 = 0; + unsigned int old_start, from; + + /* Search an extent in the VH */ + for (j = 0; j < HFSP_EXT_NB; j++) { + if ( priv_data->vh->extents_file.extents[j].block_count + && PED_BE32_TO_CPU ( + priv_data->vh->extents_file.extents[j].start_block) + == (*ptr_fblock) ) + goto plus_ext_file_found; + if ( priv_data->vh->catalog_file.extents[j].block_count + && PED_BE32_TO_CPU ( + priv_data->vh->catalog_file.extents[j].start_block) + == (*ptr_fblock) ) + goto plus_cat_file_found; + if ( priv_data->vh->allocation_file.extents[j].block_count + && PED_BE32_TO_CPU ( + priv_data->vh->allocation_file.extents[j].start_block) + == (*ptr_fblock) ) + goto plus_alloc_file_found; + if ( priv_data->vh->attributes_file.extents[j].block_count + && PED_BE32_TO_CPU ( + priv_data->vh->attributes_file.extents[j].start_block) + == (*ptr_fblock) ) + goto plus_attrib_file_found; + if ( priv_data->vh->startup_file.extents[j].block_count + && PED_BE32_TO_CPU ( + priv_data->vh->startup_file.extents[j].start_block) + == (*ptr_fblock) ) + goto plus_start_file_found; + } + return 0; + + plus_start_file_found: + new_start = hfsplus_effect_move_extent (fs, ptr_fblock, ptr_to_fblock, + PED_BE32_TO_CPU ( + priv_data->vh->startup_file + .extents[j].block_count)); + if (new_start != -1) { + old_start = PED_BE32_TO_CPU(priv_data->vh->startup_file + .extents[j].start_block); + priv_data->vh->startup_file.extents[j].start_block = + PED_CPU_TO_BE32 (new_start); +#if 0 + memcpy (priv_data->startup_file->first, priv_data->vh->startup_file.extents, sizeof (HfsPExtDataRec)); + if (!priv_data->startup_file->start_cache) + memcpy (priv_data->startup_file->cache, priv_data->startup_file->first, sizeof (HfsPExtDataRec)); +#endif + from = 0; goto plus_update_vh; + } else + return -1; + + plus_attrib_file_found: + new_start = hfsplus_effect_move_extent (fs, ptr_fblock, ptr_to_fblock, + PED_BE32_TO_CPU ( + priv_data->vh + ->attributes_file + .extents[j].block_count)); + if (new_start != -1) { + old_start = PED_BE32_TO_CPU(priv_data->vh->attributes_file + .extents[j].start_block); + priv_data->vh->attributes_file.extents[j].start_block = + PED_CPU_TO_BE32 (new_start); + memcpy (priv_data->attributes_file->first, + priv_data->vh->attributes_file.extents, + sizeof (HfsPExtDataRec)); + if (!priv_data->attributes_file->start_cache) + memcpy (priv_data->attributes_file->cache, + priv_data->attributes_file->first, + sizeof (HfsPExtDataRec)); + from = 1; goto plus_update_vh; + } else + return -1; + + plus_alloc_file_found: + new_start = hfsplus_effect_move_extent (fs, ptr_fblock, ptr_to_fblock, + PED_BE32_TO_CPU ( + priv_data->vh + ->allocation_file + .extents[j].block_count)); + if (new_start != -1) { + old_start = PED_BE32_TO_CPU(priv_data->vh->allocation_file + .extents[j].start_block); + priv_data->vh->allocation_file.extents[j].start_block = + PED_CPU_TO_BE32 (new_start); +#if 0 + memcpy (priv_data->allocation_file->first, priv_data->vh->allocation_file.extents, sizeof (HfsPExtDataRec)); + if (!priv_data->allocation_file->start_cache) + memcpy (priv_data->allocation_file->cache, priv_data->allocation_file->first, sizeof (HfsPExtDataRec)); +#endif + from = 2; goto plus_update_vh; + } else + return -1; + + plus_ext_file_found: + new_start = hfsplus_effect_move_extent (fs, ptr_fblock, ptr_to_fblock, + PED_BE32_TO_CPU ( + priv_data->vh->extents_file + .extents[j].block_count)); + if (new_start != -1) { + old_start = PED_BE32_TO_CPU(priv_data->vh->extents_file + .extents[j].start_block); + priv_data->vh->extents_file.extents[j].start_block = + PED_CPU_TO_BE32 (new_start); + memcpy (priv_data->extents_file->first, + priv_data->vh->extents_file.extents, + sizeof (HfsPExtDataRec)); + if (!priv_data->extents_file->start_cache) + memcpy (priv_data->extents_file->cache, + priv_data->extents_file->first, + sizeof (HfsPExtDataRec)); + from = 3; goto plus_update_vh; + } else + return -1; + + plus_cat_file_found: + new_start = hfsplus_effect_move_extent (fs, ptr_fblock, ptr_to_fblock, + PED_BE32_TO_CPU ( + priv_data->vh->catalog_file + .extents[j].block_count)); + if (new_start != -1) { + old_start = PED_BE32_TO_CPU(priv_data->vh->catalog_file + .extents[j].start_block); + priv_data->vh->catalog_file.extents[j].start_block = + PED_CPU_TO_BE32 (new_start); + memcpy (priv_data->catalog_file->first, + priv_data->vh->catalog_file.extents, + sizeof (HfsPExtDataRec)); + if (!priv_data->catalog_file->start_cache) + memcpy (priv_data->catalog_file->cache, + priv_data->catalog_file->first, + sizeof (HfsPExtDataRec)); + from = 4; goto plus_update_vh; + } else + return -1; + + plus_update_vh: + if (!ped_geometry_read (priv_data->plus_geom, node, 2, 1)) + return -1; + memcpy (node, priv_data->vh, sizeof (HfsPVolumeHeader)); + if (!ped_geometry_write (priv_data->plus_geom, node, 2, 1)) + return -1; + if (!ped_geometry_write (priv_data->plus_geom, node, + priv_data->plus_geom->length - 2, 1)) + return -1; + /* !!!!!!!!!!!!!!!!! */ + /* This "if" by itself makes the need to rewrite the whole function */ + /* !!!!!!!!!!!!!!!!! */ + if ((unsigned) new_start > old_start) { + if (pass2) return -1; + pass2 = 1; src_fblock = &new_start; + switch (from) { + case 0: goto plus_start_file_found; + case 1: goto plus_attrib_file_found; + case 2: goto plus_alloc_file_found; + case 3: goto plus_ext_file_found; + case 4: goto plus_cat_file_found; + } + } + return 1; +} + +/* This function moves an extent starting at block fblock + to block to_fblock if there's enough room */ +/* Return 1 if everything was fine */ +/* Return -1 if an error occured */ +/* Return 0 if no extent was found */ +static int +hfsplus_move_extent_starting_at (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock) +{ + int ret; + + /* order = decreasing probability to be found */ + if ((ret = hfsplus_search_move_catalog (fs, ptr_fblock, ptr_to_fblock)) + || (ret = hfsplus_search_move_extent (fs, ptr_fblock, + ptr_to_fblock)) + || (ret = hfsplus_search_move_primary (fs, ptr_fblock, + ptr_to_fblock)) + || (ret = hfsplus_search_move_attributes (fs, ptr_fblock, + ptr_to_fblock))) { + return ret; + } + + return 0; +} + +/* This function moves file's data to compact used and free space, + starting at fblock block */ +/* return 0 on error */ +static int +hfsplus_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock, + PedTimer* timer, unsigned int to_free) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPVolumeHeader* vh = priv_data->vh; + unsigned int to_fblock = fblock; + int byte, bit, ret; + int to_byte, to_bit; + unsigned int start = fblock; + unsigned int div = PED_BE32_TO_CPU (vh->total_blocks) + + 1 - start - to_free; + + to_byte = byte = fblock / 8; + to_bit = bit = 7 - (fblock & 7); + + if (!hfsplus_read_bad_blocks (fs)) + return 0; + + while ( fblock < ( priv_data->plus_geom->length - 2 ) + / ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE ) ) { + if (((priv_data->alloc_map[byte] >> bit) & 1) + && (!hfsplus_is_bad_block (fs, fblock))) { + if (!(ret = hfsplus_move_extent_starting_at (fs, + &fblock, + &to_fblock))) + to_fblock = ++fblock; + else if (ret == -1) + return 0; + } else { + fblock++; + } + + byte = fblock / 8; + bit = 7 - (fblock & 7); + + to_byte = to_fblock / 8; + to_bit = 7 - (to_fblock & 7); + + ped_timer_update(timer, (float)(to_fblock - start) / div); + } + + return 1; +} + +/* This function returns the first sector of the last free block of + an HFS+ volume we can get after a hfsplus_pack_free_space_from_block call */ +static PedSector +hfsplus_get_empty_end (const PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPVolumeHeader* vh = priv_data->vh; + HfsPPrivateLinkExtent* link; + unsigned int block, last_bad, end_free_blocks; + int byte, bit; + + /* find the next block to the last bad block of the volume */ + if (!hfsplus_read_bad_blocks (fs)) + return 0; + + last_bad = 0; + for (link = priv_data->bad_blocks_xtent_list; link; link = link->next) { + if ((unsigned int) PED_BE32_TO_CPU (link->extent.start_block) + + PED_BE32_TO_CPU (link->extent.block_count) > last_bad) + last_bad = PED_BE32_TO_CPU (link->extent.start_block) + + PED_BE32_TO_CPU (link->extent.block_count); + } + + /* Count the free blocks from last_bad to the end of the volume */ + end_free_blocks = 0; + for (block = last_bad; + block < PED_BE32_TO_CPU (vh->total_blocks); + block++) { + byte = block / 8; + bit = 7 - (block & 7); + if (!((priv_data->alloc_map[byte]>>bit)&1)) + end_free_blocks++; + } + + /* Calculate the block that will by the first free at + the end of the volume */ + block = PED_BE32_TO_CPU (vh->total_blocks) - end_free_blocks; + + return (PedSector) block * ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE ); +} + +static PedSector +hfsplus_get_min_size (const PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + PedSector min_size; + + /* don't need to add anything because every sector + is part of allocation blocks in HFS+ */ + min_size = hfsplus_get_empty_end(fs); + + if (priv_data->wrapper) { + HfsPrivateFSData* hfs_priv_data = (HfsPrivateFSData*) + priv_data->wrapper->type_specific; + unsigned int hfs_sect_block; + hfs_sect_block = + PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size) + / PED_SECTOR_SIZE; + /* + * if hfs+ is embedded in an hfs wrapper then the new size is : + * the new size of the hfs+ volume rounded up to the size + * of hfs blocks + * + the minimum size of the hfs wrapper without any hfs+ + * modification + * - the current size of the hfs+ volume in the hfs wrapper + */ + min_size = ((min_size + hfs_sect_block - 1) / hfs_sect_block) + * hfs_sect_block + + hfs_get_empty_end(priv_data->wrapper) + 2 + - (PedSector) PED_BE16_TO_CPU ( hfs_priv_data->mdb + ->old_new.embedded + .location.block_count ) + * hfs_sect_block; + } + + return min_size; +} + +static PedConstraint* +hfsplus_get_resize_constraint (const PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + PedDevice* dev = fs->geom->dev; + PedAlignment start_align; + PedGeometry start_sector; + PedGeometry full_dev; + PedSector min_size; + + if (!ped_alignment_init (&start_align, fs->geom->start, 0)) + return NULL; + if (!ped_geometry_init (&start_sector, dev, fs->geom->start, 1)) + return NULL; + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + + min_size = hfsplus_get_min_size (fs); + + return ped_constraint_new (&start_align, ped_alignment_any, + &start_sector, &full_dev, min_size, + fs->geom->length); +} + +/* return the block which should be used to pack data to have + at least free fblock blocks at the end of the volume */ +static unsigned int +hfsplus_find_start_pack (const PedFileSystem *fs, unsigned int fblock) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + unsigned int block; + + for (block = PED_BE32_TO_CPU (priv_data->vh->total_blocks) - 1; + block && fblock; + block--) { + if (!((priv_data->alloc_map[block / 8] + >> (7 - (block & 7))) & 1)) + fblock--; + } + + while (block && !((priv_data->alloc_map[block / 8] + >> (7 - (block & 7))) & 1)) + block--; + if ((priv_data->alloc_map[block / 8] >> (7 - (block & 7))) & 1) + block++; + + return block; +} + +static int +hfsplus_volume_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + uint8_t buf[PED_SECTOR_SIZE]; + unsigned int nblock, nfree; + unsigned int block, to_free; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPVolumeHeader* vh = priv_data->vh; + int resize = 1; + unsigned int hfsp_sect_block = + ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE ); + HfsPPrivateFile* allocation_file; + unsigned int map_sectors; + + /* Clear the unmounted bit */ + vh->attributes &= PED_CPU_TO_BE32 (~( 1 << HFS_UNMOUNTED )); + if (!ped_geometry_read (priv_data->plus_geom, buf, 2, 1)) + return 0; + memcpy (buf, vh, sizeof (HfsPVolumeHeader)); + if (!ped_geometry_write (priv_data->plus_geom, buf, 2, 1)) + return 0; + + ped_timer_reset (timer); + ped_timer_set_state_name(timer, _("shrinking")); + ped_timer_update(timer, 0.0); + /* relocate data */ + block = hfsplus_find_start_pack (fs, to_free = + ( priv_data->plus_geom->length + - geom->length + hfsp_sect_block + - 1 ) / hfsp_sect_block ); + if (!hfsplus_pack_free_space_from_block (fs, block, timer, to_free)) { + resize = 0; + goto write_VH; + } + + /* Calculate new block number and other VH field */ + nblock = (geom->length + hfsp_sect_block - 1) / hfsp_sect_block; + nfree = PED_BE32_TO_CPU (vh->free_blocks) + - (PED_BE32_TO_CPU (vh->total_blocks) - nblock); + if (priv_data->plus_geom->length % hfsp_sect_block == 1) + nfree++; + if (geom->length % hfsp_sect_block == 1) + nfree--; + + /* Check that all block after future end are really free */ + for ( block = nblock; + block < ( priv_data->plus_geom->length - 2 ) + / ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE ); + block++ ) { + int byte, bit; + byte = block / 8; + bit = 7 - (block & 7); + if ((priv_data->alloc_map[byte]>>bit)&1) { + resize = 0; + goto write_VH; + } + } + + /* Update geometry */ + if (resize) { + /* update in fs structure */ + if (PED_BE32_TO_CPU (vh->next_allocation) >= nblock) + vh->next_allocation = PED_CPU_TO_BE32 (0); + vh->total_blocks = PED_CPU_TO_BE32 (nblock); + vh->free_blocks = PED_CPU_TO_BE32 (nfree); + /* update parted structure */ + priv_data->plus_geom->length = geom->length; + priv_data->plus_geom->end = priv_data->plus_geom->start + + geom->length - 1; + } + + /* Effective write */ + write_VH: + /* lasts two sectors are allocated by the alternate VH + and a reserved sector */ + block = (priv_data->plus_geom->length - 1) / hfsp_sect_block; + priv_data->alloc_map[block / 8] |= 1 << (7 - (block & 7)); + block = (priv_data->plus_geom->length - 2) / hfsp_sect_block; + priv_data->alloc_map[block / 8] |= 1 << (7 - (block & 7)); + map_sectors = ( PED_BE32_TO_CPU (vh->total_blocks) + + PED_SECTOR_SIZE * 8 - 1 ) / (PED_SECTOR_SIZE * 8); + allocation_file = + hfsplus_file_open ( fs, PED_CPU_TO_BE32 (HFSP_ALLOC_ID), + vh->allocation_file.extents, + PED_BE64_TO_CPU ( + vh->allocation_file.logical_size ) + / PED_SECTOR_SIZE ); + if (!hfsplus_file_write (allocation_file, priv_data->alloc_map, + 0, map_sectors)) + resize = 0; + hfsplus_file_close (allocation_file); + + if (resize) { + /* Set the unmounted bit and clear the inconsistent bit */ + vh->attributes |= PED_CPU_TO_BE32 ( 1 << HFS_UNMOUNTED ); + vh->attributes &= ~ PED_CPU_TO_BE32 ( 1 << HFSP_INCONSISTENT ); + } + + ped_timer_set_state_name(timer, _("writing HFS+ Volume Header")); + if (!ped_geometry_read (priv_data->plus_geom, buf, 2, 1 )) + return 0; + memcpy (buf, vh, sizeof (HfsPVolumeHeader)); + if (!ped_geometry_write (priv_data->plus_geom, buf, 2, 1 )) + return 0; + if (!ped_geometry_write ( priv_data->plus_geom, buf, + priv_data->plus_geom->length - 2, 1 )) + return 0; + ped_timer_update(timer, 1.0); + + return (resize); +} + +/* Update the HFS wrapper mdb and bad blocks file to reflect + the new geometry of the embedded HFS+ volume */ +static int +hfsplus_wrapper_update (PedFileSystem* fs) +{ + uint8_t node[PED_SECTOR_SIZE]; + HfsCPrivateLeafRec ref; + HfsExtentKey key; + HfsNodeDescriptor* node_desc = (HfsNodeDescriptor*) node; + HfsExtentKey* ret_key; + HfsExtDescriptor* ret_data; + unsigned int i, j; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPrivateFSData* hfs_priv_data = (HfsPrivateFSData*) + priv_data->wrapper->type_specific; + unsigned int hfs_sect_block = + PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size) + / PED_SECTOR_SIZE ; + PedSector hfsplus_sect = (PedSector) + PED_BE32_TO_CPU (priv_data->vh->total_blocks) + * ( PED_BE32_TO_CPU (priv_data->vh->block_size) + / PED_SECTOR_SIZE ); + unsigned int hfs_blocks_embedded = + (hfsplus_sect + hfs_sect_block - 1) + / hfs_sect_block; + unsigned int hfs_blocks_embedded_old; + + /* update HFS wrapper MDB */ + hfs_blocks_embedded_old = PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new + .embedded.location.block_count ); + hfs_priv_data->mdb->old_new.embedded.location.block_count = + PED_CPU_TO_BE16 (hfs_blocks_embedded); + /* maybe macOS will boot with this */ + hfs_priv_data->mdb->free_blocks = + PED_CPU_TO_BE16 ( PED_BE16_TO_CPU (hfs_priv_data->mdb->free_blocks) + + hfs_blocks_embedded_old + - hfs_blocks_embedded ); + + if (!ped_geometry_read (fs->geom, node, 2, 1)) + return 0; + memcpy (node, hfs_priv_data->mdb, sizeof (HfsMasterDirectoryBlock)); + if (!ped_geometry_write (fs->geom, node, 2, 1)) + return 0; + if (!ped_geometry_write (fs->geom, node, fs->geom->length - 2, 1)) + return 0; + + /* force reload bad block list */ + if (hfs_priv_data->bad_blocks_loaded) { + hfs_free_bad_blocks_list (hfs_priv_data->bad_blocks_xtent_list); + hfs_priv_data->bad_blocks_xtent_list = NULL; + hfs_priv_data->bad_blocks_xtent_nb = 0; + hfs_priv_data->bad_blocks_loaded = 0; + } + + /* clean HFS wrapper allocation map */ + for (i = PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new.embedded + .location.start_block ) + + hfs_blocks_embedded; + i < PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new.embedded + .location.start_block ) + + hfs_blocks_embedded_old; + i++ ) { + hfs_priv_data->alloc_map[i/8] &= ~(1 << (7 - (i & 7))); + } + /* and save it */ + if (!ped_geometry_write (fs->geom, hfs_priv_data->alloc_map, + PED_BE16_TO_CPU ( + hfs_priv_data->mdb->volume_bitmap_block ), + ( PED_BE16_TO_CPU ( + hfs_priv_data->mdb->total_blocks ) + + PED_SECTOR_SIZE * 8 - 1 ) + / (PED_SECTOR_SIZE * 8))) + return 0; + + + /* search and update the bad blocks file */ + key.key_length = sizeof(key) - 1; + key.type = HFS_DATA_FORK; + key.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID); + key.start = 0; + if (!hfs_btree_search (hfs_priv_data->extent_file, + (HfsPrivateGenericKey*) &key, NULL, 0, &ref)) + return 0; + if (!hfs_file_read_sector (hfs_priv_data->extent_file, node, + ref.node_number)) + return 0; + ret_key = (HfsExtentKey*) (node + ref.record_pos); + ret_data = (HfsExtDescriptor*) ( node + ref.record_pos + + sizeof (HfsExtentKey) ); + + while (ret_key->type == key.type && ret_key->file_ID == key.file_ID) { + for (i = 0; i < HFS_EXT_NB; i++) { + if ( ret_data[i].start_block + == hfs_priv_data->mdb->old_new + .embedded.location.start_block) { + ret_data[i].block_count = + hfs_priv_data->mdb->old_new + .embedded.location.block_count; + /* found ! : update */ + if (!hfs_file_write_sector ( + hfs_priv_data->extent_file, + node, ref.node_number)) + return 0; + return 1; + } + } + + if (ref.record_number < PED_BE16_TO_CPU (node_desc->rec_nb)) { + ref.record_number++; + } else { + ref.node_number = PED_BE32_TO_CPU (node_desc->next); + if (!ref.node_number + || !hfs_file_read_sector(hfs_priv_data->extent_file, + node, ref.node_number)) + return 0; + ref.record_number = 1; + } + + ref.record_pos = PED_BE16_TO_CPU (*((uint16_t *) + (node+(PED_SECTOR_SIZE - 2*ref.record_number)))); + ret_key = (HfsExtentKey*) (node + ref.record_pos); + ret_data = (HfsExtDescriptor*) ( node + ref.record_pos + + sizeof (HfsExtentKey) ); + } + + /* not found : not a valid hfs+ wrapper : failure */ + return 0; +} + +static int +hfsplus_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + PedTimer* timer_plus; + PedGeometry* embedded_geom; + + /* check preconditions */ + PED_ASSERT (fs->geom->dev == geom->dev, return 0); + + if (fs->geom->start != geom->start) + { + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Sorry, can't move the start of hfs partitions yet!")); + return 0; + } + + if (geom->length > fs->geom->length + || geom->length < hfsplus_get_min_size (fs)) + return 0; + + if (priv_data->wrapper) { + PedSector red, hgee; + HfsPrivateFSData* hfs_priv_data = (HfsPrivateFSData*) + priv_data->wrapper->type_specific; + unsigned int hfs_sect_block = + PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size) + / PED_SECTOR_SIZE; + + /* There is a wrapper so we must calculate the new geometry + of the embedded HFS+ volume */ + red = ( (fs->geom->length - geom->length + hfs_sect_block - 1) + / hfs_sect_block ) * hfs_sect_block; + /* Can't we shrink the hfs+ volume by the desired size ? */ + if ( red > priv_data->plus_geom->length + - (hgee = hfsplus_get_empty_end (fs))) { + /* No, shrink hfs+ by the greatest possible value */ + hgee = ((hgee + hfs_sect_block - 1) / hfs_sect_block) + * hfs_sect_block; + red = priv_data->plus_geom->length - hgee; + } + embedded_geom = ped_geometry_new (geom->dev, + priv_data->plus_geom->start, + priv_data->plus_geom->length + - red); + + /* There is a wrapper so the resize process is a two stages + process (embedded resizing then wrapper resizing) : + we create a sub timer */ + ped_timer_reset (timer); + ped_timer_set_state_name (timer, + _("shrinking embedded HFS+ volume")); + ped_timer_update(timer, 0.0); + timer_plus = ped_timer_new_nested (timer, 0.98); + } else { + /* No wrapper : the desired geometry is the desired + HFS+ volume geometry */ + embedded_geom = geom; + timer_plus = timer; + } + + /* Resize the HFS+ volume */ + if (!hfsplus_volume_resize (fs, embedded_geom, timer_plus)) { + if (timer_plus != timer) ped_timer_destroy_nested (timer_plus); + return 0; + } + + if (priv_data->wrapper) { + ped_geometry_destroy (embedded_geom); + ped_timer_destroy_nested (timer_plus); + ped_timer_set_state_name(timer, _("shrinking HFS wrapper")); + timer_plus = ped_timer_new_nested (timer, 0.02); + /* There's a wrapper : second stage = resizing it */ + if (!hfsplus_wrapper_update (fs) + || !hfs_resize (priv_data->wrapper, geom, timer_plus)) { + ped_timer_destroy_nested (timer_plus); + return 0; + } + ped_timer_destroy_nested (timer_plus); + } + ped_timer_update(timer, 1.0); + + return 1; } #endif /* !DISCOVER_ONLY */ @@ -64,9 +3232,44 @@ probe: hfs_probe, #ifndef DISCOVER_ONLY clobber: hfs_clobber, + open: hfs_open, + create: NULL, + close: hfs_close, + check: NULL, + copy: NULL, + resize: hfs_resize, + get_create_constraint: NULL, + get_resize_constraint: hfs_get_resize_constraint, + get_copy_constraint: NULL, #else clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + copy: NULL, + resize: NULL, + get_create_constraint: NULL, + get_resize_constraint: NULL, + get_copy_constraint: NULL, #endif +}; + +static PedFileSystemOps hfsplus_ops = { + probe: hfsplus_probe, +#ifndef DISCOVER_ONLY + clobber: hfsplus_clobber, + open: hfsplus_open, + create: NULL, + close: hfsplus_close, + check: NULL, + copy: NULL, + resize: hfsplus_resize, + get_create_constraint: NULL, + get_resize_constraint: hfsplus_get_resize_constraint, + get_copy_constraint: NULL, +#else + clobber: NULL, open: NULL, create: NULL, close: NULL, @@ -76,23 +3279,32 @@ get_create_constraint: NULL, get_resize_constraint: NULL, get_copy_constraint: NULL, +#endif }; + static PedFileSystemType hfs_type = { next: NULL, ops: &hfs_ops, name: "hfs" }; +static PedFileSystemType hfsplus_type = { + next: NULL, + ops: &hfsplus_ops, + name: "hfs+" +}; + void ped_file_system_hfs_init () { ped_file_system_type_register (&hfs_type); + ped_file_system_type_register (&hfsplus_type); } void ped_file_system_hfs_done () { ped_file_system_type_unregister (&hfs_type); + ped_file_system_type_unregister (&hfsplus_type); } - diff -Nru parted-1.6.10/libparted/fs_hfs/hfs.h parted-1.6.10-hfs/libparted/fs_hfs/hfs.h --- parted-1.6.10/libparted/fs_hfs/hfs.h 1969-12-31 19:00:00.000000000 -0500 +++ parted-1.6.10-hfs/libparted/fs_hfs/hfs.h 2004-04-23 22:44:10.951666976 -0400 @@ -0,0 +1,543 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _HFS_H +#define _HFS_H + + +#undef HFS_DEBUG + +#define HFS_SIGNATURE 0x4244 +#define HFSP_SIGNATURE 0x482B + +#define HFS_HARD_LOCK 7 +#define HFS_UNMOUNTED 8 +#define HFS_BAD_SPARED 9 +#define HFS_SOFT_LOCK 15 +#define HFSP_NO_CACHE 10 +#define HFSP_INCONSISTENT 11 + +#define HFS_IDX_NODE 0x00 +#define HFS_HDR_NODE 0x01 +#define HFS_MAP_NODE 0x02 +#define HFS_LEAF_NODE 0xFF + +#define HFS_FIRST_REC 0x0E +#define HFS_NSD_HD_REC 0x78 +#define HFS_MAP_REC 0xF8 + +#define HFS_DATA_FORK 0x00 +#define HFS_RES_FORK 0xFF + +#define HFS_CAT_DIR 0x01 +#define HFS_CAT_FILE 0x02 +#define HFS_CAT_DIR_TH 0x03 +#define HFS_CAT_FILE_TH 0x04 + +#define HFSP_ATTR_INLINE 0x10 +#define HFSP_ATTR_FORK 0x20 +#define HFSP_ATTR_EXTENTS 0x30 + +#define HFS_ROOT_PAR_ID 0x01 +#define HFS_ROOT_DIR_ID 0x02 +#define HFS_XTENT_ID 0x03 +#define HFS_CATALOG_ID 0x04 +#define HFS_BAD_BLOCK_ID 0x05 +#define HFSP_ALLOC_ID 0x06 +#define HFSP_STARTUP_ID 0x07 +#define HFSP_ATTRIB_ID 0x08 +#define HFSP_BOGUS_ID 0x0F +#define HFSP_FIRST_AV_ID 0x10 + +#define HFS_EXT_NB 3 +#define HFSP_EXT_NB 8 + +static PedFileSystemType hfs_type; +static PedFileSystemType hfsplus_type; + + + +/* ----------------------------------- */ +/* -- HFS DATA STRUCTURES -- */ +/* ----------------------------------- */ + +/* Extent descriptor */ +struct __attribute__ ((packed)) _HfsExtDescriptor { + uint16_t start_block; + uint16_t block_count; +}; +typedef struct _HfsExtDescriptor HfsExtDescriptor; +typedef HfsExtDescriptor HfsExtDataRec[HFS_EXT_NB]; + +/* Volume header */ +struct __attribute__ ((packed)) _HfsMasterDirectoryBlock { + uint16_t signature; + uint32_t create_date; + uint32_t modify_date; + uint16_t volume_attributes; + uint16_t files_in_root; + uint16_t volume_bitmap_block; /* in sectors */ + uint16_t next_allocation; + uint16_t total_blocks; + uint32_t block_size; /* in bytes */ + uint32_t def_clump_size; /* in bytes */ + uint16_t start_block; /* in sectors */ + uint32_t next_free_node; + uint16_t free_blocks; + uint8_t name_length; + char name[27]; + uint32_t backup_date; + uint16_t backup_number; + uint32_t write_count; + uint32_t extents_clump; + uint32_t catalog_clump; + uint16_t dirs_in_root; + uint32_t file_count; + uint32_t dir_count; + uint32_t finder_info[8]; + union __attribute__ ((packed)) { + struct __attribute__ ((packed)) { + uint16_t volume_cache_size; /* in blocks */ + uint16_t bitmap_cache_size; /* in blocks */ + uint16_t common_cache_size; /* in blocks */ + } legacy; + struct __attribute__ ((packed)) { + uint16_t signature; + HfsExtDescriptor location; + } embedded; + } old_new; + uint32_t extents_file_size; /* in bytes, block size multiple */ + HfsExtDataRec extents_file_rec; + uint32_t catalog_file_size; /* in bytes, block size multiple */ + HfsExtDataRec catalog_file_rec; +}; +typedef struct _HfsMasterDirectoryBlock HfsMasterDirectoryBlock; + +/* B*-Tree Node Descriptor */ +struct __attribute__ ((packed)) _HfsNodeDescriptor { + uint32_t next; + uint32_t previous; + int8_t type; + uint8_t height; + uint16_t rec_nb; + uint16_t reserved; +}; +typedef struct _HfsNodeDescriptor HfsNodeDescriptor; + +/* Header record of a whole B*-Tree */ +struct __attribute__ ((packed)) _HfsHeaderRecord { + uint16_t depth; + uint32_t root_node; + uint32_t leaf_records; + uint32_t first_leaf_node; + uint32_t last_leaf_node; + uint16_t node_size; + uint16_t max_key_len; + uint32_t total_nodes; + uint32_t free_nodes; + int8_t reserved[76]; +}; +typedef struct _HfsHeaderRecord HfsHeaderRecord; + +/* Catalog key for B*-Tree lookup in the catalog file */ +struct __attribute__ ((packed)) _HfsCatalogKey { + int8_t key_length; /* length of the key without key_length */ + int8_t reserved; + uint32_t parent_ID; + int8_t name_length; + char name[31]; /* in fact physicaly 1 upto 31 */ +}; +typedef struct _HfsCatalogKey HfsCatalogKey; + +/* Extents overflow key for B*-Tree lookup */ +struct __attribute__ ((packed)) _HfsExtentKey { + int8_t key_length; /* length of the key without key_length */ + int8_t type; /* data or ressource fork */ + uint32_t file_ID; + uint16_t start; +}; +typedef struct _HfsExtentKey HfsExtentKey; + +/* Catalog subdata case directory */ +struct __attribute__ ((packed)) _HfsDir { + uint16_t flags; + uint16_t valence; /* number of files in this directory */ + uint32_t dir_ID; + uint32_t create_date; + uint32_t modify_date; + uint32_t backup_date; + int8_t DInfo[16]; /* used by Finder, handle as reserved */ + int8_t DXInfo[16]; /* used by Finder, handle as reserved */ + uint32_t reserved[4]; +}; +typedef struct _HfsDir HfsDir; + +/* Catalog subdata case file */ +struct __attribute__ ((packed)) _HfsFile { + int8_t flags; + int8_t type; /* should be 0 */ + int8_t FInfo[16]; /* used by Finder, handle as reserved */ + uint32_t file_ID; + uint16_t data_start_block; + uint32_t data_sz_byte; + uint32_t data_sz_block; + uint16_t res_start_block; + uint32_t res_sz_byte; + uint32_t res_sz_block; + uint32_t create_date; + uint32_t modify_date; + uint32_t backup_date; + int8_t FXInfo[16]; /* used by Finder, handle as reserved */ + uint16_t clump_size; + HfsExtDataRec extents_data; + HfsExtDataRec extents_res; + uint32_t reserved; +}; +typedef struct _HfsFile HfsFile; + +/* Catalog subdata case directory thread */ +struct __attribute__ ((packed)) _HfsDirTh { + uint32_t reserved[2]; + uint32_t parent_ID; + int8_t name_length; + char name[31]; +}; +typedef struct _HfsDirTh HfsDirTh; + +/* Catalog subdata case file thread */ +typedef struct _HfsDirTh HfsFileTh; /* same as directory thread */ + +/* Catalog data */ +struct __attribute__ ((packed)) _HfsCatalog { + int8_t type; + int8_t reserved; + union { + HfsDir dir; + HfsFile file; + HfsDirTh dir_th; + HfsFileTh file_th; + } sel; +}; +typedef struct _HfsCatalog HfsCatalog; + + + +/* ------------------------------------ */ +/* -- HFS+ DATA STRUCTURES -- */ +/* ------------------------------------ */ + +/* Permission struct is reserved in HFS+ official specification */ +/* May be it could be used in unix implementations of HFS+ */ +struct __attribute__ ((packed)) _HfsPPerms { + uint32_t owner_ID; /* reserved */ + uint32_t group_ID; /* reserved */ + uint32_t permissions; /* reserved */ + uint32_t special_devices; /* reserved */ +}; +typedef struct _HfsPPerms HfsPPerms; + +/* HFS+ extent descriptor*/ +struct __attribute__ ((packed)) _HfsPExtDescriptor { + uint32_t start_block; + uint32_t block_count; +}; +typedef struct _HfsPExtDescriptor HfsPExtDescriptor; +typedef HfsPExtDescriptor HfsPExtDataRec[HFSP_EXT_NB]; + +/* HFS+ fork data structure */ +struct __attribute__ ((packed)) _HfsPForkData { + uint64_t logical_size; + uint32_t clump_size; + uint32_t total_blocks; + HfsPExtDataRec extents; +}; +typedef struct _HfsPForkData HfsPForkData; + +/* HFS+ catalog node ID */ +typedef uint32_t HfsPNodeID; + +/* HFS+ file names */ +typedef uint16_t unichar; +struct __attribute__ ((packed)) _HfsPUniStr255 { + uint16_t length; + unichar unicode[255]; /* 1 upto 255 */ +}; +typedef struct _HfsPUniStr255 HfsPUniStr255; + +/* HFS+ volume header */ +struct __attribute__ ((packed)) _HfsPVolumeHeader { + uint16_t signature; + uint16_t version; + uint32_t attributes; + uint32_t last_mounted_version; + uint32_t reserved; + + uint32_t create_date; + uint32_t modify_date; + uint32_t backup_date; + uint32_t checked_date; + + uint32_t file_count; + uint32_t dir_count; + + uint32_t block_size; + uint32_t total_blocks; + uint32_t free_blocks; + + uint32_t next_allocation; + uint32_t res_clump_size; + uint32_t data_clump_size; + HfsPNodeID next_catalog_ID; + + uint32_t write_count; + uint64_t encodings_bitmap; + + uint8_t finder_info[32]; + + HfsPForkData allocation_file; + HfsPForkData extents_file; + HfsPForkData catalog_file; + HfsPForkData attributes_file; + HfsPForkData startup_file; +}; +typedef struct _HfsPVolumeHeader HfsPVolumeHeader; + +/* HFS+ B-Tree Node Descriptor */ /* same as HFS btree */ +struct __attribute__ ((packed)) _HfsPNodeDescriptor { + uint32_t next; + uint32_t previous; + int8_t type; + uint8_t height; + uint16_t rec_nb; + uint16_t reserved; +}; +typedef struct _HfsPNodeDescriptor HfsPNodeDescriptor; + +/* Header record of a whole HFS+ B-Tree */ +struct __attribute__ ((packed)) _HfsPHeaderRecord { + uint16_t depth; + uint32_t root_node; + uint32_t leaf_records; + uint32_t first_leaf_node; + uint32_t last_leaf_node; + uint16_t node_size; + uint16_t max_key_len; + uint32_t total_nodes; + uint32_t free_nodes; /* same as hfs btree until here */ + uint16_t reserved1; + + uint32_t clump_size; + uint8_t btree_type; /* must be 0 for HFS+ B-Tree */ + uint8_t reserved2; + uint32_t attributes; + uint32_t reserved3[16]; +}; +typedef struct _HfsPHeaderRecord HfsPHeaderRecord; + +/* Catalog key for B-Tree lookup in the HFS+ catalog file */ +struct __attribute__ ((packed)) _HfsPCatalogKey { + uint16_t key_length; + HfsPNodeID parent_ID; + HfsPUniStr255 node_name; +}; +typedef struct _HfsPCatalogKey HfsPCatalogKey; + +/* HFS+ catalog subdata case dir */ +struct __attribute__ ((packed)) _HfsPDir { + uint16_t flags; + uint32_t valence; + HfsPNodeID dir_ID; + uint32_t create_date; + uint32_t modify_date; + uint32_t attrib_mod_date; + uint32_t access_date; + uint32_t backup_date; + HfsPPerms permissions; + int8_t DInfo[16]; /* used by Finder, handle as reserved */ + int8_t DXInfo[16]; /* used by Finder, handle as reserved */ + uint32_t text_encoding; + uint32_t reserved; +}; +typedef struct _HfsPDir HfsPDir; + +/* HFS+ catalog subdata case file */ +struct __attribute__ ((packed)) _HfsPFile { + uint16_t flags; + uint32_t reserved1; + HfsPNodeID file_ID; + uint32_t create_date; + uint32_t modify_date; + uint32_t attrib_mod_date; + uint32_t access_date; + uint32_t backup_date; + HfsPPerms permissions; + int8_t FInfo[16]; /* used by Finder, handle as reserved */ + int8_t FXInfo[16]; /* used by Finder, handle as reserved */ + uint32_t text_encoding; + uint32_t reserved2; + + HfsPForkData data_fork; + HfsPForkData res_fork; +}; +typedef struct _HfsPFile HfsPFile; + +/* HFS+ catalog subdata case thread */ +struct __attribute__ ((packed)) _HfsPThread { + int16_t reserved; + HfsPNodeID parent_ID; + HfsPUniStr255 node_name; +}; +typedef struct _HfsPThread HfsPDirTh; +typedef struct _HfsPThread HfsPFileTh; + +/* HFS+ Catalog leaf data */ +struct __attribute__ ((packed)) _HfsPCatalog { + int16_t type; + union { + HfsPDir dir; + HfsPFile file; + HfsPDirTh dir_th; + HfsPFileTh file_th; + } sel; +}; +typedef struct _HfsPCatalog HfsPCatalog; + +/* HFS+ extents file key */ +struct __attribute__ ((packed)) _HfsPExtentKey { + uint16_t key_length; + uint8_t type; + uint8_t pad; + HfsPNodeID file_ID; + uint32_t start; +}; +typedef struct _HfsPExtentKey HfsPExtentKey; + +/* extent file data is HfsPExtDataRec */ + +/* Fork data attribute file */ +struct __attribute__ ((packed)) _HfsPForkDataAttr { + uint32_t record_type; + uint32_t reserved; + union __attribute__ ((packed)) { + HfsPForkData fork; + HfsPExtDataRec extents; + } fork_res; +}; +typedef struct _HfsPForkDataAttr HfsPForkDataAttr; + + + +/* ---------------------------------------- */ +/* -- INTERNAL DATA STRUCTURES -- */ +/* ---------------------------------------- */ + +/* Data of an opened HFS file */ +struct _HfsPrivateFile { + PedSector sect_nb; + PedFileSystem* fs; + uint32_t CNID; /* disk order (BE) */ + HfsExtDataRec first; /* disk order (BE) */ + HfsExtDataRec cache; /* disk order (BE) */ + uint16_t start_cache; /* CPU order */ +}; +typedef struct _HfsPrivateFile HfsPrivateFile; + +/* To store bad block list */ +struct _HfsPrivateLinkExtent { + HfsExtDescriptor extent; + struct _HfsPrivateLinkExtent* next; +}; +typedef struct _HfsPrivateLinkExtent HfsPrivateLinkExtent; + +/* HFS Filesystem specific data */ +struct _HfsPrivateFSData { + uint8_t alloc_map[(1<<16) / 8]; + HfsMasterDirectoryBlock* mdb; + HfsPrivateFile* extent_file; + HfsPrivateFile* catalog_file; + char bad_blocks_loaded; + unsigned int bad_blocks_xtent_nb; + HfsPrivateLinkExtent* bad_blocks_xtent_list; +}; +typedef struct _HfsPrivateFSData HfsPrivateFSData; + +/* Generic btree key */ +struct __attribute__ ((packed)) _HfsPrivateGenericKey { + int8_t key_length; + uint8_t key_content[1]; /* we use 1 as a minimum size */ +}; +typedef struct _HfsPrivateGenericKey HfsPrivateGenericKey; + +/* ----- HFS+ ----- */ + +/* Data of an opened HFS file */ +struct _HfsPPrivateFile { + PedSector sect_nb; + PedFileSystem* fs; + HfsPNodeID CNID; /* disk order (BE) */ + HfsPExtDataRec first; /* disk order (BE) */ + HfsPExtDataRec cache; /* disk order (BE) */ + uint32_t start_cache; /* CPU order */ +}; +typedef struct _HfsPPrivateFile HfsPPrivateFile; + +/* To store bad block list */ +struct _HfsPPrivateLinkExtent { + HfsPExtDescriptor extent; + struct _HfsPPrivateLinkExtent* next; +}; +typedef struct _HfsPPrivateLinkExtent HfsPPrivateLinkExtent; + +/* HFS+ filesystem specific data */ +struct _HfsPPrivateFSData { + PedFileSystem* wrapper; /* NULL if hfs+ is not embedded */ + PedGeometry* plus_geom; /* Geometry of HFS+ _volume_ */ + char free_geom; /* 1 = plus_geom must be freed */ + uint8_t* alloc_map; + HfsPVolumeHeader* vh; + HfsPPrivateFile* extents_file; + HfsPPrivateFile* catalog_file; + HfsPPrivateFile* attributes_file; + char bad_blocks_loaded; + unsigned int bad_blocks_xtent_nb; + HfsPPrivateLinkExtent* bad_blocks_xtent_list; +}; +typedef struct _HfsPPrivateFSData HfsPPrivateFSData; + +/* Generic + btree key */ +struct __attribute__ ((packed)) _HfsPPrivateGenericKey { + uint16_t key_length; + uint8_t key_content[1]; /* we use 1 as a minimum size */ +}; +typedef struct _HfsPPrivateGenericKey HfsPPrivateGenericKey; + +/* ---- common ---- */ + +/* node and lead record reference for a BTree search */ +struct _HfsCPrivateLeafRec { + unsigned int node_size; /* in sectors */ + unsigned int node_number; + unsigned int record_pos; + unsigned int record_number; +}; +typedef struct _HfsCPrivateLeafRec HfsCPrivateLeafRec; + + + +#endif /* _HFS_H */