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 jbd_revoke_entry_cmp(struct revoke_entry *a, struct revoke_entry *b)
35 if (a->block > b->block)
37 else if (a->block < b->block)
42 RB_GENERATE_INTERNAL(jbd_revoke, revoke_entry, revoke_node,
43 jbd_revoke_entry_cmp, static inline)
45 #define jbd_alloc_revoke_entry() calloc(1, sizeof(struct revoke_entry))
46 #define jbd_free_revoke_entry(addr) free(addr)
48 int jbd_inode_bmap(struct jbd_fs *jbd_fs,
50 ext4_fsblk_t *fblock);
52 int jbd_sb_write(struct jbd_fs *jbd_fs, struct jbd_sb *s)
55 struct ext4_fs *fs = jbd_fs->inode_ref.fs;
58 rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
62 offset = fblock * ext4_sb_get_block_size(&fs->sb);
63 return ext4_block_writebytes(fs->bdev, offset, s,
64 EXT4_SUPERBLOCK_SIZE);
67 int jbd_sb_read(struct jbd_fs *jbd_fs, struct jbd_sb *s)
70 struct ext4_fs *fs = jbd_fs->inode_ref.fs;
73 rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
77 offset = fblock * ext4_sb_get_block_size(&fs->sb);
78 return ext4_block_readbytes(fs->bdev, offset, s,
79 EXT4_SUPERBLOCK_SIZE);
82 static bool jbd_verify_sb(struct jbd_sb *sb)
84 struct jbd_bhdr *header = &sb->header;
85 if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER)
88 if (jbd_get32(header, blocktype) != JBD_SUPERBLOCK &&
89 jbd_get32(header, blocktype) != JBD_SUPERBLOCK_V2)
95 int jbd_get_fs(struct ext4_fs *fs,
96 struct jbd_fs *jbd_fs)
101 memset(jbd_fs, 0, sizeof(struct jbd_fs));
102 journal_ino = ext4_get32(&fs->sb, journal_inode_number);
104 rc = ext4_fs_get_inode_ref(fs,
108 memset(jbd_fs, 0, sizeof(struct jbd_fs));
111 rc = jbd_sb_read(jbd_fs, &jbd_fs->sb);
113 memset(jbd_fs, 0, sizeof(struct jbd_fs));
114 ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
120 int jbd_put_fs(struct jbd_fs *jbd_fs)
123 rc = ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
127 int jbd_inode_bmap(struct jbd_fs *jbd_fs,
129 ext4_fsblk_t *fblock)
131 int rc = ext4_fs_get_inode_data_block_index(
139 int jbd_block_get(struct jbd_fs *jbd_fs,
140 struct ext4_block *block,
143 /* TODO: journal device. */
145 ext4_lblk_t iblock = (ext4_lblk_t)fblock;
146 rc = jbd_inode_bmap(jbd_fs, iblock,
151 struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
152 rc = ext4_block_get(bdev, block, fblock);
156 int jbd_block_get_noread(struct jbd_fs *jbd_fs,
157 struct ext4_block *block,
160 /* TODO: journal device. */
162 ext4_lblk_t iblock = (ext4_lblk_t)fblock;
163 rc = jbd_inode_bmap(jbd_fs, iblock,
168 struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
169 rc = ext4_block_get_noread(bdev, block, fblock);
173 int jbd_block_set(struct jbd_fs *jbd_fs,
174 struct ext4_block *block)
176 return ext4_block_set(jbd_fs->inode_ref.fs->bdev,
181 * helper functions to deal with 32 or 64bit block numbers.
183 int jbd_tag_bytes(struct jbd_fs *jbd_fs)
187 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
188 JBD_FEATURE_INCOMPAT_CSUM_V3))
189 return sizeof(struct jbd_block_tag3);
191 size = sizeof(struct jbd_block_tag);
193 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
194 JBD_FEATURE_INCOMPAT_CSUM_V2))
195 size += sizeof(uint16_t);
197 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
198 JBD_FEATURE_INCOMPAT_64BIT))
201 return size - sizeof(uint32_t);
205 jbd_extract_block_tag(struct jbd_fs *jbd_fs,
216 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
217 JBD_FEATURE_INCOMPAT_CSUM_V3)) {
218 struct jbd_block_tag3 *tag = __tag;
219 *block = jbd_get32(tag, blocknr);
220 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
221 JBD_FEATURE_INCOMPAT_64BIT))
222 *block |= (uint64_t)jbd_get32(tag, blocknr_high) << 32;
224 if (jbd_get32(tag, flags) & JBD_FLAG_ESCAPE)
227 if (!(jbd_get32(tag, flags) & JBD_FLAG_SAME_UUID)) {
228 uuid_start = (char *)tag + tag_bytes;
230 memcpy(uuid, uuid_start, UUID_SIZE);
233 if (jbd_get32(tag, flags) & JBD_FLAG_LAST_TAG)
237 struct jbd_block_tag *tag = __tag;
238 *block = jbd_get32(tag, blocknr);
239 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
240 JBD_FEATURE_INCOMPAT_64BIT))
241 *block |= (uint64_t)jbd_get32(tag, blocknr_high) << 32;
243 if (jbd_get16(tag, flags) & JBD_FLAG_ESCAPE)
246 if (!(jbd_get16(tag, flags) & JBD_FLAG_SAME_UUID)) {
247 uuid_start = (char *)tag + tag_bytes;
249 memcpy(uuid, uuid_start, UUID_SIZE);
252 if (jbd_get16(tag, flags) & JBD_FLAG_LAST_TAG)
259 jbd_iterate_block_table(struct jbd_fs *jbd_fs,
261 uint32_t tag_tbl_size,
262 void (*func)(struct jbd_fs * jbd_fs,
268 ext4_fsblk_t block = 0;
269 uint8_t uuid[UUID_SIZE];
270 char *tag_start, *tag_ptr;
271 uint32_t tag_bytes = jbd_tag_bytes(jbd_fs);
272 tag_start = __tag_start;
275 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
276 JBD_FEATURE_INCOMPAT_CSUM_V2) ||
277 JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
278 JBD_FEATURE_INCOMPAT_CSUM_V3))
279 tag_tbl_size -= sizeof(struct jbd_block_tail);
281 while (tag_ptr - tag_start + tag_bytes <= tag_tbl_size) {
284 jbd_extract_block_tag(jbd_fs,
292 func(jbd_fs, block, uuid, arg);
297 tag_ptr += tag_bytes;
299 tag_ptr += UUID_SIZE;
304 static void jbd_display_block_tags(struct jbd_fs *jbd_fs,
309 uint32_t *iblock = arg;
310 ext4_dbg(DEBUG_JBD, "Block in block_tag: %" PRIu64 "\n", block);
317 static struct revoke_entry *
318 jbd_revoke_entry_lookup(struct recover_info *info, ext4_fsblk_t block)
320 struct revoke_entry tmp = {
324 return RB_FIND(jbd_revoke, &info->revoke_root, &tmp);
327 static void jbd_add_revoke_block_tags(struct recover_info *info,
330 struct revoke_entry *revoke_entry;
332 ext4_dbg(DEBUG_JBD, "Add block %" PRIu64 " to revoke tree\n", block);
333 revoke_entry = jbd_revoke_entry_lookup(info, block);
335 revoke_entry->trans_id = info->this_trans_id;
339 revoke_entry = jbd_alloc_revoke_entry();
340 ext4_assert(revoke_entry);
341 revoke_entry->block = block;
342 revoke_entry->trans_id = info->this_trans_id;
343 RB_INSERT(jbd_revoke, &info->revoke_root, revoke_entry);
348 static void jbd_destroy_revoke_tree(struct recover_info *info)
350 while (!RB_EMPTY(&info->revoke_root)) {
351 struct revoke_entry *revoke_entry =
352 RB_MIN(jbd_revoke, &info->revoke_root);
353 ext4_assert(revoke_entry);
354 RB_REMOVE(jbd_revoke, &info->revoke_root, revoke_entry);
355 jbd_free_revoke_entry(revoke_entry);
359 /* Make sure we wrap around the log correctly! */
360 #define wrap(sb, var) \
362 if (var >= jbd_get32((sb), maxlen)) \
363 var -= (jbd_get32((sb), maxlen) - jbd_get32((sb), first)); \
366 #define ACTION_SCAN 0
367 #define ACTION_REVOKE 1
368 #define ACTION_RECOVER 2
371 static void jbd_build_revoke_tree(struct jbd_fs *jbd_fs,
372 struct jbd_bhdr *header,
373 struct recover_info *info)
376 struct jbd_revoke_header *revoke_hdr =
377 (struct jbd_revoke_header *)header;
378 uint32_t i, nr_entries, record_len = 4;
379 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
380 JBD_FEATURE_INCOMPAT_64BIT))
383 nr_entries = (revoke_hdr->count -
384 sizeof(struct jbd_revoke_header)) /
387 blocks_entry = (char *)(revoke_hdr + 1);
389 for (i = 0;i < nr_entries;i++) {
390 if (record_len == 8) {
392 (uint64_t *)blocks_entry;
393 jbd_add_revoke_block_tags(info, *blocks);
396 (uint32_t *)blocks_entry;
397 jbd_add_revoke_block_tags(info, *blocks);
399 blocks_entry += record_len;
403 static void jbd_debug_descriptor_block(struct jbd_fs *jbd_fs,
404 struct jbd_bhdr *header,
407 jbd_iterate_block_table(jbd_fs,
409 jbd_get32(&jbd_fs->sb, blocksize) -
410 sizeof(struct jbd_bhdr),
411 jbd_display_block_tags,
415 int jbd_iterate_log(struct jbd_fs *jbd_fs,
416 struct recover_info *info,
420 bool log_end = false;
421 struct jbd_sb *sb = &jbd_fs->sb;
422 uint32_t start_trans_id, this_trans_id;
423 uint32_t start_block, this_block;
425 start_trans_id = this_trans_id = jbd_get32(sb, sequence);
426 start_block = this_block = jbd_get32(sb, start);
428 ext4_dbg(DEBUG_JBD, "Start of journal at trans id: %" PRIu32 "\n",
432 struct ext4_block block;
433 struct jbd_bhdr *header;
434 if (action != ACTION_SCAN)
435 if (this_trans_id > info->last_trans_id) {
440 r = jbd_block_get(jbd_fs, &block, this_block);
444 header = (struct jbd_bhdr *)block.data;
445 if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER) {
446 jbd_block_set(jbd_fs, &block);
451 if (jbd_get32(header, sequence) != this_trans_id) {
452 if (action != ACTION_SCAN)
455 jbd_block_set(jbd_fs, &block);
460 switch (jbd_get32(header, blocktype)) {
461 case JBD_DESCRIPTOR_BLOCK:
462 ext4_dbg(DEBUG_JBD, "Descriptor block: %u, "
464 this_block, this_trans_id);
465 if (action == ACTION_SCAN)
466 jbd_debug_descriptor_block(jbd_fs,
467 header, &this_block);
469 case JBD_COMMIT_BLOCK:
470 ext4_dbg(DEBUG_JBD, "Commit block: %u, "
472 this_block, this_trans_id);
475 case JBD_REVOKE_BLOCK:
476 ext4_dbg(DEBUG_JBD, "Revoke block: %u, "
478 this_block, this_trans_id);
479 if (action == ACTION_REVOKE) {
480 info->this_trans_id = this_trans_id;
481 jbd_build_revoke_tree(jbd_fs,
489 jbd_block_set(jbd_fs, &block);
491 wrap(sb, this_block);
492 if (this_block == start_block)
496 ext4_dbg(DEBUG_JBD, "End of journal.\n");
497 if (r == EOK && action == ACTION_SCAN) {
498 info->start_trans_id = start_trans_id;
499 if (this_trans_id > start_trans_id)
500 info->last_trans_id = this_trans_id - 1;
502 info->last_trans_id = this_trans_id;
508 int jbd_recover(struct jbd_fs *jbd_fs)
511 struct recover_info info;
512 struct jbd_sb *sb = &jbd_fs->sb;
516 RB_INIT(&info.revoke_root);
518 r = jbd_iterate_log(jbd_fs, &info, ACTION_SCAN);
522 r = jbd_iterate_log(jbd_fs, &info, ACTION_REVOKE);
523 jbd_destroy_revoke_tree(&info);