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 "reloc_plus.h" 37 38 #include "journal.h" 39 40 static int hfsj_vh_replayed = 0; 41 static int is_le = 0; 42 43 static uint32_t 44 hfsj_calc_checksum(uint8_t *ptr, int len) 45 { 46 int i; 47 uint32_t cksum=0; 48 49 for (i=0; i < len; i++, ptr++) { 50 cksum = (cksum << 8) ^ (cksum + *ptr); 51 } 52 53 return (~cksum); 54 } 55 56 int 57 hfsj_update_jib(PedFileSystem* fs, uint32_t block) 58 { 59 HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) 60 fs->type_specific; 61 62 priv_data->vh->journal_info_block = PED_CPU_TO_BE32(block); 63 64 if (!hfsplus_update_vh (fs)) 65 return 0; 66 67 priv_data->jib_start_block = block; 68 return 1; 69 } 70 71 int 72 hfsj_update_jl(PedFileSystem* fs, uint32_t block) 73 { 74 uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; 75 PedSector sector; 76 uint64_t offset; 77 HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) 78 fs->type_specific; 79 HfsJJournalInfoBlock* jib; 80 int binsect; 81 82 binsect = HFS_32_TO_CPU(priv_data->vh->block_size, is_le) / PED_SECTOR_SIZE_DEFAULT; 83 sector = (PedSector) priv_data->jib_start_block * binsect; 84 if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1)) 85 return 0; 86 jib = (HfsJJournalInfoBlock*) buf; 87 88 offset = (uint64_t)block * PED_SECTOR_SIZE_DEFAULT * binsect; 89 jib->offset = HFS_CPU_TO_64(offset, is_le); 90 91 if (!ped_geometry_write(priv_data->plus_geom, buf, sector, 1) 92 || !ped_geometry_sync(priv_data->plus_geom)) 93 return 0; 94 95 priv_data->jl_start_block = block; 96 return 1; 97 } 98 99 /* Return the sector in the journal that is after the area read */ 100 /* or 0 on error */ 101 static PedSector 102 hfsj_journal_read(PedGeometry* geom, HfsJJournalHeader* jh, 103 PedSector journ_sect, PedSector journ_length, 104 PedSector read_sect, unsigned int nb_sect, 105 void* buf) 106 { 107 int r; 108 109 while (nb_sect--) { 110 r = ped_geometry_read(geom, buf, journ_sect + read_sect, 1); 111 if (!r) return 0; 112 113 buf = ((uint8_t*)buf) + PED_SECTOR_SIZE_DEFAULT; 114 read_sect++; 115 if (read_sect == journ_length) 116 read_sect = 1; /* skip journal header 117 which is asserted to be 118 1 sector long */ 119 } 120 121 return read_sect; 122 } 123 124 static int 125 hfsj_replay_transaction(PedFileSystem* fs, HfsJJournalHeader* jh, 126 PedSector jsector, PedSector jlength) 127 { 128 PedSector start, sector; 129 HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) 130 fs->type_specific; 131 HfsJBlockListHeader* blhdr; 132 uint8_t* block; 133 unsigned int blhdr_nbsect; 134 int i, r; 135 uint32_t cksum, size; 136 137 blhdr_nbsect = HFS_32_TO_CPU(jh->blhdr_size, is_le) / PED_SECTOR_SIZE_DEFAULT; 138 blhdr = (HfsJBlockListHeader*) 139 ped_malloc (blhdr_nbsect * PED_SECTOR_SIZE_DEFAULT); 140 if (!blhdr) return 0; 141 142 start = HFS_64_TO_CPU(jh->start, is_le) / PED_SECTOR_SIZE_DEFAULT; 143 do { 144 start = hfsj_journal_read(priv_data->plus_geom, jh, jsector, 145 jlength, start, blhdr_nbsect, blhdr); 146 if (!start) goto err_replay; 147 148 cksum = HFS_32_TO_CPU(blhdr->checksum, is_le); 149 blhdr->checksum = 0; 150 if (cksum!=hfsj_calc_checksum((uint8_t*)blhdr, sizeof(*blhdr))){ 151 ped_exception_throw ( 152 PED_EXCEPTION_ERROR, 153 PED_EXCEPTION_CANCEL, 154 _("Bad block list header checksum.")); 155 goto err_replay; 156 } 157 blhdr->checksum = HFS_CPU_TO_32(cksum, is_le); 158 159 for (i=1; i < HFS_16_TO_CPU(blhdr->num_blocks, is_le); ++i) { 160 size = HFS_32_TO_CPU(blhdr->binfo[i].bsize, is_le); 161 sector = HFS_64_TO_CPU(blhdr->binfo[i].bnum, is_le); 162 if (!size) continue; 163 if (size % PED_SECTOR_SIZE_DEFAULT) { 164 ped_exception_throw( 165 PED_EXCEPTION_ERROR, 166 PED_EXCEPTION_CANCEL, 167 _("Invalid size of a transaction " 168 "block while replaying the journal " 169 "(%i bytes)."), 170 size); 171 goto err_replay; 172 } 173 block = (uint8_t*) ped_malloc(size); 174 if (!block) goto err_replay; 175 start = hfsj_journal_read(priv_data->plus_geom, jh, 176 jsector, jlength, start, 177 size / PED_SECTOR_SIZE_DEFAULT, 178 block); 179 if (!start) { 180 ped_free (block); 181 goto err_replay; 182 } 183 /* the sector stored in the journal seems to be 184 relative to the begin of the block device which 185 contains the hfs+ journaled volume */ 186 if (sector != ~0LL) 187 r = ped_geometry_write (fs->geom, block, sector, 188 size / PED_SECTOR_SIZE_DEFAULT); 189 else 190 r = 1; 191 ped_free (block); 192 /* check if wrapper mdb or vh with no wrapper has 193 changed */ 194 if ( (sector != ~0LL) 195 && (2 >= sector) 196 && (2 < sector + size / PED_SECTOR_SIZE_DEFAULT) ) 197 hfsj_vh_replayed = 1; 198 /* check if vh of embedded hfs+ has changed */ 199 if ( (sector != ~0LL) 200 && (priv_data->plus_geom != fs->geom) 201 && (sector 202 + fs->geom->start 203 - priv_data->plus_geom->start <= 2) 204 && (sector 205 + size / PED_SECTOR_SIZE_DEFAULT 206 + fs->geom->start 207 - priv_data->plus_geom->start > 2) ) 208 hfsj_vh_replayed = 1; 209 if (!r) goto err_replay; 210 } 211 } while (blhdr->binfo[0].next); 212 213 jh->start = HFS_CPU_TO_64(start * PED_SECTOR_SIZE_DEFAULT, is_le); 214 215 ped_free (blhdr); 216 return (ped_geometry_sync (fs->geom)); 217 218 err_replay: 219 ped_free (blhdr); 220 return 0; 221 } 222 223 /* 0 => Failure, don't continue to open ! */ 224 /* 1 => Success, the journal has been completly replayed, or don't need to */ 225 int 226 hfsj_replay_journal(PedFileSystem* fs) 227 { 228 uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; 229 PedSector sector, length; 230 HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) 231 fs->type_specific; 232 HfsJJournalInfoBlock* jib; 233 HfsJJournalHeader* jh; 234 int binsect; 235 uint32_t cksum; 236 237 binsect = PED_BE32_TO_CPU(priv_data->vh->block_size) / PED_SECTOR_SIZE_DEFAULT; 238 priv_data->jib_start_block = 239 PED_BE32_TO_CPU(priv_data->vh->journal_info_block); 240 sector = (PedSector) priv_data->jib_start_block * binsect; 241 if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1)) 242 return 0; 243 jib = (HfsJJournalInfoBlock*) buf; 244 245 if ( (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_IN_FS)) 246 && !(jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_OTHER_DEV)) ) { 247 priv_data->jl_start_block = HFS_64_TO_CPU(jib->offset, is_le) 248 / ( PED_SECTOR_SIZE_DEFAULT * binsect ); 249 } 250 251 if (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_NEED_INIT)) 252 return 1; 253 254 if ( !(jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_IN_FS)) 255 || (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_OTHER_DEV)) ) { 256 ped_exception_throw ( 257 PED_EXCEPTION_NO_FEATURE, 258 PED_EXCEPTION_CANCEL, 259 _("Journal stored outside of the volume are " 260 "not supported. Try to desactivate the " 261 "journal and run Parted again.")); 262 return 0; 263 } 264 265 if ( (PED_BE64_TO_CPU(jib->offset) % PED_SECTOR_SIZE_DEFAULT) 266 || (PED_BE64_TO_CPU(jib->size) % PED_SECTOR_SIZE_DEFAULT) ) { 267 ped_exception_throw ( 268 PED_EXCEPTION_NO_FEATURE, 269 PED_EXCEPTION_CANCEL, 270 _("Journal offset or size is not multiple of " 271 "the sector size.")); 272 return 0; 273 } 274 275 sector = PED_BE64_TO_CPU(jib->offset) / PED_SECTOR_SIZE_DEFAULT; 276 length = PED_BE64_TO_CPU(jib->size) / PED_SECTOR_SIZE_DEFAULT; 277 278 jib = NULL; 279 if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1)) 280 return 0; 281 jh = (HfsJJournalHeader*) buf; 282 283 if (jh->endian == PED_LE32_TO_CPU(HFSJ_ENDIAN_MAGIC)) 284 is_le = 1; 285 286 if ( (jh->magic != HFS_32_TO_CPU(HFSJ_HEADER_MAGIC, is_le)) 287 || (jh->endian != HFS_32_TO_CPU(HFSJ_ENDIAN_MAGIC, is_le)) ) { 288 ped_exception_throw ( 289 PED_EXCEPTION_ERROR, 290 PED_EXCEPTION_CANCEL, 291 _("Incorrect magic values in the journal header.")); 292 return 0; 293 } 294 295 if ( (HFS_64_TO_CPU(jh->size, is_le)%PED_SECTOR_SIZE_DEFAULT) 296 || (HFS_64_TO_CPU(jh->size, is_le)/PED_SECTOR_SIZE_DEFAULT 297 != (uint64_t)length) ) { 298 ped_exception_throw ( 299 PED_EXCEPTION_ERROR, 300 PED_EXCEPTION_CANCEL, 301 _("Journal size mismatch between journal info block " 302 "and journal header.")); 303 return 0; 304 } 305 306 if ( (HFS_64_TO_CPU(jh->start, is_le) % PED_SECTOR_SIZE_DEFAULT) 307 || (HFS_64_TO_CPU(jh->end, is_le) % PED_SECTOR_SIZE_DEFAULT) 308 || (HFS_32_TO_CPU(jh->blhdr_size, is_le) % PED_SECTOR_SIZE_DEFAULT) 309 || (HFS_32_TO_CPU(jh->jhdr_size, is_le) % PED_SECTOR_SIZE_DEFAULT) ) { 310 ped_exception_throw ( 311 PED_EXCEPTION_ERROR, 312 PED_EXCEPTION_CANCEL, 313 _("Some header fields are not multiple of the sector " 314 "size.")); 315 return 0; 316 } 317 318 if (HFS_32_TO_CPU(jh->jhdr_size, is_le) != PED_SECTOR_SIZE_DEFAULT) { 319 ped_exception_throw ( 320 PED_EXCEPTION_ERROR, 321 PED_EXCEPTION_CANCEL, 322 _("The sector size stored in the journal is not 512 " 323 "bytes. Parted only supports 512 bytes length " 324 "sectors.")); 325 return 0; 326 } 327 328 cksum = HFS_32_TO_CPU(jh->checksum, is_le); 329 jh->checksum = 0; 330 if (cksum != hfsj_calc_checksum((uint8_t*)jh, sizeof(*jh))) { 331 ped_exception_throw ( 332 PED_EXCEPTION_ERROR, 333 PED_EXCEPTION_CANCEL, 334 _("Bad journal checksum.")); 335 return 0; 336 } 337 jh->checksum = HFS_CPU_TO_32(cksum, is_le); 338 339 /* The 2 following test are in the XNU Darwin source code */ 340 /* so I assume they're needed */ 341 if (jh->start == jh->size) 342 jh->start = HFS_CPU_TO_64(PED_SECTOR_SIZE_DEFAULT, is_le); 343 if (jh->end == jh->size) 344 jh->start = HFS_CPU_TO_64(PED_SECTOR_SIZE_DEFAULT, is_le); 345 346 if (jh->start == jh->end) 347 return 1; 348 349 if (ped_exception_throw ( 350 PED_EXCEPTION_WARNING, 351 PED_EXCEPTION_FIX | PED_EXCEPTION_CANCEL, 352 _("The journal is not empty. Parted must replay the " 353 "transactions before opening the file system. This will " 354 "modify the file system.")) 355 != PED_EXCEPTION_FIX) 356 return 0; 357 358 while (jh->start != jh->end) { 359 /* Replay one complete transaction */ 360 if (!hfsj_replay_transaction(fs, jh, sector, length)) 361 return 0; 362 363 /* Recalculate cksum of the journal header */ 364 jh->checksum = 0; /* need to be 0 while calculating the cksum */ 365 cksum = hfsj_calc_checksum((uint8_t*)jh, sizeof(*jh)); 366 jh->checksum = HFS_CPU_TO_32(cksum, is_le); 367 368 /* Update the Journal Header */ 369 if (!ped_geometry_write(priv_data->plus_geom, buf, sector, 1) 370 || !ped_geometry_sync(priv_data->plus_geom)) 371 return 0; 372 } 373 374 if (hfsj_vh_replayed) { 375 /* probe could have reported incorrect info ! */ 376 /* is there a way to ask parted to quit ? */ 377 ped_exception_throw( 378 PED_EXCEPTION_WARNING, 379 PED_EXCEPTION_OK, 380 _("The volume header or the master directory block has " 381 "changed while replaying the journal. You should " 382 "restart Parted.")); 383 return 0; 384 } 385 386 return 1; 387 } 388 389 #endif /* DISCOVER_ONLY */