/* warning : only works on data forks */ 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; }
/* This function reads bad blocks extents in the extents file and store it in f.s. specific data of fs */ int hfs_read_bad_blocks (const PedFileSystem *fs) { HfsPrivateFSData* priv_data = (HfsPrivateFSData*) fs->type_specific; if (priv_data->bad_blocks_loaded) return 1; { 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) || ret_key->file_ID != search.file_ID || ret_key->type != search.type) { if (first_pass) break; else goto errbb; } 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)); if (!new_xt) goto errbb; 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;} errbb: hfs_free_bad_blocks_list(priv_data->bad_blocks_xtent_list); priv_data->bad_blocks_xtent_list=NULL; priv_data->bad_blocks_xtent_nb=0; return 0; }
/* 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_DEFAULT]; HfsCPrivateLeafRec ref; HfsExtentKey key; HfsNodeDescriptor* node_desc = (HfsNodeDescriptor*) node; HfsExtentKey* ret_key; HfsExtDescriptor* ret_data; unsigned int i; 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_DEFAULT ; 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_DEFAULT ); 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 */ /* update : yes it does \o/ :) */ 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 (!hfs_update_mdb(priv_data->wrapper)) 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++ ) { CLR_BLOC_OCCUPATION(hfs_priv_data->alloc_map, i); } /* 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_DEFAULT * 8 - 1 ) / (PED_SECTOR_SIZE_DEFAULT * 8))) return 0; if (!ped_geometry_sync (fs->geom)) 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)) { ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("An error occurred while looking for the mandatory " "bad blocks file.")); 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) || !ped_geometry_sync(fs->geom)) 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)) goto bb_not_found; ref.record_number = 1; } ref.record_pos = PED_BE16_TO_CPU (*((uint16_t *) (node + (PED_SECTOR_SIZE_DEFAULT - 2*ref.record_number)))); ret_key = (HfsExtentKey*) (node + ref.record_pos); ret_data = (HfsExtDescriptor*) ( node + ref.record_pos + sizeof (HfsExtentKey) ); } bb_not_found: /* not found : not a valid hfs+ wrapper : failure */ ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("It seems there is an error in the HFS wrapper: the bad " "blocks file doesn't contain the embedded HFS+ volume.")); return 0; }