1/* 2* HFSPlus journal implementation Tathagata Das 2010 3*/ 4 5#include <linux/fs.h> 6#include <linux/blkdev.h> 7#include <linux/pagemap.h> 8#include <linux/slab.h> 9 10#include <asm/current.h> 11#include <asm/unaligned.h> 12 13#include "hfsplus_jbd.h" 14#include "hfsplus_fs.h" 15#include "hfsplus_raw.h" 16 17/* Calculate chesum of ptr of size len */ 18static int calc_checksum(unsigned char *ptr, int len) 19{ 20 int i, chksum = 0; 21 22 for (i=0; i<len; i++, ptr++) 23 chksum = (chksum << 8) ^ (chksum + *ptr); 24 25 return (~chksum); 26} 27 28void swap_block_list_header(struct hfsplus_blhdr *blhdr) 29{ 30 int i; 31 32 blhdr->num_blocks = swab16(blhdr->num_blocks); 33 blhdr->bytes_used = swab32(blhdr->bytes_used); 34 blhdr->checksum = swab32(blhdr->checksum); 35 36 for (i=1; i<blhdr->num_blocks; i++) { 37 blhdr->binfo[i].bnum = swab64(blhdr->binfo[i].bnum); 38 blhdr->binfo[i].bsize = swab32(blhdr->binfo[i].bsize); 39 } 40} 41 42static void swap_journal_header(struct hfsplus_journal_header *jh) 43{ 44 jh->magic = swab32(jh->magic); 45 jh->endian = swab32(jh->endian); 46 jh->start = swab64(jh->start); 47 jh->end = swab64(jh->end); 48 jh->size = swab64(jh->size); 49 jh->blhdr_size = swab32(jh->blhdr_size); 50 jh->checksum = swab32(jh->checksum); 51 jh->jhdr_size = swab32(jh->jhdr_size); 52} 53 54void print_volume_header(struct super_block *sb) 55{ 56 int i; 57 unsigned char *vh_ptr = (unsigned char *)HFSPLUS_SB(sb).s_vhdr; 58 59 dprint(DBG_JOURNAL, "VOLUME HEADER\n"); 60 for (i=0; i<102; i++) 61 dprint(DBG_JOURNAL, "%x ", vh_ptr[i]); 62 dprint(DBG_JOURNAL, "\n"); 63} 64 65static void print_journal_header(struct hfsplus_journal_header *jh) 66{ 67 dprint(DBG_JOURNAL, "HFS+-fs: magic: %x\n endian: %x\n start: %llx\n end: %llx\n size: %llx\n blhdr_size: %x\n checksum: %x\n jhdr_size: %x\n", jh->magic, jh->endian, jh->start, jh->end, jh->size, jh->blhdr_size, jh->checksum, jh->jhdr_size); 68} 69 70static int map_journal_header(struct super_block *sb) 71{ 72 struct hfsplus_journal *jnl = &(HFSPLUS_SB(sb).jnl); 73 u32 jh_block_number; 74 75 jnl->jh_offset = be64_to_cpu(jnl->jibhdr->offset); 76 jh_block_number = jnl->jh_offset >> sb->s_blocksize_bits; 77 dprint(DBG_JOURNAL, "HFS+-fs: jh_block_number: %x\n", jh_block_number); 78 jnl->jh_bh = sb_bread(sb, HFSPLUS_SB(sb).blockoffset + jh_block_number); 79 if (!jnl->jh_bh) { 80 printk("HFS+-fs Line=%d: Error in buffer read\n", __LINE__); 81 return HFSPLUS_JOURNAL_FAIL; 82 } 83 jnl->jhdr = (struct hfsplus_journal_header *)(jnl->jh_bh->b_data); 84 85 return HFSPLUS_JOURNAL_SUCCESS; 86} 87 88/* START: JBD specfic functions */ 89static void hfsplus_abort(struct super_block * sb, const char * function, 90 const char * fmt, ...) 91{ 92 va_list args; 93 94 printk (KERN_ERR "hfsplus_abort called.\n"); 95 96 va_start(args, fmt); 97 printk(KERN_ERR "HFS+-fs error (device %s): %s: ",sb->s_id, function); 98 vprintk(fmt, args); 99 printk("\n"); 100 va_end(args); 101 102 if (sb->s_flags & MS_RDONLY) 103 return; 104 105 printk(KERN_ERR "Remounting filesystem read-only\n"); 106 sb->s_flags |= MS_RDONLY; 107 hfsplus_jbd_abort(HFSPLUS_SB(sb).jnl.s_journal, -EIO); 108} 109 110static void hfsplus_journal_abort_handle(const char *err_no, struct buffer_head *bh, hfsplus_handle_t *hfsplus_handle) 111{ 112 if (hfsplus_jbd_is_handle_aborted(hfsplus_handle->handle)) 113 return; 114 115 printk(KERN_ERR "HFS+-fs: Aborting transaction\n"); 116 hfsplus_jbd_abort_handle(hfsplus_handle->handle); 117} 118 119int hfsplus_journal_get_write_access(const char *err_fn, hfsplus_handle_t *hfsplus_handle, struct buffer_head *bh) 120{ 121 int err; 122 123 if (hfsplus_handle->journaled != HFSPLUS_JOURNAL_PRESENT) 124 return HFSPLUS_JOURNAL_SUCCESS; 125 126 err = hfsplus_jbd_get_write_access(hfsplus_handle->handle, bh); 127 if (err) { 128 printk(KERN_ERR "HFS+-fs: %s() unable to get journal write access\n", err_fn); 129 hfsplus_journal_abort_handle(err_fn, bh, hfsplus_handle); 130 } 131 132 return err; 133} 134 135int hfsplus_journal_dirty_metadata(const char *err_fn, struct buffer_head *bh, hfsplus_handle_t *hfsplus_handle) 136{ 137 int err; 138 139 if (hfsplus_handle->journaled != HFSPLUS_JOURNAL_PRESENT) 140 return HFSPLUS_JOURNAL_SUCCESS; 141 142 err = hfsplus_jbd_dirty_metadata(hfsplus_handle->handle, bh); 143 if (err) { 144 if (!hfsplus_handle->handle->h_err) 145 hfsplus_handle->handle->h_err = err; 146 147 if (hfsplus_jbd_is_handle_aborted(hfsplus_handle->handle)) 148 return err; 149 150 printk(KERN_ERR "HFS+-fs: %s() aborting transaction:\n", err_fn); 151 152 hfsplus_jbd_abort_handle(hfsplus_handle->handle); 153 } else 154 hfsplus_handle->hcnt++; 155 156 return err; 157} 158 159/* 160 * Force the running and committing transactions to commit, 161 * and wait on the commit. 162 */ 163int hfsplus_force_commit(struct super_block *sb) 164{ 165 hfsplus_jbd_t *journal; 166 int ret; 167 168 if (sb->s_flags & MS_RDONLY) 169 return 0; 170 171 journal = HFSPLUS_SB(sb).jnl.s_journal; 172 sb->s_dirt = 0; 173 ret = hfsplus_jbd_force_commit(journal); 174 return ret; 175} 176 177int hfsplus_journal_start(const char *err_fn, struct super_block *sb, hfsplus_handle_t *hfsplus_handle) 178{ 179 hfsplus_jbd_t *journal = HFSPLUS_SB(sb).jnl.s_journal; 180 181 hfsplus_handle->journaled = HFSPLUS_SB(sb).jnl.journaled; 182 if ((hfsplus_handle->journaled != HFSPLUS_JOURNAL_PRESENT) || (journal == NULL)) { 183 hfsplus_handle->handle = NULL; 184 dprint(DBG_JTRANS, "HFS+-fs: Journal was not loaded\n"); 185 return HFSPLUS_JOURNAL_SUCCESS; 186 } 187 188 if (is_hfsplus_jbd_aborted(journal)) { 189 hfsplus_abort(sb, __FUNCTION__, "Detected aborted journal"); 190 return -EROFS; 191 } 192 193 hfsplus_handle->hcnt = 0; 194 hfsplus_handle->maxblock = HFSPLUS_SB(sb).jnl.journal_maxblock; 195 hfsplus_handle->handle = hfsplus_jbd_start(journal, HFSPLUS_SB(sb).jnl.journal_maxblock, hfsplus_handle); 196 if (IS_ERR(hfsplus_handle->handle)) { 197 printk(KERN_ERR "HFS+fs: journal cannot be started from %s. Error number: %ld\n", err_fn, PTR_ERR(hfsplus_handle->handle)); 198 return PTR_ERR(hfsplus_handle->handle); 199 } 200 201 return HFSPLUS_JOURNAL_SUCCESS; 202} 203 204int hfsplus_journal_stop(hfsplus_handle_t *hfsplus_handle) 205{ 206 if ((hfsplus_handle->journaled != HFSPLUS_JOURNAL_PRESENT) || (hfsplus_handle->handle == NULL)) { 207 return HFSPLUS_JOURNAL_SUCCESS; 208 } 209 210 return hfsplus_jbd_stop(hfsplus_handle->handle); 211} 212 213/* Fill in the initial static fields in the new journal superblock */ 214static int hfsplus_journal_superblock_init(hfsplus_jbd_t *journal, int start) 215{ 216 hfsplus_jbd_superblock_t *jsb; 217 struct buffer_head *bh; 218 unsigned long blocknr; 219 int i; 220 221 if (journal->j_superblock == NULL) 222 return HFSPLUS_JOURNAL_FAIL; 223 224 jsb = journal->j_superblock; 225 memset(jsb->mac_padding, 0, sizeof(jsb->mac_padding)); 226 227 if (jsb->s_header.h_magic == cpu_to_be32(JFS_MAGIC_NUMBER)) { 228 dprint(DBG_JOURNAL, "HFS+-fs: Journal superblock is already initialized\n"); 229 } 230 231 dprint(DBG_JOURNAL, "HFS+-fs: Zeroing out journal blocks...\n"); 232 for (i = start; i < journal->j_maxlen; i++) { 233 blocknr = i; 234 bh = __getblk(journal->j_dev, blocknr, journal->j_blocksize); 235 lock_buffer(bh); 236 if (i == start) 237 memset(bh->b_data + HFSPLUS_SECTOR_SIZE, 0, journal->j_blocksize - HFSPLUS_SECTOR_SIZE); 238 else 239 memset(bh->b_data, 0, journal->j_blocksize); 240 mark_buffer_dirty(bh); 241 set_buffer_uptodate(bh); 242 unlock_buffer(bh); 243 __brelse(bh); 244 } 245 246 sync_blockdev(journal->j_dev); 247 dprint(DBG_JOURNAL, "HFS+-fs: journal cleared.\n"); 248 249 jsb->s_header.h_magic = cpu_to_be32(JFS_MAGIC_NUMBER); 250 jsb->s_header.h_blocktype = cpu_to_be32(JFS_SUPERBLOCK_V2); 251 252 jsb->s_blocksize = cpu_to_be32(journal->j_blocksize); 253 jsb->s_maxlen = cpu_to_be32(journal->j_maxlen); 254 jsb->s_first = cpu_to_be32(start+1); /* First block is for Mac's journal header and JBD superblock */ 255 256 journal->j_transaction_sequence = 1; 257 258 journal->j_flags &= ~JFS_ABORT; 259 journal->j_format_version = 2; 260 261 /* Update journal superblock on disk and wait for the IO to complete.*/ 262 hfsplus_jbd_update_superblock(journal, 1); 263 264 return HFSPLUS_JOURNAL_SUCCESS; 265} 266 267hfsplus_jbd_t * hfsplus_get_dev_journal(struct super_block *sb) 268{ 269 struct hfsplus_journal *jnl = &(HFSPLUS_SB(sb).jnl); 270 int start, len, blocksize; 271 hfsplus_jbd_t *journal = NULL; 272 struct hfsplus_journal_header *jh; 273 274 jnl->journal_maxblock = (jnl->jhdr->blhdr_size / sizeof(struct hfsplus_block_info)) - 1; 275 jh = (struct hfsplus_journal_header *)(HFSPLUS_SB(sb).jnl.jhdr); 276 blocksize = sb->s_blocksize; 277 start = (int)(jnl->jh_offset)/blocksize; 278 len = (int)(jh->size + jnl->jh_offset)/blocksize; 279 jnl->journal_maxblock = 512; 280 dprint(DBG_JOURNAL, "start: %d, len: %d, blocksize: %d, journal_maxblock: %d\n", start, len, blocksize, jnl->journal_maxblock); 281 journal = hfsplus_jbd_init_dev(sb->s_bdev, sb->s_bdev, start, len, blocksize); 282 283 if (journal) { 284 dprint(DBG_JOURNAL, "HFS+-fs: Able to create device journal\n"); 285 journal->j_private = sb; 286 ll_rw_block(READ, 1, &journal->j_sb_buffer); 287 wait_on_buffer(journal->j_sb_buffer); 288 if (!buffer_uptodate(journal->j_sb_buffer)) { 289 printk(KERN_ERR "HFS+-fs: I/O error on journal device\n"); 290 goto out_journal; 291 } 292 293 /* Initialize journal superblock for the first time. 294 * Otherwise hfsplus_jbd_load will fail. 295 */ 296 if (hfsplus_journal_superblock_init(journal, start) == HFSPLUS_JOURNAL_SUCCESS) { 297 dprint(DBG_JOURNAL, "HFS+-fs: success in initializeing journal superblock.\n"); 298 } else { 299 printk(KERN_ERR "HFS+-fs: error in initializeing journal superblock.\n"); 300 goto out_journal; 301 } 302 } 303 304 return journal; 305 306out_journal: 307 hfsplus_jbd_destroy(journal); 308 return NULL; 309} 310 311/* Caller should lock the page before calling this function */ 312static int hfsplus_journalled_write_full_page(hfsplus_handle_t *hfsplus_handle, struct page *page) 313{ 314 int err, ret; 315 316 ret = block_prepare_write(page, 0, PAGE_CACHE_SIZE, hfsplus_get_block); 317 if (ret) { 318 printk("%s: Error in block_prepare_write\n", __FUNCTION__); 319 return ret; 320 } 321 322 ret = hfsplus_walk_page_buffers(hfsplus_handle, page_buffers(page), 0, 323 PAGE_CACHE_SIZE, NULL, hfsplus_do_journal_get_write_access); 324 325 err = hfsplus_walk_page_buffers(hfsplus_handle, page_buffers(page), 0, 326 PAGE_CACHE_SIZE, NULL, hfsplus_commit_write_fn); 327 if (ret == 0) 328 ret = err; 329 330 return ret; 331} 332 333int hfsplus_journalled_set_page_dirty(hfsplus_handle_t *hfsplus_handle, struct page *page) 334{ 335 if (hfsplus_handle->journaled != HFSPLUS_JOURNAL_PRESENT) { 336 set_page_dirty(page); 337 return HFSPLUS_JOURNAL_SUCCESS; 338 } 339 340 /* Page contains metadata or data */ 341 if (page->mapping->a_ops == &hfsplus_journalled_btree_aops) { 342 if (hfsplus_handle->hcnt < hfsplus_handle->maxblock) { 343 int ret; 344 345 lock_page(page); 346 ret = hfsplus_journalled_write_full_page(hfsplus_handle, page); 347 unlock_page(page); 348 return ret; 349 } else { 350 printk("BTREE: HFS+-fs: Count exceeds (count %d, maxblock: %d)\n", hfsplus_handle->hcnt++, hfsplus_handle->maxblock); 351 set_page_dirty(page); 352 return HFSPLUS_JOURNAL_SUCCESS; 353 } 354 } 355 else if (page->mapping->a_ops == &hfsplus_journalled_aops) { 356 if (hfsplus_handle->hcnt < hfsplus_handle->maxblock) { 357 int ret; 358 359 lock_page(page); 360 ret = hfsplus_journalled_write_full_page(hfsplus_handle, page); 361 unlock_page(page); 362 return ret; 363 } else { 364 printk("HFS+-fs: Count exceeds (count %d, maxblock: %d)\n", hfsplus_handle->hcnt++, hfsplus_handle->maxblock); 365 set_page_dirty(page); 366 return HFSPLUS_JOURNAL_SUCCESS; 367 } 368 } else { 369 set_page_dirty(page); 370 return HFSPLUS_JOURNAL_SUCCESS; 371 } 372} 373 374int hfsplus_journalled_mark_inode_dirty(const char *err_fn, hfsplus_handle_t *hfsplus_handle, struct inode *inode) 375{ 376 struct hfsplus_vh *vhdr; 377 int ret = 0; 378 379 if (hfsplus_handle->journaled != HFSPLUS_JOURNAL_PRESENT) { 380 mark_inode_dirty(inode); 381 return HFSPLUS_JOURNAL_SUCCESS; 382 } 383 384 dprint(DBG_JTRANS, "hfsplus_journalled_mark_inode_dirty: %lu, called from: %s\n", inode->i_ino, err_fn); 385 hfsplus_ext_write_extent(hfsplus_handle, inode); 386 if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID) { 387 return hfsplus_cat_write_inode(hfsplus_handle, inode); 388 } 389 vhdr = HFSPLUS_SB(inode->i_sb).s_vhdr; 390 switch (inode->i_ino) { 391 case HFSPLUS_ROOT_CNID: 392 ret = hfsplus_cat_write_inode(hfsplus_handle, inode); 393 break; 394 case HFSPLUS_EXT_CNID: 395 if (vhdr->ext_file.total_size != cpu_to_be64(inode->i_size)) { 396 HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP; 397 inode->i_sb->s_dirt = 1; 398 } 399 hfsplus_inode_write_fork(inode, &vhdr->ext_file); 400 hfs_btree_write(hfsplus_handle, HFSPLUS_SB(inode->i_sb).ext_tree); 401 break; 402 case HFSPLUS_CAT_CNID: 403 if (vhdr->cat_file.total_size != cpu_to_be64(inode->i_size)) { 404 HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP; 405 inode->i_sb->s_dirt = 1; 406 } 407 hfsplus_inode_write_fork(inode, &vhdr->cat_file); 408 hfs_btree_write(hfsplus_handle, HFSPLUS_SB(inode->i_sb).cat_tree); 409 break; 410 case HFSPLUS_ALLOC_CNID: 411 if (vhdr->alloc_file.total_size != cpu_to_be64(inode->i_size)) { 412 HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP; 413 inode->i_sb->s_dirt = 1; 414 } 415 hfsplus_inode_write_fork(inode, &vhdr->alloc_file); 416 break; 417 case HFSPLUS_START_CNID: 418 if (vhdr->start_file.total_size != cpu_to_be64(inode->i_size)) { 419 HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP; 420 inode->i_sb->s_dirt = 1; 421 } 422 hfsplus_inode_write_fork(inode, &vhdr->start_file); 423 break; 424 case HFSPLUS_ATTR_CNID: 425 if (vhdr->attr_file.total_size != cpu_to_be64(inode->i_size)) { 426 HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP; 427 inode->i_sb->s_dirt = 1; 428 } 429 hfsplus_inode_write_fork(inode, &vhdr->attr_file); 430 hfs_btree_write(hfsplus_handle, HFSPLUS_SB(inode->i_sb).attr_tree); 431 break; 432 } 433 if (ret) 434 printk(KERN_ERR "HFS+-fs: [%s] unable to do hfsplus_journalled_mark_inode_dirty()\n", err_fn); 435 return ret; 436} 437 438void hfsplus_jhdr_checksum_calculate(hfsplus_jbd_t *journal) 439{ 440 struct hfsplus_journal_header *jh = &(journal->j_superblock->mac_jh); 441 442 jh->checksum = 0; 443 jh->checksum = calc_checksum((unsigned char *)jh, sizeof(struct hfsplus_journal_header)); 444} 445 446void hfsplus_journal_header_start_update(hfsplus_jbd_t *journal, unsigned long freed) 447{ 448 struct super_block *sb = journal->j_private; 449 hfsplus_jbd_superblock_t *jsb = journal->j_superblock; 450 struct hfsplus_journal_header *jh = &(jsb->mac_jh); 451 __be32 mac_jh_start_blocknum = (jh->start + HFSPLUS_SB(sb).jnl.jh_offset) >> sb->s_blocksize_bits; 452 453 dprint(DBG_JCHKPT, "jh->start:%llx, jh_offset: %llx, mac_jh_start_blocknum: %x, freed: %lx, last: %lx, j_first: %lx\n", 454 jh->start, HFSPLUS_SB(sb).jnl.jh_offset, mac_jh_start_blocknum, freed, journal->j_last, journal->j_first); 455 if ((mac_jh_start_blocknum + freed) < journal->j_last) { 456 jh->start += freed * sb->s_blocksize; 457 } 458 else { 459 jh->start = (journal->j_first + (freed - (journal->j_last - mac_jh_start_blocknum)))*sb->s_blocksize; 460 } 461} 462 463void inline hfsplus_journal_header_end_update(hfsplus_jbd_t *journal, struct hfsplus_journal_header *jh) 464{ 465 struct super_block *sb = journal->j_private; 466 467 jh->end = ((journal->j_head - journal->j_first) * sb->s_blocksize) + jh->blhdr_size; 468} 469 470void hfsplus_journal_mark_journal_empty(hfsplus_jbd_t *journal) 471{ 472 struct hfsplus_journal_header *jh = &(journal->j_superblock->mac_jh); 473 474 jh->start = jh->end; 475 jh->checksum = 0; 476 jh->checksum = calc_checksum((unsigned char *)jh, sizeof(struct hfsplus_journal_header)); 477} 478/* END: JBD specfic functions */ 479 480 481/* Write journal header during replay */ 482static int hfsplus_replay_write_journal_header(struct super_block *sb) 483{ 484 struct hfsplus_journal_header *jh = (struct hfsplus_journal_header *)(HFSPLUS_SB(sb).jnl.jhdr); 485 486 jh->start = sb->s_blocksize; 487 jh->end = sb->s_blocksize; 488 jh->blhdr_size = sb->s_blocksize; 489 490 if (HFSPLUS_SB(sb).jnl.flags == HFSPLUS_JOURNAL_SWAP) { 491 swap_journal_header(jh); 492 jh->checksum = 0; 493 jh->checksum = swab32(calc_checksum((unsigned char *)jh, sizeof(struct hfsplus_journal_header))); 494 } 495 else { 496 jh->checksum = 0; 497 jh->checksum = calc_checksum((unsigned char *)jh, sizeof(struct hfsplus_journal_header)); 498 } 499 500 /* Write it to disk */ 501 mark_buffer_dirty(HFSPLUS_SB(sb).jnl.jh_bh); 502 sync_dirty_buffer(HFSPLUS_SB(sb).jnl.jh_bh); 503 504 if (HFSPLUS_SB(sb).jnl.flags == HFSPLUS_JOURNAL_SWAP) 505 swap_journal_header(jh); 506 507 return HFSPLUS_JOURNAL_SUCCESS; 508} 509 510static int hfsplus_write_journal_header(struct super_block *sb) 511{ 512 struct hfsplus_journal_header *jh = (struct hfsplus_journal_header *)(HFSPLUS_SB(sb).jnl.jhdr); 513 514 jh->checksum = 0; 515 jh->checksum = calc_checksum((unsigned char *)jh, sizeof(struct hfsplus_journal_header)); 516 517 /* Write it to disk */ 518 mark_buffer_dirty(HFSPLUS_SB(sb).jnl.jh_bh); 519 520 return HFSPLUS_JOURNAL_SUCCESS; 521} 522 523/* Create journal header, journal buffer and initialize them 524 * Assume that presence of journal is already verified 525*/ 526int hfsplus_journalled_create(struct super_block *sb) 527{ 528 struct hfsplus_journal_header *jhdr; 529 u64 jibsize = be64_to_cpu(HFSPLUS_SB(sb).jnl.jibhdr->offset); 530 531 dprint(DBG_JOURNAL, "sb->s_blocksize: %lx, jibsize: %llx\n", sb->s_blocksize, jibsize); 532 533 /* Journal size is not aligned */ 534 if (((jibsize >> sb->s_blocksize_bits) << sb->s_blocksize_bits) != jibsize) { 535 printk("HFS+-fs: journal size is not aligned\n"); 536 return HFSPLUS_JOURNAL_FAIL; 537 } 538 539 if (map_journal_header(sb) == HFSPLUS_JOURNAL_FAIL) { 540 printk("HFS+-fs: Error in mapping journal header\n"); 541 return HFSPLUS_JOURNAL_FAIL; 542 } 543 544 jhdr = (struct hfsplus_journal_header *)HFSPLUS_SB(sb).jnl.jhdr; 545 546 /* Populate journal header and write it to the disk */ 547 jhdr->magic = HFSPLUS_JOURNAL_HEADER_MAGIC; 548 jhdr->endian = HFSPLUS_JOURNAL_HEADER_ENDIAN; 549 jhdr->start = sb->s_blocksize; /* First block is for journal header itself */ 550 jhdr->end = sb->s_blocksize; /* Initially journal buffer is empty */ 551 jhdr->size = 0x800000; /* Default size of journal log is 8MB */ 552 jhdr->blhdr_size = sb->s_blocksize; 553 jhdr->jhdr_size = sb->s_blocksize; /* Assign first block for journal header */ 554 555 if (jhdr->start != jhdr->end) { 556 printk("HFS+-fs: hfsplus_write_journal_header fail: Journal is not empty\n"); 557 return HFSPLUS_JOURNAL_FAIL; 558 } 559 560 return hfsplus_write_journal_header(sb); 561} 562 563void hfsplus_test_block_list_header(const char *func, struct hfsplus_journal_header *jh, struct hfsplus_journal *jnl) 564{ 565 u32 start_block_number; 566 struct buffer_head *blhdr_bh = NULL; 567 struct hfsplus_blhdr *blhdr; 568 struct super_block *sb = jnl->sbp; 569 570 printk("called from %s()\n", func); 571 start_block_number = (jh->start + jnl->jh_offset) >> sb->s_blocksize_bits; 572 blhdr_bh = sb_bread(sb, start_block_number); 573 if (!blhdr_bh) { 574 printk("HFS+-fs Line=%d: Error in read\n", __LINE__); 575 return; 576 } 577 blhdr = (hfsplus_blhdr_t *)blhdr_bh->b_data; 578 if (jnl->flags == HFSPLUS_JOURNAL_SWAP) 579 swap_block_list_header(blhdr); 580 printk("start block number: %x, max_blocks: %x, num_blocks: %x, bytes_used: %x\n", start_block_number, blhdr->max_blocks, blhdr->num_blocks, blhdr->bytes_used); 581 brelse(blhdr_bh); 582} 583 584/* If the journal consists transaction then write them to disk. 585 * Return success if it brings the file system into consistent state. 586 * Otherwise return fail. 587*/ 588static int hfsplus_journal_replay(struct super_block *sb) 589{ 590 struct hfsplus_journal *jnl = &(HFSPLUS_SB(sb).jnl); 591 struct buffer_head *blhdr_bh = NULL, *tr_bh = NULL, *disk_bh = NULL; 592 struct hfsplus_blhdr *blhdr; 593 u32 start_sector_number, tr_sector_number, disk_sector_number, i, ret = HFSPLUS_JOURNAL_FAIL; 594 u64 tr_offset, disk_offset; 595 struct hfsplus_journal_header *jh = (struct hfsplus_journal_header *)(HFSPLUS_SB(sb).jnl.jhdr); 596 unsigned char *tr_buf, *disk_buf; 597 __be32 bufsize; 598 599 if (jh->start == jh->end) { 600 dprint(DBG_JREPLAY, "HFS+-fs: Journal is empty, nothing to replay\n"); 601 ret = hfsplus_replay_write_journal_header(sb); 602 return ret; 603 } 604 605 if ((jh->start > jh->size) || (jh->end > jh->size)) { 606 printk("HFS+-fs: Wrong start or end offset, start: %llx, end: %llx, jh_offset: %llx, size: %llx\n", jh->start, jh->end, jnl->jh_offset, jh->size); 607 return ret; 608 } 609 610 //hfsplus_test_block_list_header(__FUNCTION__, jh, jnl); 611 612 if (jh->start == jh->size) 613 jh->start = jh->jhdr_size; 614 615 down(&jnl->jnl_lock); 616 /* Go through each transaction */ 617 while (jh->start != jh->end) { 618 if (blhdr_bh) 619 brelse(blhdr_bh); 620 621 start_sector_number = (jh->start + jnl->jh_offset) >> HFSPLUS_SECTOR_SHIFT; 622 dprint(DBG_JREPLAY, "start: %llx, start_sector_number: %x\n", jh->start, start_sector_number); 623 blhdr_bh = sb_bread512(sb, HFSPLUS_SB(sb).blockoffset + start_sector_number, blhdr); 624 if (!blhdr_bh) { 625 printk("HFS+-fs Line=%d: Error in read\n", __LINE__); 626 up(&jnl->jnl_lock); 627 return ret; 628 } 629 630 if (jnl->flags == HFSPLUS_JOURNAL_SWAP) 631 swap_block_list_header(blhdr); 632 633 dprint(DBG_JREPLAY, "HFS+-fs: num_blocks: %x, bytes_used: %x\n", blhdr->num_blocks, blhdr->bytes_used); 634 /* Point to the second block in the Volume, first block is already in block list header */ 635 tr_offset = jnl->jh_offset + jh->start + jh->blhdr_size; 636 637 for (i=1; i<blhdr->num_blocks; i++) { 638 bufsize = blhdr->binfo[i].bsize; 639 disk_offset = blhdr->binfo[i].bnum << HFSPLUS_SECTOR_SHIFT; 640 641 dprint(DBG_JREPLAY, "[i:%x] bnum: %llx, bsize: %x, bufsize: %x, blocksize: %lx\n", i, blhdr->binfo[i].bnum, blhdr->binfo[i].bsize, bufsize, sb->s_blocksize); 642 643 while (bufsize > 0) { 644 /* Read one block */ 645 tr_sector_number = tr_offset >> HFSPLUS_SECTOR_SHIFT; 646 dprint(DBG_JREPLAY, "tr_sector_number: %x, tr_offset: %llx\n", tr_sector_number, tr_offset); 647 tr_bh = sb_bread512(sb, HFSPLUS_SB(sb).blockoffset + tr_sector_number, tr_buf); 648 if (!tr_bh) { 649 printk("HFS+-fs Line=%d: Error in read\n", __LINE__); 650 if (blhdr_bh) 651 brelse(blhdr_bh); 652 up(&jnl->jnl_lock); 653 return ret; 654 } 655 656 disk_sector_number = disk_offset >> HFSPLUS_SECTOR_SHIFT; 657 dprint(DBG_JREPLAY, "disk_sector_number: %x, disk_offset: %llx, bufsize: %x\n", disk_sector_number, disk_offset, bufsize); 658 /* Read the same sector from the Volume */ 659 disk_bh = sb_bread512(sb, HFSPLUS_SB(sb).blockoffset + disk_sector_number, disk_buf); 660 if (!disk_bh) { 661 printk("HFS+-fs Line=%d: Error in read\n", __LINE__); 662 if (blhdr_bh) 663 brelse(blhdr_bh); 664 if (tr_bh) 665 brelse(tr_bh); 666 up(&jnl->jnl_lock); 667 return ret; 668 } 669 670 /* Write transaction block to the disk block in sector wise */ 671 memcpy(disk_buf, tr_buf, HFSPLUS_SECTOR_SIZE); 672 mark_buffer_dirty(disk_bh); 673 sync_dirty_buffer(disk_bh); 674 675 /* Free buffer heads */ 676 brelse(disk_bh); 677 brelse(tr_bh); 678 679 tr_offset += HFSPLUS_SECTOR_SIZE; 680 disk_offset += HFSPLUS_SECTOR_SIZE; 681 bufsize -= HFSPLUS_SECTOR_SIZE; 682 683 /* Check tr_offset reaches at the end of journal buffer */ 684 if (tr_offset == (jnl->jh_offset + jh->size)) { 685 printk("tr_offset: %llx, jh->size: %llx, jh_offset: %llx\n", tr_offset, jh->size, jnl->jh_offset); 686 tr_offset = jnl->jh_offset + jh->jhdr_size; /* Set to the beginning of journal buffer */ 687 } 688 } 689 } 690 691 /* Check position of start index, wrap around if necessary */ 692 if ((jh->start + blhdr->bytes_used) < jh->size) 693 jh->start += blhdr->bytes_used; 694 else 695 jh->start = jh->jhdr_size + (jh->start + blhdr->bytes_used) - jh->size; 696 } 697 698 if (blhdr_bh) 699 brelse(blhdr_bh); 700 701 if (jh->start == jh->end) { 702 ret = hfsplus_replay_write_journal_header(sb); 703 } else { 704 printk("HFS+-fs: %s Error in journal replay\n", __func__); 705 } 706 707 /* Populate Volume Header with new values */ 708 if (ret == HFSPLUS_JOURNAL_SUCCESS && HFSPLUS_SB(sb).s_vhdr) { 709 struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr; 710 struct buffer_head *bh; 711 712 dprint(DBG_JREPLAY, "Populate Volume Header again\n"); 713 HFSPLUS_SB(sb).s_vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_UNMNT); 714 mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh); 715 sync_dirty_buffer(HFSPLUS_SB(sb).s_vhbh); 716 717 if (HFSPLUS_SB(sb).s_vhbh) 718 brelse(HFSPLUS_SB(sb).s_vhbh); 719 720 bh = sb_bread512(sb, HFSPLUS_SB(sb).blockoffset + HFSPLUS_VOLHEAD_SECTOR, vhdr); 721 if (!bh) { 722 printk("HFS+-fs Line=%d: Error in read\n", __LINE__); 723 HFSPLUS_SB(sb).s_vhdr = NULL; 724 up(&jnl->jnl_lock); 725 return HFSPLUS_JOURNAL_FAIL; 726 } 727 728 /* should still be the same... */ 729 if (be16_to_cpu(vhdr->signature) != HFSPLUS_VOLHEAD_SIG) { 730 printk("Volume header signature (%x) is wrong\n", be16_to_cpu(vhdr->signature)); 731 brelse(bh); 732 HFSPLUS_SB(sb).s_vhdr = NULL; 733 up(&jnl->jnl_lock); 734 return HFSPLUS_JOURNAL_FAIL; 735 } 736 737 HFSPLUS_SB(sb).s_vhbh = bh; 738 HFSPLUS_SB(sb).s_vhdr = vhdr; 739 } 740 741 up(&jnl->jnl_lock); 742 return ret; 743} 744 745/* Check consistency of journal log file in hfsplus volume 746*/ 747int hfsplus_journalled_check(struct super_block *sb) 748{ 749 struct hfsplus_journal_info_block *jib; 750 struct hfsplus_journal_header *jh; 751 u32 checksum, org_checksum; 752 753 print_volume_header(sb); 754 755 if (HFSPLUS_SB(sb).jnl.journaled != HFSPLUS_JOURNAL_PRESENT) { 756 printk("HFS+-fs: Journal is not present\n"); 757 return HFSPLUS_JOURNAL_CONSISTENT; 758 } 759 760 jib = (struct hfsplus_journal_info_block *)(HFSPLUS_SB(sb).jnl.jibhdr); 761 dprint(DBG_JOURNAL, "HFS+-fs: be32_to_cpu(jib->flags): %x\n", be32_to_cpu(jib->flags)); 762 763 /* Journal is on another volume, and the "on this volume" flag 764 * isn't set 765 */ 766 if(be32_to_cpu(jib->flags) & HFSPLUS_JOURNAL_ON_OTHER_DEVICE && 767 !(be32_to_cpu(jib->flags) & HFSPLUS_JOURNAL_IN_FS)) { 768 printk("HFS+-fs: Unable to access the journal.\n"); 769 return HFSPLUS_JOURNAL_INCONSISTENT; 770 } 771 772 /* Journal should be created in initialization. 773 * Mark inconsistent if the journal is still not created yet 774 */ 775 if (be32_to_cpu(jib->flags) & HFSPLUS_JOURNAL_NEED_INIT) { 776 printk("HFS+-fs: Error, journal is not created\n"); 777 return HFSPLUS_JOURNAL_INCONSISTENT; 778 } 779 780 dprint(DBG_JOURNAL, "HFS+-fs: Found Info Block and verified successfully.\n"); 781 jh = (struct hfsplus_journal_header *)(HFSPLUS_SB(sb).jnl.jhdr); 782 783 org_checksum = jh->checksum; 784 jh->checksum = 0; 785 786 if (jh->magic == swab32(HFSPLUS_JOURNAL_HEADER_MAGIC)) { 787 org_checksum = swab32(org_checksum); 788 checksum = calc_checksum((unsigned char *)jh, sizeof(struct hfsplus_journal_header)); 789 swap_journal_header(jh); 790 HFSPLUS_SB(sb).jnl.flags = HFSPLUS_JOURNAL_SWAP; 791 } 792 else 793 checksum = calc_checksum((unsigned char *)jh, sizeof(struct hfsplus_journal_header)); 794 795 print_journal_header(jh); 796 797 /* Verify the journal header */ 798 if(jh->magic != HFSPLUS_JOURNAL_HEADER_MAGIC || jh->endian != HFSPLUS_JOURNAL_HEADER_ENDIAN){ 799 printk("HFS+-fs: Journal header verification failed.\n"); 800 return HFSPLUS_JOURNAL_INCONSISTENT; 801 } 802 803 if (checksum != org_checksum) { 804 jh->checksum = checksum; 805 printk("HFS+-fs: Error in journal header checksum checksum: %x, org_checksum: %x\n", checksum, org_checksum); 806 return HFSPLUS_JOURNAL_INCONSISTENT; 807 } 808 jh->checksum = checksum; 809 810 dprint(DBG_JOURNAL, "HFS+-fs: No problem in magic number, endian and checksum\n"); 811 812 /* Compare start to end */ 813 if(jh->start == jh->end) { 814 hfsplus_replay_write_journal_header(sb); 815 /* If they're the same, we can mount, it's clean */ 816 printk("HFS+-fs: Journal is empty means consistent\n"); 817 return HFSPLUS_JOURNAL_CONSISTENT; 818 } else { 819 /* Replay journal and bring the file system in consistent state */ 820 if (hfsplus_journal_replay(sb) == HFSPLUS_JOURNAL_FAIL) { 821 /* Unable to replay */ 822 printk("HFS+-fs: Journal is non empty means inconsistent, please run fsck.hfsplus\n"); 823 return HFSPLUS_JOURNAL_INCONSISTENT; 824 } else 825 dprint(DBG_JOURNAL, "HFS+-fs: Journal replay done\n"); 826 } 827 828 return HFSPLUS_JOURNAL_CONSISTENT; 829} 830 831/* Check journal present or not and initialize hfsplus_journal accordingly 832 * Assume that super block and volume header are already initialized 833*/ 834void hfsplus_journalled_init(struct super_block *sb, struct hfsplus_vh *vhdr) 835{ 836 struct hfsplus_journal *jnl = &(HFSPLUS_SB(sb).jnl); 837 u32 jib_flags; 838 839 jnl->journaled = !HFSPLUS_JOURNAL_PRESENT; /* Initialize as non-journaled */ 840 jnl->sbp = NULL; 841 jnl->jh_bh = NULL; 842 jnl->alloc_block = be32_to_cpu(vhdr->alloc_file.extents[0].start_block); 843 jnl->ext_block = be32_to_cpu(vhdr->ext_file.extents[0].start_block); 844 jnl->catalog_block = be32_to_cpu(vhdr->cat_file.extents[0].start_block); 845 dprint(DBG_JOURNAL, "alloc_block: %x, ext_block: %x, catalog_block: %x\n", jnl->alloc_block, jnl->ext_block, jnl->catalog_block); 846 847 if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) { 848 dprint(DBG_JOURNAL,"HFS+-fs: Journaled filesystem\n"); 849 jnl->jib_offset = be32_to_cpu(vhdr->journal_info_block); 850 /* Check the journal info block to find the block # of the journal */ 851 jnl->jib_bh = sb_bread(sb, HFSPLUS_SB(sb).blockoffset + jnl->jib_offset); 852 if (!jnl->jib_bh) { 853 printk("HFS+-fs Line=%d: Error in buffer read\n", __LINE__); 854 return; 855 } 856 jnl->jibhdr = (struct hfsplus_journal_info_block *)(jnl->jib_bh->b_data); 857 jib_flags = be32_to_cpu(jnl->jibhdr->flags); 858 dprint(DBG_JOURNAL, "HFS+-fs: jib_flags: %x\n", jib_flags); 859 if ((jib_flags & HFSPLUS_JOURNAL_ON_OTHER_DEVICE) && !(jib_flags & HFSPLUS_JOURNAL_IN_FS)) 860 goto init_fail; 861 862 dprint(DBG_JOURNAL, "HFS+-fs: jib size: %x\n", be32_to_cpu(jnl->jibhdr->size)); 863 if (jib_flags & HFSPLUS_JOURNAL_NEED_INIT) { 864 dprint(DBG_JOURNAL, "HFS+-fs: Journal is not created\n"); 865 if (hfsplus_journalled_create(sb) == 0) { 866 HFSPLUS_SB(sb).jnl.jibhdr->flags &= be32_to_cpu(~HFSPLUS_JOURNAL_NEED_INIT); 867 /* write it to disk */ 868 mark_buffer_dirty(HFSPLUS_SB(sb).jnl.jib_bh); 869 sync_dirty_buffer(HFSPLUS_SB(sb).jnl.jib_bh); 870 } else { 871 printk("HFS+-fs: Fail to create journal\n"); 872 goto init_fail; 873 } 874 } 875 876 /* Check already initialize in journal create */ 877 if (jnl->jh_bh == NULL) { 878 if (map_journal_header(sb) == HFSPLUS_JOURNAL_FAIL) { 879 printk("HFS+-fs Line=%d: Error in buffer read\n", __LINE__); 880 goto init_fail; 881 } 882 } 883 884 init_MUTEX(&jnl->jnl_lock); 885 jnl->sbp = sb; 886 jnl->flags = !HFSPLUS_JOURNAL_SWAP; 887 jnl->journaled = HFSPLUS_JOURNAL_PRESENT; 888 } 889 890 return; 891 892init_fail: 893 printk("HFS+-fs: Journal initialization fails\n"); 894 if (jnl->jib_bh) 895 brelse(jnl->jib_bh); 896} 897 898/* Deinitialize journal if it is present */ 899void hfsplus_journalled_deinit(struct super_block *sb) 900{ 901 if (HFSPLUS_SB(sb).jnl.journaled != HFSPLUS_JOURNAL_PRESENT) { 902 return; 903 } 904 905 if (HFSPLUS_SB(sb).jnl.s_journal != NULL) { 906 hfsplus_jbd_destroy(HFSPLUS_SB(sb).jnl.s_journal); 907 } 908 909 if (HFSPLUS_SB(sb).jnl.jib_bh) 910 brelse(HFSPLUS_SB(sb).jnl.jib_bh); 911 912 if (HFSPLUS_SB(sb).jnl.jh_bh) 913 brelse(HFSPLUS_SB(sb).jnl.jh_bh); 914 915 HFSPLUS_SB(sb).jnl.journaled = !HFSPLUS_JOURNAL_PRESENT; 916} 917