2 * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
3 * Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com)
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 /** @addtogroup lwext4
34 * @file ext4_journal.c
35 * @brief Journal handle functions
38 #include "ext4_config.h"
39 #include "ext4_types.h"
41 #include "ext4_super.h"
42 #include "ext4_errno.h"
43 #include "ext4_blockdev.h"
44 #include "ext4_crc32c.h"
45 #include "ext4_debug.h"
54 RB_ENTRY(revoke_entry) revoke_node;
58 uint32_t start_trans_id;
59 uint32_t last_trans_id;
60 uint32_t this_trans_id;
61 RB_HEAD(jbd_revoke, revoke_entry) revoke_root;
65 struct recover_info *info;
67 uint32_t this_trans_id;
71 jbd_revoke_entry_cmp(struct revoke_entry *a, struct revoke_entry *b)
73 if (a->block > b->block)
75 else if (a->block < b->block)
80 RB_GENERATE_INTERNAL(jbd_revoke, revoke_entry, revoke_node,
81 jbd_revoke_entry_cmp, static inline)
83 #define jbd_alloc_revoke_entry() calloc(1, sizeof(struct revoke_entry))
84 #define jbd_free_revoke_entry(addr) free(addr)
86 int jbd_inode_bmap(struct jbd_fs *jbd_fs,
88 ext4_fsblk_t *fblock);
90 int jbd_sb_write(struct jbd_fs *jbd_fs, struct jbd_sb *s)
93 struct ext4_fs *fs = jbd_fs->inode_ref.fs;
96 rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
100 offset = fblock * ext4_sb_get_block_size(&fs->sb);
101 return ext4_block_writebytes(fs->bdev, offset, s,
102 EXT4_SUPERBLOCK_SIZE);
105 int jbd_sb_read(struct jbd_fs *jbd_fs, struct jbd_sb *s)
108 struct ext4_fs *fs = jbd_fs->inode_ref.fs;
111 rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
115 offset = fblock * ext4_sb_get_block_size(&fs->sb);
116 return ext4_block_readbytes(fs->bdev, offset, s,
117 EXT4_SUPERBLOCK_SIZE);
120 static bool jbd_verify_sb(struct jbd_sb *sb)
122 struct jbd_bhdr *header = &sb->header;
123 if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER)
126 if (jbd_get32(header, blocktype) != JBD_SUPERBLOCK &&
127 jbd_get32(header, blocktype) != JBD_SUPERBLOCK_V2)
133 int jbd_get_fs(struct ext4_fs *fs,
134 struct jbd_fs *jbd_fs)
137 uint32_t journal_ino;
139 memset(jbd_fs, 0, sizeof(struct jbd_fs));
140 journal_ino = ext4_get32(&fs->sb, journal_inode_number);
142 rc = ext4_fs_get_inode_ref(fs,
146 memset(jbd_fs, 0, sizeof(struct jbd_fs));
149 rc = jbd_sb_read(jbd_fs, &jbd_fs->sb);
151 memset(jbd_fs, 0, sizeof(struct jbd_fs));
152 ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
158 int jbd_put_fs(struct jbd_fs *jbd_fs)
162 jbd_sb_write(jbd_fs, &jbd_fs->sb);
164 rc = ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
168 int jbd_inode_bmap(struct jbd_fs *jbd_fs,
170 ext4_fsblk_t *fblock)
172 int rc = ext4_fs_get_inode_data_block_index(
180 int jbd_block_get(struct jbd_fs *jbd_fs,
181 struct ext4_block *block,
184 /* TODO: journal device. */
186 ext4_lblk_t iblock = (ext4_lblk_t)fblock;
187 rc = jbd_inode_bmap(jbd_fs, iblock,
192 struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
193 rc = ext4_block_get(bdev, block, fblock);
197 int jbd_block_get_noread(struct jbd_fs *jbd_fs,
198 struct ext4_block *block,
201 /* TODO: journal device. */
203 ext4_lblk_t iblock = (ext4_lblk_t)fblock;
204 rc = jbd_inode_bmap(jbd_fs, iblock,
209 struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
210 rc = ext4_block_get_noread(bdev, block, fblock);
214 int jbd_block_set(struct jbd_fs *jbd_fs,
215 struct ext4_block *block)
217 return ext4_block_set(jbd_fs->inode_ref.fs->bdev,
222 * helper functions to deal with 32 or 64bit block numbers.
224 int jbd_tag_bytes(struct jbd_fs *jbd_fs)
228 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
229 JBD_FEATURE_INCOMPAT_CSUM_V3))
230 return sizeof(struct jbd_block_tag3);
232 size = sizeof(struct jbd_block_tag);
234 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
235 JBD_FEATURE_INCOMPAT_CSUM_V2))
236 size += sizeof(uint16_t);
238 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
239 JBD_FEATURE_INCOMPAT_64BIT))
242 return size - sizeof(uint32_t);
246 jbd_extract_block_tag(struct jbd_fs *jbd_fs,
257 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
258 JBD_FEATURE_INCOMPAT_CSUM_V3)) {
259 struct jbd_block_tag3 *tag = __tag;
260 *block = jbd_get32(tag, blocknr);
261 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
262 JBD_FEATURE_INCOMPAT_64BIT))
263 *block |= (uint64_t)jbd_get32(tag, blocknr_high) << 32;
265 if (jbd_get32(tag, flags) & JBD_FLAG_ESCAPE)
268 if (!(jbd_get32(tag, flags) & JBD_FLAG_SAME_UUID)) {
269 uuid_start = (char *)tag + tag_bytes;
271 memcpy(uuid, uuid_start, UUID_SIZE);
274 if (jbd_get32(tag, flags) & JBD_FLAG_LAST_TAG)
278 struct jbd_block_tag *tag = __tag;
279 *block = jbd_get32(tag, blocknr);
280 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
281 JBD_FEATURE_INCOMPAT_64BIT))
282 *block |= (uint64_t)jbd_get32(tag, blocknr_high) << 32;
284 if (jbd_get16(tag, flags) & JBD_FLAG_ESCAPE)
287 if (!(jbd_get16(tag, flags) & JBD_FLAG_SAME_UUID)) {
288 uuid_start = (char *)tag + tag_bytes;
290 memcpy(uuid, uuid_start, UUID_SIZE);
293 if (jbd_get16(tag, flags) & JBD_FLAG_LAST_TAG)
300 jbd_iterate_block_table(struct jbd_fs *jbd_fs,
302 uint32_t tag_tbl_size,
303 void (*func)(struct jbd_fs * jbd_fs,
309 ext4_fsblk_t block = 0;
310 uint8_t uuid[UUID_SIZE];
311 char *tag_start, *tag_ptr;
312 uint32_t tag_bytes = jbd_tag_bytes(jbd_fs);
313 tag_start = __tag_start;
316 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
317 JBD_FEATURE_INCOMPAT_CSUM_V2) ||
318 JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
319 JBD_FEATURE_INCOMPAT_CSUM_V3))
320 tag_tbl_size -= sizeof(struct jbd_block_tail);
322 while (tag_ptr - tag_start + tag_bytes <= tag_tbl_size) {
325 jbd_extract_block_tag(jbd_fs,
333 func(jbd_fs, block, uuid, arg);
338 tag_ptr += tag_bytes;
340 tag_ptr += UUID_SIZE;
345 static void jbd_display_block_tags(struct jbd_fs *jbd_fs,
350 uint32_t *iblock = arg;
351 ext4_dbg(DEBUG_JBD, "Block in block_tag: %" PRIu64 "\n", block);
358 static struct revoke_entry *
359 jbd_revoke_entry_lookup(struct recover_info *info, ext4_fsblk_t block)
361 struct revoke_entry tmp = {
365 return RB_FIND(jbd_revoke, &info->revoke_root, &tmp);
368 static void jbd_replay_block_tags(struct jbd_fs *jbd_fs,
370 uint8_t *uuid __unused,
374 struct replay_arg *arg = __arg;
375 struct recover_info *info = arg->info;
376 uint32_t *this_block = arg->this_block;
377 struct revoke_entry *revoke_entry;
378 struct ext4_block journal_block, ext4_block;
379 struct ext4_fs *fs = jbd_fs->inode_ref.fs;
383 revoke_entry = jbd_revoke_entry_lookup(info, block);
385 arg->this_trans_id < revoke_entry->trans_id)
389 "Replaying block in block_tag: %" PRIu64 "\n",
392 r = jbd_block_get(jbd_fs, &journal_block, *this_block);
397 r = ext4_block_get_noread(fs->bdev, &ext4_block, block);
399 jbd_block_set(jbd_fs, &journal_block);
403 memcpy(ext4_block.data,
405 jbd_get32(&jbd_fs->sb, blocksize));
407 ext4_block.dirty = true;
408 ext4_block_set(fs->bdev, &ext4_block);
410 uint16_t mount_count, state;
411 mount_count = ext4_get16(&fs->sb, mount_count);
412 state = ext4_get16(&fs->sb, state);
415 journal_block.data + EXT4_SUPERBLOCK_OFFSET,
416 EXT4_SUPERBLOCK_SIZE);
418 /* Mark system as mounted */
419 ext4_set16(&fs->sb, state, state);
420 r = ext4_sb_write(fs->bdev, &fs->sb);
424 /*Update mount count*/
425 ext4_set16(&fs->sb, mount_count, mount_count);
428 jbd_block_set(jbd_fs, &journal_block);
433 static void jbd_add_revoke_block_tags(struct recover_info *info,
436 struct revoke_entry *revoke_entry;
438 ext4_dbg(DEBUG_JBD, "Add block %" PRIu64 " to revoke tree\n", block);
439 revoke_entry = jbd_revoke_entry_lookup(info, block);
441 revoke_entry->trans_id = info->this_trans_id;
445 revoke_entry = jbd_alloc_revoke_entry();
446 ext4_assert(revoke_entry);
447 revoke_entry->block = block;
448 revoke_entry->trans_id = info->this_trans_id;
449 RB_INSERT(jbd_revoke, &info->revoke_root, revoke_entry);
454 static void jbd_destroy_revoke_tree(struct recover_info *info)
456 while (!RB_EMPTY(&info->revoke_root)) {
457 struct revoke_entry *revoke_entry =
458 RB_MIN(jbd_revoke, &info->revoke_root);
459 ext4_assert(revoke_entry);
460 RB_REMOVE(jbd_revoke, &info->revoke_root, revoke_entry);
461 jbd_free_revoke_entry(revoke_entry);
465 /* Make sure we wrap around the log correctly! */
466 #define wrap(sb, var) \
468 if (var >= jbd_get32((sb), maxlen)) \
469 var -= (jbd_get32((sb), maxlen) - jbd_get32((sb), first)); \
472 #define ACTION_SCAN 0
473 #define ACTION_REVOKE 1
474 #define ACTION_RECOVER 2
477 static void jbd_build_revoke_tree(struct jbd_fs *jbd_fs,
478 struct jbd_bhdr *header,
479 struct recover_info *info)
482 struct jbd_revoke_header *revoke_hdr =
483 (struct jbd_revoke_header *)header;
484 uint32_t i, nr_entries, record_len = 4;
485 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
486 JBD_FEATURE_INCOMPAT_64BIT))
489 nr_entries = (jbd_get32(revoke_hdr, count) -
490 sizeof(struct jbd_revoke_header)) /
493 blocks_entry = (char *)(revoke_hdr + 1);
495 for (i = 0;i < nr_entries;i++) {
496 if (record_len == 8) {
498 (uint64_t *)blocks_entry;
499 jbd_add_revoke_block_tags(info, to_be64(*blocks));
502 (uint32_t *)blocks_entry;
503 jbd_add_revoke_block_tags(info, to_be32(*blocks));
505 blocks_entry += record_len;
509 static void jbd_debug_descriptor_block(struct jbd_fs *jbd_fs,
510 struct jbd_bhdr *header,
513 jbd_iterate_block_table(jbd_fs,
515 jbd_get32(&jbd_fs->sb, blocksize) -
516 sizeof(struct jbd_bhdr),
517 jbd_display_block_tags,
521 static void jbd_replay_descriptor_block(struct jbd_fs *jbd_fs,
522 struct jbd_bhdr *header,
523 struct replay_arg *arg)
525 jbd_iterate_block_table(jbd_fs,
527 jbd_get32(&jbd_fs->sb, blocksize) -
528 sizeof(struct jbd_bhdr),
529 jbd_replay_block_tags,
533 int jbd_iterate_log(struct jbd_fs *jbd_fs,
534 struct recover_info *info,
538 bool log_end = false;
539 struct jbd_sb *sb = &jbd_fs->sb;
540 uint32_t start_trans_id, this_trans_id;
541 uint32_t start_block, this_block;
543 start_trans_id = this_trans_id = jbd_get32(sb, sequence);
544 start_block = this_block = jbd_get32(sb, start);
546 ext4_dbg(DEBUG_JBD, "Start of journal at trans id: %" PRIu32 "\n",
550 struct ext4_block block;
551 struct jbd_bhdr *header;
552 if (action != ACTION_SCAN)
553 if (this_trans_id > info->last_trans_id) {
558 r = jbd_block_get(jbd_fs, &block, this_block);
562 header = (struct jbd_bhdr *)block.data;
563 if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER) {
564 jbd_block_set(jbd_fs, &block);
569 if (jbd_get32(header, sequence) != this_trans_id) {
570 if (action != ACTION_SCAN)
573 jbd_block_set(jbd_fs, &block);
578 switch (jbd_get32(header, blocktype)) {
579 case JBD_DESCRIPTOR_BLOCK:
580 ext4_dbg(DEBUG_JBD, "Descriptor block: %" PRIu32", "
581 "trans_id: %" PRIu32"\n",
582 this_block, this_trans_id);
583 if (action == ACTION_RECOVER) {
584 struct replay_arg replay_arg;
585 replay_arg.info = info;
586 replay_arg.this_block = &this_block;
587 replay_arg.this_trans_id = this_trans_id;
589 jbd_replay_descriptor_block(jbd_fs,
590 header, &replay_arg);
592 jbd_debug_descriptor_block(jbd_fs,
593 header, &this_block);
596 case JBD_COMMIT_BLOCK:
597 ext4_dbg(DEBUG_JBD, "Commit block: %" PRIu32", "
598 "trans_id: %" PRIu32"\n",
599 this_block, this_trans_id);
602 case JBD_REVOKE_BLOCK:
603 ext4_dbg(DEBUG_JBD, "Revoke block: %" PRIu32", "
604 "trans_id: %" PRIu32"\n",
605 this_block, this_trans_id);
606 if (action == ACTION_REVOKE) {
607 info->this_trans_id = this_trans_id;
608 jbd_build_revoke_tree(jbd_fs,
616 jbd_block_set(jbd_fs, &block);
618 wrap(sb, this_block);
619 if (this_block == start_block)
623 ext4_dbg(DEBUG_JBD, "End of journal.\n");
624 if (r == EOK && action == ACTION_SCAN) {
625 info->start_trans_id = start_trans_id;
626 if (this_trans_id > start_trans_id)
627 info->last_trans_id = this_trans_id - 1;
629 info->last_trans_id = this_trans_id;
635 int jbd_recover(struct jbd_fs *jbd_fs)
638 struct recover_info info;
639 struct jbd_sb *sb = &jbd_fs->sb;
643 RB_INIT(&info.revoke_root);
645 r = jbd_iterate_log(jbd_fs, &info, ACTION_SCAN);
649 r = jbd_iterate_log(jbd_fs, &info, ACTION_REVOKE);
653 r = jbd_iterate_log(jbd_fs, &info, ACTION_RECOVER);
655 jbd_set32(&jbd_fs->sb, start, 0);
656 jbd_fs->dirty = true;
658 jbd_destroy_revoke_tree(&info);