1 /*
   2     libparted - a library for manipulating disk partitions
   3     Copyright (C) 2000, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
   4 
   5     This program is free software; you can redistribute it and/or modify
   6     it under the terms of the GNU General Public License as published by
   7     the Free Software Foundation; either version 3 of the License, or
   8     (at your option) any later version.
   9 
  10     This program is distributed in the hope that it will be useful,
  11     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13     GNU General Public License for more details.
  14 
  15     You should have received a copy of the GNU General Public License
  16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17 */
  18 
  19 /* 
  20    Author : Guillaume Knispel <k_guillaume@libertysurf.fr> 
  21    Report bug to <bug-parted@gnu.org>
  22 */
  23 
  24 #include <config.h>
  25 
  26 #include <parted/parted.h>
  27 #include <parted/endian.h>
  28 #include <parted/debug.h>
  29 #include <stdint.h>
  30 
  31 #if ENABLE_NLS
  32 #  include <libintl.h>
  33 #  define _(String) dgettext (PACKAGE, String)
  34 #else
  35 #  define _(String) (String)
  36 #endif /* ENABLE_NLS */
  37 
  38 #include "hfs.h"
  39 #include "probe.h"
  40 
  41 uint8_t* hfs_block = NULL;
  42 uint8_t* hfsp_block = NULL;
  43 unsigned hfs_block_count;
  44 unsigned hfsp_block_count;
  45 
  46 #define HFS_BLOCK_SIZES       ((int[2]){512, 0})
  47 #define HFSP_BLOCK_SIZES       ((int[2]){512, 0})
  48 #define HFSX_BLOCK_SIZES       ((int[2]){512, 0})
  49 
  50 #ifndef DISCOVER_ONLY
  51 #include "file.h"
  52 #include "reloc.h"
  53 #include "advfs.h"
  54 
  55 static PedFileSystemType hfs_type;
  56 static PedFileSystemType hfsplus_type;
  57 
  58 
  59 /* ----- HFS ----- */
  60 
  61 /* This is a very unundoable operation */
  62 /* Maybe I shouldn't touch the alternate MDB ? */
  63 /* Anyway clobber is call before other fs creation */
  64 /* So this is a non-issue */
  65 static int
  66 hfs_clobber (PedGeometry* geom)
  67 {
  68         uint8_t buf[PED_SECTOR_SIZE_DEFAULT];
  69 
  70         memset (buf, 0, PED_SECTOR_SIZE_DEFAULT);
  71         
  72         /* destroy boot blocks, mdb, alternate mdb ... */
  73         return  (!!ped_geometry_write (geom, buf, 0, 1)) &
  74                 (!!ped_geometry_write (geom, buf, 1, 1)) &
  75                 (!!ped_geometry_write (geom, buf, 2, 1)) &
  76                 (!!ped_geometry_write (geom, buf, geom->length - 2, 1)) &
  77                 (!!ped_geometry_write (geom, buf, geom->length - 1, 1)) &
  78                 (!!ped_geometry_sync  (geom));
  79 }
  80 
  81 static PedFileSystem*
  82 hfs_open (PedGeometry* geom)
  83 {
  84         uint8_t                 buf[PED_SECTOR_SIZE_DEFAULT];
  85         PedFileSystem*          fs;
  86         HfsMasterDirectoryBlock* mdb;
  87         HfsPrivateFSData*       priv_data;
  88 
  89         if (!hfsc_can_use_geom (geom))
  90                 return NULL;
  91 
  92         /* Read MDB */
  93         if (!ped_geometry_read (geom, buf, 2, 1))
  94                 return NULL;
  95         
  96         /* Allocate memory */
  97         fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem));
  98         if (!fs) goto ho;
  99         mdb = (HfsMasterDirectoryBlock*)
 100                 ped_malloc (sizeof (HfsMasterDirectoryBlock));
 101         if (!mdb) goto ho_fs;
 102         priv_data = (HfsPrivateFSData*)
 103                 ped_malloc (sizeof (HfsPrivateFSData));
 104         if (!priv_data) goto ho_mdb;
 105 
 106         memcpy (mdb, buf, sizeof (HfsMasterDirectoryBlock));
 107 
 108         /* init structures */
 109         priv_data->mdb = mdb;
 110         priv_data->bad_blocks_loaded = 0;
 111         priv_data->bad_blocks_xtent_nb = 0;
 112         priv_data->bad_blocks_xtent_list = NULL;
 113         priv_data->extent_file =
 114             hfs_file_open (fs, PED_CPU_TO_BE32 (HFS_XTENT_ID),
 115                            mdb->extents_file_rec,
 116                            PED_CPU_TO_BE32 (mdb->extents_file_size)
 117                            / PED_SECTOR_SIZE_DEFAULT);
 118         if (!priv_data->extent_file) goto ho_pd;
 119         priv_data->catalog_file =
 120             hfs_file_open (fs, PED_CPU_TO_BE32 (HFS_CATALOG_ID),
 121                            mdb->catalog_file_rec,
 122                            PED_CPU_TO_BE32 (mdb->catalog_file_size)
 123                            / PED_SECTOR_SIZE_DEFAULT);
 124         if (!priv_data->catalog_file) goto ho_ce;
 125         /* Read allocation blocks */
 126         if (!ped_geometry_read(geom, priv_data->alloc_map,
 127                                PED_BE16_TO_CPU (mdb->volume_bitmap_block),
 128                                ( PED_BE16_TO_CPU (mdb->total_blocks)
 129                                  + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
 130                                / (PED_SECTOR_SIZE_DEFAULT * 8) ) )
 131                 goto ho_cf;
 132 
 133         fs->type = &hfs_type;
 134         fs->geom = ped_geometry_duplicate (geom);
 135         if (!fs->geom) goto ho_cf;
 136         fs->type_specific = (void*) priv_data;
 137         fs->checked = ( PED_BE16_TO_CPU (mdb->volume_attributes)
 138                         >> HFS_UNMOUNTED ) & 1;
 139 
 140         return fs;
 141 
 142 /*--- clean error handling ---*/
 143 ho_cf:  hfs_file_close(priv_data->catalog_file);
 144 ho_ce:  hfs_file_close(priv_data->extent_file);
 145 ho_pd:  ped_free(priv_data);
 146 ho_mdb: ped_free(mdb);
 147 ho_fs:  ped_free(fs);
 148 ho:     return NULL;
 149 }
 150 
 151 static int
 152 hfs_close (PedFileSystem *fs)
 153 {
 154         HfsPrivateFSData* priv_data = (HfsPrivateFSData*) fs->type_specific;
 155 
 156         hfs_file_close (priv_data->extent_file);
 157         hfs_file_close (priv_data->catalog_file);
 158         if (priv_data->bad_blocks_loaded)
 159                 hfs_free_bad_blocks_list (priv_data->bad_blocks_xtent_list);
 160         ped_free (priv_data->mdb);
 161         ped_free (priv_data);
 162         ped_geometry_destroy (fs->geom);
 163         ped_free (fs);
 164 
 165         return 1;
 166 }
 167 
 168 static PedConstraint* 
 169 hfs_get_resize_constraint (const PedFileSystem *fs)
 170 {
 171         PedDevice*      dev = fs->geom->dev;
 172         PedAlignment    start_align;
 173         PedGeometry     start_sector;
 174         PedGeometry     full_dev;
 175         PedSector       min_size;
 176 
 177         if (!ped_alignment_init (&start_align, fs->geom->start, 0))
 178                 return NULL;
 179         if (!ped_geometry_init (&start_sector, dev, fs->geom->start, 1))
 180                 return NULL;
 181         if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
 182                 return NULL;
 183         /* 2 = last two sectors (alternate MDB and unused sector) */
 184         min_size = hfs_get_empty_end(fs) + 2;
 185         if (min_size == 2) return NULL;
 186 
 187         return ped_constraint_new (&start_align, ped_alignment_any,
 188                                    &start_sector, &full_dev, min_size,
 189                                    fs->geom->length);
 190 }
 191 
 192 static int
 193 hfs_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
 194 {
 195         uint8_t                 buf[PED_SECTOR_SIZE_DEFAULT];
 196         unsigned int            nblock, nfree;
 197         unsigned int            block, to_free;
 198         HfsPrivateFSData*       priv_data;
 199         HfsMasterDirectoryBlock* mdb;
 200         int                     resize = 1;
 201         unsigned int            hfs_sect_block;
 202         PedSector               hgee;
 203 
 204         /* check preconditions */
 205         PED_ASSERT (fs != NULL, return 0);
 206         PED_ASSERT (fs->geom != NULL, return 0);
 207         PED_ASSERT (geom != NULL, return 0);
 208 #ifdef DEBUG
 209         PED_ASSERT ((hgee = hfs_get_empty_end(fs)) != 0, return 0);
 210 #else
 211         if ((hgee = hfs_get_empty_end(fs)) == 0)
 212                 return 0;
 213 #endif
 214 
 215         PED_ASSERT ((hgee = hfs_get_empty_end(fs)) != 0, return 0);
 216 
 217         if (ped_geometry_test_equal(fs->geom, geom))
 218                 return 1;
 219 
 220         priv_data = (HfsPrivateFSData*) fs->type_specific;
 221         mdb = priv_data->mdb;
 222         hfs_sect_block = PED_BE32_TO_CPU (mdb->block_size)
 223                          / PED_SECTOR_SIZE_DEFAULT;
 224 
 225         if (fs->geom->start != geom->start
 226             || geom->length > fs->geom->length
 227             || geom->length < hgee + 2) {
 228                 ped_exception_throw (
 229                         PED_EXCEPTION_NO_FEATURE,
 230                         PED_EXCEPTION_CANCEL,
 231                         _("Sorry, HFS cannot be resized that way yet."));
 232                 return 0;
 233         }
 234 
 235         /* Flush caches */
 236         if (!ped_geometry_sync(fs->geom))
 237                 return 0;
 238 
 239         /* Clear the unmounted bit */
 240         mdb->volume_attributes &= PED_CPU_TO_BE16 (~( 1 << HFS_UNMOUNTED ));
 241         if (!ped_geometry_read (fs->geom, buf, 2, 1))
 242                 return 0;
 243         memcpy (buf, mdb, sizeof (HfsMasterDirectoryBlock));
 244         if (   !ped_geometry_write (fs->geom, buf, 2, 1)
 245             || !ped_geometry_sync  (fs->geom))
 246                 return 0;
 247 
 248         ped_timer_reset (timer);
 249         ped_timer_set_state_name(timer, _("shrinking"));
 250         ped_timer_update(timer, 0.0);
 251         /* relocate data */
 252         to_free = ( fs->geom->length - geom->length
 253                     + hfs_sect_block - 1 )
 254                   / hfs_sect_block ;
 255         block = hfs_find_start_pack (fs, to_free);
 256         if (!hfs_pack_free_space_from_block (fs, block, timer, to_free)) {
 257                 resize = 0;
 258                 ped_exception_throw (
 259                         PED_EXCEPTION_ERROR,
 260                         PED_EXCEPTION_CANCEL,
 261                         _("Data relocation has failed."));
 262                 goto write_MDB;
 263         }
 264 
 265         /* Calculate new block number and other MDB field */
 266         nblock = ( geom->length - (PED_BE16_TO_CPU (mdb->start_block) + 2) )
 267                  / hfs_sect_block;
 268         nfree = PED_BE16_TO_CPU (mdb->free_blocks)
 269                 - ( PED_BE16_TO_CPU (mdb->total_blocks) - nblock );
 270 
 271         /* Check that all block after future end are really free */
 272         for (block = nblock;
 273              block < PED_BE16_TO_CPU (mdb->total_blocks);
 274              block++) {
 275                 if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) {
 276                         resize = 0;
 277                         ped_exception_throw (
 278                                 PED_EXCEPTION_ERROR,
 279                                 PED_EXCEPTION_CANCEL,
 280                                 _("Data relocation left some data in the end "
 281                                   "of the volume."));
 282                         goto write_MDB;
 283                 }
 284         }
 285 
 286         /* Mark out of volume blocks as used
 287         (broken implementations compatibility) */
 288         for ( block = nblock; block < (1 << 16); ++block)
 289                 SET_BLOC_OCCUPATION(priv_data->alloc_map,block);
 290 
 291         /* save the allocation map
 292         I do not write until start of allocation blocks 
 293         but only until pre-resize end of bitmap blocks
 294         because the specifications do _not_ assert that everything
 295         until allocation blocks is boot, mdb and alloc */
 296         ped_geometry_write(fs->geom, priv_data->alloc_map,
 297                 PED_BE16_TO_CPU (priv_data->mdb->volume_bitmap_block),
 298                 ( PED_BE16_TO_CPU (priv_data->mdb->total_blocks)
 299                   + PED_SECTOR_SIZE_DEFAULT * 8 - 1)
 300                 / (PED_SECTOR_SIZE_DEFAULT * 8));
 301 
 302         /* Update geometry */
 303         if (resize) {
 304                 /* update in fs structure */
 305                 if (PED_BE16_TO_CPU (mdb->next_allocation) >= nblock)
 306                         mdb->next_allocation = PED_CPU_TO_BE16 (0);
 307                 mdb->total_blocks = PED_CPU_TO_BE16 (nblock);
 308                 mdb->free_blocks = PED_CPU_TO_BE16 (nfree);
 309                 /* update parted structure */
 310                 fs->geom->length = geom->length;
 311                 fs->geom->end = fs->geom->start + geom->length - 1;
 312         }
 313 
 314         /* Set the unmounted bit */
 315         mdb->volume_attributes |= PED_CPU_TO_BE16 ( 1 << HFS_UNMOUNTED );
 316 
 317         /* Effective write */
 318     write_MDB:
 319         ped_timer_set_state_name(timer,_("writing HFS Master Directory Block"));
 320 
 321         if (!hfs_update_mdb(fs)) {
 322                 ped_geometry_sync(geom);
 323                 return 0;
 324         }
 325 
 326         if (!ped_geometry_sync(geom))
 327                 return 0;
 328 
 329         ped_timer_update(timer, 1.0);
 330 
 331         return (resize);
 332 }
 333 
 334 /* ----- HFS+ ----- */
 335 
 336 #include "file_plus.h"
 337 #include "advfs_plus.h"
 338 #include "reloc_plus.h"
 339 #include "journal.h"
 340 
 341 static int
 342 hfsplus_clobber (PedGeometry* geom)
 343 {
 344         unsigned int i = 1;
 345         uint8_t                         buf[PED_SECTOR_SIZE_DEFAULT];
 346         HfsMasterDirectoryBlock         *mdb;
 347 
 348         mdb = (HfsMasterDirectoryBlock *) buf;
 349 
 350         if (!ped_geometry_read (geom, buf, 2, 1))
 351                 return 0;
 352 
 353         if (PED_BE16_TO_CPU (mdb->signature) == HFS_SIGNATURE) {
 354                 /* embedded hfs+ */
 355                 PedGeometry     *embedded;
 356 
 357                 i = PED_BE32_TO_CPU(mdb->block_size) / PED_SECTOR_SIZE_DEFAULT;
 358                 embedded = ped_geometry_new (
 359                     geom->dev,
 360                     (PedSector) geom->start
 361                      + PED_BE16_TO_CPU (mdb->start_block)
 362                      + (PedSector) PED_BE16_TO_CPU (
 363                         mdb->old_new.embedded.location.start_block ) * i, 
 364                     (PedSector) PED_BE16_TO_CPU (
 365                         mdb->old_new.embedded.location.block_count ) * i );
 366                 if (!embedded) i = 0;
 367                 else {
 368                         i = hfs_clobber (embedded);
 369                         ped_geometry_destroy (embedded);
 370                 }
 371         }
 372 
 373         /* non-embedded or envelop destroy as hfs */
 374         return ( hfs_clobber (geom) && i );
 375 }
 376 
 377 static int
 378 hfsplus_close (PedFileSystem *fs)
 379 {
 380         HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
 381                                                 fs->type_specific;
 382 
 383         if (priv_data->bad_blocks_loaded)
 384                 hfsplus_free_bad_blocks_list(priv_data->bad_blocks_xtent_list);
 385         ped_free(priv_data->alloc_map);
 386         ped_free(priv_data->dirty_alloc_map);
 387         hfsplus_file_close (priv_data->allocation_file);
 388         hfsplus_file_close (priv_data->attributes_file);
 389         hfsplus_file_close (priv_data->catalog_file);
 390         hfsplus_file_close (priv_data->extents_file);
 391         if (priv_data->free_geom) ped_geometry_destroy (priv_data->plus_geom);
 392         if (priv_data->wrapper) hfs_close(priv_data->wrapper);
 393         ped_geometry_destroy (fs->geom);
 394         ped_free(priv_data->vh);
 395         ped_free(priv_data);
 396         ped_free(fs);
 397 
 398         return 1;
 399 }
 400 
 401 static PedFileSystem*
 402 hfsplus_open (PedGeometry* geom)
 403 {
 404         uint8_t                 buf[PED_SECTOR_SIZE_DEFAULT];
 405         PedFileSystem*          fs;
 406         HfsPVolumeHeader*       vh;
 407         HfsPPrivateFSData*      priv_data;
 408         PedGeometry*            wrapper_geom;
 409         unsigned int            map_sectors;
 410 
 411         if (!hfsc_can_use_geom (geom))
 412                 return NULL;
 413 
 414         fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem));
 415         if (!fs) goto hpo;
 416         vh = (HfsPVolumeHeader*) ped_malloc (sizeof (HfsPVolumeHeader));
 417         if (!vh) goto hpo_fs;
 418         priv_data = (HfsPPrivateFSData*)ped_malloc (sizeof (HfsPPrivateFSData));
 419         if (!priv_data) goto hpo_vh;
 420 
 421         fs->geom = ped_geometry_duplicate (geom);
 422         if (!fs->geom) goto hpo_pd;
 423         fs->type_specific = (void*) priv_data;
 424 
 425         if ((wrapper_geom = hfs_and_wrapper_probe (geom))) {
 426                 HfsPrivateFSData*       hfs_priv_data;
 427                 PedSector               abs_sect, length;
 428                 unsigned int            bs;
 429 
 430                 ped_geometry_destroy (wrapper_geom);
 431                 priv_data->wrapper = hfs_open(geom);
 432                 if (!priv_data->wrapper) goto hpo_gm;
 433                 hfs_priv_data = (HfsPrivateFSData*)
 434                         priv_data->wrapper->type_specific;
 435                 bs = PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size)
 436                      / PED_SECTOR_SIZE_DEFAULT;
 437                 abs_sect = (PedSector) geom->start
 438                            + (PedSector) PED_BE16_TO_CPU (
 439                                             hfs_priv_data->mdb->start_block)
 440                            + (PedSector) PED_BE16_TO_CPU (
 441                                             hfs_priv_data->mdb->old_new
 442                                             .embedded.location.start_block )
 443                                          * bs;
 444                 length = (PedSector) PED_BE16_TO_CPU (
 445                                             hfs_priv_data->mdb->old_new
 446                                             .embedded.location.block_count)
 447                                      * bs;
 448                 priv_data->plus_geom = ped_geometry_new (geom->dev, abs_sect,
 449                                                          length);
 450                 if (!priv_data->plus_geom) goto hpo_wr;
 451                 priv_data->free_geom = 1;
 452         } else {
 453                 priv_data->wrapper = NULL;
 454                 priv_data->plus_geom = fs->geom;
 455                 priv_data->free_geom = 0;
 456         }
 457 
 458         if (!ped_geometry_read (priv_data->plus_geom, buf, 2, 1)) goto hpo_pg;
 459         memcpy (vh, buf, sizeof (HfsPVolumeHeader));
 460         priv_data->vh = vh;
 461 
 462         if (vh->signature != PED_CPU_TO_BE16(HFSP_SIGNATURE)
 463             && vh->signature != PED_CPU_TO_BE16(HFSX_SIGNATURE)) {
 464                 ped_exception_throw (
 465                         PED_EXCEPTION_BUG,
 466                         PED_EXCEPTION_CANCEL,
 467                         _("No valid HFS[+X] signature has been found while "
 468                           "opening."));
 469                 goto hpo_pg;
 470         }
 471 
 472         if (vh->signature == PED_CPU_TO_BE16(HFSP_SIGNATURE)
 473             && vh->version != PED_CPU_TO_BE16(HFSP_VERSION)) {
 474                 if (ped_exception_throw (
 475                         PED_EXCEPTION_NO_FEATURE,
 476                         PED_EXCEPTION_IGNORE_CANCEL,
 477                         _("Version %d of HFS+ isn't supported."),
 478                         PED_BE16_TO_CPU(vh->version))
 479                                 != PED_EXCEPTION_IGNORE)
 480                         goto hpo_pg;
 481         }
 482 
 483         if (vh->signature == PED_CPU_TO_BE16(HFSX_SIGNATURE)
 484             && vh->version != PED_CPU_TO_BE16(HFSX_VERSION)) {
 485                 if (ped_exception_throw (
 486                         PED_EXCEPTION_NO_FEATURE,
 487                         PED_EXCEPTION_IGNORE_CANCEL,
 488                         _("Version %d of HFSX isn't supported."),
 489                         PED_BE16_TO_CPU(vh->version))
 490                                 != PED_EXCEPTION_IGNORE)
 491                         goto hpo_pg;
 492         }
 493 
 494         priv_data->jib_start_block = 0;
 495         priv_data->jl_start_block = 0;
 496         if (vh->attributes & PED_CPU_TO_BE32(1<<HFSP_JOURNALED)) {
 497                 if (!hfsj_replay_journal(fs))
 498                         goto hpo_pg;
 499         }
 500 
 501         priv_data->bad_blocks_loaded = 0;
 502         priv_data->bad_blocks_xtent_nb = 0;
 503         priv_data->bad_blocks_xtent_list = NULL;
 504         priv_data->extents_file =
 505                 hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFS_XTENT_ID),
 506                                    vh->extents_file.extents,
 507                                    PED_BE64_TO_CPU (
 508                                         vh->extents_file.logical_size )
 509                                    / PED_SECTOR_SIZE_DEFAULT);
 510         if (!priv_data->extents_file) goto hpo_pg;
 511         priv_data->catalog_file =
 512                 hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFS_CATALOG_ID),
 513                                    vh->catalog_file.extents,
 514                                    PED_BE64_TO_CPU (
 515                                         vh->catalog_file.logical_size )
 516                                    / PED_SECTOR_SIZE_DEFAULT);
 517         if (!priv_data->catalog_file) goto hpo_ce;
 518         priv_data->attributes_file =
 519                 hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFSP_ATTRIB_ID),
 520                                    vh->attributes_file.extents,
 521                                    PED_BE64_TO_CPU (
 522                                         vh->attributes_file.logical_size)
 523                                    / PED_SECTOR_SIZE_DEFAULT);
 524         if (!priv_data->attributes_file) goto hpo_cc;
 525 
 526         map_sectors = ( PED_BE32_TO_CPU (vh->total_blocks) 
 527                         + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
 528                       / (PED_SECTOR_SIZE_DEFAULT * 8);
 529         priv_data->dirty_alloc_map = (uint8_t*)
 530                 ped_malloc ((map_sectors + 7) / 8);
 531         if (!priv_data->dirty_alloc_map) goto hpo_cl;
 532         memset(priv_data->dirty_alloc_map, 0, (map_sectors + 7) / 8);
 533         priv_data->alloc_map = (uint8_t*)
 534                 ped_malloc (map_sectors * PED_SECTOR_SIZE_DEFAULT);
 535         if (!priv_data->alloc_map) goto hpo_dm;
 536 
 537         priv_data->allocation_file =
 538                 hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFSP_ALLOC_ID),
 539                                    vh->allocation_file.extents,
 540                                    PED_BE64_TO_CPU (
 541                                         vh->allocation_file.logical_size)
 542                                    / PED_SECTOR_SIZE_DEFAULT);
 543         if (!priv_data->allocation_file) goto hpo_am;
 544         if (!hfsplus_file_read (priv_data->allocation_file,
 545                                 priv_data->alloc_map, 0, map_sectors)) {
 546                 hfsplus_close(fs);
 547                 return NULL;
 548         }
 549 
 550         fs->type = &hfsplus_type;
 551         fs->checked = ((PED_BE32_TO_CPU (vh->attributes) >> HFS_UNMOUNTED) & 1)
 552               && !((PED_BE32_TO_CPU (vh->attributes) >> HFSP_INCONSISTENT) & 1);
 553 
 554         return fs;
 555 
 556 /*--- clean error handling ---*/
 557 hpo_am: ped_free(priv_data->alloc_map);
 558 hpo_dm: ped_free(priv_data->dirty_alloc_map);
 559 hpo_cl: hfsplus_file_close (priv_data->attributes_file);
 560 hpo_cc: hfsplus_file_close (priv_data->catalog_file);
 561 hpo_ce: hfsplus_file_close (priv_data->extents_file);
 562 hpo_pg: if (priv_data->free_geom) ped_geometry_destroy (priv_data->plus_geom);
 563 hpo_wr: if (priv_data->wrapper) hfs_close(priv_data->wrapper);
 564 hpo_gm: ped_geometry_destroy (fs->geom);
 565 hpo_pd: ped_free(priv_data);
 566 hpo_vh: ped_free(vh);
 567 hpo_fs: ped_free(fs);
 568 hpo:    return NULL;
 569 }
 570 
 571 static PedConstraint* 
 572 hfsplus_get_resize_constraint (const PedFileSystem *fs)
 573 {
 574         PedDevice*      dev = fs->geom->dev;
 575         PedAlignment    start_align;
 576         PedGeometry     start_sector;
 577         PedGeometry     full_dev;
 578         PedSector       min_size;
 579 
 580         if (!ped_alignment_init (&start_align, fs->geom->start, 0))
 581                 return NULL;
 582         if (!ped_geometry_init (&start_sector, dev, fs->geom->start, 1))
 583                 return NULL;
 584         if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
 585                 return NULL;
 586 
 587         min_size = hfsplus_get_min_size (fs);
 588         if (!min_size) return NULL;
 589 
 590         return ped_constraint_new (&start_align, ped_alignment_any,
 591                                    &start_sector, &full_dev, min_size,
 592                                    fs->geom->length);
 593 }
 594 
 595 static int
 596 hfsplus_volume_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
 597 {
 598         uint8_t                 buf[PED_SECTOR_SIZE_DEFAULT];
 599         unsigned int            nblock, nfree, mblock;
 600         unsigned int            block, to_free, old_blocks;
 601         HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
 602                                                 fs->type_specific;
 603         HfsPVolumeHeader*       vh = priv_data->vh;
 604         int                     resize = 1;
 605         unsigned int            hfsp_sect_block =
 606                                     ( PED_BE32_TO_CPU (vh->block_size)
 607                                       / PED_SECTOR_SIZE_DEFAULT );
 608         unsigned int            map_sectors;
 609 
 610         old_blocks = PED_BE32_TO_CPU (vh->total_blocks);
 611 
 612         /* Flush caches */
 613         if (!ped_geometry_sync(priv_data->plus_geom))
 614                 return 0;
 615 
 616         /* Clear the unmounted bit */
 617         /* and set the implementation code (Apple Creator Code) */
 618         vh->attributes &= PED_CPU_TO_BE32 (~( 1 << HFS_UNMOUNTED ));
 619         vh->last_mounted_version = PED_CPU_TO_BE32(HFSP_IMPL_Shnk);
 620         if (!ped_geometry_read (priv_data->plus_geom, buf, 2, 1))
 621                 return 0;
 622         memcpy (buf, vh, sizeof (HfsPVolumeHeader));
 623         if (   !ped_geometry_write (priv_data->plus_geom, buf, 2, 1)
 624             || !ped_geometry_sync (priv_data->plus_geom))
 625                 return 0;
 626 
 627         ped_timer_reset (timer);
 628         ped_timer_set_state_name(timer, _("shrinking"));
 629         ped_timer_update(timer, 0.0);
 630         /* relocate data */
 631         to_free = ( priv_data->plus_geom->length 
 632                   - geom->length + hfsp_sect_block
 633                   - 1 ) / hfsp_sect_block;
 634         block = hfsplus_find_start_pack (fs, to_free);
 635         if (!hfsplus_pack_free_space_from_block (fs, block, timer, to_free)) {
 636                 resize = 0;
 637                 ped_exception_throw (
 638                         PED_EXCEPTION_ERROR,
 639                         PED_EXCEPTION_CANCEL,
 640                         _("Data relocation has failed."));
 641                 goto write_VH;
 642         }
 643 
 644         /* Calculate new block number and other VH field */
 645         /* nblock must be rounded _down_ */
 646         nblock = geom->length / hfsp_sect_block;
 647         nfree = PED_BE32_TO_CPU (vh->free_blocks) 
 648                 - (old_blocks - nblock);
 649         /* free block readjustement is only needed when incorrect nblock
 650            was used by my previous implementation, so detect the case */
 651         if (priv_data->plus_geom->length < old_blocks
 652                                            * ( PED_BE32_TO_CPU (vh->block_size)
 653                                                / PED_SECTOR_SIZE_DEFAULT) ) {
 654                 if (priv_data->plus_geom->length % hfsp_sect_block == 1)
 655                         nfree++;
 656         }
 657 
 658         /* Check that all block after future end are really free */
 659         mblock = ( priv_data->plus_geom->length - 2 )
 660                  / hfsp_sect_block;
 661         if (mblock > old_blocks - 1)
 662                 mblock = old_blocks - 1;
 663         for ( block = nblock;
 664               block < mblock;
 665               block++ ) {
 666                 if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) {
 667                         resize = 0;
 668                         ped_exception_throw (
 669                                 PED_EXCEPTION_ERROR,
 670                                 PED_EXCEPTION_CANCEL,
 671                                 _("Data relocation left some data at the end "
 672                                   "of the volume."));
 673                         goto write_VH;
 674                 }
 675         }
 676 
 677         /* Mark out of volume blocks as used */
 678         map_sectors = ( ( old_blocks + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
 679                         / (PED_SECTOR_SIZE_DEFAULT * 8) )
 680                       * (PED_SECTOR_SIZE_DEFAULT * 8);
 681         for ( block = nblock; block < map_sectors; ++block)
 682                 SET_BLOC_OCCUPATION(priv_data->alloc_map, block);
 683 
 684         /* Update geometry */
 685         if (resize) {
 686                 /* update in fs structure */
 687                 if (PED_BE32_TO_CPU (vh->next_allocation) >= nblock)
 688                         vh->next_allocation = PED_CPU_TO_BE32 (0);
 689                 vh->total_blocks = PED_CPU_TO_BE32 (nblock);
 690                 vh->free_blocks = PED_CPU_TO_BE32 (nfree);
 691                 /* update parted structure */
 692                 priv_data->plus_geom->length = geom->length;
 693                 priv_data->plus_geom->end = priv_data->plus_geom->start
 694                                             + geom->length - 1;
 695         }
 696 
 697         /* Effective write */
 698     write_VH:
 699         /* lasts two sectors are allocated by the alternate VH
 700            and a reserved sector, and last block is always reserved */
 701         block = (priv_data->plus_geom->length - 1) / hfsp_sect_block;
 702         if (block < PED_BE32_TO_CPU (vh->total_blocks))
 703                 SET_BLOC_OCCUPATION(priv_data->alloc_map, block);
 704         block = (priv_data->plus_geom->length - 2) / hfsp_sect_block;
 705         if (block < PED_BE32_TO_CPU (vh->total_blocks))
 706                 SET_BLOC_OCCUPATION(priv_data->alloc_map, block);
 707         SET_BLOC_OCCUPATION(priv_data->alloc_map,
 708                             PED_BE32_TO_CPU (vh->total_blocks) - 1);
 709 
 710         /* Write the _old_ area to set out of volume blocks as used */
 711         map_sectors = ( old_blocks + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
 712                       / (PED_SECTOR_SIZE_DEFAULT * 8);
 713         if (!hfsplus_file_write (priv_data->allocation_file,
 714                                  priv_data->alloc_map, 0, map_sectors)) {
 715                 resize = 0;
 716                 ped_exception_throw (
 717                         PED_EXCEPTION_ERROR,
 718                         PED_EXCEPTION_CANCEL,
 719                         _("Error while writing the allocation file."));
 720         } else {
 721         /* Write remaining part of allocation bitmap */
 722         /* This is necessary to handle pre patch-11 and third party */
 723         /* implementations */
 724                 memset(buf, 0xFF, PED_SECTOR_SIZE_DEFAULT);
 725                 for (block = map_sectors;
 726                      block < priv_data->allocation_file->sect_nb;
 727                      ++block) {
 728                         if (!hfsplus_file_write_sector (
 729                                         priv_data->allocation_file,
 730                                         buf, block)) {
 731                                 ped_exception_throw (
 732                                         PED_EXCEPTION_WARNING,
 733                                         PED_EXCEPTION_IGNORE,
 734                                         _("Error while writing the "
 735                                           "compatibility part of the "
 736                                           "allocation file."));
 737                                 break;
 738                         }
 739                 }
 740         }
 741         ped_geometry_sync (priv_data->plus_geom);
 742 
 743         if (resize) {
 744                 /* Set the unmounted bit and clear the inconsistent bit */
 745                 vh->attributes |= PED_CPU_TO_BE32 ( 1 << HFS_UNMOUNTED );
 746                 vh->attributes &= ~ PED_CPU_TO_BE32 ( 1 << HFSP_INCONSISTENT );
 747         }
 748 
 749         ped_timer_set_state_name(timer, _("writing HFS+ Volume Header"));
 750         if (!hfsplus_update_vh(fs)) {
 751                 ped_geometry_sync(priv_data->plus_geom);
 752                 return 0;
 753         }
 754 
 755         if (!ped_geometry_sync(priv_data->plus_geom))
 756                 return 0;
 757 
 758         ped_timer_update(timer, 1.0);
 759 
 760         return (resize);
 761 }
 762 
 763 /* Update the HFS wrapper mdb and bad blocks file to reflect
 764    the new geometry of the embedded HFS+ volume */
 765 static int
 766 hfsplus_wrapper_update (PedFileSystem* fs)
 767 {
 768         uint8_t                 node[PED_SECTOR_SIZE_DEFAULT];
 769         HfsCPrivateLeafRec      ref;
 770         HfsExtentKey            key;
 771         HfsNodeDescriptor*      node_desc = (HfsNodeDescriptor*) node;
 772         HfsExtentKey*           ret_key;
 773         HfsExtDescriptor*       ret_data;
 774         unsigned int            i;
 775         HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
 776                                                 fs->type_specific;
 777         HfsPrivateFSData*       hfs_priv_data = (HfsPrivateFSData*)
 778                                             priv_data->wrapper->type_specific;
 779         unsigned int            hfs_sect_block =
 780                         PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size)
 781                         / PED_SECTOR_SIZE_DEFAULT ;
 782         PedSector               hfsplus_sect = (PedSector)
 783                         PED_BE32_TO_CPU (priv_data->vh->total_blocks)
 784                         * ( PED_BE32_TO_CPU (priv_data->vh->block_size)
 785                             / PED_SECTOR_SIZE_DEFAULT );
 786         unsigned int            hfs_blocks_embedded =
 787                                     (hfsplus_sect + hfs_sect_block - 1)
 788                                     / hfs_sect_block;
 789         unsigned int            hfs_blocks_embedded_old;
 790 
 791         /* update HFS wrapper MDB */
 792         hfs_blocks_embedded_old = PED_BE16_TO_CPU (
 793                                         hfs_priv_data->mdb->old_new
 794                                         .embedded.location.block_count );
 795         hfs_priv_data->mdb->old_new.embedded.location.block_count =
 796                 PED_CPU_TO_BE16 (hfs_blocks_embedded);
 797         /* maybe macOS will boot with this */
 798         /* update : yes it does \o/ :) */
 799         hfs_priv_data->mdb->free_blocks =
 800             PED_CPU_TO_BE16 ( PED_BE16_TO_CPU (hfs_priv_data->mdb->free_blocks)
 801                             + hfs_blocks_embedded_old
 802                             - hfs_blocks_embedded );
 803 
 804         if (!hfs_update_mdb(priv_data->wrapper))
 805                 return 0;
 806 
 807         /* force reload bad block list */
 808         if (hfs_priv_data->bad_blocks_loaded) {
 809                 hfs_free_bad_blocks_list (hfs_priv_data->bad_blocks_xtent_list);
 810                 hfs_priv_data->bad_blocks_xtent_list = NULL;
 811                 hfs_priv_data->bad_blocks_xtent_nb = 0;
 812                 hfs_priv_data->bad_blocks_loaded = 0;
 813         }
 814 
 815         /* clean HFS wrapper allocation map */
 816         for (i = PED_BE16_TO_CPU (
 817                         hfs_priv_data->mdb->old_new.embedded
 818                         .location.start_block )
 819                  + hfs_blocks_embedded;
 820              i < PED_BE16_TO_CPU (
 821                         hfs_priv_data->mdb->old_new.embedded
 822                         .location.start_block )
 823                  + hfs_blocks_embedded_old;
 824              i++ ) {
 825                 CLR_BLOC_OCCUPATION(hfs_priv_data->alloc_map, i);
 826         }
 827         /* and save it */
 828         if (!ped_geometry_write (fs->geom, hfs_priv_data->alloc_map,
 829                                  PED_BE16_TO_CPU (
 830                                       hfs_priv_data->mdb->volume_bitmap_block ),
 831                                  ( PED_BE16_TO_CPU (
 832                                         hfs_priv_data->mdb->total_blocks ) 
 833                                    + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
 834                                  / (PED_SECTOR_SIZE_DEFAULT * 8)))
 835                 return 0;
 836         if (!ped_geometry_sync (fs->geom))
 837                 return 0;
 838 
 839         /* search and update the bad blocks file */
 840         key.key_length = sizeof(key) - 1;
 841         key.type = HFS_DATA_FORK;
 842         key.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID);
 843         key.start = 0;
 844         if (!hfs_btree_search (hfs_priv_data->extent_file,
 845                                (HfsPrivateGenericKey*) &key, NULL, 0, &ref)) {
 846                 ped_exception_throw (
 847                         PED_EXCEPTION_ERROR,
 848                         PED_EXCEPTION_CANCEL,
 849                         _("An error occurred while looking for the mandatory "
 850                           "bad blocks file."));
 851                 return 0;
 852         }
 853         if (!hfs_file_read_sector (hfs_priv_data->extent_file, node,
 854                                    ref.node_number))
 855                 return 0;
 856         ret_key = (HfsExtentKey*) (node + ref.record_pos);
 857         ret_data = (HfsExtDescriptor*) ( node + ref.record_pos
 858                                          + sizeof (HfsExtentKey) );
 859 
 860         while (ret_key->type == key.type && ret_key->file_ID == key.file_ID) {
 861                 for (i = 0; i < HFS_EXT_NB; i++) {
 862                         if ( ret_data[i].start_block 
 863                              == hfs_priv_data->mdb->old_new
 864                                 .embedded.location.start_block) {
 865                                 ret_data[i].block_count =
 866                                     hfs_priv_data->mdb->old_new
 867                                     .embedded.location.block_count;
 868                                 /* found ! : update */
 869                                 if (!hfs_file_write_sector (
 870                                           hfs_priv_data->extent_file,
 871                                           node, ref.node_number)
 872                                     || !ped_geometry_sync(fs->geom))
 873                                         return 0;
 874                                 return 1;
 875                         }
 876                 }
 877 
 878                 if (ref.record_number < PED_BE16_TO_CPU (node_desc->rec_nb)) {
 879                         ref.record_number++;
 880                 } else {
 881                         ref.node_number = PED_BE32_TO_CPU (node_desc->next);
 882                         if (!ref.node_number
 883                             || !hfs_file_read_sector(hfs_priv_data->extent_file,
 884                                                      node, ref.node_number))
 885                                 goto bb_not_found;
 886                         ref.record_number = 1;
 887                 }
 888 
 889                 ref.record_pos =
 890                         PED_BE16_TO_CPU (*((uint16_t *)
 891                                 (node + (PED_SECTOR_SIZE_DEFAULT
 892                                          - 2*ref.record_number))));
 893                 ret_key = (HfsExtentKey*) (node + ref.record_pos);
 894                 ret_data = (HfsExtDescriptor*) ( node + ref.record_pos
 895                                                  + sizeof (HfsExtentKey) );
 896         }
 897 
 898 bb_not_found:
 899         /* not found : not a valid hfs+ wrapper : failure */
 900         ped_exception_throw (
 901                 PED_EXCEPTION_ERROR,
 902                 PED_EXCEPTION_CANCEL,
 903                 _("It seems there is an error in the HFS wrapper: the bad "
 904                   "blocks file doesn't contain the embedded HFS+ volume."));
 905         return 0;
 906 }
 907 
 908 static int
 909 hfsplus_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
 910 {
 911         HfsPPrivateFSData*      priv_data;
 912         PedTimer*               timer_plus;
 913         PedGeometry*            embedded_geom;
 914         PedSector               hgms;
 915 
 916         /* check preconditions */
 917         PED_ASSERT (fs != NULL, return 0);
 918         PED_ASSERT (fs->geom != NULL, return 0); 
 919         PED_ASSERT (geom != NULL, return 0);
 920         PED_ASSERT (fs->geom->dev == geom->dev, return 0);
 921 #ifdef DEBUG
 922         PED_ASSERT ((hgms = hfsplus_get_min_size (fs)) != 0, return 0);
 923 #else
 924         if ((hgms = hfsplus_get_min_size (fs)) == 0)
 925                 return 0;
 926 #endif
 927 
 928         if (ped_geometry_test_equal(fs->geom, geom))
 929                 return 1;
 930 
 931         priv_data = (HfsPPrivateFSData*) fs->type_specific;
 932 
 933         if (fs->geom->start != geom->start
 934             || geom->length > fs->geom->length
 935             || geom->length < hgms) {
 936                 ped_exception_throw (
 937                         PED_EXCEPTION_NO_FEATURE,
 938                         PED_EXCEPTION_CANCEL,
 939                         _("Sorry, HFS+ cannot be resized that way yet."));
 940                 return 0;
 941         }
 942 
 943         if (priv_data->wrapper) {
 944                 PedSector               red, hgee;
 945                 HfsPrivateFSData*       hfs_priv_data = (HfsPrivateFSData*)
 946                                             priv_data->wrapper->type_specific;
 947                 unsigned int            hfs_sect_block =
 948                             PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size)
 949                             / PED_SECTOR_SIZE_DEFAULT;
 950 
 951                 /* There is a wrapper so we must calculate the new geometry
 952                    of the embedded HFS+ volume */
 953                 red = ( (fs->geom->length - geom->length + hfs_sect_block - 1)
 954                         / hfs_sect_block ) * hfs_sect_block;
 955                 /* Can't we shrink the hfs+ volume by the desired size ? */
 956                 hgee = hfsplus_get_empty_end (fs);
 957                 if (!hgee) return 0;
 958                 if (red > priv_data->plus_geom->length - hgee) {
 959                         /* No, shrink hfs+ by the greatest possible value */
 960                         hgee = ((hgee + hfs_sect_block - 1) / hfs_sect_block)
 961                                * hfs_sect_block;
 962                         red = priv_data->plus_geom->length - hgee;
 963                 }
 964                 embedded_geom = ped_geometry_new (geom->dev,
 965                                                   priv_data->plus_geom->start,
 966                                                   priv_data->plus_geom->length
 967                                                   - red);
 968 
 969                 /* There is a wrapper so the resize process is a two stages
 970                    process (embedded resizing then wrapper resizing) :
 971                    we create a sub timer */
 972                 ped_timer_reset (timer);
 973                 ped_timer_set_state_name (timer,
 974                                           _("shrinking embedded HFS+ volume"));
 975                 ped_timer_update(timer, 0.0);
 976                 timer_plus = ped_timer_new_nested (timer, 0.98);
 977         } else {
 978                 /* No wrapper : the desired geometry is the desired
 979                    HFS+ volume geometry */
 980                 embedded_geom = geom;
 981                 timer_plus = timer;
 982         }
 983 
 984         /* Resize the HFS+ volume */
 985         if (!hfsplus_volume_resize (fs, embedded_geom, timer_plus)) {
 986                 if (timer_plus != timer) ped_timer_destroy_nested (timer_plus);
 987                 ped_exception_throw (
 988                         PED_EXCEPTION_ERROR,
 989                         PED_EXCEPTION_CANCEL,
 990                         _("Resizing the HFS+ volume has failed."));
 991                 return 0;
 992         }
 993 
 994         if (priv_data->wrapper) {
 995                 ped_geometry_destroy (embedded_geom);
 996                 ped_timer_destroy_nested (timer_plus);
 997                 ped_timer_set_state_name(timer, _("shrinking HFS wrapper"));
 998                 timer_plus = ped_timer_new_nested (timer, 0.02);
 999                 /* There's a wrapper : second stage = resizing it */
1000                 if (!hfsplus_wrapper_update (fs)
1001                     || !hfs_resize (priv_data->wrapper, geom, timer_plus)) {
1002                         ped_timer_destroy_nested (timer_plus);
1003                         ped_exception_throw (
1004                                 PED_EXCEPTION_ERROR,
1005                                 PED_EXCEPTION_CANCEL,
1006                                 _("Updating the HFS wrapper has failed."));
1007                         return 0;
1008                 }
1009                 ped_timer_destroy_nested (timer_plus);
1010         }
1011         ped_timer_update(timer, 1.0);
1012 
1013         return 1;
1014 }
1015 
1016 #ifdef HFS_EXTRACT_FS
1017 /* The following is for debugging purpose only, NOT for packaging */
1018 
1019 #include <stdio.h>
1020 
1021 uint8_t* extract_buffer = NULL;
1022 
1023 static int
1024 hfs_extract_file(const char* filename, HfsPrivateFile* hfs_file)
1025 {
1026         FILE*           fout;
1027         PedSector       sect;
1028 
1029         fout = fopen(filename, "w");
1030         if (!fout) return 0;
1031 
1032         for (sect = 0; sect < hfs_file->sect_nb; ++sect) {
1033                 if (!hfs_file_read_sector(hfs_file, extract_buffer, sect))
1034                         goto err_close;
1035                 if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout))
1036                         goto err_close;
1037         }
1038 
1039         return (fclose(fout) == 0 ? 1 : 0);
1040 
1041 err_close:
1042         fclose(fout);
1043         return 0;
1044 }
1045 
1046 static int
1047 hfs_extract_bitmap(const char* filename, PedFileSystem* fs)
1048 {
1049         HfsPrivateFSData*               priv_data = (HfsPrivateFSData*)
1050                                                 fs->type_specific;
1051         HfsMasterDirectoryBlock*        mdb = priv_data->mdb;
1052         unsigned int    count;
1053         FILE*           fout;
1054         PedSector       sect;
1055 
1056         fout = fopen(filename, "w");
1057         if (!fout) return 0;
1058 
1059         for (sect = PED_BE16_TO_CPU(mdb->volume_bitmap_block);
1060              sect < PED_BE16_TO_CPU(mdb->start_block);
1061              sect += count) {
1062                 uint16_t st_block = PED_BE16_TO_CPU(mdb->start_block);
1063                 count = (st_block-sect) < BLOCK_MAX_BUFF ?
1064                         (st_block-sect) : BLOCK_MAX_BUFF;
1065                 if (!ped_geometry_read(fs->geom, extract_buffer, sect, count))
1066                         goto err_close;
1067                 if (!fwrite (extract_buffer, count * PED_SECTOR_SIZE_DEFAULT,
1068                              1, fout))
1069                         goto err_close;
1070         }
1071 
1072         return (fclose(fout) == 0 ? 1 : 0);
1073 
1074 err_close:
1075         fclose(fout);
1076         return 0;
1077 }
1078 
1079 static int
1080 hfs_extract_mdb (const char* filename, PedFileSystem* fs)
1081 {
1082         FILE*           fout;
1083 
1084         fout = fopen(filename, "w");
1085         if (!fout) return 0;
1086 
1087         if (!ped_geometry_read(fs->geom, extract_buffer, 2, 1))
1088                 goto err_close;
1089         if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout))
1090                 goto err_close;
1091 
1092         return (fclose(fout) == 0 ? 1 : 0);
1093 
1094 err_close:
1095         fclose(fout);
1096         return 0;
1097 }
1098 
1099 static int
1100 hfs_extract (PedFileSystem* fs, PedTimer* timer)
1101 {
1102         HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
1103                                                 fs->type_specific;
1104 
1105         ped_exception_throw (
1106                 PED_EXCEPTION_INFORMATION,
1107                 PED_EXCEPTION_OK,
1108                 _("This is not a real %s check.  This is going to extract "
1109                   "special low level files for debugging purposes."),
1110                 "HFS");
1111 
1112         extract_buffer = ped_malloc(BLOCK_MAX_BUFF * PED_SECTOR_SIZE_DEFAULT);
1113         if (!extract_buffer) return 0;
1114 
1115         hfs_extract_mdb(HFS_MDB_FILENAME, fs);
1116         hfs_extract_file(HFS_CATALOG_FILENAME, priv_data->catalog_file);
1117         hfs_extract_file(HFS_EXTENTS_FILENAME, priv_data->extent_file);
1118         hfs_extract_bitmap(HFS_BITMAP_FILENAME, fs);
1119 
1120         ped_free(extract_buffer); extract_buffer = NULL;
1121         return 0; /* nothing has been fixed by us ! */
1122 }
1123 
1124 static int
1125 hfsplus_extract_file(const char* filename, HfsPPrivateFile* hfsp_file)
1126 {
1127         FILE*           fout;
1128         unsigned int    cp_sect;
1129         PedSector       rem_sect;
1130 
1131         fout = fopen(filename, "w");
1132         if (!fout) return 0;
1133 
1134         for (rem_sect = hfsp_file->sect_nb; rem_sect; rem_sect -= cp_sect) {
1135                 cp_sect = rem_sect < BLOCK_MAX_BUFF ? rem_sect : BLOCK_MAX_BUFF;
1136                 if (!hfsplus_file_read(hfsp_file, extract_buffer, 
1137                                        hfsp_file->sect_nb - rem_sect, cp_sect))
1138                         goto err_close;
1139                 if (!fwrite (extract_buffer, cp_sect * PED_SECTOR_SIZE_DEFAULT,
1140                              1, fout))
1141                         goto err_close;
1142         }
1143 
1144         return (fclose(fout) == 0 ? 1 : 0);
1145 
1146 err_close:
1147         fclose(fout);
1148         return 0;
1149 }
1150 
1151 static int
1152 hfsplus_extract_vh (const char* filename, PedFileSystem* fs)
1153 {
1154         HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
1155                                                 fs->type_specific;
1156         FILE*           fout;
1157         PedGeometry*    geom = priv_data->plus_geom;
1158 
1159 
1160         fout = fopen(filename, "w");
1161         if (!fout) return 0;
1162 
1163         if (!ped_geometry_read(geom, extract_buffer, 2, 1))
1164                 goto err_close;
1165         if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout))
1166                 goto err_close;
1167 
1168         return (fclose(fout) == 0 ? 1 : 0);
1169 
1170 err_close:
1171         fclose(fout);
1172         return 0;
1173 }
1174 
1175 /* TODO : use the timer to report what is happening */
1176 /* TODO : use exceptions to report errors */
1177 static int
1178 hfsplus_extract (PedFileSystem* fs, PedTimer* timer)
1179 {
1180         HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
1181                                                 fs->type_specific;
1182         HfsPVolumeHeader*       vh = priv_data->vh;
1183         HfsPPrivateFile*        startup_file;
1184 
1185         if (priv_data->wrapper) {
1186                 /* TODO : create nested timer */
1187                 hfs_extract (priv_data->wrapper, timer);
1188         }
1189 
1190         ped_exception_throw (
1191                 PED_EXCEPTION_INFORMATION,
1192                 PED_EXCEPTION_OK,
1193                 _("This is not a real %s check.  This is going to extract "
1194                   "special low level files for debugging purposes."),
1195                 "HFS+");
1196 
1197         extract_buffer = ped_malloc(BLOCK_MAX_BUFF * PED_SECTOR_SIZE_DEFAULT);
1198         if (!extract_buffer) return 0;
1199 
1200         hfsplus_extract_vh(HFSP_VH_FILENAME, fs);
1201         hfsplus_extract_file(HFSP_CATALOG_FILENAME, priv_data->catalog_file);
1202         hfsplus_extract_file(HFSP_EXTENTS_FILENAME, priv_data->extents_file);
1203         hfsplus_extract_file(HFSP_ATTRIB_FILENAME, priv_data->attributes_file);
1204         hfsplus_extract_file(HFSP_BITMAP_FILENAME, priv_data->allocation_file);
1205 
1206         startup_file = hfsplus_file_open(fs, PED_CPU_TO_BE32(HFSP_STARTUP_ID),
1207                                         vh->startup_file.extents,
1208                                         PED_BE64_TO_CPU (
1209                                            vh->startup_file.logical_size)
1210                                         / PED_SECTOR_SIZE_DEFAULT);
1211         if (startup_file) {
1212                 hfsplus_extract_file(HFSP_STARTUP_FILENAME, startup_file);
1213                 hfsplus_file_close(startup_file); startup_file = NULL;
1214         }
1215 
1216         ped_free(extract_buffer); extract_buffer = NULL;
1217         return 0; /* nothing has been fixed by us ! */
1218 }
1219 #endif /* HFS_EXTRACT_FS */
1220 
1221 #endif /* !DISCOVER_ONLY */
1222 
1223 static PedFileSystemOps hfs_ops = {
1224         .probe =                hfs_probe,
1225 #ifndef DISCOVER_ONLY
1226         .clobber =      hfs_clobber,
1227         .open =         hfs_open,
1228         .create =               NULL,
1229         .close =                hfs_close,
1230 #ifndef HFS_EXTRACT_FS
1231         .check =                NULL,
1232 #else
1233         .check =                hfs_extract,
1234 #endif
1235         .copy =         NULL,
1236         .resize =               hfs_resize,
1237         .get_create_constraint =        NULL,
1238         .get_resize_constraint =        hfs_get_resize_constraint,
1239         .get_copy_constraint =  NULL,
1240 #else /* DISCOVER_ONLY */
1241         .clobber =      NULL,
1242         .open =         NULL,
1243         .create =               NULL,
1244         .close =                NULL,
1245         .check =                NULL,
1246         .copy =         NULL,
1247         .resize =               NULL,
1248         .get_create_constraint =        NULL,
1249         .get_resize_constraint =        NULL,
1250         .get_copy_constraint =  NULL,
1251 #endif /* DISCOVER_ONLY */
1252 };
1253 
1254 static PedFileSystemOps hfsplus_ops = {
1255         .probe =                hfsplus_probe,
1256 #ifndef DISCOVER_ONLY
1257         .clobber =      hfsplus_clobber,
1258         .open =         hfsplus_open,
1259         .create =               NULL,
1260         .close =                hfsplus_close,
1261 #ifndef HFS_EXTRACT_FS
1262         .check =                NULL,
1263 #else
1264         .check =                hfsplus_extract,
1265 #endif
1266         .copy =         NULL,
1267         .resize =               hfsplus_resize,
1268         .get_create_constraint =        NULL,
1269         .get_resize_constraint =        hfsplus_get_resize_constraint,
1270         .get_copy_constraint =  NULL,
1271 #else /* DISCOVER_ONLY */
1272         .clobber =      NULL,
1273         .open =         NULL,
1274         .create =               NULL,
1275         .close =                NULL,
1276         .check =                NULL,
1277         .copy =         NULL,
1278         .resize =               NULL,
1279         .get_create_constraint =        NULL,
1280         .get_resize_constraint =        NULL,
1281         .get_copy_constraint =  NULL,
1282 #endif /* DISCOVER_ONLY */
1283 };
1284 
1285 static PedFileSystemOps hfsx_ops = {
1286         .probe =                hfsx_probe,
1287 #ifndef DISCOVER_ONLY
1288         .clobber =      hfs_clobber, /* NOT hfsplus_clobber !
1289                                         HFSX can't be embedded */
1290         .open =         hfsplus_open,
1291         .create =               NULL,
1292         .close =                hfsplus_close,
1293 #ifndef HFS_EXTRACT_FS
1294         .check =                NULL,
1295 #else
1296         .check =                hfsplus_extract,
1297 #endif
1298         .copy =         NULL,
1299         .resize =               hfsplus_resize,
1300         .get_create_constraint =        NULL,
1301         .get_resize_constraint =        hfsplus_get_resize_constraint,
1302         .get_copy_constraint =  NULL,
1303 #else /* DISCOVER_ONLY */
1304         .clobber =      NULL,
1305         .open =         NULL,
1306         .create =               NULL,
1307         .close =                NULL,
1308         .check =                NULL,
1309         .copy =         NULL,
1310         .resize =               NULL,
1311         .get_create_constraint =        NULL,
1312         .get_resize_constraint =        NULL,
1313         .get_copy_constraint =  NULL,
1314 #endif /* DISCOVER_ONLY */
1315 };
1316 
1317 
1318 static PedFileSystemType hfs_type = {
1319         .next = NULL,
1320         .ops =  &hfs_ops,
1321         .name = "hfs",
1322         .block_sizes = HFS_BLOCK_SIZES
1323 };
1324 
1325 static PedFileSystemType hfsplus_type = {
1326         .next = NULL,
1327         .ops =  &hfsplus_ops,
1328         .name = "hfs+",
1329         .block_sizes = HFSP_BLOCK_SIZES
1330 };
1331 
1332 static PedFileSystemType hfsx_type = {
1333         .next = NULL,
1334         .ops =  &hfsx_ops,
1335         .name = "hfsx",
1336         .block_sizes = HFSX_BLOCK_SIZES
1337 };
1338 
1339 void
1340 ped_file_system_hfs_init ()
1341 {
1342         ped_file_system_type_register (&hfs_type);
1343         ped_file_system_type_register (&hfsplus_type);
1344         ped_file_system_type_register (&hfsx_type);
1345 }
1346 
1347 void
1348 ped_file_system_hfs_done ()
1349 {
1350         ped_file_system_type_unregister (&hfs_type);
1351         ped_file_system_type_unregister (&hfsplus_type);
1352         ped_file_system_type_unregister (&hfsx_type);
1353 }