/* libparted - a library for manipulating disk partitions Copyright (C) 2000, 2003-2005, 2007, 2009-2014, 2019-2021 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 3 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, see . */ /* Author : Guillaume Knispel Report bug to */ #include #include #include #include #include #if ENABLE_NLS # include # define _(String) dgettext (PACKAGE, String) #else # define _(String) (String) #endif /* ENABLE_NLS */ #include "hfs.h" #include "probe.h" uint8_t* hfs_block = NULL; uint8_t* hfsp_block = NULL; unsigned hfs_block_count; unsigned hfsp_block_count; #define HFS_BLOCK_SIZES ((int[2]){512, 0}) #define HFSP_BLOCK_SIZES ((int[2]){512, 0}) #define HFSX_BLOCK_SIZES ((int[2]){512, 0}) #ifndef DISCOVER_ONLY #include "file.h" #include "reloc.h" #include "advfs.h" static PedFileSystemType hfs_type; static PedFileSystemType hfsplus_type; /* ----- HFS ----- */ PedFileSystem * hfs_open (PedGeometry* geom) { uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; PedFileSystem* fs; HfsMasterDirectoryBlock* mdb; HfsPrivateFSData* priv_data; if (!hfsc_can_use_geom (geom)) return NULL; /* Read MDB */ if (!ped_geometry_read (geom, buf, 2, 1)) return NULL; /* Allocate memory */ fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); if (!fs) goto ho; mdb = (HfsMasterDirectoryBlock*) ped_malloc (sizeof (HfsMasterDirectoryBlock)); if (!mdb) goto ho_fs; priv_data = (HfsPrivateFSData*) ped_malloc (sizeof (HfsPrivateFSData)); if (!priv_data) goto ho_mdb; 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_DEFAULT); if (!priv_data->extent_file) goto ho_pd; 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_DEFAULT); if (!priv_data->catalog_file) goto ho_ce; /* 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_DEFAULT * 8 - 1 ) / (PED_SECTOR_SIZE_DEFAULT * 8) ) ) goto ho_cf; fs->type = &hfs_type; fs->geom = ped_geometry_duplicate (geom); if (!fs->geom) goto ho_cf; fs->type_specific = (void*) priv_data; fs->checked = ( PED_BE16_TO_CPU (mdb->volume_attributes) >> HFS_UNMOUNTED ) & 1; return fs; /*--- clean error handling ---*/ ho_cf: hfs_file_close(priv_data->catalog_file); ho_ce: hfs_file_close(priv_data->extent_file); ho_pd: free(priv_data); ho_mdb: free(mdb); ho_fs: free(fs); ho: return NULL; } int hfs_close (PedFileSystem *fs) { HfsPrivateFSData* priv_data = (HfsPrivateFSData*) fs->type_specific; hfs_file_close (priv_data->extent_file); hfs_file_close (priv_data->catalog_file); if (priv_data->bad_blocks_loaded) hfs_free_bad_blocks_list (priv_data->bad_blocks_xtent_list); free (priv_data->mdb); free (priv_data); ped_geometry_destroy (fs->geom); free (fs); return 1; } 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; if (min_size == 2) return NULL; return ped_constraint_new (&start_align, ped_alignment_any, &start_sector, &full_dev, min_size, fs->geom->length); } 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); } /* ----- HFS+ ----- */ #include "file_plus.h" #include "advfs_plus.h" #include "reloc_plus.h" #include "journal.h" int hfsplus_close (PedFileSystem *fs) { HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) fs->type_specific; if (priv_data->bad_blocks_loaded) hfsplus_free_bad_blocks_list(priv_data->bad_blocks_xtent_list); free(priv_data->alloc_map); free(priv_data->dirty_alloc_map); hfsplus_file_close (priv_data->allocation_file); hfsplus_file_close (priv_data->attributes_file); hfsplus_file_close (priv_data->catalog_file); hfsplus_file_close (priv_data->extents_file); if (priv_data->free_geom) ped_geometry_destroy (priv_data->plus_geom); if (priv_data->wrapper) hfs_close(priv_data->wrapper); ped_geometry_destroy (fs->geom); free(priv_data->vh); free(priv_data); free(fs); return 1; } PedFileSystem* hfsplus_open (PedGeometry* geom) { uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; PedFileSystem* fs; HfsPVolumeHeader* vh; HfsPPrivateFSData* priv_data; PedGeometry* wrapper_geom; unsigned int map_sectors; if (!hfsc_can_use_geom (geom)) return NULL; fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); if (!fs) goto hpo; vh = (HfsPVolumeHeader*) ped_malloc (sizeof (HfsPVolumeHeader)); if (!vh) goto hpo_fs; priv_data = (HfsPPrivateFSData*)ped_malloc (sizeof (HfsPPrivateFSData)); if (!priv_data) goto hpo_vh; fs->geom = ped_geometry_duplicate (geom); if (!fs->geom) goto hpo_pd; 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); if (!priv_data->wrapper) goto hpo_gm; hfs_priv_data = (HfsPrivateFSData*) priv_data->wrapper->type_specific; bs = PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size) / PED_SECTOR_SIZE_DEFAULT; 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); if (!priv_data->plus_geom) goto hpo_wr; 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)) goto hpo_pg; memcpy (vh, buf, sizeof (HfsPVolumeHeader)); priv_data->vh = vh; if (vh->signature != PED_CPU_TO_BE16(HFSP_SIGNATURE) && vh->signature != PED_CPU_TO_BE16(HFSX_SIGNATURE)) { ped_exception_throw ( PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL, _("No valid HFS[+X] signature has been found while " "opening.")); goto hpo_pg; } if (vh->signature == PED_CPU_TO_BE16(HFSP_SIGNATURE) && vh->version != PED_CPU_TO_BE16(HFSP_VERSION)) { if (ped_exception_throw ( PED_EXCEPTION_NO_FEATURE, PED_EXCEPTION_IGNORE_CANCEL, _("Version %d of HFS+ isn't supported."), PED_BE16_TO_CPU(vh->version)) != PED_EXCEPTION_IGNORE) goto hpo_pg; } if (vh->signature == PED_CPU_TO_BE16(HFSX_SIGNATURE) && vh->version != PED_CPU_TO_BE16(HFSX_VERSION)) { if (ped_exception_throw ( PED_EXCEPTION_NO_FEATURE, PED_EXCEPTION_IGNORE_CANCEL, _("Version %d of HFSX isn't supported."), PED_BE16_TO_CPU(vh->version)) != PED_EXCEPTION_IGNORE) goto hpo_pg; } priv_data->jib_start_block = 0; priv_data->jl_start_block = 0; if (vh->attributes & PED_CPU_TO_BE32(1<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_DEFAULT); if (!priv_data->extents_file) goto hpo_pg; 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_DEFAULT); if (!priv_data->catalog_file) goto hpo_ce; 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_DEFAULT); if (!priv_data->attributes_file) goto hpo_cc; map_sectors = ( PED_BE32_TO_CPU (vh->total_blocks) + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) / (PED_SECTOR_SIZE_DEFAULT * 8); priv_data->dirty_alloc_map = (uint8_t*) ped_malloc ((map_sectors + 7) / 8); if (!priv_data->dirty_alloc_map) goto hpo_cl; memset(priv_data->dirty_alloc_map, 0, (map_sectors + 7) / 8); priv_data->alloc_map = (uint8_t*) ped_malloc (map_sectors * PED_SECTOR_SIZE_DEFAULT); if (!priv_data->alloc_map) goto hpo_dm; priv_data->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_DEFAULT); if (!priv_data->allocation_file) goto hpo_am; if (!hfsplus_file_read (priv_data->allocation_file, priv_data->alloc_map, 0, map_sectors)) { hfsplus_close(fs); return NULL; } 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; /*--- clean error handling ---*/ hpo_am: free(priv_data->alloc_map); hpo_dm: free(priv_data->dirty_alloc_map); hpo_cl: hfsplus_file_close (priv_data->attributes_file); hpo_cc: hfsplus_file_close (priv_data->catalog_file); hpo_ce: hfsplus_file_close (priv_data->extents_file); hpo_pg: if (priv_data->free_geom) ped_geometry_destroy (priv_data->plus_geom); hpo_wr: if (priv_data->wrapper) hfs_close(priv_data->wrapper); hpo_gm: ped_geometry_destroy (fs->geom); hpo_pd: free(priv_data); hpo_vh: free(vh); hpo_fs: free(fs); hpo: return NULL; } PedConstraint * hfsplus_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; min_size = hfsplus_get_min_size (fs); if (!min_size) return NULL; return ped_constraint_new (&start_align, ped_alignment_any, &start_sector, &full_dev, min_size, fs->geom->length); } 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); } /* 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; } uint16_t value; memcpy(&value, node+PED_SECTOR_SIZE_DEFAULT - (2*ref.record_number), sizeof(uint16_t)); ref.record_pos = PED_BE16_TO_CPU(value); 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; } int hfsplus_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) { HfsPPrivateFSData* priv_data; PedTimer* timer_plus; PedGeometry* embedded_geom; PedSector hgms; /* check preconditions */ PED_ASSERT (fs != NULL); PED_ASSERT (fs->geom != NULL); PED_ASSERT (geom != NULL); PED_ASSERT (fs->geom->dev == geom->dev); #ifdef DEBUG PED_ASSERT ((hgms = hfsplus_get_min_size (fs)) != 0); #else if ((hgms = hfsplus_get_min_size (fs)) == 0) return 0; #endif if (ped_geometry_test_equal(fs->geom, geom)) return 1; priv_data = (HfsPPrivateFSData*) fs->type_specific; if (fs->geom->start != geom->start || geom->length > fs->geom->length || geom->length < hgms) { ped_exception_throw ( PED_EXCEPTION_NO_FEATURE, PED_EXCEPTION_CANCEL, _("Sorry, HFS+ cannot be resized that way yet.")); 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_DEFAULT; /* 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 ? */ hgee = hfsplus_get_empty_end (fs); if (!hgee) return 0; if (red > priv_data->plus_geom->length - hgee) { /* 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); if (priv_data->wrapper) ped_geometry_destroy (embedded_geom); ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("Resizing the HFS+ volume has failed.")); 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); ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("Updating the HFS wrapper has failed.")); return 0; } ped_timer_destroy_nested (timer_plus); } ped_timer_update(timer, 1.0); return 1; } #ifdef HFS_EXTRACT_FS /* The following is for debugging purpose only, NOT for packaging */ #include uint8_t* extract_buffer = NULL; static int hfs_extract_file(const char* filename, HfsPrivateFile* hfs_file) { FILE* fout; PedSector sect; fout = fopen(filename, "w"); if (!fout) return 0; for (sect = 0; sect < hfs_file->sect_nb; ++sect) { if (!hfs_file_read_sector(hfs_file, extract_buffer, sect)) goto err_close; if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout)) goto err_close; } return (fclose(fout) == 0 ? 1 : 0); err_close: fclose(fout); return 0; } static int hfs_extract_bitmap(const char* filename, PedFileSystem* fs) { HfsPrivateFSData* priv_data = (HfsPrivateFSData*) fs->type_specific; HfsMasterDirectoryBlock* mdb = priv_data->mdb; unsigned int count; FILE* fout; PedSector sect; fout = fopen(filename, "w"); if (!fout) return 0; for (sect = PED_BE16_TO_CPU(mdb->volume_bitmap_block); sect < PED_BE16_TO_CPU(mdb->start_block); sect += count) { uint16_t st_block = PED_BE16_TO_CPU(mdb->start_block); count = (st_block-sect) < BLOCK_MAX_BUFF ? (st_block-sect) : BLOCK_MAX_BUFF; if (!ped_geometry_read(fs->geom, extract_buffer, sect, count)) goto err_close; if (!fwrite (extract_buffer, count * PED_SECTOR_SIZE_DEFAULT, 1, fout)) goto err_close; } return (fclose(fout) == 0 ? 1 : 0); err_close: fclose(fout); return 0; } static int hfs_extract_mdb (const char* filename, PedFileSystem* fs) { FILE* fout; fout = fopen(filename, "w"); if (!fout) return 0; if (!ped_geometry_read(fs->geom, extract_buffer, 2, 1)) goto err_close; if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout)) goto err_close; return (fclose(fout) == 0 ? 1 : 0); err_close: fclose(fout); return 0; } static int hfs_extract (PedFileSystem* fs, PedTimer* timer) { HfsPrivateFSData* priv_data = (HfsPrivateFSData*) fs->type_specific; ped_exception_throw ( PED_EXCEPTION_INFORMATION, PED_EXCEPTION_OK, _("This is not a real %s check. This is going to extract " "special low level files for debugging purposes."), "HFS"); extract_buffer = ped_malloc(BLOCK_MAX_BUFF * PED_SECTOR_SIZE_DEFAULT); if (!extract_buffer) return 0; hfs_extract_mdb(HFS_MDB_FILENAME, fs); hfs_extract_file(HFS_CATALOG_FILENAME, priv_data->catalog_file); hfs_extract_file(HFS_EXTENTS_FILENAME, priv_data->extent_file); hfs_extract_bitmap(HFS_BITMAP_FILENAME, fs); free(extract_buffer); extract_buffer = NULL; return 0; /* nothing has been fixed by us ! */ } static int hfsplus_extract_file(const char* filename, HfsPPrivateFile* hfsp_file) { FILE* fout; unsigned int cp_sect; PedSector rem_sect; fout = fopen(filename, "w"); if (!fout) return 0; for (rem_sect = hfsp_file->sect_nb; rem_sect; rem_sect -= cp_sect) { cp_sect = rem_sect < BLOCK_MAX_BUFF ? rem_sect : BLOCK_MAX_BUFF; if (!hfsplus_file_read(hfsp_file, extract_buffer, hfsp_file->sect_nb - rem_sect, cp_sect)) goto err_close; if (!fwrite (extract_buffer, cp_sect * PED_SECTOR_SIZE_DEFAULT, 1, fout)) goto err_close; } return (fclose(fout) == 0 ? 1 : 0); err_close: fclose(fout); return 0; } static int hfsplus_extract_vh (const char* filename, PedFileSystem* fs) { HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) fs->type_specific; FILE* fout; PedGeometry* geom = priv_data->plus_geom; fout = fopen(filename, "w"); if (!fout) return 0; if (!ped_geometry_read(geom, extract_buffer, 2, 1)) goto err_close; if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout)) goto err_close; return (fclose(fout) == 0 ? 1 : 0); err_close: fclose(fout); return 0; } /* TODO : use the timer to report what is happening */ /* TODO : use exceptions to report errors */ static int hfsplus_extract (PedFileSystem* fs, PedTimer* timer) { HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) fs->type_specific; HfsPVolumeHeader* vh = priv_data->vh; HfsPPrivateFile* startup_file; if (priv_data->wrapper) { /* TODO : create nested timer */ hfs_extract (priv_data->wrapper, timer); } ped_exception_throw ( PED_EXCEPTION_INFORMATION, PED_EXCEPTION_OK, _("This is not a real %s check. This is going to extract " "special low level files for debugging purposes."), "HFS+"); extract_buffer = ped_malloc(BLOCK_MAX_BUFF * PED_SECTOR_SIZE_DEFAULT); if (!extract_buffer) return 0; hfsplus_extract_vh(HFSP_VH_FILENAME, fs); hfsplus_extract_file(HFSP_CATALOG_FILENAME, priv_data->catalog_file); hfsplus_extract_file(HFSP_EXTENTS_FILENAME, priv_data->extents_file); hfsplus_extract_file(HFSP_ATTRIB_FILENAME, priv_data->attributes_file); hfsplus_extract_file(HFSP_BITMAP_FILENAME, priv_data->allocation_file); startup_file = hfsplus_file_open(fs, PED_CPU_TO_BE32(HFSP_STARTUP_ID), vh->startup_file.extents, PED_BE64_TO_CPU ( vh->startup_file.logical_size) / PED_SECTOR_SIZE_DEFAULT); if (startup_file) { hfsplus_extract_file(HFSP_STARTUP_FILENAME, startup_file); hfsplus_file_close(startup_file); startup_file = NULL; } free(extract_buffer); extract_buffer = NULL; return 0; /* nothing has been fixed by us ! */ } #endif /* HFS_EXTRACT_FS */ #endif /* !DISCOVER_ONLY */