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;
38 jbd_revoke_entry_cmp(struct revoke_entry *a, struct revoke_entry *b)
40 if (a->block > b->block)
42 else if (a->block < b->block)
47 RB_GENERATE_INTERNAL(jbd_revoke, revoke_entry, revoke_node,
48 jbd_revoke_entry_cmp, static inline)
50 #define jbd_alloc_revoke_entry() calloc(1, sizeof(struct revoke_entry))
51 #define jbd_free_revoke_entry(addr) free(addr)
53 int jbd_inode_bmap(struct jbd_fs *jbd_fs,
55 ext4_fsblk_t *fblock);
57 int jbd_sb_write(struct jbd_fs *jbd_fs, struct jbd_sb *s)
60 struct ext4_fs *fs = jbd_fs->inode_ref.fs;
63 rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
67 offset = fblock * ext4_sb_get_block_size(&fs->sb);
68 return ext4_block_writebytes(fs->bdev, offset, s,
69 EXT4_SUPERBLOCK_SIZE);
72 int jbd_sb_read(struct jbd_fs *jbd_fs, struct jbd_sb *s)
75 struct ext4_fs *fs = jbd_fs->inode_ref.fs;
78 rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
82 offset = fblock * ext4_sb_get_block_size(&fs->sb);
83 return ext4_block_readbytes(fs->bdev, offset, s,
84 EXT4_SUPERBLOCK_SIZE);
87 static bool jbd_verify_sb(struct jbd_sb *sb)
89 struct jbd_bhdr *header = &sb->header;
90 if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER)
93 if (jbd_get32(header, blocktype) != JBD_SUPERBLOCK &&
94 jbd_get32(header, blocktype) != JBD_SUPERBLOCK_V2)
100 int jbd_get_fs(struct ext4_fs *fs,
101 struct jbd_fs *jbd_fs)
104 uint32_t journal_ino;
106 memset(jbd_fs, 0, sizeof(struct jbd_fs));
107 journal_ino = ext4_get32(&fs->sb, journal_inode_number);
109 rc = ext4_fs_get_inode_ref(fs,
113 memset(jbd_fs, 0, sizeof(struct jbd_fs));
116 rc = jbd_sb_read(jbd_fs, &jbd_fs->sb);
118 memset(jbd_fs, 0, sizeof(struct jbd_fs));
119 ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
125 int jbd_put_fs(struct jbd_fs *jbd_fs)
129 jbd_sb_write(jbd_fs, &jbd_fs->sb);
131 rc = ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
135 int jbd_inode_bmap(struct jbd_fs *jbd_fs,
137 ext4_fsblk_t *fblock)
139 int rc = ext4_fs_get_inode_data_block_index(
147 int jbd_block_get(struct jbd_fs *jbd_fs,
148 struct ext4_block *block,
151 /* TODO: journal device. */
153 ext4_lblk_t iblock = (ext4_lblk_t)fblock;
154 rc = jbd_inode_bmap(jbd_fs, iblock,
159 struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
160 rc = ext4_block_get(bdev, block, fblock);
164 int jbd_block_get_noread(struct jbd_fs *jbd_fs,
165 struct ext4_block *block,
168 /* TODO: journal device. */
170 ext4_lblk_t iblock = (ext4_lblk_t)fblock;
171 rc = jbd_inode_bmap(jbd_fs, iblock,
176 struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
177 rc = ext4_block_get_noread(bdev, block, fblock);
181 int jbd_block_set(struct jbd_fs *jbd_fs,
182 struct ext4_block *block)
184 return ext4_block_set(jbd_fs->inode_ref.fs->bdev,
189 * helper functions to deal with 32 or 64bit block numbers.
191 int jbd_tag_bytes(struct jbd_fs *jbd_fs)
195 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
196 JBD_FEATURE_INCOMPAT_CSUM_V3))
197 return sizeof(struct jbd_block_tag3);
199 size = sizeof(struct jbd_block_tag);
201 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
202 JBD_FEATURE_INCOMPAT_CSUM_V2))
203 size += sizeof(uint16_t);
205 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
206 JBD_FEATURE_INCOMPAT_64BIT))
209 return size - sizeof(uint32_t);
213 jbd_extract_block_tag(struct jbd_fs *jbd_fs,
224 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
225 JBD_FEATURE_INCOMPAT_CSUM_V3)) {
226 struct jbd_block_tag3 *tag = __tag;
227 *block = jbd_get32(tag, blocknr);
228 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
229 JBD_FEATURE_INCOMPAT_64BIT))
230 *block |= (uint64_t)jbd_get32(tag, blocknr_high) << 32;
232 if (jbd_get32(tag, flags) & JBD_FLAG_ESCAPE)
235 if (!(jbd_get32(tag, flags) & JBD_FLAG_SAME_UUID)) {
236 uuid_start = (char *)tag + tag_bytes;
238 memcpy(uuid, uuid_start, UUID_SIZE);
241 if (jbd_get32(tag, flags) & JBD_FLAG_LAST_TAG)
245 struct jbd_block_tag *tag = __tag;
246 *block = jbd_get32(tag, blocknr);
247 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
248 JBD_FEATURE_INCOMPAT_64BIT))
249 *block |= (uint64_t)jbd_get32(tag, blocknr_high) << 32;
251 if (jbd_get16(tag, flags) & JBD_FLAG_ESCAPE)
254 if (!(jbd_get16(tag, flags) & JBD_FLAG_SAME_UUID)) {
255 uuid_start = (char *)tag + tag_bytes;
257 memcpy(uuid, uuid_start, UUID_SIZE);
260 if (jbd_get16(tag, flags) & JBD_FLAG_LAST_TAG)
267 jbd_iterate_block_table(struct jbd_fs *jbd_fs,
269 uint32_t tag_tbl_size,
270 void (*func)(struct jbd_fs * jbd_fs,
276 ext4_fsblk_t block = 0;
277 uint8_t uuid[UUID_SIZE];
278 char *tag_start, *tag_ptr;
279 uint32_t tag_bytes = jbd_tag_bytes(jbd_fs);
280 tag_start = __tag_start;
283 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
284 JBD_FEATURE_INCOMPAT_CSUM_V2) ||
285 JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
286 JBD_FEATURE_INCOMPAT_CSUM_V3))
287 tag_tbl_size -= sizeof(struct jbd_block_tail);
289 while (tag_ptr - tag_start + tag_bytes <= tag_tbl_size) {
292 jbd_extract_block_tag(jbd_fs,
300 func(jbd_fs, block, uuid, arg);
305 tag_ptr += tag_bytes;
307 tag_ptr += UUID_SIZE;
312 static void jbd_display_block_tags(struct jbd_fs *jbd_fs,
317 uint32_t *iblock = arg;
318 ext4_dbg(DEBUG_JBD, "Block in block_tag: %" PRIu64 "\n", block);
325 static struct revoke_entry *
326 jbd_revoke_entry_lookup(struct recover_info *info, ext4_fsblk_t block)
328 struct revoke_entry tmp = {
332 return RB_FIND(jbd_revoke, &info->revoke_root, &tmp);
335 static void jbd_replay_block_tags(struct jbd_fs *jbd_fs,
337 uint8_t *uuid __unused,
341 struct replay_arg *arg = __arg;
342 struct recover_info *info = arg->info;
343 uint32_t *this_block = arg->this_block;
344 struct revoke_entry *revoke_entry;
345 struct ext4_block journal_block, ext4_block;
346 struct ext4_fs *fs = jbd_fs->inode_ref.fs;
348 "Replaying block in block_tag: %" PRIu64 "\n",
352 revoke_entry = jbd_revoke_entry_lookup(info, block);
356 r = jbd_block_get(jbd_fs, &journal_block, *this_block);
361 r = ext4_block_get_noread(fs->bdev, &ext4_block, block);
363 jbd_block_set(jbd_fs, &journal_block);
367 memcpy(ext4_block.data,
369 jbd_get32(&jbd_fs->sb, blocksize));
371 ext4_block.dirty = true;
372 ext4_block_set(fs->bdev, &ext4_block);
374 uint16_t mount_count, state;
375 mount_count = ext4_get16(&fs->sb, mount_count);
376 state = ext4_get16(&fs->sb, state);
379 journal_block.data + EXT4_SUPERBLOCK_OFFSET,
380 EXT4_SUPERBLOCK_SIZE);
382 /* Mark system as mounted */
383 ext4_set16(&fs->sb, state, state);
384 r = ext4_sb_write(fs->bdev, &fs->sb);
388 /*Update mount count*/
389 ext4_set16(&fs->sb, mount_count, mount_count);
392 jbd_block_set(jbd_fs, &journal_block);
397 static void jbd_add_revoke_block_tags(struct recover_info *info,
400 struct revoke_entry *revoke_entry;
402 ext4_dbg(DEBUG_JBD, "Add block %" PRIu64 " to revoke tree\n", block);
403 revoke_entry = jbd_revoke_entry_lookup(info, block);
405 revoke_entry->trans_id = info->this_trans_id;
409 revoke_entry = jbd_alloc_revoke_entry();
410 ext4_assert(revoke_entry);
411 revoke_entry->block = block;
412 revoke_entry->trans_id = info->this_trans_id;
413 RB_INSERT(jbd_revoke, &info->revoke_root, revoke_entry);
418 static void jbd_destroy_revoke_tree(struct recover_info *info)
420 while (!RB_EMPTY(&info->revoke_root)) {
421 struct revoke_entry *revoke_entry =
422 RB_MIN(jbd_revoke, &info->revoke_root);
423 ext4_assert(revoke_entry);
424 RB_REMOVE(jbd_revoke, &info->revoke_root, revoke_entry);
425 jbd_free_revoke_entry(revoke_entry);
429 /* Make sure we wrap around the log correctly! */
430 #define wrap(sb, var) \
432 if (var >= jbd_get32((sb), maxlen)) \
433 var -= (jbd_get32((sb), maxlen) - jbd_get32((sb), first)); \
436 #define ACTION_SCAN 0
437 #define ACTION_REVOKE 1
438 #define ACTION_RECOVER 2
441 static void jbd_build_revoke_tree(struct jbd_fs *jbd_fs,
442 struct jbd_bhdr *header,
443 struct recover_info *info)
446 struct jbd_revoke_header *revoke_hdr =
447 (struct jbd_revoke_header *)header;
448 uint32_t i, nr_entries, record_len = 4;
449 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
450 JBD_FEATURE_INCOMPAT_64BIT))
453 nr_entries = (revoke_hdr->count -
454 sizeof(struct jbd_revoke_header)) /
457 blocks_entry = (char *)(revoke_hdr + 1);
459 for (i = 0;i < nr_entries;i++) {
460 if (record_len == 8) {
462 (uint64_t *)blocks_entry;
463 jbd_add_revoke_block_tags(info, *blocks);
466 (uint32_t *)blocks_entry;
467 jbd_add_revoke_block_tags(info, *blocks);
469 blocks_entry += record_len;
473 static void jbd_debug_descriptor_block(struct jbd_fs *jbd_fs,
474 struct jbd_bhdr *header,
477 jbd_iterate_block_table(jbd_fs,
479 jbd_get32(&jbd_fs->sb, blocksize) -
480 sizeof(struct jbd_bhdr),
481 jbd_display_block_tags,
485 static void jbd_replay_descriptor_block(struct jbd_fs *jbd_fs,
486 struct jbd_bhdr *header,
487 struct replay_arg *arg)
489 jbd_iterate_block_table(jbd_fs,
491 jbd_get32(&jbd_fs->sb, blocksize) -
492 sizeof(struct jbd_bhdr),
493 jbd_replay_block_tags,
497 int jbd_iterate_log(struct jbd_fs *jbd_fs,
498 struct recover_info *info,
502 bool log_end = false;
503 struct jbd_sb *sb = &jbd_fs->sb;
504 uint32_t start_trans_id, this_trans_id;
505 uint32_t start_block, this_block;
507 start_trans_id = this_trans_id = jbd_get32(sb, sequence);
508 start_block = this_block = jbd_get32(sb, start);
510 ext4_dbg(DEBUG_JBD, "Start of journal at trans id: %" PRIu32 "\n",
514 struct ext4_block block;
515 struct jbd_bhdr *header;
516 if (action != ACTION_SCAN)
517 if (this_trans_id > info->last_trans_id) {
522 r = jbd_block_get(jbd_fs, &block, this_block);
526 header = (struct jbd_bhdr *)block.data;
527 if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER) {
528 jbd_block_set(jbd_fs, &block);
533 if (jbd_get32(header, sequence) != this_trans_id) {
534 if (action != ACTION_SCAN)
537 jbd_block_set(jbd_fs, &block);
542 switch (jbd_get32(header, blocktype)) {
543 case JBD_DESCRIPTOR_BLOCK:
544 ext4_dbg(DEBUG_JBD, "Descriptor block: %u, "
546 this_block, this_trans_id);
547 if (action == ACTION_SCAN)
548 jbd_debug_descriptor_block(jbd_fs,
549 header, &this_block);
550 else if (action == ACTION_RECOVER) {
551 struct replay_arg replay_arg;
552 replay_arg.info = info;
553 replay_arg.this_block = &this_block;
554 jbd_replay_descriptor_block(jbd_fs,
555 header, &replay_arg);
559 case JBD_COMMIT_BLOCK:
560 ext4_dbg(DEBUG_JBD, "Commit block: %u, "
562 this_block, this_trans_id);
565 case JBD_REVOKE_BLOCK:
566 ext4_dbg(DEBUG_JBD, "Revoke block: %u, "
568 this_block, this_trans_id);
569 if (action == ACTION_REVOKE) {
570 info->this_trans_id = this_trans_id;
571 jbd_build_revoke_tree(jbd_fs,
579 jbd_block_set(jbd_fs, &block);
581 wrap(sb, this_block);
582 if (this_block == start_block)
586 ext4_dbg(DEBUG_JBD, "End of journal.\n");
587 if (r == EOK && action == ACTION_SCAN) {
588 info->start_trans_id = start_trans_id;
589 if (this_trans_id > start_trans_id)
590 info->last_trans_id = this_trans_id - 1;
592 info->last_trans_id = this_trans_id;
598 int jbd_recover(struct jbd_fs *jbd_fs)
601 struct recover_info info;
602 struct jbd_sb *sb = &jbd_fs->sb;
606 RB_INIT(&info.revoke_root);
608 r = jbd_iterate_log(jbd_fs, &info, ACTION_SCAN);
612 r = jbd_iterate_log(jbd_fs, &info, ACTION_REVOKE);
616 r = jbd_iterate_log(jbd_fs, &info, ACTION_RECOVER);
618 jbd_set32(&jbd_fs->sb, start, 0);
619 jbd_fs->dirty = true;
621 jbd_destroy_revoke_tree(&info);