diff options
Diffstat (limited to 'fs/ext4/ext4_journal.c')
| -rw-r--r-- | fs/ext4/ext4_journal.c | 667 | 
1 files changed, 667 insertions, 0 deletions
| diff --git a/fs/ext4/ext4_journal.c b/fs/ext4/ext4_journal.c new file mode 100644 index 00000000000..8a252d66c33 --- /dev/null +++ b/fs/ext4/ext4_journal.c @@ -0,0 +1,667 @@ +/* + * (C) Copyright 2011 - 2012 Samsung Electronics + * EXT4 filesystem implementation in Uboot by + * Uma Shankar <uma.shankar@samsung.com> + * Manjunatha C Achar <a.manjunatha@samsung.com> + * + * Journal data structures and headers for Journaling feature of ext4 + * have been referred from JBD2 (Journaling Block device 2) + * implementation in Linux Kernel. + * Written by Stephen C. Tweedie <sct@redhat.com> + * + * Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved + * This file is part of the Linux kernel and is made available under + * the terms of the GNU General Public License, version 2, or at your + * option, any later version, incorporated herein by reference. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <common.h> +#include <ext4fs.h> +#include <malloc.h> +#include <ext_common.h> +#include "ext4_common.h" + +static struct revoke_blk_list *revk_blk_list; +static struct revoke_blk_list *prev_node; +static int first_node = TRUE; + +int gindex; +int gd_index; +int jrnl_blk_idx; +struct journal_log *journal_ptr[MAX_JOURNAL_ENTRIES]; +struct dirty_blocks *dirty_block_ptr[MAX_JOURNAL_ENTRIES]; + +int ext4fs_init_journal(void) +{ +	int i; +	char *temp = NULL; +	struct ext_filesystem *fs = get_fs(); + +	/* init globals */ +	revk_blk_list = NULL; +	prev_node = NULL; +	gindex = 0; +	gd_index = 0; +	jrnl_blk_idx = 1; + +	for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) { +		journal_ptr[i] = zalloc(sizeof(struct journal_log)); +		if (!journal_ptr[i]) +			goto fail; +		dirty_block_ptr[i] = zalloc(sizeof(struct dirty_blocks)); +		if (!dirty_block_ptr[i]) +			goto fail; +		journal_ptr[i]->buf = NULL; +		journal_ptr[i]->blknr = -1; + +		dirty_block_ptr[i]->buf = NULL; +		dirty_block_ptr[i]->blknr = -1; +	} + +	if (fs->blksz == 4096) { +		temp = zalloc(fs->blksz); +		if (!temp) +			goto fail; +		journal_ptr[gindex]->buf = zalloc(fs->blksz); +		if (!journal_ptr[gindex]->buf) +			goto fail; +		ext4fs_devread(0, 0, fs->blksz, temp); +		memcpy(temp + SUPERBLOCK_SIZE, fs->sb, SUPERBLOCK_SIZE); +		memcpy(journal_ptr[gindex]->buf, temp, fs->blksz); +		journal_ptr[gindex++]->blknr = 0; +		free(temp); +	} else { +		journal_ptr[gindex]->buf = zalloc(fs->blksz); +		if (!journal_ptr[gindex]->buf) +			goto fail; +		memcpy(journal_ptr[gindex]->buf, fs->sb, SUPERBLOCK_SIZE); +		journal_ptr[gindex++]->blknr = 1; +	} + +	/* Check the file system state using journal super block */ +	if (ext4fs_check_journal_state(SCAN)) +		goto fail; +	/* Check the file system state using journal super block */ +	if (ext4fs_check_journal_state(RECOVER)) +		goto fail; + +	return 0; +fail: +	return -1; +} + +void ext4fs_dump_metadata(void) +{ +	struct ext_filesystem *fs = get_fs(); +	int i; +	for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) { +		if (dirty_block_ptr[i]->blknr == -1) +			break; +		put_ext4((uint64_t) ((uint64_t)dirty_block_ptr[i]->blknr * +				(uint64_t)fs->blksz), dirty_block_ptr[i]->buf, +								fs->blksz); +	} +} + +void ext4fs_free_journal(void) +{ +	int i; +	for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) { +		if (dirty_block_ptr[i]->blknr == -1) +			break; +		if (dirty_block_ptr[i]->buf) +			free(dirty_block_ptr[i]->buf); +	} + +	for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) { +		if (journal_ptr[i]->blknr == -1) +			break; +		if (journal_ptr[i]->buf) +			free(journal_ptr[i]->buf); +	} + +	for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) { +		if (journal_ptr[i]) +			free(journal_ptr[i]); +		if (dirty_block_ptr[i]) +			free(dirty_block_ptr[i]); +	} +	gindex = 0; +	gd_index = 0; +	jrnl_blk_idx = 1; +} + +int ext4fs_log_gdt(char *gd_table) +{ +	struct ext_filesystem *fs = get_fs(); +	short i; +	long int var = fs->gdtable_blkno; +	for (i = 0; i < fs->no_blk_pergdt; i++) { +		journal_ptr[gindex]->buf = zalloc(fs->blksz); +		if (!journal_ptr[gindex]->buf) +			return -ENOMEM; +		memcpy(journal_ptr[gindex]->buf, gd_table, fs->blksz); +		gd_table += fs->blksz; +		journal_ptr[gindex++]->blknr = var++; +	} + +	return 0; +} + +/* + * This function stores the backup copy of meta data in RAM + * journal_buffer -- Buffer containing meta data + * blknr -- Block number on disk of the meta data buffer + */ +int ext4fs_log_journal(char *journal_buffer, long int blknr) +{ +	struct ext_filesystem *fs = get_fs(); +	short i; + +	if (!journal_buffer) { +		printf("Invalid input arguments %s\n", __func__); +		return -EINVAL; +	} + +	for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) { +		if (journal_ptr[i]->blknr == -1) +			break; +		if (journal_ptr[i]->blknr == blknr) +			return 0; +	} + +	journal_ptr[gindex]->buf = zalloc(fs->blksz); +	if (!journal_ptr[gindex]->buf) +		return -ENOMEM; + +	memcpy(journal_ptr[gindex]->buf, journal_buffer, fs->blksz); +	journal_ptr[gindex++]->blknr = blknr; + +	return 0; +} + +/* + * This function stores the modified meta data in RAM + * metadata_buffer -- Buffer containing meta data + * blknr -- Block number on disk of the meta data buffer + */ +int ext4fs_put_metadata(char *metadata_buffer, long int blknr) +{ +	struct ext_filesystem *fs = get_fs(); +	if (!metadata_buffer) { +		printf("Invalid input arguments %s\n", __func__); +		return -EINVAL; +	} +	dirty_block_ptr[gd_index]->buf = zalloc(fs->blksz); +	if (!dirty_block_ptr[gd_index]->buf) +		return -ENOMEM; +	memcpy(dirty_block_ptr[gd_index]->buf, metadata_buffer, fs->blksz); +	dirty_block_ptr[gd_index++]->blknr = blknr; + +	return 0; +} + +void print_revoke_blks(char *revk_blk) +{ +	int offset; +	int max; +	long int blocknr; +	struct journal_revoke_header_t *header; + +	if (revk_blk == NULL) +		return; + +	header = (struct journal_revoke_header_t *) revk_blk; +	offset = sizeof(struct journal_revoke_header_t); +	max = be32_to_cpu(header->r_count); +	printf("total bytes %d\n", max); + +	while (offset < max) { +		blocknr = be32_to_cpu(*((long int *)(revk_blk + offset))); +		printf("revoke blknr is %ld\n", blocknr); +		offset += 4; +	} +} + +static struct revoke_blk_list *_get_node(void) +{ +	struct revoke_blk_list *tmp_node; +	tmp_node = zalloc(sizeof(struct revoke_blk_list)); +	if (tmp_node == NULL) +		return NULL; +	tmp_node->content = NULL; +	tmp_node->next = NULL; + +	return tmp_node; +} + +void ext4fs_push_revoke_blk(char *buffer) +{ +	struct revoke_blk_list *node = NULL; +	struct ext_filesystem *fs = get_fs(); +	if (buffer == NULL) { +		printf("buffer ptr is NULL\n"); +		return; +	} +	node = _get_node(); +	if (!node) { +		printf("_get_node: malloc failed\n"); +		return; +	} + +	node->content = zalloc(fs->blksz); +	if (node->content == NULL) +		return; +	memcpy(node->content, buffer, fs->blksz); + +	if (first_node == TRUE) { +		revk_blk_list = node; +		prev_node = node; +		 first_node = FALSE; +	} else { +		prev_node->next = node; +		prev_node = node; +	} +} + +void ext4fs_free_revoke_blks(void) +{ +	struct revoke_blk_list *tmp_node = revk_blk_list; +	struct revoke_blk_list *next_node = NULL; + +	while (tmp_node != NULL) { +		if (tmp_node->content) +			free(tmp_node->content); +		tmp_node = tmp_node->next; +	} + +	tmp_node = revk_blk_list; +	while (tmp_node != NULL) { +		next_node = tmp_node->next; +		free(tmp_node); +		tmp_node = next_node; +	} + +	revk_blk_list = NULL; +	prev_node = NULL; +	first_node = TRUE; +} + +int check_blknr_for_revoke(long int blknr, int sequence_no) +{ +	struct journal_revoke_header_t *header; +	int offset; +	int max; +	long int blocknr; +	char *revk_blk; +	struct revoke_blk_list *tmp_revk_node = revk_blk_list; +	while (tmp_revk_node != NULL) { +		revk_blk = tmp_revk_node->content; + +		header = (struct journal_revoke_header_t *) revk_blk; +		if (sequence_no < be32_to_cpu(header->r_header.h_sequence)) { +			offset = sizeof(struct journal_revoke_header_t); +			max = be32_to_cpu(header->r_count); + +			while (offset < max) { +				blocknr = be32_to_cpu(*((long int *) +						  (revk_blk + offset))); +				if (blocknr == blknr) +					goto found; +				offset += 4; +			} +		} +		tmp_revk_node = tmp_revk_node->next; +	} + +	return -1; + +found: +	return 0; +} + +/* + * This function parses the journal blocks and replays the + * suceessful transactions. A transaction is successfull + * if commit block is found for a descriptor block + * The tags in descriptor block contain the disk block + * numbers of the metadata  to be replayed + */ +void recover_transaction(int prev_desc_logical_no) +{ +	struct ext2_inode inode_journal; +	struct ext_filesystem *fs = get_fs(); +	struct journal_header_t *jdb; +	long int blknr; +	char *p_jdb; +	int ofs, flags; +	int i; +	struct ext3_journal_block_tag *tag; +	char *temp_buff = zalloc(fs->blksz); +	char *metadata_buff = zalloc(fs->blksz); +	if (!temp_buff || !metadata_buff) +		goto fail; +	i = prev_desc_logical_no; +	ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO, +			  (struct ext2_inode *)&inode_journal); +	blknr = read_allocated_block((struct ext2_inode *) +				     &inode_journal, i); +	ext4fs_devread(blknr * fs->sect_perblk, 0, fs->blksz, temp_buff); +	p_jdb = (char *)temp_buff; +	jdb = (struct journal_header_t *) temp_buff; +	ofs = sizeof(struct journal_header_t); + +	do { +		tag = (struct ext3_journal_block_tag *)&p_jdb[ofs]; +		ofs += sizeof(struct ext3_journal_block_tag); + +		if (ofs > fs->blksz) +			break; + +		flags = be32_to_cpu(tag->flags); +		if (!(flags & EXT3_JOURNAL_FLAG_SAME_UUID)) +			ofs += 16; + +		i++; +		debug("\t\ttag %u\n", be32_to_cpu(tag->block)); +		if (revk_blk_list != NULL) { +			if (check_blknr_for_revoke(be32_to_cpu(tag->block), +				be32_to_cpu(jdb->h_sequence)) == 0) +				continue; +		} +		blknr = read_allocated_block(&inode_journal, i); +		ext4fs_devread(blknr * fs->sect_perblk, 0, +			       fs->blksz, metadata_buff); +		put_ext4((uint64_t)(be32_to_cpu(tag->block) * fs->blksz), +			 metadata_buff, (uint32_t) fs->blksz); +	} while (!(flags & EXT3_JOURNAL_FLAG_LAST_TAG)); +fail: +	free(temp_buff); +	free(metadata_buff); +} + +void print_jrnl_status(int recovery_flag) +{ +	if (recovery_flag == RECOVER) +		printf("Journal Recovery Completed\n"); +	else +		printf("Journal Scan Completed\n"); +} + +int ext4fs_check_journal_state(int recovery_flag) +{ +	int i; +	int DB_FOUND = NO; +	long int blknr; +	int transaction_state = TRANSACTION_COMPLETE; +	int prev_desc_logical_no = 0; +	int curr_desc_logical_no = 0; +	int ofs, flags, block; +	struct ext2_inode inode_journal; +	struct journal_superblock_t *jsb = NULL; +	struct journal_header_t *jdb = NULL; +	char *p_jdb = NULL; +	struct ext3_journal_block_tag *tag = NULL; +	char *temp_buff = NULL; +	char *temp_buff1 = NULL; +	struct ext_filesystem *fs = get_fs(); + +	temp_buff = zalloc(fs->blksz); +	if (!temp_buff) +		return -ENOMEM; +	temp_buff1 = zalloc(fs->blksz); +	if (!temp_buff1) { +		free(temp_buff); +		return -ENOMEM; +	} + +	ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO, &inode_journal); +	blknr = read_allocated_block(&inode_journal, EXT2_JOURNAL_SUPERBLOCK); +	ext4fs_devread(blknr * fs->sect_perblk, 0, fs->blksz, temp_buff); +	jsb = (struct journal_superblock_t *) temp_buff; + +	if (fs->sb->feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) { +		if (recovery_flag == RECOVER) +			printf("Recovery required\n"); +	} else { +		if (recovery_flag == RECOVER) +			printf("File System is consistent\n"); +		goto end; +	} + +	if (be32_to_cpu(jsb->s_start) == 0) +		goto end; + +	if (!(jsb->s_feature_compat & +				cpu_to_be32(JBD2_FEATURE_COMPAT_CHECKSUM))) +		jsb->s_feature_compat |= +				cpu_to_be32(JBD2_FEATURE_COMPAT_CHECKSUM); + +	i = be32_to_cpu(jsb->s_first); +	while (1) { +		block = be32_to_cpu(jsb->s_first); +		blknr = read_allocated_block(&inode_journal, i); +		memset(temp_buff1, '\0', fs->blksz); +		ext4fs_devread(blknr * fs->sect_perblk, +			       0, fs->blksz, temp_buff1); +		jdb = (struct journal_header_t *) temp_buff1; + +		if (be32_to_cpu(jdb->h_blocktype) == +		    EXT3_JOURNAL_DESCRIPTOR_BLOCK) { +			if (be32_to_cpu(jdb->h_sequence) != +			    be32_to_cpu(jsb->s_sequence)) { +				print_jrnl_status(recovery_flag); +				break; +			} + +			curr_desc_logical_no = i; +			if (transaction_state == TRANSACTION_COMPLETE) +				transaction_state = TRANSACTION_RUNNING; +			else +				return -1; +			p_jdb = (char *)temp_buff1; +			ofs = sizeof(struct journal_header_t); +			do { +				tag = (struct ext3_journal_block_tag *) +				    &p_jdb[ofs]; +				ofs += sizeof(struct ext3_journal_block_tag); +				if (ofs > fs->blksz) +					break; +				flags = be32_to_cpu(tag->flags); +				if (!(flags & EXT3_JOURNAL_FLAG_SAME_UUID)) +					ofs += 16; +				i++; +				debug("\t\ttag %u\n", be32_to_cpu(tag->block)); +			} while (!(flags & EXT3_JOURNAL_FLAG_LAST_TAG)); +			i++; +			DB_FOUND = YES; +		} else if (be32_to_cpu(jdb->h_blocktype) == +				EXT3_JOURNAL_COMMIT_BLOCK) { +			if (be32_to_cpu(jdb->h_sequence) != +			     be32_to_cpu(jsb->s_sequence)) { +				print_jrnl_status(recovery_flag); +				break; +			} + +			if (transaction_state == TRANSACTION_RUNNING || +					(DB_FOUND == NO)) { +				transaction_state = TRANSACTION_COMPLETE; +				i++; +				jsb->s_sequence = +					cpu_to_be32(be32_to_cpu( +						jsb->s_sequence) + 1); +			} +			prev_desc_logical_no = curr_desc_logical_no; +			if ((recovery_flag == RECOVER) && (DB_FOUND == YES)) +				recover_transaction(prev_desc_logical_no); + +			DB_FOUND = NO; +		} else if (be32_to_cpu(jdb->h_blocktype) == +				EXT3_JOURNAL_REVOKE_BLOCK) { +			if (be32_to_cpu(jdb->h_sequence) != +			    be32_to_cpu(jsb->s_sequence)) { +				print_jrnl_status(recovery_flag); +				break; +			} +			if (recovery_flag == SCAN) +				ext4fs_push_revoke_blk((char *)jdb); +			i++; +		} else { +			debug("Else Case\n"); +			if (be32_to_cpu(jdb->h_sequence) != +			    be32_to_cpu(jsb->s_sequence)) { +				print_jrnl_status(recovery_flag); +				break; +			} +		} +	} + +end: +	if (recovery_flag == RECOVER) { +		jsb->s_start = cpu_to_be32(1); +		jsb->s_sequence = cpu_to_be32(be32_to_cpu(jsb->s_sequence) + 1); +		/* get the superblock */ +		ext4fs_devread(SUPERBLOCK_SECTOR, 0, SUPERBLOCK_SIZE, +			       (char *)fs->sb); +		fs->sb->feature_incompat |= EXT3_FEATURE_INCOMPAT_RECOVER; + +		/* Update the super block */ +		put_ext4((uint64_t) (SUPERBLOCK_SIZE), +			 (struct ext2_sblock *)fs->sb, +			 (uint32_t) SUPERBLOCK_SIZE); +		ext4fs_devread(SUPERBLOCK_SECTOR, 0, SUPERBLOCK_SIZE, +			       (char *)fs->sb); + +		blknr = read_allocated_block(&inode_journal, +					 EXT2_JOURNAL_SUPERBLOCK); +		put_ext4((uint64_t) (blknr * fs->blksz), +			 (struct journal_superblock_t *)temp_buff, +			 (uint32_t) fs->blksz); +		ext4fs_free_revoke_blks(); +	} +	free(temp_buff); +	free(temp_buff1); + +	return 0; +} + +static void update_descriptor_block(long int blknr) +{ +	int i; +	long int jsb_blknr; +	struct journal_header_t jdb; +	struct ext3_journal_block_tag tag; +	struct ext2_inode inode_journal; +	struct journal_superblock_t *jsb = NULL; +	char *buf = NULL; +	char *temp = NULL; +	struct ext_filesystem *fs = get_fs(); +	char *temp_buff = zalloc(fs->blksz); +	if (!temp_buff) +		return; + +	ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO, &inode_journal); +	jsb_blknr = read_allocated_block(&inode_journal, +					 EXT2_JOURNAL_SUPERBLOCK); +	ext4fs_devread(jsb_blknr * fs->sect_perblk, 0, fs->blksz, temp_buff); +	jsb = (struct journal_superblock_t *) temp_buff; + +	jdb.h_blocktype = cpu_to_be32(EXT3_JOURNAL_DESCRIPTOR_BLOCK); +	jdb.h_magic = cpu_to_be32(EXT3_JOURNAL_MAGIC_NUMBER); +	jdb.h_sequence = jsb->s_sequence; +	buf = zalloc(fs->blksz); +	if (!buf) { +		free(temp_buff); +		return; +	} +	temp = buf; +	memcpy(buf, &jdb, sizeof(struct journal_header_t)); +	temp += sizeof(struct journal_header_t); + +	for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) { +		if (journal_ptr[i]->blknr == -1) +			break; + +		tag.block = cpu_to_be32(journal_ptr[i]->blknr); +		tag.flags = cpu_to_be32(EXT3_JOURNAL_FLAG_SAME_UUID); +		memcpy(temp, &tag, sizeof(struct ext3_journal_block_tag)); +		temp = temp + sizeof(struct ext3_journal_block_tag); +	} + +	tag.block = cpu_to_be32(journal_ptr[--i]->blknr); +	tag.flags = cpu_to_be32(EXT3_JOURNAL_FLAG_LAST_TAG); +	memcpy(temp - sizeof(struct ext3_journal_block_tag), &tag, +	       sizeof(struct ext3_journal_block_tag)); +	put_ext4((uint64_t) (blknr * fs->blksz), buf, (uint32_t) fs->blksz); + +	free(temp_buff); +	free(buf); +} + +static void update_commit_block(long int blknr) +{ +	struct journal_header_t jdb; +	struct ext_filesystem *fs = get_fs(); +	char *buf = NULL; +	struct ext2_inode inode_journal; +	struct journal_superblock_t *jsb; +	long int jsb_blknr; +	char *temp_buff = zalloc(fs->blksz); +	if (!temp_buff) +		return; + +	ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO, &inode_journal); +	jsb_blknr = read_allocated_block(&inode_journal, +					 EXT2_JOURNAL_SUPERBLOCK); +	ext4fs_devread(jsb_blknr * fs->sect_perblk, 0, fs->blksz, temp_buff); +	jsb = (struct journal_superblock_t *) temp_buff; + +	jdb.h_blocktype = cpu_to_be32(EXT3_JOURNAL_COMMIT_BLOCK); +	jdb.h_magic = cpu_to_be32(EXT3_JOURNAL_MAGIC_NUMBER); +	jdb.h_sequence = jsb->s_sequence; +	buf = zalloc(fs->blksz); +	if (!buf) { +		free(temp_buff); +		return; +	} +	memcpy(buf, &jdb, sizeof(struct journal_header_t)); +	put_ext4((uint64_t) (blknr * fs->blksz), buf, (uint32_t) fs->blksz); + +	free(temp_buff); +	free(buf); +} + +void ext4fs_update_journal(void) +{ +	struct ext2_inode inode_journal; +	struct ext_filesystem *fs = get_fs(); +	long int blknr; +	int i; +	ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO, &inode_journal); +	blknr = read_allocated_block(&inode_journal, jrnl_blk_idx++); +	update_descriptor_block(blknr); +	for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) { +		if (journal_ptr[i]->blknr == -1) +			break; +		blknr = read_allocated_block(&inode_journal, jrnl_blk_idx++); +		put_ext4((uint64_t) ((uint64_t)blknr * (uint64_t)fs->blksz), +			 journal_ptr[i]->buf, fs->blksz); +	} +	blknr = read_allocated_block(&inode_journal, jrnl_blk_idx++); +	update_commit_block(blknr); +	printf("update journal finished\n"); +} | 
