/* libparted - a library for manipulating disk partitions Copyright (C) 2004-2005, 2007, 2009-2014, 2019-2023 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DISCOVER_ONLY #include #include #include #include #include #if ENABLE_NLS # include # define _(String) dgettext (PACKAGE, String) #else # define _(String) (String) #endif /* ENABLE_NLS */ #include "hfs.h" #include "file_plus.h" #include "advfs_plus.h" #include "cache.h" #include "journal.h" #include "reloc_plus.h" /* This function moves data of size blocks starting at block *ptr_fblock to block *ptr_to_fblock */ /* return new start or -1 on failure */ /* -1 is ok because there can only be 2^32-1 blocks, so the max possible last one is 2^32-2 (and anyway it contains Alternate VH), so -1 (== 2^32-1[2^32]) never represent a valid block */ static int hfsplus_effect_move_extent (PedFileSystem *fs, unsigned int *ptr_fblock, unsigned int *ptr_to_fblock, unsigned int size) { HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) fs->type_specific; unsigned int i, ok = 0; unsigned int next_to_fblock; unsigned int start, stop; PED_ASSERT (hfsp_block != NULL); PED_ASSERT (*ptr_to_fblock <= *ptr_fblock); /* quiet GCC */ start = stop = 0; /* Try to fit the extent AT or _BEFORE_ the wanted place, or then in the gap between dest and source. If failed try to fit the extent after source, for 2 pass relocation The extent is always copied in a non overlapping way */ /* Backward search */ /* 1 pass relocation AT or BEFORE *ptr_to_fblock */ if (*ptr_to_fblock != *ptr_fblock) { start = stop = *ptr_fblock < *ptr_to_fblock+size ? *ptr_fblock : *ptr_to_fblock+size; while (start && stop-start != size) { --start; if (TST_BLOC_OCCUPATION(priv_data->alloc_map,start)) stop = start; } ok = (stop-start == size); } /* Forward search */ /* 1 pass relocation in the gap merged with 2 pass reloc after source */ if (!ok && *ptr_to_fblock != *ptr_fblock) { start = stop = *ptr_to_fblock+1; while (stop < PED_BE32_TO_CPU(priv_data->vh->total_blocks) && stop-start != size) { if (TST_BLOC_OCCUPATION(priv_data->alloc_map,stop)) start = stop + 1; ++stop; } ok = (stop-start == size); } /* new non overlapping room has been found ? */ if (ok) { /* enough room */ PedSector abs_sector; unsigned int ai, j, block; unsigned int block_sz = (PED_BE32_TO_CPU ( priv_data->vh->block_size) / PED_SECTOR_SIZE_DEFAULT); if (stop > *ptr_to_fblock && stop <= *ptr_fblock) /* Fit in the gap */ next_to_fblock = stop; else /* Before or after the gap */ next_to_fblock = *ptr_to_fblock; /* move blocks */ for (i = 0; i < size; /*i++*/) { j = size - i; j = (j < hfsp_block_count) ? j : hfsp_block_count ; abs_sector = (PedSector) (*ptr_fblock + i) * block_sz; if (!ped_geometry_read (priv_data->plus_geom, hfsp_block, abs_sector, block_sz * j)) return -1; abs_sector = (PedSector) (start + i) * block_sz; if (!ped_geometry_write (priv_data->plus_geom, hfsp_block, abs_sector, block_sz * j)) return -1; for (ai = i+j; i < ai; i++) { /* free source block */ block = *ptr_fblock + i; CLR_BLOC_OCCUPATION(priv_data->alloc_map,block); SET_BLOC_OCCUPATION(priv_data->dirty_alloc_map, block/(PED_SECTOR_SIZE_DEFAULT*8)); /* set dest block */ block = start + i; SET_BLOC_OCCUPATION(priv_data->alloc_map,block); SET_BLOC_OCCUPATION(priv_data->dirty_alloc_map, block/(PED_SECTOR_SIZE_DEFAULT*8)); } } if (!ped_geometry_sync_fast (priv_data->plus_geom)) return -1; *ptr_fblock += size; *ptr_to_fblock = next_to_fblock; } else { if (*ptr_fblock != *ptr_to_fblock) /* not enough room */ ped_exception_throw (PED_EXCEPTION_WARNING, PED_EXCEPTION_IGNORE, _("An extent has not been relocated.")); start = *ptr_fblock; *ptr_fblock = *ptr_to_fblock = start + size; } return start; } /* Returns 0 on error */ /* 1 on succes */ int hfsplus_update_vh (PedFileSystem *fs) { HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) fs->type_specific; uint8_t node[PED_SECTOR_SIZE_DEFAULT]; if (!ped_geometry_read (priv_data->plus_geom, node, 2, 1)) return 0; memcpy (node, priv_data->vh, sizeof (HfsPVolumeHeader)); if (!ped_geometry_write (priv_data->plus_geom, node, 2, 1) || !ped_geometry_write (priv_data->plus_geom, node, priv_data->plus_geom->length - 2, 1) || !ped_geometry_sync_fast (priv_data->plus_geom)) return 0; return 1; } static int hfsplus_do_move (PedFileSystem* fs, unsigned int *ptr_src, unsigned int *ptr_dest, HfsCPrivateCache* cache, HfsCPrivateExtent* ref) { HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) fs->type_specific; HfsPPrivateFile* file; HfsPExtDescriptor* extent; HfsCPrivateExtent* move; int new_start; new_start = hfsplus_effect_move_extent (fs, ptr_src, ptr_dest, ref->ext_length); if (new_start == -1) return -1; if (ref->ext_start != (unsigned) new_start) { switch (ref->where) { /************ VH ************/ case CR_PRIM_CAT : priv_data->catalog_file ->first[ref->ref_index].start_block = PED_CPU_TO_BE32(new_start); goto CR_PRIM; case CR_PRIM_EXT : priv_data->extents_file ->first[ref->ref_index].start_block = PED_CPU_TO_BE32(new_start); goto CR_PRIM; case CR_PRIM_ATTR : priv_data->attributes_file ->first[ref->ref_index].start_block = PED_CPU_TO_BE32(new_start); goto CR_PRIM; case CR_PRIM_ALLOC : priv_data->allocation_file ->first[ref->ref_index].start_block = PED_CPU_TO_BE32(new_start); goto CR_PRIM; case CR_PRIM_START : /* No startup file opened */ CR_PRIM : extent = ( HfsPExtDescriptor* ) ( (uint8_t*)priv_data->vh + ref->ref_offset ); extent[ref->ref_index].start_block = PED_CPU_TO_BE32(new_start); if (!hfsplus_update_vh(fs)) return -1; break; /************** BTREE *************/ case CR_BTREE_CAT_JIB : if (!hfsj_update_jib(fs, new_start)) return -1; goto BTREE_CAT; case CR_BTREE_CAT_JL : if (!hfsj_update_jl(fs, new_start)) return -1; goto BTREE_CAT; BTREE_CAT: case CR_BTREE_CAT : file = priv_data->catalog_file; goto CR_BTREE; case CR_BTREE_ATTR : file = priv_data->attributes_file; goto CR_BTREE; case CR_BTREE_EXT_ATTR : if (priv_data->attributes_file ->cache[ref->ref_index].start_block == PED_CPU_TO_BE32(ref->ext_start)) priv_data->attributes_file ->cache[ref->ref_index].start_block = PED_CPU_TO_BE32(new_start); goto CR_BTREE_EXT; case CR_BTREE_EXT_CAT : if (priv_data->catalog_file ->cache[ref->ref_index].start_block == PED_CPU_TO_BE32(ref->ext_start)) priv_data->catalog_file ->cache[ref->ref_index].start_block = PED_CPU_TO_BE32(new_start); goto CR_BTREE_EXT; case CR_BTREE_EXT_ALLOC : if (priv_data->allocation_file ->cache[ref->ref_index].start_block == PED_CPU_TO_BE32(ref->ext_start)) priv_data->allocation_file ->cache[ref->ref_index].start_block = PED_CPU_TO_BE32(new_start); goto CR_BTREE_EXT; case CR_BTREE_EXT_START : /* No startup file opened */ CR_BTREE_EXT : case CR_BTREE_EXT_0 : file = priv_data->extents_file; CR_BTREE : PED_ASSERT(PED_SECTOR_SIZE_DEFAULT * ref->sect_by_block > ref->ref_offset); if (!hfsplus_file_read(file, hfsp_block, (PedSector)ref->ref_block * ref->sect_by_block, ref->sect_by_block)) return -1; extent = ( HfsPExtDescriptor* ) ( hfsp_block + ref->ref_offset ); extent[ref->ref_index].start_block = PED_CPU_TO_BE32(new_start); if (!hfsplus_file_write(file, hfsp_block, (PedSector)ref->ref_block * ref->sect_by_block, ref->sect_by_block) || !ped_geometry_sync_fast (priv_data->plus_geom)) return -1; break; /********** BUG *********/ default : ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("A reference to an extent comes from a place " "it should not. You should check the file " "system!")); return -1; break; } move = hfsc_cache_move_extent(cache, ref->ext_start, new_start); if (!move) return -1; PED_ASSERT(move == ref); } return new_start; } /* save any dirty sector of the allocation bitmap file */ static int hfsplus_save_allocation(PedFileSystem *fs) { HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) fs->type_specific; unsigned int map_sectors, i, j; int ret = 1; map_sectors = ( PED_BE32_TO_CPU (priv_data->vh->total_blocks) + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) / (PED_SECTOR_SIZE_DEFAULT * 8); for (i = 0; i < map_sectors;) { for (j = i; (TST_BLOC_OCCUPATION(priv_data->dirty_alloc_map,j)); ++j) CLR_BLOC_OCCUPATION(priv_data->dirty_alloc_map,j); if (j-i) { ret = hfsplus_file_write(priv_data->allocation_file, priv_data->alloc_map + i * PED_SECTOR_SIZE_DEFAULT, i, j-i) && ret; i = j; } else ++i; } return ret; } /* This function moves an extent starting at block fblock to block to_fblock if there's enough room */ /* Return 1 if everything was fine */ /* Return -1 if an error occurred */ /* Return 0 if no extent was found */ static int hfsplus_move_extent_starting_at (PedFileSystem *fs, unsigned int *ptr_fblock, unsigned int *ptr_to_fblock, HfsCPrivateCache* cache) { HfsCPrivateExtent* ref; unsigned int old_start, new_start; ref = hfsc_cache_search_extent(cache, *ptr_fblock); if (!ref) return 0; old_start = *ptr_fblock; new_start = hfsplus_do_move(fs, ptr_fblock, ptr_to_fblock, cache, ref); if (new_start == (unsigned)-1) return -1; if (new_start > old_start) { new_start = hfsplus_do_move(fs, &new_start, ptr_to_fblock, cache, ref); if (new_start == (unsigned)-1 || new_start > old_start) return -1; } hfsplus_save_allocation(fs); return 1; } static int hfsplus_cache_from_vh(HfsCPrivateCache* cache, PedFileSystem* fs, PedTimer* timer) { HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) fs->type_specific; HfsPExtDescriptor* extent; unsigned int j; extent = priv_data->vh->allocation_file.extents; for (j = 0; j < HFSP_EXT_NB; ++j) { if (!extent[j].block_count) break; if (!hfsc_cache_add_extent( cache, PED_BE32_TO_CPU(extent[j].start_block), PED_BE32_TO_CPU(extent[j].block_count), 0, /* unused for vh */ ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), 1, /* load / save 1 sector */ CR_PRIM_ALLOC, j ) ) return 0; } extent = priv_data->vh->extents_file.extents; for (j = 0; j < HFSP_EXT_NB; ++j) { if (!extent[j].block_count) break; if (!hfsc_cache_add_extent( cache, PED_BE32_TO_CPU(extent[j].start_block), PED_BE32_TO_CPU(extent[j].block_count), 0, /* unused for vh */ ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), 1, /* load / save 1 sector */ CR_PRIM_EXT, j ) ) return 0; } extent = priv_data->vh->catalog_file.extents; for (j = 0; j < HFSP_EXT_NB; ++j) { if (!extent[j].block_count) break; if (!hfsc_cache_add_extent( cache, PED_BE32_TO_CPU(extent[j].start_block), PED_BE32_TO_CPU(extent[j].block_count), 0, /* unused for vh */ ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), 1, /* load / save 1 sector */ CR_PRIM_CAT, j ) ) return 0; } extent = priv_data->vh->attributes_file.extents; for (j = 0; j < HFSP_EXT_NB; ++j) { if (!extent[j].block_count) break; if (!hfsc_cache_add_extent( cache, PED_BE32_TO_CPU(extent[j].start_block), PED_BE32_TO_CPU(extent[j].block_count), 0, /* unused for vh */ ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), 1, /* load / save 1 sector */ CR_PRIM_ATTR, j ) ) return 0; } extent = priv_data->vh->startup_file.extents; for (j = 0; j < HFSP_EXT_NB; ++j) { if (!extent[j].block_count) break; if (!hfsc_cache_add_extent( cache, PED_BE32_TO_CPU(extent[j].start_block), PED_BE32_TO_CPU(extent[j].block_count), 0, /* unused for vh */ ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), 1, /* load / save 1 sector */ CR_PRIM_START, j ) ) return 0; } return 1; } static int hfsplus_cache_from_catalog(HfsCPrivateCache* cache, PedFileSystem* fs, PedTimer* timer) { HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) fs->type_specific; uint8_t node_1[PED_SECTOR_SIZE_DEFAULT]; uint8_t* node; HfsPHeaderRecord* header; HfsPCatalogKey* catalog_key; HfsPCatalog* catalog_data; HfsPExtDescriptor* extent; unsigned int leaf_node, record_number; unsigned int i, j, size, bsize; uint32_t jib = priv_data->jib_start_block, jl = priv_data->jl_start_block; uint16_t catalog_pos; if (!priv_data->catalog_file->sect_nb) { ped_exception_throw ( PED_EXCEPTION_INFORMATION, PED_EXCEPTION_OK, _("This HFS+ volume has no catalog file. " "This is very unusual!")); return 1; } /* Search the extent starting at *ptr_block in the catalog file */ if (!hfsplus_file_read_sector (priv_data->catalog_file, node_1, 0)) return 0; header = (HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC); leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); bsize = PED_BE16_TO_CPU (header->node_size); size = bsize / PED_SECTOR_SIZE_DEFAULT; PED_ASSERT(size < 256); node = (uint8_t*) ped_malloc(bsize); if (!node) return 0; HfsPNodeDescriptor *desc = (HfsPNodeDescriptor*) node; for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) { if (!hfsplus_file_read (priv_data->catalog_file, node, (PedSector) leaf_node * size, size)) { free (node); return 0; } record_number = PED_BE16_TO_CPU (desc->rec_nb); for (i = 1; i <= record_number; i++) { unsigned int skip; uint8_t where; uint16_t value; memcpy(&value, node+(bsize - (2*i)), sizeof(uint16_t)); catalog_pos = PED_BE16_TO_CPU(value); catalog_key = (HfsPCatalogKey*)(node + catalog_pos); skip = ( 2 + PED_BE16_TO_CPU (catalog_key->key_length) + 1) & ~1; catalog_data = (HfsPCatalog*) (((uint8_t*)catalog_key) + skip); /* check for obvious error in FS */ if ((catalog_pos < HFS_FIRST_REC) || ((uint8_t*)catalog_data - node >= (signed) bsize - 2 * (signed)(record_number+1))) { ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("The file system contains errors.")); free (node); return 0; } if (PED_BE16_TO_CPU(catalog_data->type)!=HFS_CAT_FILE) continue; extent = catalog_data->sel.file.data_fork.extents; for (j = 0; j < HFSP_EXT_NB; ++j) { if (!extent[j].block_count) break; where = CR_BTREE_CAT; if ( PED_BE32_TO_CPU(extent[j].start_block) == jib ) { jib = 0; where = CR_BTREE_CAT_JIB; } else if ( PED_BE32_TO_CPU(extent[j].start_block) == jl ) { jl = 0; where = CR_BTREE_CAT_JL; } if (!hfsc_cache_add_extent( cache, PED_BE32_TO_CPU(extent[j].start_block), PED_BE32_TO_CPU(extent[j].block_count), leaf_node, (uint8_t*)extent - node, size, where, j ) ) { free (node); return 0; } } extent = catalog_data->sel.file.res_fork.extents; for (j = 0; j < HFSP_EXT_NB; ++j) { if (!extent[j].block_count) break; if (!hfsc_cache_add_extent( cache, PED_BE32_TO_CPU(extent[j].start_block), PED_BE32_TO_CPU(extent[j].block_count), leaf_node, (uint8_t*)extent - node, size, CR_BTREE_CAT, j ) ) { free (node); return 0; } } } } free (node); return 1; } static int hfsplus_cache_from_extent(HfsCPrivateCache* cache, PedFileSystem* fs, PedTimer* timer) { HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) fs->type_specific; uint8_t node_1[PED_SECTOR_SIZE_DEFAULT]; uint8_t* node; HfsPHeaderRecord* header; HfsPExtentKey* extent_key; HfsPExtDescriptor* extent; unsigned int leaf_node, record_number; unsigned int i, j, size, bsize; uint16_t extent_pos; if (!priv_data->extents_file->sect_nb) { ped_exception_throw ( PED_EXCEPTION_INFORMATION, PED_EXCEPTION_OK, _("This HFS+ volume has no extents overflow " "file. This is quite unusual!")); return 1; } if (!hfsplus_file_read_sector (priv_data->extents_file, node_1, 0)) return 0; header = ((HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC)); leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); bsize = PED_BE16_TO_CPU (header->node_size); size = bsize / PED_SECTOR_SIZE_DEFAULT; PED_ASSERT(size < 256); node = (uint8_t*) ped_malloc (bsize); if (!node) return -1; HfsPNodeDescriptor *desc = (HfsPNodeDescriptor*) node; for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) { if (!hfsplus_file_read (priv_data->extents_file, node, (PedSector) leaf_node * size, size)) { free (node); return 0; } record_number = PED_BE16_TO_CPU (desc->rec_nb); for (i = 1; i <= record_number; i++) { uint8_t where; uint16_t value; memcpy(&value, node+(bsize - (2*i)), sizeof(uint16_t)); extent_pos = PED_BE16_TO_CPU(value); extent_key = (HfsPExtentKey*)(node + extent_pos); extent = (HfsPExtDescriptor*) (((uint8_t*)extent_key) + sizeof (HfsPExtentKey)); /* check for obvious error in FS */ if ((extent_pos < HFS_FIRST_REC) || ((uint8_t*)extent - node >= (signed)bsize - 2 * (signed)(record_number+1))) { ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("The file system contains errors.")); free (node); return -1; } switch (extent_key->file_ID) { case PED_CPU_TO_BE32 (HFS_XTENT_ID) : if (ped_exception_throw ( PED_EXCEPTION_WARNING, PED_EXCEPTION_IGNORE_CANCEL, _("The extents overflow file should not" " contain its own extents! You should " "check the file system.")) != PED_EXCEPTION_IGNORE) return 0; where = CR_BTREE_EXT_EXT; break; case PED_CPU_TO_BE32 (HFS_CATALOG_ID) : where = CR_BTREE_EXT_CAT; break; case PED_CPU_TO_BE32 (HFSP_ALLOC_ID) : where = CR_BTREE_EXT_ALLOC; break; case PED_CPU_TO_BE32 (HFSP_STARTUP_ID) : where = CR_BTREE_EXT_START; break; case PED_CPU_TO_BE32 (HFSP_ATTRIB_ID) : where = CR_BTREE_EXT_ATTR; break; default : where = CR_BTREE_EXT_0; break; } for (j = 0; j < HFSP_EXT_NB; ++j) { if (!extent[j].block_count) break; if (!hfsc_cache_add_extent( cache, PED_BE32_TO_CPU(extent[j].start_block), PED_BE32_TO_CPU(extent[j].block_count), leaf_node, (uint8_t*)extent - node, size, where, j ) ) { free (node); return 0; } } } } free (node); return 1; } static int hfsplus_cache_from_attributes(HfsCPrivateCache* cache, PedFileSystem* fs, PedTimer* timer) { HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) fs->type_specific; uint8_t node_1[PED_SECTOR_SIZE_DEFAULT]; uint8_t* node; HfsPHeaderRecord* header; HfsPPrivateGenericKey* generic_key; HfsPForkDataAttr* fork_ext_data; HfsPExtDescriptor* extent; unsigned int leaf_node, record_number; unsigned int i, j, size, bsize; uint16_t generic_pos; /* attributes file is facultative */ if (!priv_data->attributes_file->sect_nb) return 1; /* Search the extent starting at *ptr_block in the catalog file */ if (!hfsplus_file_read_sector (priv_data->attributes_file, node_1, 0)) return 0; header = ((HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC)); leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); bsize = PED_BE16_TO_CPU (header->node_size); size = bsize / PED_SECTOR_SIZE_DEFAULT; PED_ASSERT(size < 256); node = (uint8_t*) ped_malloc(bsize); if (!node) return 0; HfsPNodeDescriptor *desc = (HfsPNodeDescriptor*) node; for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) { if (!hfsplus_file_read (priv_data->attributes_file, node, (PedSector) leaf_node * size, size)) { free (node); return 0; } record_number = PED_BE16_TO_CPU (desc->rec_nb); for (i = 1; i <= record_number; i++) { unsigned int skip; uint16_t value; memcpy(&value, node+(bsize - (2*i)), sizeof(uint16_t)); generic_pos = PED_BE16_TO_CPU(value); generic_key = (HfsPPrivateGenericKey*)(node + generic_pos); skip = ( 2 + PED_BE16_TO_CPU (generic_key->key_length) + 1 ) & ~1; fork_ext_data = (HfsPForkDataAttr*)(node+generic_pos+skip); /* check for obvious error in FS */ if ((generic_pos < HFS_FIRST_REC) || ((uint8_t*)fork_ext_data - node >= (signed) bsize - 2 * (signed)(record_number+1))) { ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("The file system contains errors.")); free (node); return 0; } if (fork_ext_data->record_type == PED_CPU_TO_BE32 ( HFSP_ATTR_FORK ) ) { extent = fork_ext_data->fork_res.fork.extents; for (j = 0; j < HFSP_EXT_NB; ++j) { if (!extent[j].block_count) break; if (!hfsc_cache_add_extent( cache, PED_BE32_TO_CPU ( extent[j].start_block ), PED_BE32_TO_CPU ( extent[j].block_count ), leaf_node, (uint8_t*)extent-node, size, CR_BTREE_ATTR, j ) ) { free(node); return 0; } } } else if (fork_ext_data->record_type == PED_CPU_TO_BE32 ( HFSP_ATTR_EXTENTS ) ) { extent = fork_ext_data->fork_res.extents; for (j = 0; j < HFSP_EXT_NB; ++j) { if (!extent[j].block_count) break; if (!hfsc_cache_add_extent( cache, PED_BE32_TO_CPU ( extent[j].start_block ), PED_BE32_TO_CPU ( extent[j].block_count ), leaf_node, (uint8_t*)extent-node, size, CR_BTREE_ATTR, j ) ) { free(node); return 0; } } } else continue; } } free (node); return 1; } static HfsCPrivateCache* hfsplus_cache_extents(PedFileSystem* fs, PedTimer* timer) { HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) fs->type_specific; HfsCPrivateCache* ret; unsigned int file_number, block_number; file_number = PED_BE32_TO_CPU(priv_data->vh->file_count); block_number = PED_BE32_TO_CPU(priv_data->vh->total_blocks); ret = hfsc_new_cache(block_number, file_number); if (!ret) return NULL; if (!hfsplus_cache_from_vh(ret, fs, timer) || !hfsplus_cache_from_catalog(ret, fs, timer) || !hfsplus_cache_from_extent(ret, fs, timer) || !hfsplus_cache_from_attributes(ret, fs, timer)) { ped_exception_throw( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("Could not cache the file system in memory.")); hfsc_delete_cache(ret); return NULL; } return ret; } /* This function moves file's data to compact used and free space, starting at fblock block */ /* return 0 on error */ int hfsplus_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock, PedTimer* timer, unsigned int to_free) { PedSector bytes_buff; HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) fs->type_specific; HfsPVolumeHeader* vh = priv_data->vh; HfsCPrivateCache* cache; unsigned int to_fblock = fblock; unsigned int start = fblock; unsigned int divisor = PED_BE32_TO_CPU (vh->total_blocks) + 1 - start - to_free; int ret; PED_ASSERT (!hfsp_block); cache = hfsplus_cache_extents (fs, timer); if (!cache) return 0; /* Calculate the size of the copy buffer : * Takes BLOCK_MAX_BUFF HFS blocks, but if > BYTES_MAX_BUFF * takes the maximum number of HFS blocks so that the buffer * will remain smaller than or equal to BYTES_MAX_BUFF, with * a minimum of 1 HFS block */ bytes_buff = PED_BE32_TO_CPU (priv_data->vh->block_size) * (PedSector) BLOCK_MAX_BUFF; if (bytes_buff > BYTES_MAX_BUFF) { hfsp_block_count = BYTES_MAX_BUFF / PED_BE32_TO_CPU (priv_data->vh->block_size); if (!hfsp_block_count) hfsp_block_count = 1; bytes_buff = (PedSector) hfsp_block_count * PED_BE32_TO_CPU (priv_data->vh->block_size); } else hfsp_block_count = BLOCK_MAX_BUFF; /* If the cache code requests more space, give it to him */ if (bytes_buff < hfsc_cache_needed_buffer (cache)) bytes_buff = hfsc_cache_needed_buffer (cache); hfsp_block = (uint8_t*) ped_malloc (bytes_buff); if (!hfsp_block) goto error_cache; if (!hfsplus_read_bad_blocks (fs)) { ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("Bad blocks list could not be loaded.")); goto error_alloc; } while ( fblock < ( priv_data->plus_geom->length - 2 ) / ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT ) ) { if (TST_BLOC_OCCUPATION (priv_data->alloc_map, fblock) && (!hfsplus_is_bad_block (fs, fblock))) { if (!(ret = hfsplus_move_extent_starting_at (fs, &fblock, &to_fblock, cache))) to_fblock = ++fblock; else if (ret == -1) { ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("An error occurred during extent " "relocation.")); goto error_alloc; } } else { fblock++; } ped_timer_update(timer, (float)(to_fblock - start) / divisor); } free (hfsp_block); hfsp_block = NULL; hfsp_block_count = 0; hfsc_delete_cache (cache); return 1; error_alloc: free (hfsp_block); hfsp_block = NULL; hfsp_block_count = 0; error_cache: hfsc_delete_cache (cache); return 0; } #endif /* !DISCOVER_ONLY */