/* save any dirty sector of the allocation bitmap file */ static int hfsplus_save_allocation(PedFileSystem *fs) { HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) fs->type_specific; unsigned int map_sectors, i, j; int ret = 1; map_sectors = ( PED_BE32_TO_CPU (priv_data->vh->total_blocks) + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) / (PED_SECTOR_SIZE_DEFAULT * 8); for (i = 0; i < map_sectors;) { for (j = i; (TST_BLOC_OCCUPATION(priv_data->dirty_alloc_map,j)); ++j) CLR_BLOC_OCCUPATION(priv_data->dirty_alloc_map,j); if (j-i) { ret = hfsplus_file_write(priv_data->allocation_file, priv_data->alloc_map + i * PED_SECTOR_SIZE_DEFAULT, i, j-i) && ret; i = j; } else ++i; } return ret; }
/* -1 is ok because there can only be 2^32-1 blocks, so the max possible last one is 2^32-2 (and anyway it contains Alternate VH), so -1 (== 2^32-1[2^32]) never represent a valid block */ static int 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; unsigned int i, ok = 0; unsigned int next_to_fblock; unsigned int start, stop; PED_ASSERT (hfsp_block != NULL); PED_ASSERT (*ptr_to_fblock <= *ptr_fblock); /* quiet GCC */ start = stop = 0; /* Try to fit the extent AT or _BEFORE_ the wanted place, or then in the gap between dest and source. If failed try to fit the extent after source, for 2 pass relocation The extent is always copied in a non overlapping way */ /* Backward search */ /* 1 pass relocation AT or BEFORE *ptr_to_fblock */ if (*ptr_to_fblock != *ptr_fblock) { start = stop = *ptr_fblock < *ptr_to_fblock+size ? *ptr_fblock : *ptr_to_fblock+size; while (start && stop-start != size) { --start; if (TST_BLOC_OCCUPATION(priv_data->alloc_map,start)) stop = start; } ok = (stop-start == size); } /* Forward search */ /* 1 pass relocation in the gap merged with 2 pass reloc after source */ if (!ok && *ptr_to_fblock != *ptr_fblock) { start = stop = *ptr_to_fblock+1; while (stop < PED_BE32_TO_CPU(priv_data->vh->total_blocks) && stop-start != size) { if (TST_BLOC_OCCUPATION(priv_data->alloc_map,stop)) start = stop + 1; ++stop; } ok = (stop-start == size); } /* new non overlapping room has been found ? */ if (ok) { /* enough room */ PedSector abs_sector; unsigned int ai, j, block; unsigned int block_sz = (PED_BE32_TO_CPU ( priv_data->vh->block_size) / PED_SECTOR_SIZE_DEFAULT); if (stop > *ptr_to_fblock && stop <= *ptr_fblock) /* Fit in the gap */ next_to_fblock = stop; else /* Before or after the gap */ next_to_fblock = *ptr_to_fblock; /* move blocks */ for (i = 0; i < size; /*i++*/) { j = size - i; j = (j < hfsp_block_count) ? j : hfsp_block_count ; abs_sector = (PedSector) (*ptr_fblock + i) * block_sz; if (!ped_geometry_read (priv_data->plus_geom, hfsp_block, abs_sector, block_sz * j)) return -1; abs_sector = (PedSector) (start + i) * block_sz; if (!ped_geometry_write (priv_data->plus_geom, hfsp_block, abs_sector, block_sz * j)) return -1; for (ai = i+j; i < ai; i++) { /* free source block */ block = *ptr_fblock + i; CLR_BLOC_OCCUPATION(priv_data->alloc_map,block); SET_BLOC_OCCUPATION(priv_data->dirty_alloc_map, block/(PED_SECTOR_SIZE_DEFAULT*8)); /* set dest block */ block = start + i; SET_BLOC_OCCUPATION(priv_data->alloc_map,block); SET_BLOC_OCCUPATION(priv_data->dirty_alloc_map, block/(PED_SECTOR_SIZE_DEFAULT*8)); } } if (!ped_geometry_sync_fast (priv_data->plus_geom)) return -1; *ptr_fblock += size; *ptr_to_fblock = next_to_fblock; } else { if (*ptr_fblock != *ptr_to_fblock) /* not enough room */ ped_exception_throw (PED_EXCEPTION_WARNING, PED_EXCEPTION_IGNORE, _("An extent has not been relocated.")); start = *ptr_fblock; *ptr_fblock = *ptr_to_fblock = start + size; } return start; }
/* 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; }