2293 lines · 60.0 KB · C
RawDownload
/* * 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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t