summaryrefslogtreecommitdiffstats
path: root/libparted/fs/r/hfs/hfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'libparted/fs/r/hfs/hfs.c')
-rw-r--r--libparted/fs/r/hfs/hfs.c1166
1 files changed, 1166 insertions, 0 deletions
diff --git a/libparted/fs/r/hfs/hfs.c b/libparted/fs/r/hfs/hfs.c
new file mode 100644
index 0000000..8959b47
--- /dev/null
+++ b/libparted/fs/r/hfs/hfs.c
@@ -0,0 +1,1166 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2000, 2003-2005, 2007, 2009-2014, 2019-2023 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ Author : Guillaume Knispel <k_guillaume@libertysurf.fr>
+ Report bug to <bug-parted@gnu.org>
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+#include <stdint.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# 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<<HFSP_JOURNALED)) {
+ if (!hfsj_replay_journal(fs))
+ goto hpo_pg;
+ }
+
+ 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_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 <stdio.h>
+
+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 */