/* * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com) * Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup lwext4 * @{ *//** * @file ext4_journal.c * @brief Journal handle functions */ #include <ext4_config.h>#include <ext4_types.h>#include <ext4_misc.h>#include <ext4_errno.h>#include <ext4_debug.h> #include <ext4_fs.h>#include <ext4_super.h>#include <ext4_journal.h>#include <ext4_blockdev.h>#include <ext4_crc32.h>#include <ext4_journal.h> #include <string.h>#include <stdlib.h> /**@brief Revoke entry during journal replay.*/struct revoke_entry { /**@brief Block number not to be replayed.*/ ext4_fsblk_t block; /**@brief For any transaction id smaller * than trans_id, records of @block * in those transactions should not * be replayed.*/ uint32_t trans_id; /**@brief Revoke tree node.*/ RB_ENTRY(revoke_entry) revoke_node;}; /**@brief Valid journal replay information.*/struct recover_info { /**@brief Starting transaction id.*/ uint32_t start_trans_id; /**@brief Ending transaction id.*/ uint32_t last_trans_id; /**@brief Used as internal argument.*/ uint32_t this_trans_id; /**@brief No of transactions went through.*/ uint32_t trans_cnt; /**@brief RB-Tree storing revoke entries.*/ RB_HEAD(jbd_revoke, revoke_entry) revoke_root;}; /**@brief Journal replay internal arguments.*/struct replay_arg { /**@brief Journal replay information.*/ struct recover_info *info; /**@brief Current block we are on.*/ uint32_t *this_block; /**@brief Current trans_id we are on.*/ uint32_t this_trans_id;}; /* Make sure we wrap around the log correctly! */#define wrap(sb, var) \do { \ if (var >= jbd_get32((sb), maxlen)) \ var -= (jbd_get32((sb), maxlen) - jbd_get32((sb), first)); \} while (0) static inline int32_ttrans_id_diff(uint32_t x, uint32_t y){ int32_t diff = x - y; return diff;} static intjbd_revoke_entry_cmp(struct revoke_entry *a, struct revoke_entry *b){ if (a->block > b->block) return 1; else if (a->block < b->block) return -1; return 0;} static intjbd_block_rec_cmp(struct jbd_block_rec *a, struct jbd_block_rec *b){ if (a->lba > b->lba) return 1; else if (a->lba < b->lba) return -1; return 0;} static intjbd_revoke_rec_cmp(struct jbd_revoke_rec *a, struct jbd_revoke_rec *b){ if (a->lba > b->lba) return 1; else if (a->lba < b->lba) return -1; return 0;} RB_GENERATE_INTERNAL(jbd_revoke, revoke_entry, revoke_node, jbd_revoke_entry_cmp, static inline)RB_GENERATE_INTERNAL(jbd_block, jbd_block_rec, block_rec_node, jbd_block_rec_cmp, static inline)RB_GENERATE_INTERNAL(jbd_revoke_tree, jbd_revoke_rec, revoke_node, jbd_revoke_rec_cmp, static inline) #define jbd_alloc_revoke_entry() ext4_calloc(1, sizeof(struct revoke_entry))#define jbd_free_revoke_entry(addr) ext4_free(addr) static int jbd_has_csum(struct jbd_sb *jbd_sb){ if (JBD_HAS_INCOMPAT_FEATURE(jbd_sb, JBD_FEATURE_INCOMPAT_CSUM_V2)) return 2; if (JBD_HAS_INCOMPAT_FEATURE(jbd_sb, JBD_FEATURE_INCOMPAT_CSUM_V3)) return 3; return 0;} #if CONFIG_META_CSUM_ENABLEstatic uint32_t jbd_sb_csum(struct jbd_sb *jbd_sb){ uint32_t checksum = 0; if (jbd_has_csum(jbd_sb)) { uint32_t orig_checksum = jbd_sb->checksum; jbd_set32(jbd_sb, checksum, 0); /* Calculate crc32c checksum against tho whole superblock */ checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_sb, JBD_SUPERBLOCK_SIZE); jbd_sb->checksum = orig_checksum; } return checksum;}#else#define jbd_sb_csum(...) 0#endif static void jbd_sb_csum_set(struct jbd_sb *jbd_sb){ if (!jbd_has_csum(jbd_sb)) return; jbd_set32(jbd_sb, checksum, jbd_sb_csum(jbd_sb));} #if CONFIG_META_CSUM_ENABLEstatic booljbd_verify_sb_csum(struct jbd_sb *jbd_sb){ if (!jbd_has_csum(jbd_sb)) return true; return jbd_sb_csum(jbd_sb) == jbd_get32(jbd_sb, checksum);}#else#define jbd_verify_sb_csum(...) true#endif #if CONFIG_META_CSUM_ENABLEstatic uint32_t jbd_meta_csum(struct jbd_fs *jbd_fs, struct jbd_bhdr *bhdr){ uint32_t checksum = 0; if (jbd_has_csum(&jbd_fs->sb)) { uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize); struct jbd_block_tail *tail = (struct jbd_block_tail *)((char *)bhdr + block_size - sizeof(struct jbd_block_tail)); uint32_t orig_checksum = tail->checksum; tail->checksum = 0; /* First calculate crc32c checksum against fs uuid */ checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_fs->sb.uuid, sizeof(jbd_fs->sb.uuid)); /* Calculate crc32c checksum against tho whole block */ checksum = ext4_crc32c(checksum, bhdr, block_size); tail->checksum = orig_checksum; } return checksum;}#else#define jbd_meta_csum(...) 0#endif static void jbd_meta_csum_set(struct jbd_fs *jbd_fs, struct jbd_bhdr *bhdr){ uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize); struct jbd_block_tail *tail = (struct jbd_block_tail *) ((char *)bhdr + block_size - sizeof(struct jbd_block_tail)); if (!jbd_has_csum(&jbd_fs->sb)) return; tail->checksum = to_be32(jbd_meta_csum(jbd_fs, bhdr));} #if CONFIG_META_CSUM_ENABLEstatic booljbd_verify_meta_csum(struct jbd_fs *jbd_fs, struct jbd_bhdr *bhdr){ uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize); struct jbd_block_tail *tail = (struct jbd_block_tail *) ((char *)bhdr + block_size - sizeof(struct jbd_block_tail)); if (!jbd_has_csum(&jbd_fs->sb)) return true; return jbd_meta_csum(jbd_fs, bhdr) == to_be32(tail->checksum);}#else#define jbd_verify_meta_csum(...) true#endif #if CONFIG_META_CSUM_ENABLEstatic uint32_t jbd_commit_csum(struct jbd_fs *jbd_fs, struct jbd_commit_header *header){ uint32_t checksum = 0; if (jbd_has_csum(&jbd_fs->sb)) { uint8_t orig_checksum_type = header->chksum_type, orig_checksum_size = header->chksum_size; uint32_t orig_checksum = header->chksum[0]; uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize); header->chksum_type = 0; header->chksum_size = 0; header->chksum[0] = 0; /* First calculate crc32c checksum against fs uuid */ checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_fs->sb.uuid, sizeof(jbd_fs->sb.uuid)); /* Calculate crc32c checksum against tho whole block */ checksum = ext4_crc32c(checksum, header, block_size); header->chksum_type = orig_checksum_type; header->chksum_size = orig_checksum_size; header->chksum[0] = orig_checksum; } return checksum;}#else#define jbd_commit_csum(...) 0#endif static void jbd_commit_csum_set(struct jbd_fs *jbd_fs, struct jbd_commit_header *header){ if (!jbd_has_csum(&jbd_fs->sb)) return; header->chksum_type = 0; header->chksum_size = 0; header->chksum[0] = jbd_commit_csum(jbd_fs, header);} #if CONFIG_META_CSUM_ENABLEstatic bool jbd_verify_commit_csum(struct jbd_fs *jbd_fs, struct jbd_commit_header *header){ if (!jbd_has_csum(&jbd_fs->sb)) return true; return header->chksum[0] == to_be32(jbd_commit_csum(jbd_fs, header));}#else#define jbd_verify_commit_csum(...) true#endif #if CONFIG_META_CSUM_ENABLE/* * NOTE: We only make use of @csum parameter when * JBD_FEATURE_COMPAT_CHECKSUM is enabled. */static uint32_t jbd_block_csum(struct jbd_fs *jbd_fs, const void *buf, uint32_t csum, uint32_t sequence){ uint32_t checksum = 0; if (jbd_has_csum(&jbd_fs->sb)) { uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize); /* First calculate crc32c checksum against fs uuid */ checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_fs->sb.uuid, sizeof(jbd_fs->sb.uuid)); /* Then calculate crc32c checksum against sequence no. */ checksum = ext4_crc32c(checksum, &sequence, sizeof(uint32_t)); /* Calculate crc32c checksum against tho whole block */ checksum = ext4_crc32c(checksum, buf, block_size); } else if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb, JBD_FEATURE_COMPAT_CHECKSUM)) { uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize); /* Calculate crc32c checksum against tho whole block */ checksum = ext4_crc32(csum, buf, block_size); } return checksum;}#else#define jbd_block_csum(...) 0#endif static void jbd_block_tag_csum_set(struct jbd_fs *jbd_fs, void *__tag, uint32_t checksum){ int ver = jbd_has_csum(&jbd_fs->sb); if (!ver) return; if (ver == 2) { struct jbd_block_tag *tag = __tag; tag->checksum = (uint16_t)to_be32(checksum); } else { struct jbd_block_tag3 *tag = __tag; tag->checksum = to_be32(checksum); }} /**@brief Write jbd superblock to disk. * @param jbd_fs jbd filesystem * @param s jbd superblock * @return standard error code*/static int jbd_sb_write(struct jbd_fs *jbd_fs, struct jbd_sb *s){ int rc; struct ext4_fs *fs = jbd_fs->inode_ref.fs; uint64_t offset; ext4_fsblk_t fblock; rc = jbd_inode_bmap(jbd_fs, 0, &fblock); if (rc != EOK) return rc; jbd_sb_csum_set(s); offset = fblock * ext4_sb_get_block_size(&fs->sb); return ext4_block_writebytes(fs->bdev, offset, s, EXT4_SUPERBLOCK_SIZE);} /**@brief Read jbd superblock from disk. * @param jbd_fs jbd filesystem * @param s jbd superblock * @return standard error code*/static int jbd_sb_read(struct jbd_fs *jbd_fs, struct jbd_sb *s){ int rc; struct ext4_fs *fs = jbd_fs->inode_ref.fs; uint64_t offset; ext4_fsblk_t fblock; rc = jbd_inode_bmap(jbd_fs, 0, &fblock); if (rc != EOK) return rc; offset = fblock * ext4_sb_get_block_size(&fs->sb); return ext4_block_readbytes(fs->bdev, offset, s, EXT4_SUPERBLOCK_SIZE);} /**@brief Verify jbd superblock. * @param sb jbd superblock * @return true if jbd superblock is valid */static bool jbd_verify_sb(struct jbd_sb *sb){ struct jbd_bhdr *header = &sb->header; if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER) return false; if (jbd_get32(header, blocktype) != JBD_SUPERBLOCK && jbd_get32(header, blocktype) != JBD_SUPERBLOCK_V2) return false; return jbd_verify_sb_csum(sb);} /**@brief Write back dirty jbd superblock to disk. * @param jbd_fs jbd filesystem * @return standard error code*/static int jbd_write_sb(struct jbd_fs *jbd_fs){ int rc = EOK; if (jbd_fs->dirty) { rc = jbd_sb_write(jbd_fs, &jbd_fs->sb); if (rc != EOK) return rc; jbd_fs->dirty = false; } return rc;} /**@brief Get reference to jbd filesystem. * @param fs Filesystem to load journal of * @param jbd_fs jbd filesystem * @return standard error code*/int jbd_get_fs(struct ext4_fs *fs, struct jbd_fs *jbd_fs){ int rc; uint32_t journal_ino; memset(jbd_fs, 0, sizeof(struct jbd_fs)); /* See if there is journal inode on this filesystem.*/ /* FIXME: detection on existance ofbkejournal bdev is * missing.*/ journal_ino = ext4_get32(&fs->sb, journal_inode_number); rc = ext4_fs_get_inode_ref(fs, journal_ino, &jbd_fs->inode_ref); if (rc != EOK) return rc; rc = jbd_sb_read(jbd_fs, &jbd_fs->sb); if (rc != EOK) goto Error; if (!jbd_verify_sb(&jbd_fs->sb)) { rc = EIO; goto Error; } if (rc == EOK) jbd_fs->bdev = fs->bdev; return rc;Error: ext4_fs_put_inode_ref(&jbd_fs->inode_ref); memset(jbd_fs, 0, sizeof(struct jbd_fs)); return rc;} /**@brief Put reference of jbd filesystem. * @param jbd_fs jbd filesystem * @return standard error code*/int jbd_put_fs(struct jbd_fs *jbd_fs){ int rc; rc = jbd_write_sb(jbd_fs); ext4_fs_put_inode_ref(&jbd_fs->inode_ref); return rc;} /**@brief Data block lookup helper. * @param jbd_fs jbd filesystem * @param iblock block index * @param fblock logical block address * @return standard error code*/Lines 1–500 of 2293. Load more
/* * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com) * Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup lwext4 * @{ */ /** * @file ext4_journal.c * @brief Journal handle functions */ #include <ext4_config.h> #include <ext4_types.h> #include <ext4_misc.h> #include <ext4_errno.h> #include <ext4_debug.h> #include <ext4_fs.h> #include <ext4_super.h> #include <ext4_journal.h> #include <ext4_blockdev.h> #include <ext4_crc32.h> #include <ext4_journal.h> #include <string.h> #include <stdlib.h> /**@brief Revoke entry during journal replay.*/ struct revoke_entry { /**@brief Block number not to be replayed.*/ ext4_fsblk_t block; /**@brief For any transaction id smaller * than trans_id, records of @block * in those transactions should not * be replayed.*/ uint32_t trans_id; /**@brief Revoke tree node.*/ RB_ENTRY(revoke_entry) revoke_node; }; /**@brief Valid journal replay information.*/ struct recover_info { /**@brief Starting transaction id.*/ uint32_t start_trans_id; /**@brief Ending transaction id.*/ uint32_t last_trans_id; /**@brief Used as internal argument.*/ uint32_t this_trans_id; /**@brief No of transactions went through.*/ uint32_t trans_cnt; /**@brief RB-Tree storing revoke entries.*/ RB_HEAD(jbd_revoke, revoke_entry) revoke_root; }; /**@brief Journal replay internal arguments.*/ struct replay_arg { /**@brief Journal replay information.*/ struct recover_info *info; /**@brief Current block we are on.*/ uint32_t *this_block; /**@brief Current trans_id we are on.*/ uint32_t this_trans_id; }; /* Make sure we wrap around the log correctly! */ #define wrap(sb, var) \ do { \ if (var >= jbd_get32((sb), maxlen)) \ var -= (jbd_get32((sb), maxlen) - jbd_get32((sb), first)); \ } while (0) static inline int32_t trans_id_diff(uint32_t x, uint32_t y) { int32_t diff = x - y; return diff; } static int jbd_revoke_entry_cmp(struct revoke_entry *a, struct revoke_entry *b) { if (a->block > b->block) return 1; else if (a->block < b->block) return -1; return 0; } static int jbd_block_rec_cmp(struct jbd_block_rec *a, struct jbd_block_rec *b) { if (a->lba > b->lba) return 1; else if (a->lba < b->lba) return -1; return 0; } static int jbd_revoke_rec_cmp(struct jbd_revoke_rec *a, struct jbd_revoke_rec *b) { if (a->lba > b->lba) return 1; else if (a->lba < b->lba) return -1; return 0; } RB_GENERATE_INTERNAL(jbd_revoke, revoke_entry, revoke_node, jbd_revoke_entry_cmp, static inline) RB_GENERATE_INTERNAL(jbd_block, jbd_block_rec, block_rec_node, jbd_block_rec_cmp, static inline) RB_GENERATE_INTERNAL(jbd_revoke_tree, jbd_revoke_rec, revoke_node, jbd_revoke_rec_cmp, static inline) #define jbd_alloc_revoke_entry() ext4_calloc(1, sizeof(struct revoke_entry)) #define jbd_free_revoke_entry(addr) ext4_free(addr) static int jbd_has_csum(struct jbd_sb *jbd_sb) { if (JBD_HAS_INCOMPAT_FEATURE(jbd_sb, JBD_FEATURE_INCOMPAT_CSUM_V2)) return 2; if (JBD_HAS_INCOMPAT_FEATURE(jbd_sb, JBD_FEATURE_INCOMPAT_CSUM_V3)) return 3; return 0; } #if CONFIG_META_CSUM_ENABLE static uint32_t jbd_sb_csum(struct jbd_sb *jbd_sb) { uint32_t checksum = 0; if (jbd_has_csum(jbd_sb)) { uint32_t orig_checksum = jbd_sb->checksum; jbd_set32(jbd_sb, checksum, 0); /* Calculate crc32c checksum against tho whole superblock */ checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_sb, JBD_SUPERBLOCK_SIZE); jbd_sb->checksum = orig_checksum; } return checksum; } #else #define jbd_sb_csum(...) 0 #endif static void jbd_sb_csum_set(struct jbd_sb *jbd_sb) { if (!jbd_has_csum(jbd_sb)) return; jbd_set32(jbd_sb, checksum, jbd_sb_csum(jbd_sb)); } #if CONFIG_META_CSUM_ENABLE static bool jbd_verify_sb_csum(struct jbd_sb *jbd_sb) { if (!jbd_has_csum(jbd_sb)) return true; return jbd_sb_csum(jbd_sb) == jbd_get32(jbd_sb, checksum); } #else #define jbd_verify_sb_csum(...) true #endif #if CONFIG_META_CSUM_ENABLE static uint32_t jbd_meta_csum(struct jbd_fs *jbd_fs, struct jbd_bhdr *bhdr) { uint32_t checksum = 0; if (jbd_has_csum(&jbd_fs->sb)) { uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize); struct jbd_block_tail *tail = (struct jbd_block_tail *)((char *)bhdr + block_size - sizeof(struct jbd_block_tail)); uint32_t orig_checksum = tail->checksum; tail->checksum = 0; /* First calculate crc32c checksum against fs uuid */ checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_fs->sb.uuid, sizeof(jbd_fs->sb.uuid)); /* Calculate crc32c checksum against tho whole block */ checksum = ext4_crc32c(checksum, bhdr, block_size); tail->checksum = orig_checksum; } return checksum; } #else #define jbd_meta_csum(...) 0 #endif static void jbd_meta_csum_set(struct jbd_fs *jbd_fs, struct jbd_bhdr *bhdr) { uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize); struct jbd_block_tail *tail = (struct jbd_block_tail *) ((char *)bhdr + block_size - sizeof(struct jbd_block_tail)); if (!jbd_has_csum(&jbd_fs->sb)) return; tail->checksum = to_be32(jbd_meta_csum(jbd_fs, bhdr)); } #if CONFIG_META_CSUM_ENABLE static bool jbd_verify_meta_csum(struct jbd_fs *jbd_fs, struct jbd_bhdr *bhdr) { uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize); struct jbd_block_tail *tail = (struct jbd_block_tail *) ((char *)bhdr + block_size - sizeof(struct jbd_block_tail)); if (!jbd_has_csum(&jbd_fs->sb)) return true; return jbd_meta_csum(jbd_fs, bhdr) == to_be32(tail->checksum); } #else #define jbd_verify_meta_csum(...) true #endif #if CONFIG_META_CSUM_ENABLE static uint32_t jbd_commit_csum(struct jbd_fs *jbd_fs, struct jbd_commit_header *header) { uint32_t checksum = 0; if (jbd_has_csum(&jbd_fs->sb)) { uint8_t orig_checksum_type = header->chksum_type, orig_checksum_size = header->chksum_size; uint32_t orig_checksum = header->chksum[0]; uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize); header->chksum_type = 0; header->chksum_size = 0; header->chksum[0] = 0; /* First calculate crc32c checksum against fs uuid */ checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_fs->sb.uuid, sizeof(jbd_fs->sb.uuid)); /* Calculate crc32c checksum against tho whole block */ checksum = ext4_crc32c(checksum, header, block_size); header->chksum_type = orig_checksum_type; header->chksum_size = orig_checksum_size; header->chksum[0] = orig_checksum; } return checksum; } #else #define jbd_commit_csum(...) 0 #endif static void jbd_commit_csum_set(struct jbd_fs *jbd_fs, struct jbd_commit_header *header) { if (!jbd_has_csum(&jbd_fs->sb)) return; header->chksum_type = 0; header->chksum_size = 0; header->chksum[0] = jbd_commit_csum(jbd_fs, header); } #if CONFIG_META_CSUM_ENABLE static bool jbd_verify_commit_csum(struct jbd_fs *jbd_fs, struct jbd_commit_header *header) { if (!jbd_has_csum(&jbd_fs->sb)) return true; return header->chksum[0] == to_be32(jbd_commit_csum(jbd_fs, header)); } #else #define jbd_verify_commit_csum(...) true #endif #if CONFIG_META_CSUM_ENABLE /* * NOTE: We only make use of @csum parameter when * JBD_FEATURE_COMPAT_CHECKSUM is enabled. */ static uint32_t jbd_block_csum(struct jbd_fs *jbd_fs, const void *buf, uint32_t csum, uint32_t sequence) { uint32_t checksum = 0; if (jbd_has_csum(&jbd_fs->sb)) { uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize); /* First calculate crc32c checksum against fs uuid */ checksum = ext4_crc32c(EXT4_CRC32_INIT, jbd_fs->sb.uuid, sizeof(jbd_fs->sb.uuid)); /* Then calculate crc32c checksum against sequence no. */ checksum = ext4_crc32c(checksum, &sequence, sizeof(uint32_t)); /* Calculate crc32c checksum against tho whole block */ checksum = ext4_crc32c(checksum, buf, block_size); } else if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb, JBD_FEATURE_COMPAT_CHECKSUM)) { uint32_t block_size = jbd_get32(&jbd_fs->sb, blocksize); /* Calculate crc32c checksum against tho whole block */ checksum = ext4_crc32(csum, buf, block_size); } return checksum; } #else #define jbd_block_csum(...) 0 #endif static void jbd_block_tag_csum_set(struct jbd_fs *jbd_fs, void *__tag, uint32_t checksum) { int ver = jbd_has_csum(&jbd_fs->sb); if (!ver) return; if (ver == 2) { struct jbd_block_tag *tag = __tag; tag->checksum = (uint16_t)to_be32(checksum); } else { struct jbd_block_tag3 *tag = __tag; tag->checksum = to_be32(checksum); } } /**@brief Write jbd superblock to disk. * @param jbd_fs jbd filesystem * @param s jbd superblock * @return standard error code*/ static int jbd_sb_write(struct jbd_fs *jbd_fs, struct jbd_sb *s) { int rc; struct ext4_fs *fs = jbd_fs->inode_ref.fs; uint64_t offset; ext4_fsblk_t fblock; rc = jbd_inode_bmap(jbd_fs, 0, &fblock); if (rc != EOK) return rc; jbd_sb_csum_set(s); offset = fblock * ext4_sb_get_block_size(&fs->sb); return ext4_block_writebytes(fs->bdev, offset, s, EXT4_SUPERBLOCK_SIZE); } /**@brief Read jbd superblock from disk. * @param jbd_fs jbd filesystem * @param s jbd superblock * @return standard error code*/ static int jbd_sb_read(struct jbd_fs *jbd_fs, struct jbd_sb *s) { int rc; struct ext4_fs *fs = jbd_fs->inode_ref.fs; uint64_t offset; ext4_fsblk_t fblock; rc = jbd_inode_bmap(jbd_fs, 0, &fblock); if (rc != EOK) return rc; offset = fblock * ext4_sb_get_block_size(&fs->sb); return ext4_block_readbytes(fs->bdev, offset, s, EXT4_SUPERBLOCK_SIZE); } /**@brief Verify jbd superblock. * @param sb jbd superblock * @return true if jbd superblock is valid */ static bool jbd_verify_sb(struct jbd_sb *sb) { struct jbd_bhdr *header = &sb->header; if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER) return false; if (jbd_get32(header, blocktype) != JBD_SUPERBLOCK && jbd_get32(header, blocktype) != JBD_SUPERBLOCK_V2) return false; return jbd_verify_sb_csum(sb); } /**@brief Write back dirty jbd superblock to disk. * @param jbd_fs jbd filesystem * @return standard error code*/ static int jbd_write_sb(struct jbd_fs *jbd_fs) { int rc = EOK; if (jbd_fs->dirty) { rc = jbd_sb_write(jbd_fs, &jbd_fs->sb); if (rc != EOK) return rc; jbd_fs->dirty = false; } return rc; } /**@brief Get reference to jbd filesystem. * @param fs Filesystem to load journal of * @param jbd_fs jbd filesystem * @return standard error code*/ int jbd_get_fs(struct ext4_fs *fs, struct jbd_fs *jbd_fs) { int rc; uint32_t journal_ino; memset(jbd_fs, 0, sizeof(struct jbd_fs)); /* See if there is journal inode on this filesystem.*/ /* FIXME: detection on existance ofbkejournal bdev is * missing.*/ journal_ino = ext4_get32(&fs->sb, journal_inode_number); rc = ext4_fs_get_inode_ref(fs, journal_ino, &jbd_fs->inode_ref); if (rc != EOK) return rc; rc = jbd_sb_read(jbd_fs, &jbd_fs->sb); if (rc != EOK) goto Error; if (!jbd_verify_sb(&jbd_fs->sb)) { rc = EIO; goto Error; } if (rc == EOK) jbd_fs->bdev = fs->bdev; return rc; Error: ext4_fs_put_inode_ref(&jbd_fs->inode_ref); memset(jbd_fs, 0, sizeof(struct jbd_fs)); return rc; } /**@brief Put reference of jbd filesystem. * @param jbd_fs jbd filesystem * @return standard error code*/ int jbd_put_fs(struct jbd_fs *jbd_fs) { int rc; rc = jbd_write_sb(jbd_fs); ext4_fs_put_inode_ref(&jbd_fs->inode_ref); return rc; } /**@brief Data block lookup helper. * @param jbd_fs jbd filesystem * @param iblock block index * @param fblock logical block address * @return standard error code*/