static int hfsplus_volume_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) { uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; unsigned int nblock, nfree, mblock; unsigned int block, to_free, old_blocks; 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_DEFAULT ); unsigned int map_sectors; old_blocks = PED_BE32_TO_CPU (vh->total_blocks); /* Flush caches */ if (!ped_geometry_sync(priv_data->plus_geom)) return 0; /* Clear the unmounted bit */ /* and set the implementation code (Apple Creator Code) */ vh->attributes &= PED_CPU_TO_BE32 (~( 1 << HFS_UNMOUNTED )); vh->last_mounted_version = PED_CPU_TO_BE32(HFSP_IMPL_Shnk); 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) || !ped_geometry_sync (priv_data->plus_geom)) return 0; ped_timer_reset (timer); ped_timer_set_state_name(timer, _("shrinking")); ped_timer_update(timer, 0.0); /* relocate data */ to_free = ( priv_data->plus_geom->length - geom->length + hfsp_sect_block - 1 ) / hfsp_sect_block; block = hfsplus_find_start_pack (fs, to_free); if (!hfsplus_pack_free_space_from_block (fs, block, timer, to_free)) { resize = 0; ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("Data relocation has failed.")); goto write_VH; } /* Calculate new block number and other VH field */ /* nblock must be rounded _down_ */ nblock = geom->length / hfsp_sect_block; nfree = PED_BE32_TO_CPU (vh->free_blocks) - (old_blocks - nblock); /* free block readjustement is only needed when incorrect nblock was used by my previous implementation, so detect the case */ if (priv_data->plus_geom->length < old_blocks * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT) ) { if (priv_data->plus_geom->length % hfsp_sect_block == 1) nfree++; } /* Check that all block after future end are really free */ mblock = ( priv_data->plus_geom->length - 2 ) / hfsp_sect_block; if (mblock > old_blocks - 1) mblock = old_blocks - 1; for ( block = nblock; block < mblock; block++ ) { if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) { resize = 0; ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("Data relocation left some data at the end " "of the volume.")); goto write_VH; } } /* Mark out of volume blocks as used */ map_sectors = ( ( old_blocks + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) / (PED_SECTOR_SIZE_DEFAULT * 8) ) * (PED_SECTOR_SIZE_DEFAULT * 8); for ( block = nblock; block < map_sectors; ++block) SET_BLOC_OCCUPATION(priv_data->alloc_map, block); /* 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, and last block is always reserved */ block = (priv_data->plus_geom->length - 1) / hfsp_sect_block; if (block < PED_BE32_TO_CPU (vh->total_blocks)) SET_BLOC_OCCUPATION(priv_data->alloc_map, block); block = (priv_data->plus_geom->length - 2) / hfsp_sect_block; if (block < PED_BE32_TO_CPU (vh->total_blocks)) SET_BLOC_OCCUPATION(priv_data->alloc_map, block); SET_BLOC_OCCUPATION(priv_data->alloc_map, PED_BE32_TO_CPU (vh->total_blocks) - 1); /* Write the _old_ area to set out of volume blocks as used */ map_sectors = ( old_blocks + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) / (PED_SECTOR_SIZE_DEFAULT * 8); if (!hfsplus_file_write (priv_data->allocation_file, priv_data->alloc_map, 0, map_sectors)) { resize = 0; ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("Error while writing the allocation file.")); } else { /* Write remaining part of allocation bitmap */ /* This is necessary to handle pre patch-11 and third party */ /* implementations */ memset(buf, 0xFF, PED_SECTOR_SIZE_DEFAULT); for (block = map_sectors; block < priv_data->allocation_file->sect_nb; ++block) { if (!hfsplus_file_write_sector ( priv_data->allocation_file, buf, block)) { ped_exception_throw ( PED_EXCEPTION_WARNING, PED_EXCEPTION_IGNORE, _("Error while writing the " "compatibility part of the " "allocation file.")); break; } } } ped_geometry_sync (priv_data->plus_geom); 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 (!hfsplus_update_vh(fs)) { ped_geometry_sync(priv_data->plus_geom); return 0; } if (!ped_geometry_sync(priv_data->plus_geom)) return 0; ped_timer_update(timer, 1.0); return (resize); }
/* -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; }
int hfs_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) { uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; unsigned int nblock, nfree; unsigned int block, to_free; HfsPrivateFSData* priv_data; HfsMasterDirectoryBlock* mdb; int resize = 1; unsigned int hfs_sect_block; PedSector hgee; /* check preconditions */ PED_ASSERT (fs != NULL); PED_ASSERT (fs->geom != NULL); PED_ASSERT (geom != NULL); #ifdef DEBUG PED_ASSERT ((hgee = hfs_get_empty_end(fs)) != 0); #else if ((hgee = hfs_get_empty_end(fs)) == 0) return 0; #endif PED_ASSERT ((hgee = hfs_get_empty_end(fs)) != 0); if (ped_geometry_test_equal(fs->geom, geom)) return 1; priv_data = (HfsPrivateFSData*) fs->type_specific; mdb = priv_data->mdb; hfs_sect_block = PED_BE32_TO_CPU (mdb->block_size) / PED_SECTOR_SIZE_DEFAULT; if (fs->geom->start != geom->start || geom->length > fs->geom->length || geom->length < hgee + 2) { ped_exception_throw ( PED_EXCEPTION_NO_FEATURE, PED_EXCEPTION_CANCEL, _("Sorry, HFS cannot be resized that way yet.")); return 0; } /* Flush caches */ if (!ped_geometry_sync(fs->geom)) 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) || !ped_geometry_sync (fs->geom)) return 0; ped_timer_reset (timer); ped_timer_set_state_name(timer, _("shrinking")); ped_timer_update(timer, 0.0); /* relocate data */ to_free = ( fs->geom->length - geom->length + hfs_sect_block - 1 ) / hfs_sect_block ; block = hfs_find_start_pack (fs, to_free); if (!hfs_pack_free_space_from_block (fs, block, timer, to_free)) { resize = 0; ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("Data relocation has failed.")); 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++) { if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) { resize = 0; ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("Data relocation left some data in the end " "of the volume.")); goto write_MDB; } } /* Mark out of volume blocks as used (broken implementations compatibility) */ for ( block = nblock; block < (1 << 16); ++block) SET_BLOC_OCCUPATION(priv_data->alloc_map,block); /* save the allocation map I do not write until start of allocation blocks but only until pre-resize end of bitmap blocks because the specifications do _not_ assert that everything until allocation blocks is boot, mdb and alloc */ 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_DEFAULT * 8 - 1) / (PED_SECTOR_SIZE_DEFAULT * 8)); /* 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 (!hfs_update_mdb(fs)) { ped_geometry_sync(geom); return 0; } if (!ped_geometry_sync(geom)) return 0; ped_timer_update(timer, 1.0); return (resize); }