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 27 #if ENABLE_NLS 28 # include <libintl.h> 29 # define _(String) dgettext (PACKAGE, String) 30 #else 31 # define _(String) (String) 32 #endif /* ENABLE_NLS */ 33 34 #include "hfs.h" 35 #include "advfs.h" 36 #include "file_plus.h" 37 38 #include "advfs_plus.h" 39 40 /* - if a < b, 0 if a == b, + if a > b */ 41 /* Comparaison is done in the following order : */ 42 /* CNID, then fork type, then start block */ 43 static int 44 hfsplus_extent_key_cmp(HfsPPrivateGenericKey* a, HfsPPrivateGenericKey* b) 45 { 46 HfsPExtentKey* key1 = (HfsPExtentKey*) a; 47 HfsPExtentKey* key2 = (HfsPExtentKey*) b; 48 49 if (key1->file_ID != key2->file_ID) 50 return PED_BE32_TO_CPU(key1->file_ID) < 51 PED_BE32_TO_CPU(key2->file_ID) ? 52 -1 : +1; 53 54 if (key1->type != key2->type) 55 return (int)(key1->type - key2->type); 56 57 if (key1->start == key2->start) 58 return 0; 59 return PED_BE32_TO_CPU(key1->start) < 60 PED_BE32_TO_CPU(key2->start) ? 61 -1 : +1; 62 } 63 64 /* do a B-Tree lookup */ 65 /* read the first record immediatly inferior or egal to the given key */ 66 /* return 0 on error */ 67 /* record_out _must_ be large enough to receive the whole record (key + data) */ 68 /* WARNING : the search function called only handle Extents BTree */ 69 /* so modify this function if you want to do lookup in */ 70 /* other BTrees has well */ 71 int 72 hfsplus_btree_search (HfsPPrivateFile* b_tree_file, HfsPPrivateGenericKey* key, 73 void *record_out, unsigned int record_size, 74 HfsCPrivateLeafRec* record_ref) 75 { 76 uint8_t node_1[PED_SECTOR_SIZE_DEFAULT]; 77 uint8_t* node; 78 HfsPHeaderRecord* header; 79 HfsPNodeDescriptor* desc = (HfsPNodeDescriptor*) node_1; 80 HfsPPrivateGenericKey* record_key = NULL; 81 unsigned int node_number, record_number, size, bsize; 82 int i; 83 84 /* Read the header node */ 85 if (!hfsplus_file_read_sector(b_tree_file, node_1, 0)) 86 return 0; 87 header = (HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC); 88 89 /* Get the node number of the root */ 90 node_number = PED_BE32_TO_CPU (header->root_node); 91 if (!node_number) 92 return 0; 93 94 /* Get the size of a node in sectors and allocate buffer */ 95 size = (bsize = PED_BE16_TO_CPU (header->node_size)) / PED_SECTOR_SIZE_DEFAULT; 96 node = (uint8_t*) ped_malloc (bsize); 97 if (!node) 98 return 0; 99 desc = (HfsPNodeDescriptor*) node; 100 101 /* Read the root node */ 102 if (!hfsplus_file_read (b_tree_file, node, 103 (PedSector) node_number * size, size)) 104 return 0; 105 106 /* Follow the white rabbit */ 107 while (1) { 108 record_number = PED_BE16_TO_CPU (desc->rec_nb); 109 for (i = record_number; i; i--) { 110 record_key = (HfsPPrivateGenericKey*) 111 (node + PED_BE16_TO_CPU(*((uint16_t *) 112 (node+(bsize - 2*i))))); 113 /* check for obvious error in FS */ 114 if (((uint8_t*)record_key - node < HFS_FIRST_REC) 115 || ((uint8_t*)record_key - node 116 >= (signed)bsize 117 - 2 * (signed)(record_number+1))) { 118 ped_exception_throw ( 119 PED_EXCEPTION_ERROR, 120 PED_EXCEPTION_CANCEL, 121 _("The file system contains errors.")); 122 ped_free (node); 123 return 0; 124 } 125 if (hfsplus_extent_key_cmp(record_key, key) <= 0) 126 break; 127 } 128 if (!i) { ped_free (node); return 0; } 129 if (desc->type == HFS_IDX_NODE) { 130 unsigned int skip; 131 132 skip = ( 2 + PED_BE16_TO_CPU (record_key->key_length) 133 + 1 ) & ~1; 134 node_number = PED_BE32_TO_CPU (*((uint32_t *) 135 (((uint8_t *) record_key) + skip))); 136 if (!hfsplus_file_read(b_tree_file, node, 137 (PedSector) node_number * size, 138 size)) { 139 ped_free (node); 140 return 0; 141 } 142 } else 143 break; 144 } 145 146 /* copy the result if needed */ 147 if (record_size) 148 memcpy (record_out, record_key, record_size); 149 150 /* send record reference if needed */ 151 if (record_ref) { 152 record_ref->node_size = size; /* in sectors */ 153 record_ref->node_number = node_number; 154 record_ref->record_pos = (uint8_t*)record_key - node; 155 record_ref->record_number = i; 156 } 157 158 /* success */ 159 ped_free (node); 160 return 1; 161 } 162 163 /* free the bad blocks linked list */ 164 void 165 hfsplus_free_bad_blocks_list(HfsPPrivateLinkExtent* first) 166 { 167 HfsPPrivateLinkExtent* next; 168 169 while (first) { 170 next = first->next; 171 ped_free (first); 172 first = next; 173 } 174 } 175 176 /* This function reads bad blocks extents in the extents file 177 and store it in f.s. specific data of fs */ 178 int 179 hfsplus_read_bad_blocks (const PedFileSystem *fs) 180 { 181 HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) 182 fs->type_specific; 183 184 if (priv_data->bad_blocks_loaded) 185 return 1; 186 187 { 188 uint8_t record[sizeof (HfsPExtentKey) 189 + sizeof (HfsPExtDataRec)]; 190 HfsPExtentKey search; 191 HfsPExtentKey* ret_key = (HfsPExtentKey*) record; 192 HfsPExtDescriptor* ret_cache = (HfsPExtDescriptor*) 193 (record + sizeof (HfsPExtentKey)); 194 int block, first_pass = 1; 195 unsigned int last_start; 196 197 search.key_length = sizeof (HfsExtentKey) - 2; 198 search.type = HFS_DATA_FORK; 199 search.pad = 0; 200 search.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID); 201 202 last_start = -1; block = 0; 203 while (1) { 204 int i; 205 206 search.start = PED_CPU_TO_BE32 (block); 207 if (!hfsplus_btree_search (priv_data->extents_file, 208 (HfsPPrivateGenericKey*) &search, 209 record, sizeof (record), NULL) 210 || ret_key->file_ID != search.file_ID 211 || ret_key->type != search.type) { 212 if (first_pass) 213 break; 214 else 215 goto errbbp; 216 } 217 if (PED_BE32_TO_CPU (ret_key->start) == last_start) 218 break; 219 220 last_start = PED_BE32_TO_CPU (ret_key->start); 221 for (i = 0; i < HFSP_EXT_NB; i++) { 222 if (ret_cache[i].block_count) { 223 HfsPPrivateLinkExtent* new_xt = 224 (HfsPPrivateLinkExtent*) ped_malloc ( 225 sizeof (HfsPPrivateLinkExtent)); 226 if (!new_xt) 227 goto errbbp; 228 new_xt->next = priv_data->bad_blocks_xtent_list; 229 memcpy (&(new_xt->extent), ret_cache+i, 230 sizeof (HfsPExtDescriptor)); 231 priv_data->bad_blocks_xtent_list = new_xt; 232 priv_data->bad_blocks_xtent_nb++; 233 block += PED_BE32_TO_CPU ( 234 ret_cache[i].block_count); 235 } 236 } 237 first_pass = 0; 238 } 239 240 priv_data->bad_blocks_loaded = 1; 241 return 1;} 242 243 errbbp: hfsplus_free_bad_blocks_list(priv_data->bad_blocks_xtent_list); 244 priv_data->bad_blocks_xtent_list=NULL; 245 priv_data->bad_blocks_xtent_nb=0; 246 return 0; 247 } 248 249 /* This function check if fblock is a bad block */ 250 int 251 hfsplus_is_bad_block (const PedFileSystem *fs, unsigned int fblock) 252 { 253 HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) 254 fs->type_specific; 255 HfsPPrivateLinkExtent* walk; 256 257 for (walk = priv_data->bad_blocks_xtent_list; walk; walk = walk->next) { 258 /* Won't compile without the strange cast ! gcc bug ? */ 259 /* or maybe C subtilties... */ 260 if ((fblock >= PED_BE32_TO_CPU (walk->extent.start_block)) && 261 (fblock < (unsigned int)(PED_BE32_TO_CPU ( 262 walk->extent.start_block) 263 + PED_BE32_TO_CPU (walk->extent.block_count)))) 264 return 1; 265 } 266 267 return 0; 268 } 269 270 /* This function returns the first sector of the last free block of 271 an HFS+ volume we can get after a hfsplus_pack_free_space_from_block call */ 272 PedSector 273 hfsplus_get_empty_end (const PedFileSystem *fs) 274 { 275 HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) 276 fs->type_specific; 277 HfsPVolumeHeader* vh = priv_data->vh; 278 HfsPPrivateLinkExtent* link; 279 unsigned int block, last_bad, end_free_blocks; 280 281 /* find the next block to the last bad block of the volume */ 282 if (!hfsplus_read_bad_blocks (fs)) { 283 ped_exception_throw ( 284 PED_EXCEPTION_ERROR, 285 PED_EXCEPTION_CANCEL, 286 _("Bad blocks could not be read.")); 287 return 0; 288 } 289 290 last_bad = 0; 291 for (link = priv_data->bad_blocks_xtent_list; link; link = link->next) { 292 if ((unsigned int) PED_BE32_TO_CPU (link->extent.start_block) 293 + PED_BE32_TO_CPU (link->extent.block_count) > last_bad) 294 last_bad = PED_BE32_TO_CPU (link->extent.start_block) 295 + PED_BE32_TO_CPU (link->extent.block_count); 296 } 297 298 /* Count the free blocks from last_bad to the end of the volume */ 299 end_free_blocks = 0; 300 for (block = last_bad; 301 block < PED_BE32_TO_CPU (vh->total_blocks); 302 block++) { 303 if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) 304 end_free_blocks++; 305 } 306 307 /* Calculate the block that will by the first free at 308 the end of the volume */ 309 block = PED_BE32_TO_CPU (vh->total_blocks) - end_free_blocks; 310 311 return (PedSector) block * ( PED_BE32_TO_CPU (vh->block_size) 312 / PED_SECTOR_SIZE_DEFAULT ); 313 } 314 315 /* On error, returns 0 */ 316 PedSector 317 hfsplus_get_min_size (const PedFileSystem *fs) 318 { 319 HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) 320 fs->type_specific; 321 PedSector min_size; 322 323 /* don't need to add anything because every sector 324 can be part of allocation blocks in HFS+, and 325 the last block _must_ be reserved */ 326 min_size = hfsplus_get_empty_end(fs); 327 if (!min_size) return 0; 328 329 if (priv_data->wrapper) { 330 HfsPrivateFSData* hfs_priv_data = (HfsPrivateFSData*) 331 priv_data->wrapper->type_specific; 332 unsigned int hfs_sect_block; 333 PedSector hgee; 334 hfs_sect_block = 335 PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size) 336 / PED_SECTOR_SIZE_DEFAULT; 337 /* 338 * if hfs+ is embedded in an hfs wrapper then the new size is : 339 * the new size of the hfs+ volume rounded up to the size 340 * of hfs blocks 341 * + the minimum size of the hfs wrapper without any hfs+ 342 * modification 343 * - the current size of the hfs+ volume in the hfs wrapper 344 */ 345 hgee = hfs_get_empty_end(priv_data->wrapper); 346 if (!hgee) return 0; 347 min_size = ((min_size + hfs_sect_block - 1) / hfs_sect_block) 348 * hfs_sect_block 349 + hgee + 2 350 - (PedSector) PED_BE16_TO_CPU ( hfs_priv_data->mdb 351 ->old_new.embedded 352 .location.block_count ) 353 * hfs_sect_block; 354 } 355 356 return min_size; 357 } 358 359 /* return the block which should be used to pack data to have 360 at least free fblock blocks at the end of the volume */ 361 unsigned int 362 hfsplus_find_start_pack (const PedFileSystem *fs, unsigned int fblock) 363 { 364 HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) 365 fs->type_specific; 366 unsigned int block; 367 368 for (block = PED_BE32_TO_CPU (priv_data->vh->total_blocks) - 1; 369 block && fblock; 370 block--) { 371 if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) 372 fblock--; 373 } 374 375 while (block && !TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) 376 block--; 377 if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) 378 block++; 379 380 return block; 381 } 382 383 #endif /* !DISCOVER_ONLY */