6 #include "ext4_config.h"
7 #include "ext4_types.h"
9 #include "ext4_super.h"
10 #include "ext4_errno.h"
11 #include "ext4_blockdev.h"
12 #include "ext4_crc32c.h"
13 #include "ext4_debug.h"
22 RB_ENTRY(revoke_entry) revoke_node;
26 uint32_t start_trans_id;
27 uint32_t last_trans_id;
28 uint32_t this_trans_id;
29 RB_HEAD(jbd_revoke, revoke_entry) revoke_root;
33 struct recover_info *info;
35 uint32_t this_trans_id;
39 jbd_revoke_entry_cmp(struct revoke_entry *a, struct revoke_entry *b)
41 if (a->block > b->block)
43 else if (a->block < b->block)
48 RB_GENERATE_INTERNAL(jbd_revoke, revoke_entry, revoke_node,
49 jbd_revoke_entry_cmp, static inline)
51 #define jbd_alloc_revoke_entry() calloc(1, sizeof(struct revoke_entry))
52 #define jbd_free_revoke_entry(addr) free(addr)
54 int jbd_inode_bmap(struct jbd_fs *jbd_fs,
56 ext4_fsblk_t *fblock);
58 int jbd_sb_write(struct jbd_fs *jbd_fs, struct jbd_sb *s)
61 struct ext4_fs *fs = jbd_fs->inode_ref.fs;
64 rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
68 offset = fblock * ext4_sb_get_block_size(&fs->sb);
69 return ext4_block_writebytes(fs->bdev, offset, s,
70 EXT4_SUPERBLOCK_SIZE);
73 int jbd_sb_read(struct jbd_fs *jbd_fs, struct jbd_sb *s)
76 struct ext4_fs *fs = jbd_fs->inode_ref.fs;
79 rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
83 offset = fblock * ext4_sb_get_block_size(&fs->sb);
84 return ext4_block_readbytes(fs->bdev, offset, s,
85 EXT4_SUPERBLOCK_SIZE);
88 static bool jbd_verify_sb(struct jbd_sb *sb)
90 struct jbd_bhdr *header = &sb->header;
91 if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER)
94 if (jbd_get32(header, blocktype) != JBD_SUPERBLOCK &&
95 jbd_get32(header, blocktype) != JBD_SUPERBLOCK_V2)
101 int jbd_get_fs(struct ext4_fs *fs,
102 struct jbd_fs *jbd_fs)
105 uint32_t journal_ino;
107 memset(jbd_fs, 0, sizeof(struct jbd_fs));
108 journal_ino = ext4_get32(&fs->sb, journal_inode_number);
110 rc = ext4_fs_get_inode_ref(fs,
114 memset(jbd_fs, 0, sizeof(struct jbd_fs));
117 rc = jbd_sb_read(jbd_fs, &jbd_fs->sb);
119 memset(jbd_fs, 0, sizeof(struct jbd_fs));
120 ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
126 int jbd_put_fs(struct jbd_fs *jbd_fs)
130 jbd_sb_write(jbd_fs, &jbd_fs->sb);
132 rc = ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
136 int jbd_inode_bmap(struct jbd_fs *jbd_fs,
138 ext4_fsblk_t *fblock)
140 int rc = ext4_fs_get_inode_data_block_index(
148 int jbd_block_get(struct jbd_fs *jbd_fs,
149 struct ext4_block *block,
152 /* TODO: journal device. */
154 ext4_lblk_t iblock = (ext4_lblk_t)fblock;
155 rc = jbd_inode_bmap(jbd_fs, iblock,
160 struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
161 rc = ext4_block_get(bdev, block, fblock);
165 int jbd_block_get_noread(struct jbd_fs *jbd_fs,
166 struct ext4_block *block,
169 /* TODO: journal device. */
171 ext4_lblk_t iblock = (ext4_lblk_t)fblock;
172 rc = jbd_inode_bmap(jbd_fs, iblock,
177 struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
178 rc = ext4_block_get_noread(bdev, block, fblock);
182 int jbd_block_set(struct jbd_fs *jbd_fs,
183 struct ext4_block *block)
185 return ext4_block_set(jbd_fs->inode_ref.fs->bdev,
190 * helper functions to deal with 32 or 64bit block numbers.
192 int jbd_tag_bytes(struct jbd_fs *jbd_fs)
196 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
197 JBD_FEATURE_INCOMPAT_CSUM_V3))
198 return sizeof(struct jbd_block_tag3);
200 size = sizeof(struct jbd_block_tag);
202 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
203 JBD_FEATURE_INCOMPAT_CSUM_V2))
204 size += sizeof(uint16_t);
206 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
207 JBD_FEATURE_INCOMPAT_64BIT))
210 return size - sizeof(uint32_t);
214 jbd_extract_block_tag(struct jbd_fs *jbd_fs,
225 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
226 JBD_FEATURE_INCOMPAT_CSUM_V3)) {
227 struct jbd_block_tag3 *tag = __tag;
228 *block = jbd_get32(tag, blocknr);
229 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
230 JBD_FEATURE_INCOMPAT_64BIT))
231 *block |= (uint64_t)jbd_get32(tag, blocknr_high) << 32;
233 if (jbd_get32(tag, flags) & JBD_FLAG_ESCAPE)
236 if (!(jbd_get32(tag, flags) & JBD_FLAG_SAME_UUID)) {
237 uuid_start = (char *)tag + tag_bytes;
239 memcpy(uuid, uuid_start, UUID_SIZE);
242 if (jbd_get32(tag, flags) & JBD_FLAG_LAST_TAG)
246 struct jbd_block_tag *tag = __tag;
247 *block = jbd_get32(tag, blocknr);
248 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
249 JBD_FEATURE_INCOMPAT_64BIT))
250 *block |= (uint64_t)jbd_get32(tag, blocknr_high) << 32;
252 if (jbd_get16(tag, flags) & JBD_FLAG_ESCAPE)
255 if (!(jbd_get16(tag, flags) & JBD_FLAG_SAME_UUID)) {
256 uuid_start = (char *)tag + tag_bytes;
258 memcpy(uuid, uuid_start, UUID_SIZE);
261 if (jbd_get16(tag, flags) & JBD_FLAG_LAST_TAG)
268 jbd_iterate_block_table(struct jbd_fs *jbd_fs,
270 uint32_t tag_tbl_size,
271 void (*func)(struct jbd_fs * jbd_fs,
277 ext4_fsblk_t block = 0;
278 uint8_t uuid[UUID_SIZE];
279 char *tag_start, *tag_ptr;
280 uint32_t tag_bytes = jbd_tag_bytes(jbd_fs);
281 tag_start = __tag_start;
284 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
285 JBD_FEATURE_INCOMPAT_CSUM_V2) ||
286 JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
287 JBD_FEATURE_INCOMPAT_CSUM_V3))
288 tag_tbl_size -= sizeof(struct jbd_block_tail);
290 while (tag_ptr - tag_start + tag_bytes <= tag_tbl_size) {
293 jbd_extract_block_tag(jbd_fs,
301 func(jbd_fs, block, uuid, arg);
306 tag_ptr += tag_bytes;
308 tag_ptr += UUID_SIZE;
313 static void jbd_display_block_tags(struct jbd_fs *jbd_fs,
318 uint32_t *iblock = arg;
319 ext4_dbg(DEBUG_JBD, "Block in block_tag: %" PRIu64 "\n", block);
326 static struct revoke_entry *
327 jbd_revoke_entry_lookup(struct recover_info *info, ext4_fsblk_t block)
329 struct revoke_entry tmp = {
333 return RB_FIND(jbd_revoke, &info->revoke_root, &tmp);
336 static void jbd_replay_block_tags(struct jbd_fs *jbd_fs,
338 uint8_t *uuid __unused,
342 struct replay_arg *arg = __arg;
343 struct recover_info *info = arg->info;
344 uint32_t *this_block = arg->this_block;
345 struct revoke_entry *revoke_entry;
346 struct ext4_block journal_block, ext4_block;
347 struct ext4_fs *fs = jbd_fs->inode_ref.fs;
350 "Replaying block in block_tag: %" PRIu64 "\n",
354 revoke_entry = jbd_revoke_entry_lookup(info, block);
356 arg->this_trans_id < revoke_entry->trans_id)
359 r = jbd_block_get(jbd_fs, &journal_block, *this_block);
364 r = ext4_block_get_noread(fs->bdev, &ext4_block, block);
366 jbd_block_set(jbd_fs, &journal_block);
370 memcpy(ext4_block.data,
372 jbd_get32(&jbd_fs->sb, blocksize));
374 ext4_block.dirty = true;
375 ext4_block_set(fs->bdev, &ext4_block);
377 uint16_t mount_count, state;
378 mount_count = ext4_get16(&fs->sb, mount_count);
379 state = ext4_get16(&fs->sb, state);
382 journal_block.data + EXT4_SUPERBLOCK_OFFSET,
383 EXT4_SUPERBLOCK_SIZE);
385 /* Mark system as mounted */
386 ext4_set16(&fs->sb, state, state);
387 r = ext4_sb_write(fs->bdev, &fs->sb);
391 /*Update mount count*/
392 ext4_set16(&fs->sb, mount_count, mount_count);
395 jbd_block_set(jbd_fs, &journal_block);
400 static void jbd_add_revoke_block_tags(struct recover_info *info,
403 struct revoke_entry *revoke_entry;
405 ext4_dbg(DEBUG_JBD, "Add block %" PRIu64 " to revoke tree\n", block);
406 revoke_entry = jbd_revoke_entry_lookup(info, block);
408 revoke_entry->trans_id = info->this_trans_id;
412 revoke_entry = jbd_alloc_revoke_entry();
413 ext4_assert(revoke_entry);
414 revoke_entry->block = block;
415 revoke_entry->trans_id = info->this_trans_id;
416 RB_INSERT(jbd_revoke, &info->revoke_root, revoke_entry);
421 static void jbd_destroy_revoke_tree(struct recover_info *info)
423 while (!RB_EMPTY(&info->revoke_root)) {
424 struct revoke_entry *revoke_entry =
425 RB_MIN(jbd_revoke, &info->revoke_root);
426 ext4_assert(revoke_entry);
427 RB_REMOVE(jbd_revoke, &info->revoke_root, revoke_entry);
428 jbd_free_revoke_entry(revoke_entry);
432 /* Make sure we wrap around the log correctly! */
433 #define wrap(sb, var) \
435 if (var >= jbd_get32((sb), maxlen)) \
436 var -= (jbd_get32((sb), maxlen) - jbd_get32((sb), first)); \
439 #define ACTION_SCAN 0
440 #define ACTION_REVOKE 1
441 #define ACTION_RECOVER 2
444 static void jbd_build_revoke_tree(struct jbd_fs *jbd_fs,
445 struct jbd_bhdr *header,
446 struct recover_info *info)
449 struct jbd_revoke_header *revoke_hdr =
450 (struct jbd_revoke_header *)header;
451 uint32_t i, nr_entries, record_len = 4;
452 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
453 JBD_FEATURE_INCOMPAT_64BIT))
456 nr_entries = (revoke_hdr->count -
457 sizeof(struct jbd_revoke_header)) /
460 blocks_entry = (char *)(revoke_hdr + 1);
462 for (i = 0;i < nr_entries;i++) {
463 if (record_len == 8) {
465 (uint64_t *)blocks_entry;
466 jbd_add_revoke_block_tags(info, *blocks);
469 (uint32_t *)blocks_entry;
470 jbd_add_revoke_block_tags(info, *blocks);
472 blocks_entry += record_len;
476 static void jbd_debug_descriptor_block(struct jbd_fs *jbd_fs,
477 struct jbd_bhdr *header,
480 jbd_iterate_block_table(jbd_fs,
482 jbd_get32(&jbd_fs->sb, blocksize) -
483 sizeof(struct jbd_bhdr),
484 jbd_display_block_tags,
488 static void jbd_replay_descriptor_block(struct jbd_fs *jbd_fs,
489 struct jbd_bhdr *header,
490 struct replay_arg *arg)
492 jbd_iterate_block_table(jbd_fs,
494 jbd_get32(&jbd_fs->sb, blocksize) -
495 sizeof(struct jbd_bhdr),
496 jbd_replay_block_tags,
500 int jbd_iterate_log(struct jbd_fs *jbd_fs,
501 struct recover_info *info,
505 bool log_end = false;
506 struct jbd_sb *sb = &jbd_fs->sb;
507 uint32_t start_trans_id, this_trans_id;
508 uint32_t start_block, this_block;
510 start_trans_id = this_trans_id = jbd_get32(sb, sequence);
511 start_block = this_block = jbd_get32(sb, start);
513 ext4_dbg(DEBUG_JBD, "Start of journal at trans id: %" PRIu32 "\n",
517 struct ext4_block block;
518 struct jbd_bhdr *header;
519 if (action != ACTION_SCAN)
520 if (this_trans_id > info->last_trans_id) {
525 r = jbd_block_get(jbd_fs, &block, this_block);
529 header = (struct jbd_bhdr *)block.data;
530 if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER) {
531 jbd_block_set(jbd_fs, &block);
536 if (jbd_get32(header, sequence) != this_trans_id) {
537 if (action != ACTION_SCAN)
540 jbd_block_set(jbd_fs, &block);
545 switch (jbd_get32(header, blocktype)) {
546 case JBD_DESCRIPTOR_BLOCK:
547 ext4_dbg(DEBUG_JBD, "Descriptor block: %" PRIu32", "
548 "trans_id: %" PRIu32"\n",
549 this_block, this_trans_id);
550 if (action == ACTION_SCAN)
551 jbd_debug_descriptor_block(jbd_fs,
552 header, &this_block);
553 else if (action == ACTION_RECOVER) {
554 struct replay_arg replay_arg;
555 replay_arg.info = info;
556 replay_arg.this_block = &this_block;
557 replay_arg.this_trans_id = this_trans_id;
559 jbd_replay_descriptor_block(jbd_fs,
560 header, &replay_arg);
564 case JBD_COMMIT_BLOCK:
565 ext4_dbg(DEBUG_JBD, "Commit block: %" PRIu32", "
566 "trans_id: %" PRIu32"\n",
567 this_block, this_trans_id);
570 case JBD_REVOKE_BLOCK:
571 ext4_dbg(DEBUG_JBD, "Revoke block: %" PRIu32", "
572 "trans_id: %" PRIu32"\n",
573 this_block, this_trans_id);
574 if (action == ACTION_REVOKE) {
575 info->this_trans_id = this_trans_id;
576 jbd_build_revoke_tree(jbd_fs,
584 jbd_block_set(jbd_fs, &block);
586 wrap(sb, this_block);
587 if (this_block == start_block)
591 ext4_dbg(DEBUG_JBD, "End of journal.\n");
592 if (r == EOK && action == ACTION_SCAN) {
593 info->start_trans_id = start_trans_id;
594 if (this_trans_id > start_trans_id)
595 info->last_trans_id = this_trans_id - 1;
597 info->last_trans_id = this_trans_id;
603 int jbd_recover(struct jbd_fs *jbd_fs)
606 struct recover_info info;
607 struct jbd_sb *sb = &jbd_fs->sb;
611 RB_INIT(&info.revoke_root);
613 r = jbd_iterate_log(jbd_fs, &info, ACTION_SCAN);
617 r = jbd_iterate_log(jbd_fs, &info, ACTION_REVOKE);
621 r = jbd_iterate_log(jbd_fs, &info, ACTION_RECOVER);
623 jbd_set32(&jbd_fs->sb, start, 0);
624 jbd_fs->dirty = true;
626 jbd_destroy_revoke_tree(&info);