| /* |
| libparted - a library for manipulating disk partitions |
| Copyright (C) 2000, 2003, 2004, 2005, 2007 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 2 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, write to the Free Software |
| Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| /* |
| 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 ----- */ |
| |
| /* This is a very unundoable operation */ |
| /* Maybe I shouldn't touch the alternate MDB ? */ |
| /* Anyway clobber is call before other fs creation */ |
| /* So this is a non-issue */ |
| static int |
| hfs_clobber (PedGeometry* geom) |
| { |
| uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; |
| |
| memset (buf, 0, PED_SECTOR_SIZE_DEFAULT); |
| |
| /* destroy boot blocks, mdb, alternate mdb ... */ |
| return (!!ped_geometry_write (geom, buf, 0, 1)) & |
| (!!ped_geometry_write (geom, buf, 1, 1)) & |
| (!!ped_geometry_write (geom, buf, 2, 1)) & |
| (!!ped_geometry_write (geom, buf, geom->length - 2, 1)) & |
| (!!ped_geometry_write (geom, buf, geom->length - 1, 1)) & |
| (!!ped_geometry_sync (geom)); |
| } |
| |
| static 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: ped_free(priv_data); |
| ho_mdb: ped_free(mdb); |
| ho_fs: ped_free(fs); |
| ho: return NULL; |
| } |
| |
| static 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); |
| ped_free (priv_data->mdb); |
| ped_free (priv_data); |
| ped_geometry_destroy (fs->geom); |
| ped_free (fs); |
| |
| return 1; |
| } |
| |
| static 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); |
| } |
| |
| static 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, return 0); |
| PED_ASSERT (fs->geom != NULL, return 0); |
| PED_ASSERT (geom != NULL, return 0); |
| #ifdef DEBUG |
| PED_ASSERT ((hgee = hfs_get_empty_end(fs)) != 0, return 0); |
| #else |
| if ((hgee = hfs_get_empty_end(fs)) == 0) |
| return 0; |
| #endif |
| |
| PED_ASSERT ((hgee = hfs_get_empty_end(fs)) != 0, return 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" |
| |
| static int |
| hfsplus_clobber (PedGeometry* geom) |
| { |
| unsigned int i = 1; |
| uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; |
| HfsMasterDirectoryBlock *mdb; |
| |
| mdb = (HfsMasterDirectoryBlock *) buf; |
| |
| if (!ped_geometry_read (geom, buf, 2, 1)) |
| return 0; |
| |
| if (PED_BE16_TO_CPU (mdb->signature) == HFS_SIGNATURE) { |
| /* embedded hfs+ */ |
| PedGeometry *embedded; |
| |
| i = PED_BE32_TO_CPU(mdb->block_size) / PED_SECTOR_SIZE_DEFAULT; |
| embedded = ped_geometry_new ( |
| geom->dev, |
| (PedSector) geom->start |
| + PED_BE16_TO_CPU (mdb->start_block) |
| + (PedSector) PED_BE16_TO_CPU ( |
| mdb->old_new.embedded.location.start_block ) * i, |
| (PedSector) PED_BE16_TO_CPU ( |
| mdb->old_new.embedded.location.block_count ) * i ); |
| if (!embedded) i = 0; |
| else { |
| i = hfs_clobber (embedded); |
| ped_geometry_destroy (embedded); |
| } |
| } |
| |
| /* non-embedded or envelop destroy as hfs */ |
| return ( hfs_clobber (geom) && i ); |
| } |
| |
| static 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); |
| ped_free(priv_data->alloc_map); |
| ped_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); |
| ped_free(priv_data->vh); |
| ped_free(priv_data); |
| ped_free(fs); |
| |
| return 1; |
| } |
| |
| static 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: ped_free(priv_data->alloc_map); |
| hpo_dm: ped_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: ped_free(priv_data); |
| hpo_vh: ped_free(vh); |
| hpo_fs: ped_free(fs); |
| hpo: return NULL; |
| } |
| |
| static 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; |
| } |
| |
| 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; |
| } |
| |
| static 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, return 0); |
| PED_ASSERT (fs->geom != NULL, return 0); |
| PED_ASSERT (geom != NULL, return 0); |
| PED_ASSERT (fs->geom->dev == geom->dev, return 0); |
| #ifdef DEBUG |
| PED_ASSERT ((hgms = hfsplus_get_min_size (fs)) != 0, return 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); |
| 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); |
| |
| ped_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; |
| } |
| |
| ped_free(extract_buffer); extract_buffer = NULL; |
| return 0; /* nothing has been fixed by us ! */ |
| } |
| #endif /* HFS_EXTRACT_FS */ |
| |
| #endif /* !DISCOVER_ONLY */ |
| |
| static PedFileSystemOps hfs_ops = { |
| probe: hfs_probe, |
| #ifndef DISCOVER_ONLY |
| clobber: hfs_clobber, |
| open: hfs_open, |
| create: NULL, |
| close: hfs_close, |
| #ifndef HFS_EXTRACT_FS |
| check: NULL, |
| #else |
| check: hfs_extract, |
| #endif |
| copy: NULL, |
| resize: hfs_resize, |
| get_create_constraint: NULL, |
| get_resize_constraint: hfs_get_resize_constraint, |
| get_copy_constraint: NULL, |
| #else /* DISCOVER_ONLY */ |
| clobber: NULL, |
| open: NULL, |
| create: NULL, |
| close: NULL, |
| check: NULL, |
| copy: NULL, |
| resize: NULL, |
| get_create_constraint: NULL, |
| get_resize_constraint: NULL, |
| get_copy_constraint: NULL, |
| #endif /* DISCOVER_ONLY */ |
| }; |
| |
| static PedFileSystemOps hfsplus_ops = { |
| probe: hfsplus_probe, |
| #ifndef DISCOVER_ONLY |
| clobber: hfsplus_clobber, |
| open: hfsplus_open, |
| create: NULL, |
| close: hfsplus_close, |
| #ifndef HFS_EXTRACT_FS |
| check: NULL, |
| #else |
| check: hfsplus_extract, |
| #endif |
| copy: NULL, |
| resize: hfsplus_resize, |
| get_create_constraint: NULL, |
| get_resize_constraint: hfsplus_get_resize_constraint, |
| get_copy_constraint: NULL, |
| #else /* DISCOVER_ONLY */ |
| clobber: NULL, |
| open: NULL, |
| create: NULL, |
| close: NULL, |
| check: NULL, |
| copy: NULL, |
| resize: NULL, |
| get_create_constraint: NULL, |
| get_resize_constraint: NULL, |
| get_copy_constraint: NULL, |
| #endif /* DISCOVER_ONLY */ |
| }; |
| |
| static PedFileSystemOps hfsx_ops = { |
| probe: hfsx_probe, |
| #ifndef DISCOVER_ONLY |
| clobber: hfs_clobber, /* NOT hfsplus_clobber ! |
| HFSX can't be embedded */ |
| open: hfsplus_open, |
| create: NULL, |
| close: hfsplus_close, |
| #ifndef HFS_EXTRACT_FS |
| check: NULL, |
| #else |
| check: hfsplus_extract, |
| #endif |
| copy: NULL, |
| resize: hfsplus_resize, |
| get_create_constraint: NULL, |
| get_resize_constraint: hfsplus_get_resize_constraint, |
| get_copy_constraint: NULL, |
| #else /* DISCOVER_ONLY */ |
| clobber: NULL, |
| open: NULL, |
| create: NULL, |
| close: NULL, |
| check: NULL, |
| copy: NULL, |
| resize: NULL, |
| get_create_constraint: NULL, |
| get_resize_constraint: NULL, |
| get_copy_constraint: NULL, |
| #endif /* DISCOVER_ONLY */ |
| }; |
| |
| |
| static PedFileSystemType hfs_type = { |
| next: NULL, |
| ops: &hfs_ops, |
| name: "hfs", |
| block_sizes: HFS_BLOCK_SIZES |
| }; |
| |
| static PedFileSystemType hfsplus_type = { |
| next: NULL, |
| ops: &hfsplus_ops, |
| name: "hfs+", |
| block_sizes: HFSP_BLOCK_SIZES |
| }; |
| |
| static PedFileSystemType hfsx_type = { |
| next: NULL, |
| ops: &hfsx_ops, |
| name: "hfsx", |
| block_sizes: HFSX_BLOCK_SIZES |
| }; |
| |
| void |
| ped_file_system_hfs_init () |
| { |
| ped_file_system_type_register (&hfs_type); |
| ped_file_system_type_register (&hfsplus_type); |
| ped_file_system_type_register (&hfsx_type); |
| } |
| |
| void |
| ped_file_system_hfs_done () |
| { |
| ped_file_system_type_unregister (&hfs_type); |
| ped_file_system_type_unregister (&hfsplus_type); |
| ped_file_system_type_unregister (&hfsx_type); |
| } |