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 38 #include "advfs.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 /* Note that HFS implementation in linux has a bug */ 44 /* in this function */ 45 static int 46 hfs_extent_key_cmp(HfsPrivateGenericKey* a, HfsPrivateGenericKey* b) 47 { 48 HfsExtentKey* key1 = (HfsExtentKey*) a; 49 HfsExtentKey* key2 = (HfsExtentKey*) b; 50 51 /* do NOT use a substraction, because */ 52 /* 0xFFFFFFFF - 1 = 0xFFFFFFFE so this */ 53 /* would return -2, despite the fact */ 54 /* 0xFFFFFFFF > 1 !!! (this is the 2.4 bug) */ 55 if (key1->file_ID != key2->file_ID) 56 return PED_BE32_TO_CPU(key1->file_ID) < 57 PED_BE32_TO_CPU(key2->file_ID) ? 58 -1 : +1; 59 60 if (key1->type != key2->type) 61 return (int)(key1->type - key2->type); 62 63 if (key1->start == key2->start) 64 return 0; 65 /* the whole thing wont work with 16 bits ints */ 66 /* anyway */ 67 return (int)( PED_BE16_TO_CPU(key1->start) - 68 PED_BE16_TO_CPU(key2->start) ); 69 } 70 71 /* do a B-Tree lookup */ 72 /* read the first record immediatly inferior or egal to the given key */ 73 /* return 0 on error */ 74 /* record_out _must_ be large enough to receive record_size bytes */ 75 /* WARNING : the compare function called only handle Extents BTree */ 76 /* so modify this function if you want to do lookup in */ 77 /* other BTrees has well */ 78 int 79 hfs_btree_search (HfsPrivateFile* b_tree_file, HfsPrivateGenericKey* key, 80 void *record_out, unsigned int record_size, 81 HfsCPrivateLeafRec* record_ref) 82 { 83 uint8_t node[PED_SECTOR_SIZE_DEFAULT]; 84 HfsHeaderRecord* header; 85 HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node; 86 HfsPrivateGenericKey* record_key = NULL; 87 unsigned int node_number, record_number; 88 int i; 89 90 /* Read the header node */ 91 if (!hfs_file_read_sector(b_tree_file, node, 0)) 92 return 0; 93 header = ((HfsHeaderRecord*) (node + PED_BE16_TO_CPU(*((uint16_t *) 94 (node+(PED_SECTOR_SIZE_DEFAULT-2)))))); 95 96 /* Get the node number of the root */ 97 node_number = PED_BE32_TO_CPU(header->root_node); 98 if (!node_number) 99 return 0; 100 101 /* Read the root node */ 102 if (!hfs_file_read_sector(b_tree_file, node, node_number)) 103 return 0; 104 105 /* Follow the white rabbit */ 106 while (1) { 107 record_number = PED_BE16_TO_CPU (desc->rec_nb); 108 for (i = record_number; i; i--) { 109 record_key = (HfsPrivateGenericKey*) 110 (node + PED_BE16_TO_CPU(*((uint16_t *) 111 (node+(PED_SECTOR_SIZE_DEFAULT - 2*i))))); 112 /* check for obvious error in FS */ 113 if (((uint8_t*)record_key - node < HFS_FIRST_REC) 114 || ((uint8_t*)record_key - node 115 >= PED_SECTOR_SIZE_DEFAULT 116 - 2 * (signed)(record_number+1))) { 117 ped_exception_throw ( 118 PED_EXCEPTION_ERROR, 119 PED_EXCEPTION_CANCEL, 120 _("The file system contains errors.")); 121 return 0; 122 } 123 if (hfs_extent_key_cmp(record_key, key) <= 0) 124 break; 125 } 126 if (!i) return 0; 127 if (desc->type == HFS_IDX_NODE) { 128 unsigned int skip; 129 130 skip = (1 + record_key->key_length + 1) & ~1; 131 node_number = PED_BE32_TO_CPU (*((uint32_t *) 132 (((uint8_t *) record_key) + skip))); 133 if (!hfs_file_read_sector(b_tree_file, node, 134 node_number)) 135 return 0; 136 } else 137 break; 138 } 139 140 /* copy the result if needed */ 141 if (record_size) 142 memcpy (record_out, record_key, record_size); 143 144 /* send record reference if needed */ 145 if (record_ref) { 146 record_ref->node_size = 1; /* in sectors */ 147 record_ref->node_number = node_number; 148 record_ref->record_pos = (uint8_t*)record_key - node; 149 record_ref->record_number = i; 150 } 151 152 /* success */ 153 return 1; 154 } 155 156 /* free the bad blocks linked list */ 157 void 158 hfs_free_bad_blocks_list(HfsPrivateLinkExtent* first) 159 { 160 HfsPrivateLinkExtent* next; 161 162 while (first) { 163 next = first->next; 164 ped_free (first); 165 first = next; 166 } 167 } 168 169 /* This function reads bad blocks extents in the extents file 170 and store it in f.s. specific data of fs */ 171 int 172 hfs_read_bad_blocks (const PedFileSystem *fs) 173 { 174 HfsPrivateFSData* priv_data = (HfsPrivateFSData*) 175 fs->type_specific; 176 177 if (priv_data->bad_blocks_loaded) 178 return 1; 179 180 { 181 uint8_t record[sizeof (HfsExtentKey) 182 + sizeof (HfsExtDataRec)]; 183 HfsExtentKey search; 184 HfsExtentKey* ret_key = (HfsExtentKey*) record; 185 HfsExtDescriptor* ret_cache = (HfsExtDescriptor*) 186 (record + sizeof (HfsExtentKey)); 187 unsigned int block, last_start, first_pass = 1; 188 189 search.key_length = sizeof (HfsExtentKey) - 1; 190 search.type = HFS_DATA_FORK; 191 search.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID); 192 193 last_start = -1; block = 0; 194 while (1) { 195 int i; 196 197 search.start = PED_CPU_TO_BE16 (block); 198 if (!hfs_btree_search (priv_data->extent_file, 199 (HfsPrivateGenericKey*) &search, 200 record, sizeof (record), NULL) 201 || ret_key->file_ID != search.file_ID 202 || ret_key->type != search.type) { 203 if (first_pass) 204 break; 205 else 206 goto errbb; 207 } 208 if (PED_BE16_TO_CPU (ret_key->start) == last_start) 209 break; 210 211 last_start = PED_BE16_TO_CPU (ret_key->start); 212 for (i = 0; i < HFS_EXT_NB; i++) { 213 if (ret_cache[i].block_count) { 214 HfsPrivateLinkExtent* new_xt = 215 (HfsPrivateLinkExtent*) ped_malloc ( 216 sizeof (HfsPrivateLinkExtent)); 217 if (!new_xt) 218 goto errbb; 219 new_xt->next = priv_data->bad_blocks_xtent_list; 220 memcpy(&(new_xt->extent), ret_cache+i, 221 sizeof (HfsExtDescriptor)); 222 priv_data->bad_blocks_xtent_list = new_xt; 223 priv_data->bad_blocks_xtent_nb++; 224 block += PED_BE16_TO_CPU ( 225 ret_cache[i].block_count); 226 } 227 } 228 first_pass = 0; 229 } 230 231 priv_data->bad_blocks_loaded = 1; 232 return 1;} 233 234 errbb: hfs_free_bad_blocks_list(priv_data->bad_blocks_xtent_list); 235 priv_data->bad_blocks_xtent_list=NULL; 236 priv_data->bad_blocks_xtent_nb=0; 237 return 0; 238 } 239 240 /* This function check if fblock is a bad block */ 241 int 242 hfs_is_bad_block (const PedFileSystem *fs, unsigned int fblock) 243 { 244 HfsPrivateFSData* priv_data = (HfsPrivateFSData*) 245 fs->type_specific; 246 HfsPrivateLinkExtent* walk; 247 248 for (walk = priv_data->bad_blocks_xtent_list; walk; walk = walk->next) { 249 /* Won't compile without the strange cast ! gcc bug ? */ 250 /* or maybe C subtilties... */ 251 if ((fblock >= PED_BE16_TO_CPU (walk->extent.start_block)) && 252 (fblock < (unsigned int) (PED_BE16_TO_CPU ( 253 walk->extent.start_block) 254 + PED_BE16_TO_CPU ( 255 walk->extent.block_count)))) 256 return 1; 257 } 258 259 return 0; 260 } 261 262 /* This function returns the first sector of the last free block of an 263 HFS volume we can get after a hfs_pack_free_space_from_block call */ 264 /* On error this function returns 0 */ 265 PedSector 266 hfs_get_empty_end (const PedFileSystem *fs) 267 { 268 HfsPrivateFSData* priv_data = (HfsPrivateFSData*) 269 fs->type_specific; 270 HfsMasterDirectoryBlock* mdb = priv_data->mdb; 271 HfsPrivateLinkExtent* link; 272 unsigned int block, last_bad, end_free_blocks; 273 274 /* find the next block to the last bad block of the volume */ 275 if (!hfs_read_bad_blocks (fs)) 276 return 0; 277 278 last_bad = 0; 279 for (link = priv_data->bad_blocks_xtent_list; link; link = link->next) { 280 if ((unsigned int) PED_BE16_TO_CPU (link->extent.start_block) 281 + PED_BE16_TO_CPU (link->extent.block_count) > last_bad) 282 last_bad = PED_BE16_TO_CPU (link->extent.start_block) 283 + PED_BE16_TO_CPU (link->extent.block_count); 284 } 285 286 /* Count the free blocks from last_bad to the end of the volume */ 287 end_free_blocks = 0; 288 for (block = last_bad; 289 block < PED_BE16_TO_CPU (mdb->total_blocks); 290 block++) { 291 if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) 292 end_free_blocks++; 293 } 294 295 /* Calculate the block that will by the first free at the 296 end of the volume */ 297 block = PED_BE16_TO_CPU (mdb->total_blocks) - end_free_blocks; 298 299 return (PedSector) PED_BE16_TO_CPU (mdb->start_block) 300 + (PedSector) block * (PED_BE32_TO_CPU (mdb->block_size) 301 / PED_SECTOR_SIZE_DEFAULT); 302 } 303 304 /* return the block which should be used to pack data to have at 305 least free fblock blocks at the end of the volume */ 306 unsigned int 307 hfs_find_start_pack (const PedFileSystem *fs, unsigned int fblock) 308 { 309 HfsPrivateFSData* priv_data = (HfsPrivateFSData*) 310 fs->type_specific; 311 unsigned int block; 312 313 for (block = PED_BE16_TO_CPU (priv_data->mdb->total_blocks) - 1; 314 block && fblock; 315 block--) { 316 if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) 317 fblock--; 318 } 319 320 while (block && !TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) 321 block--; 322 if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) 323 block++; 324 325 return block; 326 } 327 328 #endif /* !DISCOVER_ONLY */