1 /*
   2     libparted - a library for manipulating disk partitions
   3     Copyright (C) 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 #ifndef DISCOVER_ONLY
  20 
  21 #include <config.h>
  22 
  23 #include <parted/parted.h>
  24 #include <parted/endian.h>
  25 #include <parted/debug.h>
  26 #include <stdint.h>
  27 
  28 #if ENABLE_NLS
  29 #  include <libintl.h>
  30 #  define _(String) dgettext (PACKAGE, String)
  31 #else
  32 #  define _(String) (String)
  33 #endif /* ENABLE_NLS */
  34 
  35 #include "hfs.h"
  36 #include "file.h"
  37 #include "advfs.h"
  38 #include "cache.h"
  39 
  40 #include "reloc.h"
  41 
  42 /* This function moves data of size blocks starting
  43    at block *ptr_fblock to block *ptr_to_fblock */
  44 /* return new start or -1 on failure */
  45 static int
  46 hfs_effect_move_extent (PedFileSystem *fs, unsigned int *ptr_fblock,
  47                         unsigned int *ptr_to_fblock, unsigned int size)
  48 {
  49         HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
  50                                                 fs->type_specific;
  51         unsigned int            i, ok = 0;
  52         unsigned int            next_to_fblock;
  53         unsigned int            start, stop;
  54 
  55         PED_ASSERT (hfs_block != NULL, return -1);
  56         PED_ASSERT (*ptr_to_fblock <= *ptr_fblock, return -1);
  57         /* quiet gcc */
  58         next_to_fblock = start = stop = 0;
  59 
  60 /*
  61         Try to fit the extent AT or _BEFORE_ the wanted place,
  62         or then in the gap between dest and source.
  63         If failed try to fit the extent after source, for 2 pass relocation
  64         The extent is always copied in a non overlapping way
  65 */
  66 
  67         /* Backward search */
  68         /* 1 pass relocation AT or BEFORE *ptr_to_fblock */
  69         if (*ptr_to_fblock != *ptr_fblock) {
  70                 start = stop = *ptr_fblock < *ptr_to_fblock+size ?
  71                                *ptr_fblock : *ptr_to_fblock+size;
  72                 while (start && stop-start != size) {
  73                         --start;
  74                         if (TST_BLOC_OCCUPATION(priv_data->alloc_map,start))
  75                                 stop = start;
  76                 }
  77                 ok = (stop-start == size);
  78         }
  79         
  80         /* Forward search */
  81         /* 1 pass relocation in the gap merged with 2 pass reloc after source */
  82         if (!ok && *ptr_to_fblock != *ptr_fblock) {
  83                 start = stop = *ptr_to_fblock+1;
  84                 while (stop < PED_BE16_TO_CPU(priv_data->mdb->total_blocks)
  85                        && stop-start != size) {
  86                         if (TST_BLOC_OCCUPATION(priv_data->alloc_map,stop))
  87                                 start = stop + 1;
  88                         ++stop;
  89                 }
  90                 ok = (stop-start == size);
  91         }
  92 
  93         /* new non overlapping room has been found ? */
  94         if (ok) {
  95                 /* enough room */
  96                 unsigned int j;
  97                 unsigned int start_block =
  98                         PED_BE16_TO_CPU (priv_data->mdb->start_block );
  99                 unsigned int block_sz = 
 100                         (PED_BE32_TO_CPU (priv_data->mdb->block_size)
 101                          / PED_SECTOR_SIZE_DEFAULT);
 102 
 103                 if (stop > *ptr_to_fblock && stop <= *ptr_fblock)
 104                         /* Fit in the gap */
 105                         next_to_fblock = stop;
 106                 else
 107                         /* Before or after the gap */
 108                         next_to_fblock = *ptr_to_fblock;
 109 
 110                 /* move blocks */
 111                 for (i = 0; i < size; /*i+=j*/) {
 112                         PedSector       abs_sector;
 113                         unsigned int    ai;
 114 
 115                         j = size - i; j = (j < hfs_block_count) ?
 116                                            j : hfs_block_count ;
 117 
 118                         abs_sector = start_block
 119                                      + (PedSector) (*ptr_fblock + i) * block_sz;
 120                         if (!ped_geometry_read (fs->geom, hfs_block, abs_sector,
 121                                                 block_sz * j))
 122                                 return -1;
 123 
 124                         abs_sector = start_block
 125                                      + (PedSector) (start + i) * block_sz;
 126                         if (!ped_geometry_write (fs->geom,hfs_block,abs_sector,
 127                                                  block_sz * j))
 128                                 return -1;
 129 
 130                         for (ai = i+j; i < ai; i++) {
 131                                 /* free source block */
 132                                 CLR_BLOC_OCCUPATION(priv_data->alloc_map,
 133                                                     *ptr_fblock + i);
 134 
 135                                 /* set dest block */
 136                                 SET_BLOC_OCCUPATION(priv_data->alloc_map,
 137                                                     start + i);
 138                         }
 139                 }
 140                 if (!ped_geometry_sync_fast (fs->geom))
 141                         return -1;
 142 
 143                 *ptr_fblock += size;
 144                 *ptr_to_fblock = next_to_fblock;
 145         } else {
 146                 if (*ptr_fblock != *ptr_to_fblock)
 147                         /* not enough room, but try to continue */
 148                         ped_exception_throw (PED_EXCEPTION_WARNING,
 149                                         PED_EXCEPTION_IGNORE,
 150                                         _("An extent has not been relocated."));
 151                 start = *ptr_fblock;
 152                 *ptr_fblock = *ptr_to_fblock = start + size;
 153         }
 154 
 155         return start;
 156 }
 157 
 158 /* Update MDB */
 159 /* Return 0 if an error occurred */
 160 /* Return 1 if everything ok */
 161 int
 162 hfs_update_mdb (PedFileSystem *fs)
 163 {
 164         HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
 165                                                 fs->type_specific;
 166         uint8_t                 node[PED_SECTOR_SIZE_DEFAULT];
 167 
 168         if (!ped_geometry_read (fs->geom, node, 2, 1))
 169                 return 0;
 170         memcpy (node, priv_data->mdb, sizeof (HfsMasterDirectoryBlock));
 171         if (   !ped_geometry_write (fs->geom, node, 2, 1)
 172             || !ped_geometry_write (fs->geom, node, fs->geom->length - 2, 1)
 173             || !ped_geometry_sync_fast (fs->geom))
 174                 return 0;
 175         return 1;
 176 }
 177 
 178 /* Generic relocator */
 179 /* replace previous hfs_do_move_* */
 180 static int
 181 hfs_do_move (PedFileSystem* fs, unsigned int *ptr_src,
 182              unsigned int *ptr_dest, HfsCPrivateCache* cache,
 183              HfsCPrivateExtent* ref)
 184 {
 185         uint8_t                 node[PED_SECTOR_SIZE_DEFAULT];
 186         HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
 187                                                 fs->type_specific;
 188         HfsPrivateFile*         file;
 189         HfsExtDescriptor*       extent;
 190         HfsCPrivateExtent*      move;
 191         int                     new_start;
 192 
 193         new_start = hfs_effect_move_extent (fs, ptr_src, ptr_dest, 
 194                                             ref->ext_length);
 195         if (new_start == -1) return -1;
 196 
 197         if (ref->ext_start != (unsigned) new_start) {
 198                 /* Load, modify & save */
 199                 switch (ref->where) {
 200                 /******** MDB *********/
 201                     case CR_PRIM_CAT :
 202                         priv_data->catalog_file
 203                         ->first[ref->ref_index].start_block = 
 204                                 PED_CPU_TO_BE16(new_start);
 205                         goto CR_PRIM;
 206                     case CR_PRIM_EXT :
 207                         priv_data->extent_file
 208                         ->first[ref->ref_index].start_block = 
 209                                 PED_CPU_TO_BE16(new_start);                 
 210                     CR_PRIM :
 211                         extent = ( HfsExtDescriptor* )
 212                                  ( (uint8_t*)priv_data->mdb + ref->ref_offset );
 213                         extent[ref->ref_index].start_block =
 214                                 PED_CPU_TO_BE16(new_start);
 215                         if (!hfs_update_mdb(fs)) return -1;
 216                         break;
 217 
 218                 /********* BTREE *******/
 219                     case CR_BTREE_EXT_CAT :
 220                         if (priv_data->catalog_file
 221                             ->cache[ref->ref_index].start_block
 222                             == PED_CPU_TO_BE16(ref->ext_start))
 223                                 priv_data->catalog_file
 224                                 ->cache[ref->ref_index].start_block =
 225                                 PED_CPU_TO_BE16(new_start);
 226                     case CR_BTREE_EXT_0 :
 227                         file = priv_data->extent_file;
 228                         goto CR_BTREE;
 229                     case CR_BTREE_CAT :
 230                         file = priv_data->catalog_file;
 231                     CR_BTREE:
 232                         PED_ASSERT(ref->sect_by_block == 1
 233                                    && ref->ref_offset < PED_SECTOR_SIZE_DEFAULT,
 234                                    return -1);
 235                         if (!hfs_file_read_sector(file, node, ref->ref_block))
 236                                 return -1;
 237                         extent = ( HfsExtDescriptor* ) (node + ref->ref_offset);
 238                         extent[ref->ref_index].start_block =
 239                                 PED_CPU_TO_BE16(new_start);
 240                         if (!hfs_file_write_sector(file, node, ref->ref_block)
 241                             || !ped_geometry_sync_fast (fs->geom))
 242                                 return -1;
 243                         break;
 244 
 245                 /********** BUG ********/
 246                     default :
 247                         ped_exception_throw (
 248                                 PED_EXCEPTION_ERROR,
 249                                 PED_EXCEPTION_CANCEL,
 250                                 _("A reference to an extent comes from a place "
 251                                   "it should not.  You should check the file "
 252                                   "system!"));
 253                         return -1;
 254                         break;
 255                 }
 256 
 257                 /* Update the cache */
 258                 move = hfsc_cache_move_extent(cache, ref->ext_start, new_start);
 259                 if (!move) return -1; /* "cleanly" fail */
 260                 PED_ASSERT(move == ref, return -1); /* generate a bug */
 261         }
 262 
 263         return new_start;
 264 }
 265 
 266 /* 0 error, 1 ok */
 267 static int
 268 hfs_save_allocation(PedFileSystem* fs)
 269 {
 270         HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
 271                                                 fs->type_specific;
 272         unsigned int            map_sectors;
 273 
 274         map_sectors = ( PED_BE16_TO_CPU (priv_data->mdb->total_blocks)
 275                         + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
 276                       / (PED_SECTOR_SIZE_DEFAULT * 8);
 277         return ( ped_geometry_write (fs->geom, priv_data->alloc_map,
 278                         PED_BE16_TO_CPU (priv_data->mdb->volume_bitmap_block),
 279                         map_sectors) );
 280 }
 281 
 282 /* This function moves an extent starting at block fblock to block to_fblock
 283    if there's enough room */
 284 /* Return 1 if everything was fine */
 285 /* Return -1 if an error occurred */
 286 /* Return 0 if no extent was found */
 287 /* Generic search thanks to the file system cache */
 288 static int
 289 hfs_move_extent_starting_at (PedFileSystem *fs, unsigned int *ptr_fblock,
 290                              unsigned int *ptr_to_fblock, 
 291                              HfsCPrivateCache* cache)
 292 {
 293         HfsCPrivateExtent*      ref;
 294         unsigned int            old_start, new_start;
 295         
 296         /* Reference search powered by the cache... */
 297         /* This is the optimisation secret :) */
 298         ref = hfsc_cache_search_extent(cache, *ptr_fblock);
 299         if (!ref) return 0; /* not found */
 300 
 301         old_start = *ptr_fblock;
 302         new_start = hfs_do_move(fs, ptr_fblock, ptr_to_fblock, cache, ref);
 303         if (new_start == (unsigned int) -1) return -1;
 304         if (new_start > old_start) { /* detect 2 pass reloc */
 305                 new_start = hfs_do_move(fs,&new_start,ptr_to_fblock,cache,ref);
 306                 if (new_start == (unsigned int) -1 || new_start > old_start)
 307                         return -1;
 308         }
 309 
 310         /* allocation bitmap save is not atomic with data relocation */
 311         /* so we only do it a few times, and without syncing */
 312         /* The unmounted bit protect us anyway */
 313         hfs_save_allocation(fs);
 314         return 1;
 315 }
 316 
 317 static int
 318 hfs_cache_from_mdb(HfsCPrivateCache* cache, PedFileSystem* fs,
 319                    PedTimer* timer)
 320 {
 321         HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
 322                                                 fs->type_specific;
 323         HfsExtDescriptor*       extent;
 324         unsigned int            j;
 325 
 326         extent = priv_data->mdb->extents_file_rec;
 327         for (j = 0; j < HFS_EXT_NB; ++j) {
 328                 if (!extent[j].block_count) break;
 329                 if (!hfsc_cache_add_extent(
 330                         cache,
 331                         PED_BE16_TO_CPU(extent[j].start_block),
 332                         PED_BE16_TO_CPU(extent[j].block_count),
 333                         0, /* unused for mdb */
 334                         ((uint8_t*)extent) - ((uint8_t*)priv_data->mdb),
 335                         1, /* load/save only 1 sector */
 336                         CR_PRIM_EXT,
 337                         j )
 338                    )
 339                         return 0;
 340         }
 341 
 342         extent = priv_data->mdb->catalog_file_rec;
 343         for (j = 0; j < HFS_EXT_NB; ++j) {
 344                 if (!extent[j].block_count) break;
 345                 if (!hfsc_cache_add_extent(
 346                         cache,
 347                         PED_BE16_TO_CPU(extent[j].start_block),
 348                         PED_BE16_TO_CPU(extent[j].block_count),
 349                         0,
 350                         ((uint8_t*)extent) - ((uint8_t*)priv_data->mdb),
 351                         1,
 352                         CR_PRIM_CAT,
 353                         j )
 354                    )
 355                         return 0;
 356         }
 357 
 358         return 1;
 359 }
 360 
 361 static int
 362 hfs_cache_from_catalog(HfsCPrivateCache* cache, PedFileSystem* fs,
 363                    PedTimer* timer)
 364 {
 365         HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
 366                                                 fs->type_specific;
 367         uint8_t                 node[PED_SECTOR_SIZE_DEFAULT];
 368         HfsHeaderRecord*        header;
 369         HfsNodeDescriptor*      desc = (HfsNodeDescriptor*) node;
 370         HfsCatalogKey*          catalog_key;
 371         HfsCatalog*             catalog_data;
 372         HfsExtDescriptor*       extent;
 373         unsigned int            leaf_node, record_number;
 374         unsigned int            i, j;
 375 
 376         if (!priv_data->catalog_file->sect_nb) {
 377                 ped_exception_throw (
 378                         PED_EXCEPTION_INFORMATION,
 379                         PED_EXCEPTION_OK,
 380                         _("This HFS volume has no catalog file.  "
 381                           "This is very unusual!"));
 382                 return 1;
 383         }
 384 
 385         if (!hfs_file_read_sector (priv_data->catalog_file, node, 0))
 386                 return 0;
 387         header = (HfsHeaderRecord*)(node +  PED_BE16_TO_CPU(*((uint16_t*)
 388                                                 (node+(PED_SECTOR_SIZE_DEFAULT-2)))));
 389 
 390         for (leaf_node = PED_BE32_TO_CPU (header->first_leaf_node);
 391              leaf_node;
 392              leaf_node = PED_BE32_TO_CPU (desc->next)) {
 393                 if (!hfs_file_read_sector (priv_data->catalog_file,
 394                                            node, leaf_node))
 395                         return 0;
 396                 record_number = PED_BE16_TO_CPU (desc->rec_nb);
 397                 for (i = 1; i <= record_number; ++i) {
 398                        /* undocumented alignement */
 399                         unsigned int skip;
 400                         catalog_key = (HfsCatalogKey*) (node + PED_BE16_TO_CPU(
 401                                 *((uint16_t*)(node+(PED_SECTOR_SIZE_DEFAULT - 2*i)))));
 402                         skip = (1 + catalog_key->key_length + 1) & ~1;
 403                         catalog_data = (HfsCatalog*)( ((uint8_t*)catalog_key)
 404                                                         + skip );
 405                         /* check for obvious error in FS */
 406                         if (((uint8_t*)catalog_key - node < HFS_FIRST_REC)
 407                             || ((uint8_t*)catalog_data - node
 408                                 >= PED_SECTOR_SIZE_DEFAULT 
 409                                    - 2 * (signed)(record_number+1))) {
 410                                 ped_exception_throw (
 411                                         PED_EXCEPTION_ERROR,
 412                                         PED_EXCEPTION_CANCEL,
 413                                         _("The file system contains errors."));
 414                                 return 0;
 415                         }
 416 
 417                         if (catalog_data->type != HFS_CAT_FILE) continue;
 418 
 419                         extent = catalog_data->sel.file.extents_data;
 420                         for (j = 0; j < HFS_EXT_NB; ++j) {
 421                                 if (!extent[j].block_count) break;
 422                                 if (!hfsc_cache_add_extent(
 423                                         cache,
 424                                         PED_BE16_TO_CPU(extent[j].start_block),
 425                                         PED_BE16_TO_CPU(extent[j].block_count),
 426                                         leaf_node,
 427                                         (uint8_t*)extent - node,
 428                                         1, /* hfs => btree block = 512 b */
 429                                         CR_BTREE_CAT,
 430                                         j )
 431                                    )
 432                                         return 0;
 433                         }
 434 
 435                         extent = catalog_data->sel.file.extents_res;
 436                         for (j = 0; j < HFS_EXT_NB; ++j) {
 437                                 if (!extent[j].block_count) break;
 438                                 if (!hfsc_cache_add_extent(
 439                                         cache,
 440                                         PED_BE16_TO_CPU(extent[j].start_block),
 441                                         PED_BE16_TO_CPU(extent[j].block_count),
 442                                         leaf_node,
 443                                         (uint8_t*)extent - node,
 444                                         1, /* hfs => btree block = 512 b */
 445                                         CR_BTREE_CAT,
 446                                         j )
 447                                    )
 448                                         return 0;
 449                         }
 450                 }
 451         }
 452 
 453         return 1;
 454 }
 455 
 456 static int
 457 hfs_cache_from_extent(HfsCPrivateCache* cache, PedFileSystem* fs,
 458                    PedTimer* timer)
 459 {
 460         HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
 461                                                 fs->type_specific;
 462         uint8_t                 node[PED_SECTOR_SIZE_DEFAULT];
 463         HfsHeaderRecord*        header;
 464         HfsNodeDescriptor*      desc = (HfsNodeDescriptor*) node;
 465         HfsExtentKey*           extent_key;
 466         HfsExtDescriptor*       extent;
 467         unsigned int            leaf_node, record_number;
 468         unsigned int            i, j;
 469 
 470         if (!priv_data->extent_file->sect_nb) {
 471                 ped_exception_throw (
 472                         PED_EXCEPTION_INFORMATION,
 473                         PED_EXCEPTION_OK,
 474                         _("This HFS volume has no extents overflow "
 475                           "file.  This is quite unusual!"));
 476                 return 1;
 477         }
 478 
 479         if (!hfs_file_read_sector (priv_data->extent_file, node, 0))
 480                 return 0;
 481         header = ((HfsHeaderRecord*) (node + PED_BE16_TO_CPU(*((uint16_t *)
 482                                                 (node+(PED_SECTOR_SIZE_DEFAULT-2))))));
 483 
 484         for (leaf_node = PED_BE32_TO_CPU (header->first_leaf_node);
 485              leaf_node;
 486              leaf_node = PED_BE32_TO_CPU (desc->next)) {
 487                 if (!hfs_file_read_sector (priv_data->extent_file, node,
 488                                            leaf_node))
 489                         return 0;
 490                 record_number = PED_BE16_TO_CPU (desc->rec_nb);
 491                 for (i = 1; i <= record_number; i++) {
 492                         uint8_t where;
 493                         extent_key = (HfsExtentKey*)
 494                                         (node + PED_BE16_TO_CPU(*((uint16_t *)
 495                                               (node+(PED_SECTOR_SIZE_DEFAULT - 2*i)))));
 496                         /* size is cst */
 497                         extent = (HfsExtDescriptor*)(((uint8_t*)extent_key)
 498                                                        + sizeof (HfsExtentKey));
 499                         /* check for obvious error in FS */
 500                         if (((uint8_t*)extent_key - node < HFS_FIRST_REC)
 501                             || ((uint8_t*)extent - node 
 502                                 >= PED_SECTOR_SIZE_DEFAULT
 503                                    - 2 * (signed)(record_number+1))) {
 504                                 ped_exception_throw (
 505                                         PED_EXCEPTION_ERROR,
 506                                         PED_EXCEPTION_CANCEL,
 507                                         _("The file system contains errors."));
 508                                 return 0;
 509                         }
 510 
 511                         switch (extent_key->file_ID) {
 512                             case PED_CPU_TO_BE32 (HFS_XTENT_ID) :
 513                                 if (ped_exception_throw (
 514                                         PED_EXCEPTION_WARNING,
 515                                         PED_EXCEPTION_IGNORE_CANCEL,
 516                                         _("The extents overflow file should not"
 517                                           " contain its own extents!  You "
 518                                           "should check the file system."))
 519                                                 != PED_EXCEPTION_IGNORE)
 520                                         return 0;
 521                                 where = CR_BTREE_EXT_EXT;
 522                                 break;
 523                             case PED_CPU_TO_BE32 (HFS_CATALOG_ID) :
 524                                 where = CR_BTREE_EXT_CAT;
 525                                 break;
 526                             default :
 527                                 where = CR_BTREE_EXT_0;
 528                                 break;
 529                         }
 530 
 531                         for (j = 0; j < HFS_EXT_NB; ++j) {
 532                                 if (!extent[j].block_count) break;
 533                                 if (!hfsc_cache_add_extent(
 534                                         cache,
 535                                         PED_BE16_TO_CPU(extent[j].start_block),
 536                                         PED_BE16_TO_CPU(extent[j].block_count),
 537                                         leaf_node,
 538                                         (uint8_t*)extent - node,
 539                                         1, /* hfs => btree block = 512 b */
 540                                         where,
 541                                         j )
 542                                    )
 543                                         return 0;
 544                         }
 545                 }
 546         }
 547 
 548         return 1;
 549 }
 550 
 551 /* This function cache every extents start and length stored in any
 552    fs structure into the adt defined in cache.[ch]
 553    Returns NULL on failure */
 554 static HfsCPrivateCache*
 555 hfs_cache_extents(PedFileSystem *fs, PedTimer* timer)
 556 {
 557         HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
 558                                                 fs->type_specific;
 559         HfsCPrivateCache*       ret;
 560         unsigned int            file_number, block_number;
 561 
 562         file_number = PED_BE32_TO_CPU(priv_data->mdb->file_count);
 563         block_number = PED_BE16_TO_CPU(priv_data->mdb->total_blocks);
 564         ret = hfsc_new_cache(block_number, file_number);
 565         if (!ret) return NULL;
 566 
 567         if (!hfs_cache_from_mdb(ret, fs, timer) ||
 568             !hfs_cache_from_catalog(ret, fs, timer) ||
 569             !hfs_cache_from_extent(ret, fs, timer)) {
 570                 ped_exception_throw(
 571                         PED_EXCEPTION_ERROR,
 572                         PED_EXCEPTION_CANCEL,
 573                         _("Could not cache the file system in memory."));
 574                 hfsc_delete_cache(ret);
 575                 return NULL;
 576         }
 577 
 578         return ret;
 579 }
 580 
 581 /* This function moves file's data to compact used and free space,
 582    starting at fblock block */
 583 /* return 0 on error */
 584 int
 585 hfs_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock,
 586                                 PedTimer* timer, unsigned int to_free)
 587 {
 588         PedSector               bytes_buff;
 589         HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
 590                                                 fs->type_specific;
 591         HfsMasterDirectoryBlock* mdb = priv_data->mdb;
 592         HfsCPrivateCache*       cache;
 593         unsigned int            to_fblock = fblock;
 594         unsigned int            start = fblock;
 595         unsigned int            divisor = PED_BE16_TO_CPU (mdb->total_blocks)
 596                                           + 1 - start - to_free;
 597         int                     ret;
 598 
 599         PED_ASSERT (!hfs_block, return 0);
 600 
 601         cache = hfs_cache_extents (fs, timer);
 602         if (!cache)
 603                 return 0;
 604 
 605         /* Calculate the size of the copy buffer :
 606          * Takes BLOCK_MAX_BUFF HFS blocks, but if > BYTES_MAX_BUFF
 607          * takes the maximum number of HFS blocks so that the buffer
 608          * will remain smaller than or equal to BYTES_MAX_BUFF, with
 609          * a minimum of 1 HFS block */
 610         bytes_buff = PED_BE32_TO_CPU (priv_data->mdb->block_size)
 611                      * (PedSector) BLOCK_MAX_BUFF;
 612         if (bytes_buff > BYTES_MAX_BUFF) {
 613                 hfs_block_count = BYTES_MAX_BUFF
 614                                  / PED_BE32_TO_CPU (priv_data->mdb->block_size);
 615                 if (!hfs_block_count)
 616                         hfs_block_count = 1;
 617                 bytes_buff = (PedSector) hfs_block_count
 618                              * PED_BE32_TO_CPU (priv_data->mdb->block_size);
 619         } else
 620                 hfs_block_count = BLOCK_MAX_BUFF;
 621 
 622         /* If the cache code requests more space, give it to him */
 623         if (bytes_buff < hfsc_cache_needed_buffer (cache))
 624                 bytes_buff = hfsc_cache_needed_buffer (cache);
 625 
 626         hfs_block = (uint8_t*) ped_malloc (bytes_buff);
 627         if (!hfs_block)
 628                 goto error_cache;
 629 
 630         if (!hfs_read_bad_blocks (fs)) {
 631                 ped_exception_throw (
 632                         PED_EXCEPTION_ERROR,
 633                         PED_EXCEPTION_CANCEL,
 634                         _("Bad blocks list could not be loaded."));
 635                 goto error_alloc;
 636         }
 637 
 638         while (fblock < PED_BE16_TO_CPU (mdb->total_blocks)) {
 639                 if (TST_BLOC_OCCUPATION(priv_data->alloc_map,fblock)
 640                     && (!hfs_is_bad_block (fs, fblock))) {
 641                         if (!(ret = hfs_move_extent_starting_at (fs, &fblock,
 642                                                 &to_fblock, cache)))
 643                                 to_fblock = ++fblock;
 644                         else if (ret == -1) {
 645                                 ped_exception_throw (
 646                                         PED_EXCEPTION_ERROR,
 647                                         PED_EXCEPTION_CANCEL,
 648                                         _("An error occurred during extent "
 649                                           "relocation."));
 650                                 goto error_alloc;
 651                         }
 652                 } else {
 653                         fblock++;
 654                 }
 655 
 656                 ped_timer_update(timer, (float)(to_fblock - start)/divisor);
 657         }
 658 
 659         ped_free (hfs_block); hfs_block = NULL; hfs_block_count = 0;
 660         hfsc_delete_cache (cache);
 661         return 1;
 662 
 663 error_alloc:
 664         ped_free (hfs_block); hfs_block = NULL; hfs_block_count = 0;
 665 error_cache:
 666         hfsc_delete_cache (cache);
 667         return 0;
 668 }
 669 
 670 #endif /* !DISCOVER_ONLY */