+/*
+ * JBD stores integers in big endian.
+ */
+
+#define JBD_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */
+
+/*
+ * Descriptor block types:
+ */
+
+#define JBD_DESCRIPTOR_BLOCK 1
+#define JBD_COMMIT_BLOCK 2
+#define JBD_SUPERBLOCK 3
+#define JBD_SUPERBLOCK_V2 4
+#define JBD_REVOKE_BLOCK 5
+
+#pragma pack(push, 1)
+
+/*
+ * Standard header for all descriptor blocks:
+ */
+struct jbd_bhdr {
+ uint32_t magic;
+ uint32_t blocktype;
+ uint32_t sequence;
+};
+
+#pragma pack(pop)
+
+/*
+ * Checksum types.
+ */
+#define JBD_CRC32_CHKSUM 1
+#define JBD_MD5_CHKSUM 2
+#define JBD_SHA1_CHKSUM 3
+#define JBD_CRC32C_CHKSUM 4
+
+#define JBD_CRC32_CHKSUM_SIZE 4
+
+#define JBD_CHECKSUM_BYTES (32 / sizeof(uint32_t))
+
+#pragma pack(push, 1)
+
+/*
+ * Commit block header for storing transactional checksums:
+ *
+ * NOTE: If FEATURE_COMPAT_CHECKSUM (checksum v1) is set, the h_chksum*
+ * fields are used to store a checksum of the descriptor and data blocks.
+ *
+ * If FEATURE_INCOMPAT_CSUM_V2 (checksum v2) is set, then the h_chksum
+ * field is used to store crc32c(uuid+commit_block). Each journal metadata
+ * block gets its own checksum, and data block checksums are stored in
+ * journal_block_tag (in the descriptor). The other h_chksum* fields are
+ * not used.
+ *
+ * If FEATURE_INCOMPAT_CSUM_V3 is set, the descriptor block uses
+ * journal_block_tag3_t to store a full 32-bit checksum. Everything else
+ * is the same as v2.
+ *
+ * Checksum v1, v2, and v3 are mutually exclusive features.
+ */
+
+struct jbd_commit_header {
+ struct jbd_bhdr header;
+ uint8_t chksum_type;
+ uint8_t chksum_size;
+ uint8_t padding[2];
+ uint32_t chksum[JBD_CHECKSUM_BYTES];
+ uint64_t commit_sec;
+ uint32_t commit_nsec;
+};
+
+/*
+ * The block tag: used to describe a single buffer in the journal
+ */
+struct jbd_block_tag3 {
+ uint32_t blocknr; /* The on-disk block number */
+ uint32_t flags; /* See below */
+ uint32_t blocknr_high; /* most-significant high 32bits. */
+ uint32_t checksum; /* crc32c(uuid+seq+block) */
+};
+
+struct jbd_block_tag {
+ uint32_t blocknr; /* The on-disk block number */
+ uint16_t checksum; /* truncated crc32c(uuid+seq+block) */
+ uint16_t flags; /* See below */
+ uint32_t blocknr_high; /* most-significant high 32bits. */
+};
+
+#pragma pack(pop)
+
+/* Definitions for the journal tag flags word: */
+#define JBD_FLAG_ESCAPE 1 /* on-disk block is escaped */
+#define JBD_FLAG_SAME_UUID 2 /* block has same uuid as previous */
+#define JBD_FLAG_DELETED 4 /* block deleted by this transaction */
+#define JBD_FLAG_LAST_TAG 8 /* last tag in this descriptor block */
+
+#pragma pack(push, 1)
+
+/* Tail of descriptor block, for checksumming */
+struct jbd_block_tail {
+ uint32_t checksum;
+};
+
+/*
+ * The revoke descriptor: used on disk to describe a series of blocks to
+ * be revoked from the log
+ */
+struct jbd_revoke_header {
+ struct jbd_bhdr header;
+ uint32_t count; /* Count of bytes used in the block */
+};
+
+/* Tail of revoke block, for checksumming */
+struct jbd_revoke_tail {
+ uint32_t checksum;
+};
+
+#pragma pack(pop)
+
+#define JBD_USERS_MAX 48
+#define JBD_USERS_SIZE (UUID_SIZE * JBD_USERS_MAX)
+
+#pragma pack(push, 1)
+
+/*
+ * The journal superblock. All fields are in big-endian byte order.
+ */
+struct jbd_sb {
+/* 0x0000 */
+ struct jbd_bhdr header;
+
+/* 0x000C */
+ /* Static information describing the journal */
+ uint32_t blocksize; /* journal device blocksize */
+ uint32_t maxlen; /* total blocks in journal file */
+ uint32_t first; /* first block of log information */
+
+/* 0x0018 */
+ /* Dynamic information describing the current state of the log */
+ uint32_t sequence; /* first commit ID expected in log */
+ uint32_t start; /* blocknr of start of log */
+
+/* 0x0020 */
+ /* Error value, as set by journal_abort(). */
+ int32_t error_val;
+
+/* 0x0024 */
+ /* Remaining fields are only valid in a version-2 superblock */
+ uint32_t feature_compat; /* compatible feature set */
+ uint32_t feature_incompat; /* incompatible feature set */
+ uint32_t feature_ro_compat; /* readonly-compatible feature set */
+/* 0x0030 */
+ uint8_t uuid[UUID_SIZE]; /* 128-bit uuid for journal */
+
+/* 0x0040 */
+ uint32_t nr_users; /* Nr of filesystems sharing log */
+
+ uint32_t dynsuper; /* Blocknr of dynamic superblock copy*/
+
+/* 0x0048 */
+ uint32_t max_transaction; /* Limit of journal blocks per trans.*/
+ uint32_t max_trandata; /* Limit of data blocks per trans. */
+
+/* 0x0050 */
+ uint8_t checksum_type; /* checksum type */
+ uint8_t padding2[3];
+ uint32_t padding[42];
+ uint32_t checksum; /* crc32c(superblock) */
+
+/* 0x0100 */
+ uint8_t users[JBD_USERS_SIZE]; /* ids of all fs'es sharing the log */
+
+/* 0x0400 */
+};
+
+#pragma pack(pop)
+
+#define JBD_SUPERBLOCK_SIZE sizeof(struct jbd_sb)
+
+#define JBD_HAS_COMPAT_FEATURE(jsb,mask) \
+ ((jsb)->header.blocktype >= to_be32(2) && \
+ ((jsb)->feature_compat & to_be32((mask))))
+#define JBD_HAS_RO_COMPAT_FEATURE(jsb,mask) \
+ ((jsb)->header.blocktype >= to_be32(2) && \
+ ((jsb)->feature_ro_compat & to_be32((mask))))
+#define JBD_HAS_INCOMPAT_FEATURE(jsb,mask) \
+ ((jsb)->header.blocktype >= to_be32(2) && \
+ ((jsb)->feature_incompat & to_be32((mask))))
+
+#define JBD_FEATURE_COMPAT_CHECKSUM 0x00000001
+
+#define JBD_FEATURE_INCOMPAT_REVOKE 0x00000001
+#define JBD_FEATURE_INCOMPAT_64BIT 0x00000002
+#define JBD_FEATURE_INCOMPAT_ASYNC_COMMIT 0x00000004
+#define JBD_FEATURE_INCOMPAT_CSUM_V2 0x00000008
+#define JBD_FEATURE_INCOMPAT_CSUM_V3 0x00000010
+
+/* Features known to this kernel version: */
+#define JBD_KNOWN_COMPAT_FEATURES 0
+#define JBD_KNOWN_ROCOMPAT_FEATURES 0
+#define JBD_KNOWN_INCOMPAT_FEATURES (JBD_FEATURE_INCOMPAT_REVOKE|\
+ JBD_FEATURE_INCOMPAT_ASYNC_COMMIT|\
+ JBD_FEATURE_INCOMPAT_64BIT|\
+ JBD_FEATURE_INCOMPAT_CSUM_V2|\
+ JBD_FEATURE_INCOMPAT_CSUM_V3)
+
+struct jbd_fs {
+ /* If journal block device is used, bdev will be non-null */
+ struct ext4_blockdev *bdev;
+ struct ext4_inode_ref inode_ref;
+ struct jbd_sb sb;
+
+ bool dirty;
+};
+
+struct jbd_buf {
+ uint64_t jbd_lba;
+ struct ext4_block block;
+ struct jbd_trans *trans;
+ struct jbd_block_rec *block_rec;
+ TAILQ_ENTRY(jbd_buf) buf_node;
+ TAILQ_ENTRY(jbd_buf) dirty_buf_node;
+};
+
+struct jbd_revoke_rec {
+ ext4_fsblk_t lba;
+ LIST_ENTRY(jbd_revoke_rec) revoke_node;
+};
+
+struct jbd_block_rec {
+ ext4_fsblk_t lba;
+ struct ext4_buf *buf;
+ struct jbd_trans *trans;
+ RB_ENTRY(jbd_block_rec) block_rec_node;
+ LIST_ENTRY(jbd_block_rec) tbrec_node;
+ TAILQ_HEAD(jbd_buf_dirty, jbd_buf) dirty_buf_queue;
+};
+
+struct jbd_trans {
+ uint32_t trans_id;
+
+ uint32_t start_iblock;
+ int alloc_blocks;
+ int data_cnt;
+ uint32_t data_csum;
+ int written_cnt;
+ int error;
+
+ struct jbd_journal *journal;
+
+ TAILQ_HEAD(jbd_trans_buf, jbd_buf) buf_queue;
+ LIST_HEAD(jbd_revoke_list, jbd_revoke_rec) revoke_list;
+ LIST_HEAD(jbd_trans_block_rec, jbd_block_rec) tbrec_list;
+ TAILQ_ENTRY(jbd_trans) trans_node;
+};
+
+struct jbd_journal {
+ uint32_t first;
+ uint32_t start;
+ uint32_t last;
+ uint32_t trans_id;
+ uint32_t alloc_trans_id;
+
+ uint32_t block_size;
+
+ TAILQ_HEAD(jbd_trans_queue, jbd_trans) trans_queue;
+ TAILQ_HEAD(jbd_cp_queue, jbd_trans) cp_queue;
+ RB_HEAD(jbd_block, jbd_block_rec) block_rec_root;
+
+ struct jbd_fs *jbd_fs;
+};
+
+/*****************************************************************************/
+
+#define EXT4_CRC32_INIT (0xFFFFFFFFUL)
+
+/*****************************************************************************/
+
+static inline uint64_t reorder64(uint64_t n)