diff options
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/ext4/Makefile | 1 | ||||
| -rw-r--r-- | fs/ext4/crc16.c | 62 | ||||
| -rw-r--r-- | fs/ext4/crc16.h | 16 | ||||
| -rw-r--r-- | fs/ext4/ext4_common.c | 1353 | ||||
| -rw-r--r-- | fs/ext4/ext4_common.h | 25 | ||||
| -rw-r--r-- | fs/ext4/ext4_journal.c | 667 | ||||
| -rw-r--r-- | fs/ext4/ext4_journal.h | 141 | ||||
| -rw-r--r-- | fs/ext4/ext4fs.c | 961 | 
8 files changed, 3226 insertions, 0 deletions
| diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile index 7b7fcbd9f..82cd9ae16 100644 --- a/fs/ext4/Makefile +++ b/fs/ext4/Makefile @@ -34,6 +34,7 @@ COBJS-$(CONFIG_CMD_EXT4) := ext4fs.o ext4_common.o dev.o  ifndef CONFIG_CMD_EXT4  COBJS-$(CONFIG_CMD_EXT2) := ext4fs.o ext4_common.o dev.o  endif +COBJS-$(CONFIG_CMD_EXT4_WRITE) += ext4_journal.o crc16.o  SRCS	:= $(AOBJS:.o=.S) $(COBJS-y:.o=.c)  OBJS	:= $(addprefix $(obj),$(AOBJS) $(COBJS-y)) diff --git a/fs/ext4/crc16.c b/fs/ext4/crc16.c new file mode 100644 index 000000000..3afb34dae --- /dev/null +++ b/fs/ext4/crc16.c @@ -0,0 +1,62 @@ +/* + *      crc16.c + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <common.h> +#include <asm/byteorder.h> +#include <linux/stat.h> +#include "crc16.h" + +/** CRC table for the CRC-16. The poly is 0x8005 (x16 + x15 + x2 + 1) */ +static __u16 const crc16_table[256] = { +	0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, +	0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, +	0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, +	0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, +	0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, +	0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, +	0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, +	0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, +	0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, +	0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, +	0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, +	0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, +	0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, +	0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, +	0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, +	0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, +	0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, +	0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, +	0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, +	0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, +	0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, +	0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, +	0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, +	0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, +	0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, +	0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, +	0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, +	0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, +	0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, +	0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, +	0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, +	0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 +}; + +/** + * Compute the CRC-16 for the data buffer +*/ + +unsigned int ext2fs_crc16(unsigned int crc, +	const void *buffer, unsigned int len) +{ +	const unsigned char *cp = buffer; + +	while (len--) +		crc = (((crc >> 8) & 0xffU) ^ +		       crc16_table[(crc ^ *cp++) & 0xffU]) & 0x0000ffffU; +	return crc; +} diff --git a/fs/ext4/crc16.h b/fs/ext4/crc16.h new file mode 100644 index 000000000..5fd113a56 --- /dev/null +++ b/fs/ext4/crc16.h @@ -0,0 +1,16 @@ +/* + * crc16.h - CRC-16 routine + * Implements the standard CRC-16: + *  Width 16 + *  Poly  0x8005 (x16 + x15 + x2 + 1) + *  Init  0 + * + * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com> + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ +#ifndef __CRC16_H +#define __CRC16_H +extern unsigned int ext2fs_crc16(unsigned int crc, +	const void *buffer, unsigned int len); +#endif diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c index 2ddbb50e8..083e45eed 100644 --- a/fs/ext4/ext4_common.c +++ b/fs/ext4/ext4_common.c @@ -14,6 +14,8 @@   * GRUB  --  GRand Unified Bootloader   * Copyright (C) 2003, 2004  Free Software Foundation, Inc.   * + * ext4write : Based on generic ext4 protocol. + *   * 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 @@ -54,6 +56,1357 @@ int ext4fs_indir3_blkno = -1;  struct ext2_inode *g_parent_inode;  static int symlinknest; +#if defined(CONFIG_CMD_EXT4_WRITE) +uint32_t ext4fs_div_roundup(uint32_t size, uint32_t n) +{ +	uint32_t res = size / n; +	if (res * n != size) +		res++; + +	return res; +} + +void put_ext4(uint64_t off, void *buf, uint32_t size) +{ +	uint64_t startblock; +	uint64_t remainder; +	unsigned char *temp_ptr = NULL; +	unsigned char sec_buf[SECTOR_SIZE]; +	struct ext_filesystem *fs = get_fs(); + +	startblock = off / (uint64_t)SECTOR_SIZE; +	startblock += part_offset; +	remainder = off % (uint64_t)SECTOR_SIZE; +	remainder &= SECTOR_SIZE - 1; + +	if (fs->dev_desc == NULL) +		return; + +	if ((startblock + (size / SECTOR_SIZE)) > +	    (part_offset + fs->total_sect)) { +		printf("part_offset is %lu\n", part_offset); +		printf("total_sector is %llu\n", fs->total_sect); +		printf("error: overflow occurs\n"); +		return; +	} + +	if (remainder) { +		if (fs->dev_desc->block_read) { +			fs->dev_desc->block_read(fs->dev_desc->dev, +						 startblock, 1, sec_buf); +			temp_ptr = sec_buf; +			memcpy((temp_ptr + remainder), +			       (unsigned char *)buf, size); +			fs->dev_desc->block_write(fs->dev_desc->dev, +						  startblock, 1, sec_buf); +		} +	} else { +		if (size / SECTOR_SIZE != 0) { +			fs->dev_desc->block_write(fs->dev_desc->dev, +						  startblock, +						  size / SECTOR_SIZE, +						  (unsigned long *)buf); +		} else { +			fs->dev_desc->block_read(fs->dev_desc->dev, +						 startblock, 1, sec_buf); +			temp_ptr = sec_buf; +			memcpy(temp_ptr, buf, size); +			fs->dev_desc->block_write(fs->dev_desc->dev, +						  startblock, 1, +						  (unsigned long *)sec_buf); +		} +	} +} + +static int _get_new_inode_no(unsigned char *buffer) +{ +	struct ext_filesystem *fs = get_fs(); +	unsigned char input; +	int operand, status; +	int count = 1; +	int j = 0; + +	/* get the blocksize of the filesystem */ +	unsigned char *ptr = buffer; +	while (*ptr == 255) { +		ptr++; +		count += 8; +		if (count > ext4fs_root->sblock.inodes_per_group) +			return -1; +	} + +	for (j = 0; j < fs->blksz; j++) { +		input = *ptr; +		int i = 0; +		while (i <= 7) { +			operand = 1 << i; +			status = input & operand; +			if (status) { +				i++; +				count++; +			} else { +				*ptr |= operand; +				return count; +			} +		} +		ptr = ptr + 1; +	} + +	return -1; +} + +static int _get_new_blk_no(unsigned char *buffer) +{ +	unsigned char input; +	int operand, status; +	int count = 0; +	int j = 0; +	unsigned char *ptr = buffer; +	struct ext_filesystem *fs = get_fs(); + +	if (fs->blksz != 1024) +		count = 0; +	else +		count = 1; + +	while (*ptr == 255) { +		ptr++; +		count += 8; +		if (count == (fs->blksz * 8)) +			return -1; +	} + +	for (j = 0; j < fs->blksz; j++) { +		input = *ptr; +		int i = 0; +		while (i <= 7) { +			operand = 1 << i; +			status = input & operand; +			if (status) { +				i++; +				count++; +			} else { +				*ptr |= operand; +				return count; +			} +		} +		ptr = ptr + 1; +	} + +	return -1; +} + +int ext4fs_set_block_bmap(long int blockno, unsigned char *buffer, int index) +{ +	int i, remainder, status; +	unsigned char *ptr = buffer; +	unsigned char operand; +	i = blockno / 8; +	remainder = blockno % 8; +	int blocksize = EXT2_BLOCK_SIZE(ext4fs_root); + +	i = i - (index * blocksize); +	if (blocksize != 1024) { +		ptr = ptr + i; +		operand = 1 << remainder; +		status = *ptr & operand; +		if (status) +			return -1; + +		*ptr = *ptr | operand; +		return 0; +	} else { +		if (remainder == 0) { +			ptr = ptr + i - 1; +			operand = (1 << 7); +		} else { +			ptr = ptr + i; +			operand = (1 << (remainder - 1)); +		} +		status = *ptr & operand; +		if (status) +			return -1; + +		*ptr = *ptr | operand; +		return 0; +	} +} + +void ext4fs_reset_block_bmap(long int blockno, unsigned char *buffer, int index) +{ +	int i, remainder, status; +	unsigned char *ptr = buffer; +	unsigned char operand; +	i = blockno / 8; +	remainder = blockno % 8; +	int blocksize = EXT2_BLOCK_SIZE(ext4fs_root); + +	i = i - (index * blocksize); +	if (blocksize != 1024) { +		ptr = ptr + i; +		operand = (1 << remainder); +		status = *ptr & operand; +		if (status) +			*ptr = *ptr & ~(operand); +	} else { +		if (remainder == 0) { +			ptr = ptr + i - 1; +			operand = (1 << 7); +		} else { +			ptr = ptr + i; +			operand = (1 << (remainder - 1)); +		} +		status = *ptr & operand; +		if (status) +			*ptr = *ptr & ~(operand); +	} +} + +int ext4fs_set_inode_bmap(int inode_no, unsigned char *buffer, int index) +{ +	int i, remainder, status; +	unsigned char *ptr = buffer; +	unsigned char operand; + +	inode_no -= (index * ext4fs_root->sblock.inodes_per_group); +	i = inode_no / 8; +	remainder = inode_no % 8; +	if (remainder == 0) { +		ptr = ptr + i - 1; +		operand = (1 << 7); +	} else { +		ptr = ptr + i; +		operand = (1 << (remainder - 1)); +	} +	status = *ptr & operand; +	if (status) +		return -1; + +	*ptr = *ptr | operand; + +	return 0; +} + +void ext4fs_reset_inode_bmap(int inode_no, unsigned char *buffer, int index) +{ +	int i, remainder, status; +	unsigned char *ptr = buffer; +	unsigned char operand; + +	inode_no -= (index * ext4fs_root->sblock.inodes_per_group); +	i = inode_no / 8; +	remainder = inode_no % 8; +	if (remainder == 0) { +		ptr = ptr + i - 1; +		operand = (1 << 7); +	} else { +		ptr = ptr + i; +		operand = (1 << (remainder - 1)); +	} +	status = *ptr & operand; +	if (status) +		*ptr = *ptr & ~(operand); +} + +int ext4fs_checksum_update(unsigned int i) +{ +	struct ext2_block_group *desc; +	struct ext_filesystem *fs = get_fs(); +	__u16 crc = 0; + +	desc = (struct ext2_block_group *)&fs->gd[i]; +	if (fs->sb->feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) { +		int offset = offsetof(struct ext2_block_group, bg_checksum); + +		crc = ext2fs_crc16(~0, fs->sb->unique_id, +				   sizeof(fs->sb->unique_id)); +		crc = ext2fs_crc16(crc, &i, sizeof(i)); +		crc = ext2fs_crc16(crc, desc, offset); +		offset += sizeof(desc->bg_checksum);	/* skip checksum */ +		assert(offset == sizeof(*desc)); +	} + +	return crc; +} + +static int check_void_in_dentry(struct ext2_dirent *dir, char *filename) +{ +	int dentry_length; +	int sizeof_void_space; +	int new_entry_byte_reqd; +	short padding_factor = 0; + +	if (dir->namelen % 4 != 0) +		padding_factor = 4 - (dir->namelen % 4); + +	dentry_length = sizeof(struct ext2_dirent) + +			dir->namelen + padding_factor; +	sizeof_void_space = dir->direntlen - dentry_length; +	if (sizeof_void_space == 0) +		return 0; + +	padding_factor = 0; +	if (strlen(filename) % 4 != 0) +		padding_factor = 4 - (strlen(filename) % 4); + +	new_entry_byte_reqd = strlen(filename) + +	    sizeof(struct ext2_dirent) + padding_factor; +	if (sizeof_void_space >= new_entry_byte_reqd) { +		dir->direntlen = dentry_length; +		return sizeof_void_space; +	} + +	return 0; +} + +void ext4fs_update_parent_dentry(char *filename, int *p_ino, int file_type) +{ +	unsigned int *zero_buffer = NULL; +	char *root_first_block_buffer = NULL; +	int direct_blk_idx; +	long int root_blknr; +	long int first_block_no_of_root = 0; +	long int previous_blknr = -1; +	int totalbytes = 0; +	short int padding_factor = 0; +	unsigned int new_entry_byte_reqd; +	unsigned int last_entry_dirlen; +	int sizeof_void_space = 0; +	int templength = 0; +	int inodeno; +	int status; +	struct ext_filesystem *fs = get_fs(); +	/* directory entry */ +	struct ext2_dirent *dir; +	char *ptr = NULL; +	char *temp_dir = NULL; + +	zero_buffer = zalloc(fs->blksz); +	if (!zero_buffer) { +		printf("No Memory\n"); +		return; +	} +	root_first_block_buffer = zalloc(fs->blksz); +	if (!root_first_block_buffer) { +		free(zero_buffer); +		printf("No Memory\n"); +		return; +	} +restart: + +	/* read the block no allocated to a file */ +	for (direct_blk_idx = 0; direct_blk_idx < INDIRECT_BLOCKS; +	     direct_blk_idx++) { +		root_blknr = read_allocated_block(g_parent_inode, +						  direct_blk_idx); +		if (root_blknr == 0) { +			first_block_no_of_root = previous_blknr; +			break; +		} +		previous_blknr = root_blknr; +	} + +	status = ext4fs_devread(first_block_no_of_root +				* fs->sect_perblk, +				0, fs->blksz, root_first_block_buffer); +	if (status == 0) +		goto fail; + +	if (ext4fs_log_journal(root_first_block_buffer, first_block_no_of_root)) +		goto fail; +	dir = (struct ext2_dirent *)root_first_block_buffer; +	ptr = (char *)dir; +	totalbytes = 0; +	while (dir->direntlen > 0) { +		/* +		 * blocksize-totalbytes because last directory length +		 * i.e. dir->direntlen is free availble space in the +		 * block that means  it is a last entry of directory +		 * entry +		 */ + +		/* traversing the each directory entry */ +		if (fs->blksz - totalbytes == dir->direntlen) { +			if (strlen(filename) % 4 != 0) +				padding_factor = 4 - (strlen(filename) % 4); + +			new_entry_byte_reqd = strlen(filename) + +			    sizeof(struct ext2_dirent) + padding_factor; +			padding_factor = 0; +			/* +			 * update last directory entry length to its +			 * length because we are creating new directory +			 * entry +			 */ +			if (dir->namelen % 4 != 0) +				padding_factor = 4 - (dir->namelen % 4); + +			last_entry_dirlen = dir->namelen + +			    sizeof(struct ext2_dirent) + padding_factor; +			if ((fs->blksz - totalbytes - last_entry_dirlen) < +				new_entry_byte_reqd) { +				printf("1st Block Full:Allocate new block\n"); + +				if (direct_blk_idx == INDIRECT_BLOCKS - 1) { +					printf("Directory exceeds limit\n"); +					goto fail; +				} +				g_parent_inode->b.blocks.dir_blocks +				    [direct_blk_idx] = ext4fs_get_new_blk_no(); +				if (g_parent_inode->b.blocks.dir_blocks +					[direct_blk_idx] == -1) { +					printf("no block left to assign\n"); +					goto fail; +				} +				put_ext4(((uint64_t) +					  (g_parent_inode->b. +					   blocks.dir_blocks[direct_blk_idx] * +					   fs->blksz)), zero_buffer, fs->blksz); +				g_parent_inode->size = +				    g_parent_inode->size + fs->blksz; +				g_parent_inode->blockcnt = +				    g_parent_inode->blockcnt + fs->sect_perblk; +				if (ext4fs_put_metadata +				    (root_first_block_buffer, +				     first_block_no_of_root)) +					goto fail; +				goto restart; +			} +			dir->direntlen = last_entry_dirlen; +			break; +		} + +		templength = dir->direntlen; +		totalbytes = totalbytes + templength; +		sizeof_void_space = check_void_in_dentry(dir, filename); +		if (sizeof_void_space) +			break; + +		dir = (struct ext2_dirent *)((char *)dir + templength); +		ptr = (char *)dir; +	} + +	/* make a pointer ready for creating next directory entry */ +	templength = dir->direntlen; +	totalbytes = totalbytes + templength; +	dir = (struct ext2_dirent *)((char *)dir + templength); +	ptr = (char *)dir; + +	/* get the next available inode number */ +	inodeno = ext4fs_get_new_inode_no(); +	if (inodeno == -1) { +		printf("no inode left to assign\n"); +		goto fail; +	} +	dir->inode = inodeno; +	if (sizeof_void_space) +		dir->direntlen = sizeof_void_space; +	else +		dir->direntlen = fs->blksz - totalbytes; + +	dir->namelen = strlen(filename); +	dir->filetype = FILETYPE_REG;	/* regular file */ +	temp_dir = (char *)dir; +	temp_dir = temp_dir + sizeof(struct ext2_dirent); +	memcpy(temp_dir, filename, strlen(filename)); + +	*p_ino = inodeno; + +	/* update or write  the 1st block of root inode */ +	if (ext4fs_put_metadata(root_first_block_buffer, +				first_block_no_of_root)) +		goto fail; + +fail: +	free(zero_buffer); +	free(root_first_block_buffer); +} + +static int search_dir(struct ext2_inode *parent_inode, char *dirname) +{ +	int status; +	int inodeno; +	int totalbytes; +	int templength; +	int direct_blk_idx; +	long int blknr; +	int found = 0; +	char *ptr = NULL; +	unsigned char *block_buffer = NULL; +	struct ext2_dirent *dir = NULL; +	struct ext2_dirent *previous_dir = NULL; +	struct ext_filesystem *fs = get_fs(); + +	/* read the block no allocated to a file */ +	for (direct_blk_idx = 0; direct_blk_idx < INDIRECT_BLOCKS; +		direct_blk_idx++) { +		blknr = read_allocated_block(parent_inode, direct_blk_idx); +		if (blknr == 0) +			goto fail; + +		/* read the blocks of parenet inode */ +		block_buffer = zalloc(fs->blksz); +		if (!block_buffer) +			goto fail; + +		status = ext4fs_devread(blknr * fs->sect_perblk, +					0, fs->blksz, (char *)block_buffer); +		if (status == 0) +			goto fail; + +		dir = (struct ext2_dirent *)block_buffer; +		ptr = (char *)dir; +		totalbytes = 0; +		while (dir->direntlen >= 0) { +			/* +			 * blocksize-totalbytes because last directory +			 * length i.e.,*dir->direntlen is free availble +			 * space in the block that means +			 * it is a last entry of directory entry +			 */ +			if (strlen(dirname) == dir->namelen) { +				if (strncmp(dirname, ptr + +					sizeof(struct ext2_dirent), +					dir->namelen) == 0) { +					previous_dir->direntlen += +							dir->direntlen; +					inodeno = dir->inode; +					dir->inode = 0; +					found = 1; +					break; +				} +			} + +			if (fs->blksz - totalbytes == dir->direntlen) +				break; + +			/* traversing the each directory entry */ +			templength = dir->direntlen; +			totalbytes = totalbytes + templength; +			previous_dir = dir; +			dir = (struct ext2_dirent *)((char *)dir + templength); +			ptr = (char *)dir; +		} + +		if (found == 1) { +			free(block_buffer); +			block_buffer = NULL; +			return inodeno; +		} + +		free(block_buffer); +		block_buffer = NULL; +	} + +fail: +	free(block_buffer); + +	return -1; +} + +static int find_dir_depth(char *dirname) +{ +	char *token = strtok(dirname, "/"); +	int count = 0; +	while (token != NULL) { +		token = strtok(NULL, "/"); +		count++; +	} +	return count + 1 + 1; +	/* +	 * for example  for string /home/temp +	 * depth=home(1)+temp(1)+1 extra for NULL; +	 * so count is 4; +	 */ +} + +static int parse_path(char **arr, char *dirname) +{ +	char *token = strtok(dirname, "/"); +	int i = 0; + +	/* add root */ +	arr[i] = zalloc(strlen("/") + 1); +	if (!arr[i]) +		return -ENOMEM; + +	arr[i++] = "/"; + +	/* add each path entry after root */ +	while (token != NULL) { +		arr[i] = zalloc(strlen(token) + 1); +		if (!arr[i]) +			return -ENOMEM; +		memcpy(arr[i++], token, strlen(token)); +		token = strtok(NULL, "/"); +	} +	arr[i] = NULL; + +	return 0; +} + +int ext4fs_iget(int inode_no, struct ext2_inode *inode) +{ +	if (ext4fs_read_inode(ext4fs_root, inode_no, inode) == 0) +		return -1; + +	return 0; +} + +/* + * Function: ext4fs_get_parent_inode_num + * Return Value: inode Number of the parent directory of  file/Directory to be + * created + * dirname : Input parmater, input path name of the file/directory to be created + * dname : Output parameter, to be filled with the name of the directory + * extracted from dirname + */ +int ext4fs_get_parent_inode_num(const char *dirname, char *dname, int flags) +{ +	int i; +	int depth = 0; +	int matched_inode_no; +	int result_inode_no = -1; +	char **ptr = NULL; +	char *depth_dirname = NULL; +	char *parse_dirname = NULL; +	struct ext2_inode *parent_inode = NULL; +	struct ext2_inode *first_inode = NULL; +	struct ext2_inode temp_inode; + +	if (*dirname != '/') { +		printf("Please supply Absolute path\n"); +		return -1; +	} + +	/* TODO: input validation make equivalent to linux */ +	depth_dirname = zalloc(strlen(dirname) + 1); +	if (!depth_dirname) +		return -ENOMEM; + +	memcpy(depth_dirname, dirname, strlen(dirname)); +	depth = find_dir_depth(depth_dirname); +	parse_dirname = zalloc(strlen(dirname) + 1); +	if (!parse_dirname) +		goto fail; +	memcpy(parse_dirname, dirname, strlen(dirname)); + +	/* allocate memory for each directory level */ +	ptr = zalloc((depth) * sizeof(char *)); +	if (!ptr) +		goto fail; +	if (parse_path(ptr, parse_dirname)) +		goto fail; +	parent_inode = zalloc(sizeof(struct ext2_inode)); +	if (!parent_inode) +		goto fail; +	first_inode = zalloc(sizeof(struct ext2_inode)); +	if (!first_inode) +		goto fail; +	memcpy(parent_inode, ext4fs_root->inode, sizeof(struct ext2_inode)); +	memcpy(first_inode, parent_inode, sizeof(struct ext2_inode)); +	if (flags & F_FILE) +		result_inode_no = EXT2_ROOT_INO; +	for (i = 1; i < depth; i++) { +		matched_inode_no = search_dir(parent_inode, ptr[i]); +		if (matched_inode_no == -1) { +			if (ptr[i + 1] == NULL && i == 1) { +				result_inode_no = EXT2_ROOT_INO; +				goto end; +			} else { +				if (ptr[i + 1] == NULL) +					break; +				printf("Invalid path\n"); +				result_inode_no = -1; +				goto fail; +			} +		} else { +			if (ptr[i + 1] != NULL) { +				memset(parent_inode, '\0', +				       sizeof(struct ext2_inode)); +				if (ext4fs_iget(matched_inode_no, +						parent_inode)) { +					result_inode_no = -1; +					goto fail; +				} +				result_inode_no = matched_inode_no; +			} else { +				break; +			} +		} +	} + +end: +	if (i == 1) +		matched_inode_no = search_dir(first_inode, ptr[i]); +	else +		matched_inode_no = search_dir(parent_inode, ptr[i]); + +	if (matched_inode_no != -1) { +		ext4fs_iget(matched_inode_no, &temp_inode); +		if (temp_inode.mode & S_IFDIR) { +			printf("It is a Directory\n"); +			result_inode_no = -1; +			goto fail; +		} +	} + +	if (strlen(ptr[i]) > 256) { +		result_inode_no = -1; +		goto fail; +	} +	memcpy(dname, ptr[i], strlen(ptr[i])); + +fail: +	free(depth_dirname); +	free(parse_dirname); +	free(ptr); +	free(parent_inode); +	free(first_inode); + +	return result_inode_no; +} + +static int check_filename(char *filename, unsigned int blknr) +{ +	unsigned int first_block_no_of_root; +	int totalbytes = 0; +	int templength = 0; +	int status, inodeno; +	int found = 0; +	char *root_first_block_buffer = NULL; +	char *root_first_block_addr = NULL; +	struct ext2_dirent *dir = NULL; +	struct ext2_dirent *previous_dir = NULL; +	char *ptr = NULL; +	struct ext_filesystem *fs = get_fs(); + +	/* get the first block of root */ +	first_block_no_of_root = blknr; +	root_first_block_buffer = zalloc(fs->blksz); +	if (!root_first_block_buffer) +		return -ENOMEM; +	root_first_block_addr = root_first_block_buffer; +	status = ext4fs_devread(first_block_no_of_root * +				fs->sect_perblk, 0, +				fs->blksz, root_first_block_buffer); +	if (status == 0) +		goto fail; + +	if (ext4fs_log_journal(root_first_block_buffer, first_block_no_of_root)) +		goto fail; +	dir = (struct ext2_dirent *)root_first_block_buffer; +	ptr = (char *)dir; +	totalbytes = 0; +	while (dir->direntlen >= 0) { +		/* +		 * blocksize-totalbytes because last +		 * directory length i.e., *dir->direntlen +		 * is free availble space in the block that +		 * means it is a last entry of directory entry +		 */ +		if (strlen(filename) == dir->namelen) { +			if (strncmp(filename, ptr + sizeof(struct ext2_dirent), +				dir->namelen) == 0) { +				printf("file found deleting\n"); +				previous_dir->direntlen += dir->direntlen; +				inodeno = dir->inode; +				dir->inode = 0; +				found = 1; +				break; +			} +		} + +		if (fs->blksz - totalbytes == dir->direntlen) +			break; + +		/* traversing the each directory entry */ +		templength = dir->direntlen; +		totalbytes = totalbytes + templength; +		previous_dir = dir; +		dir = (struct ext2_dirent *)((char *)dir + templength); +		ptr = (char *)dir; +	} + + +	if (found == 1) { +		if (ext4fs_put_metadata(root_first_block_addr, +					first_block_no_of_root)) +			goto fail; +		return inodeno; +	} +fail: +	free(root_first_block_buffer); + +	return -1; +} + +int ext4fs_filename_check(char *filename) +{ +	short direct_blk_idx = 0; +	long int blknr = -1; +	int inodeno = -1; + +	/* read the block no allocated to a file */ +	for (direct_blk_idx = 0; direct_blk_idx < INDIRECT_BLOCKS; +		direct_blk_idx++) { +		blknr = read_allocated_block(g_parent_inode, direct_blk_idx); +		if (blknr == 0) +			break; +		inodeno = check_filename(filename, blknr); +		if (inodeno != -1) +			return inodeno; +	} + +	return -1; +} + +long int ext4fs_get_new_blk_no(void) +{ +	short i; +	short status; +	int remainder; +	unsigned int bg_idx; +	static int prev_bg_bitmap_index = -1; +	unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group; +	struct ext_filesystem *fs = get_fs(); +	char *journal_buffer = zalloc(fs->blksz); +	char *zero_buffer = zalloc(fs->blksz); +	if (!journal_buffer || !zero_buffer) +		goto fail; +	struct ext2_block_group *gd = (struct ext2_block_group *)fs->gdtable; + +	if (fs->first_pass_bbmap == 0) { +		for (i = 0; i < fs->no_blkgrp; i++) { +			if (gd[i].free_blocks) { +				if (gd[i].bg_flags & EXT4_BG_BLOCK_UNINIT) { +					put_ext4(((uint64_t) (gd[i].block_id * +							      fs->blksz)), +						 zero_buffer, fs->blksz); +					gd[i].bg_flags = +					    gd[i]. +					    bg_flags & ~EXT4_BG_BLOCK_UNINIT; +					memcpy(fs->blk_bmaps[i], zero_buffer, +					       fs->blksz); +				} +				fs->curr_blkno = +				    _get_new_blk_no(fs->blk_bmaps[i]); +				if (fs->curr_blkno == -1) +					/* if block bitmap is completely fill */ +					continue; +				fs->curr_blkno = fs->curr_blkno + +						(i * fs->blksz * 8); +				fs->first_pass_bbmap++; +				gd[i].free_blocks--; +				fs->sb->free_blocks--; +				status = ext4fs_devread(gd[i].block_id * +							fs->sect_perblk, 0, +							fs->blksz, +							journal_buffer); +				if (status == 0) +					goto fail; +				if (ext4fs_log_journal(journal_buffer, +							gd[i].block_id)) +					goto fail; +				goto success; +			} else { +				debug("no space left on block group %d\n", i); +			} +		} + +		goto fail; +	} else { +restart: +		fs->curr_blkno++; +		/* get the blockbitmap index respective to blockno */ +		if (fs->blksz != 1024) { +			bg_idx = fs->curr_blkno / blk_per_grp; +		} else { +			bg_idx = fs->curr_blkno / blk_per_grp; +			remainder = fs->curr_blkno % blk_per_grp; +			if (!remainder) +				bg_idx--; +		} + +		/* +		 * To skip completely filled block group bitmaps +		 * Optimize the block allocation +		 */ +		if (bg_idx >= fs->no_blkgrp) +			goto fail; + +		if (gd[bg_idx].free_blocks == 0) { +			debug("block group %u is full. Skipping\n", bg_idx); +			fs->curr_blkno = fs->curr_blkno + blk_per_grp; +			fs->curr_blkno--; +			goto restart; +		} + +		if (gd[bg_idx].bg_flags & EXT4_BG_BLOCK_UNINIT) { +			memset(zero_buffer, '\0', fs->blksz); +			put_ext4(((uint64_t) (gd[bg_idx].block_id * fs->blksz)), +				 zero_buffer, fs->blksz); +			memcpy(fs->blk_bmaps[bg_idx], zero_buffer, fs->blksz); +			gd[bg_idx].bg_flags = gd[bg_idx].bg_flags & +						~EXT4_BG_BLOCK_UNINIT; +		} + +		if (ext4fs_set_block_bmap(fs->curr_blkno, fs->blk_bmaps[bg_idx], +				   bg_idx) != 0) { +			debug("going for restart for the block no %ld %u\n", +			      fs->curr_blkno, bg_idx); +			goto restart; +		} + +		/* journal backup */ +		if (prev_bg_bitmap_index != bg_idx) { +			memset(journal_buffer, '\0', fs->blksz); +			status = ext4fs_devread(gd[bg_idx].block_id +						* fs->sect_perblk, +						0, fs->blksz, journal_buffer); +			if (status == 0) +				goto fail; +			if (ext4fs_log_journal(journal_buffer, +						gd[bg_idx].block_id)) +				goto fail; + +			prev_bg_bitmap_index = bg_idx; +		} +		gd[bg_idx].free_blocks--; +		fs->sb->free_blocks--; +		goto success; +	} +success: +	free(journal_buffer); +	free(zero_buffer); + +	return fs->curr_blkno; +fail: +	free(journal_buffer); +	free(zero_buffer); + +	return -1; +} + +int ext4fs_get_new_inode_no(void) +{ +	short i; +	short status; +	unsigned int ibmap_idx; +	static int prev_inode_bitmap_index = -1; +	unsigned int inodes_per_grp = ext4fs_root->sblock.inodes_per_group; +	struct ext_filesystem *fs = get_fs(); +	char *journal_buffer = zalloc(fs->blksz); +	char *zero_buffer = zalloc(fs->blksz); +	if (!journal_buffer || !zero_buffer) +		goto fail; +	struct ext2_block_group *gd = (struct ext2_block_group *)fs->gdtable; + +	if (fs->first_pass_ibmap == 0) { +		for (i = 0; i < fs->no_blkgrp; i++) { +			if (gd[i].free_inodes) { +				if (gd[i].bg_itable_unused != gd[i].free_inodes) +					gd[i].bg_itable_unused = +						gd[i].free_inodes; +				if (gd[i].bg_flags & EXT4_BG_INODE_UNINIT) { +					put_ext4(((uint64_t) +						  (gd[i].inode_id * fs->blksz)), +						 zero_buffer, fs->blksz); +					gd[i].bg_flags = gd[i].bg_flags & +							~EXT4_BG_INODE_UNINIT; +					memcpy(fs->inode_bmaps[i], +					       zero_buffer, fs->blksz); +				} +				fs->curr_inode_no = +				    _get_new_inode_no(fs->inode_bmaps[i]); +				if (fs->curr_inode_no == -1) +					/* if block bitmap is completely fill */ +					continue; +				fs->curr_inode_no = fs->curr_inode_no + +							(i * inodes_per_grp); +				fs->first_pass_ibmap++; +				gd[i].free_inodes--; +				gd[i].bg_itable_unused--; +				fs->sb->free_inodes--; +				status = ext4fs_devread(gd[i].inode_id * +							fs->sect_perblk, 0, +							fs->blksz, +							journal_buffer); +				if (status == 0) +					goto fail; +				if (ext4fs_log_journal(journal_buffer, +							gd[i].inode_id)) +					goto fail; +				goto success; +			} else +				debug("no inode left on block group %d\n", i); +		} +		goto fail; +	} else { +restart: +		fs->curr_inode_no++; +		/* get the blockbitmap index respective to blockno */ +		ibmap_idx = fs->curr_inode_no / inodes_per_grp; +		if (gd[ibmap_idx].bg_flags & EXT4_BG_INODE_UNINIT) { +			memset(zero_buffer, '\0', fs->blksz); +			put_ext4(((uint64_t) (gd[ibmap_idx].inode_id * +					      fs->blksz)), zero_buffer, +				 fs->blksz); +			gd[ibmap_idx].bg_flags = +			    gd[ibmap_idx].bg_flags & ~EXT4_BG_INODE_UNINIT; +			memcpy(fs->inode_bmaps[ibmap_idx], zero_buffer, +				fs->blksz); +		} + +		if (ext4fs_set_inode_bmap(fs->curr_inode_no, +					  fs->inode_bmaps[ibmap_idx], +					  ibmap_idx) != 0) { +			debug("going for restart for the block no %d %u\n", +			      fs->curr_inode_no, ibmap_idx); +			goto restart; +		} + +		/* journal backup */ +		if (prev_inode_bitmap_index != ibmap_idx) { +			memset(journal_buffer, '\0', fs->blksz); +			status = ext4fs_devread(gd[ibmap_idx].inode_id +						* fs->sect_perblk, +						0, fs->blksz, journal_buffer); +			if (status == 0) +				goto fail; +			if (ext4fs_log_journal(journal_buffer, +						gd[ibmap_idx].inode_id)) +				goto fail; +			prev_inode_bitmap_index = ibmap_idx; +		} +		if (gd[ibmap_idx].bg_itable_unused != gd[ibmap_idx].free_inodes) +			gd[ibmap_idx].bg_itable_unused = +					gd[ibmap_idx].free_inodes; +		gd[ibmap_idx].free_inodes--; +		gd[ibmap_idx].bg_itable_unused--; +		fs->sb->free_inodes--; +		goto success; +	} + +success: +	free(journal_buffer); +	free(zero_buffer); + +	return fs->curr_inode_no; +fail: +	free(journal_buffer); +	free(zero_buffer); + +	return -1; + +} + + +static void alloc_single_indirect_block(struct ext2_inode *file_inode, +					unsigned int *total_remaining_blocks, +					unsigned int *no_blks_reqd) +{ +	short i; +	short status; +	long int actual_block_no; +	long int si_blockno; +	/* si :single indirect */ +	unsigned int *si_buffer = NULL; +	unsigned int *si_start_addr = NULL; +	struct ext_filesystem *fs = get_fs(); + +	if (*total_remaining_blocks != 0) { +		si_buffer = zalloc(fs->blksz); +		if (!si_buffer) { +			printf("No Memory\n"); +			return; +		} +		si_start_addr = si_buffer; +		si_blockno = ext4fs_get_new_blk_no(); +		if (si_blockno == -1) { +			printf("no block left to assign\n"); +			goto fail; +		} +		(*no_blks_reqd)++; +		debug("SIPB %ld: %u\n", si_blockno, *total_remaining_blocks); + +		status = ext4fs_devread(si_blockno * fs->sect_perblk, +					0, fs->blksz, (char *)si_buffer); +		memset(si_buffer, '\0', fs->blksz); +		if (status == 0) +			goto fail; + +		for (i = 0; i < (fs->blksz / sizeof(int)); i++) { +			actual_block_no = ext4fs_get_new_blk_no(); +			if (actual_block_no == -1) { +				printf("no block left to assign\n"); +				goto fail; +			} +			*si_buffer = actual_block_no; +			debug("SIAB %u: %u\n", *si_buffer, +				*total_remaining_blocks); + +			si_buffer++; +			(*total_remaining_blocks)--; +			if (*total_remaining_blocks == 0) +				break; +		} + +		/* write the block to disk */ +		put_ext4(((uint64_t) (si_blockno * fs->blksz)), +			 si_start_addr, fs->blksz); +		file_inode->b.blocks.indir_block = si_blockno; +	} +fail: +	free(si_start_addr); +} + +static void alloc_double_indirect_block(struct ext2_inode *file_inode, +					unsigned int *total_remaining_blocks, +					unsigned int *no_blks_reqd) +{ +	short i; +	short j; +	short status; +	long int actual_block_no; +	/* di:double indirect */ +	long int di_blockno_parent; +	long int di_blockno_child; +	unsigned int *di_parent_buffer = NULL; +	unsigned int *di_child_buff = NULL; +	unsigned int *di_block_start_addr = NULL; +	unsigned int *di_child_buff_start = NULL; +	struct ext_filesystem *fs = get_fs(); + +	if (*total_remaining_blocks != 0) { +		/* double indirect parent block connecting to inode */ +		di_blockno_parent = ext4fs_get_new_blk_no(); +		if (di_blockno_parent == -1) { +			printf("no block left to assign\n"); +			goto fail; +		} +		di_parent_buffer = zalloc(fs->blksz); +		if (!di_parent_buffer) +			goto fail; + +		di_block_start_addr = di_parent_buffer; +		(*no_blks_reqd)++; +		debug("DIPB %ld: %u\n", di_blockno_parent, +		      *total_remaining_blocks); + +		status = ext4fs_devread(di_blockno_parent * +					fs->sect_perblk, 0, +					fs->blksz, (char *)di_parent_buffer); +		memset(di_parent_buffer, '\0', fs->blksz); + +		/* +		 * start:for each double indirect parent +		 * block create one more block +		 */ +		for (i = 0; i < (fs->blksz / sizeof(int)); i++) { +			di_blockno_child = ext4fs_get_new_blk_no(); +			if (di_blockno_child == -1) { +				printf("no block left to assign\n"); +				goto fail; +			} +			di_child_buff = zalloc(fs->blksz); +			if (!di_child_buff) +				goto fail; + +			di_child_buff_start = di_child_buff; +			*di_parent_buffer = di_blockno_child; +			di_parent_buffer++; +			(*no_blks_reqd)++; +			debug("DICB %ld: %u\n", di_blockno_child, +			      *total_remaining_blocks); + +			status = ext4fs_devread(di_blockno_child * +						fs->sect_perblk, 0, +						fs->blksz, +						(char *)di_child_buff); +			memset(di_child_buff, '\0', fs->blksz); +			/* filling of actual datablocks for each child */ +			for (j = 0; j < (fs->blksz / sizeof(int)); j++) { +				actual_block_no = ext4fs_get_new_blk_no(); +				if (actual_block_no == -1) { +					printf("no block left to assign\n"); +					goto fail; +				} +				*di_child_buff = actual_block_no; +				debug("DIAB %ld: %u\n", actual_block_no, +				      *total_remaining_blocks); + +				di_child_buff++; +				(*total_remaining_blocks)--; +				if (*total_remaining_blocks == 0) +					break; +			} +			/* write the block  table */ +			put_ext4(((uint64_t) (di_blockno_child * fs->blksz)), +				 di_child_buff_start, fs->blksz); +			free(di_child_buff_start); +			di_child_buff_start = NULL; + +			if (*total_remaining_blocks == 0) +				break; +		} +		put_ext4(((uint64_t) (di_blockno_parent * fs->blksz)), +			 di_block_start_addr, fs->blksz); +		file_inode->b.blocks.double_indir_block = di_blockno_parent; +	} +fail: +	free(di_block_start_addr); +} + +static void alloc_triple_indirect_block(struct ext2_inode *file_inode, +					unsigned int *total_remaining_blocks, +					unsigned int *no_blks_reqd) +{ +	short i; +	short j; +	short k; +	long int actual_block_no; +	/* ti: Triple Indirect */ +	long int ti_gp_blockno; +	long int ti_parent_blockno; +	long int ti_child_blockno; +	unsigned int *ti_gp_buff = NULL; +	unsigned int *ti_parent_buff = NULL; +	unsigned int *ti_child_buff = NULL; +	unsigned int *ti_gp_buff_start_addr = NULL; +	unsigned int *ti_pbuff_start_addr = NULL; +	unsigned int *ti_cbuff_start_addr = NULL; +	struct ext_filesystem *fs = get_fs(); +	if (*total_remaining_blocks != 0) { +		/* triple indirect grand parent block connecting to inode */ +		ti_gp_blockno = ext4fs_get_new_blk_no(); +		if (ti_gp_blockno == -1) { +			printf("no block left to assign\n"); +			goto fail; +		} +		ti_gp_buff = zalloc(fs->blksz); +		if (!ti_gp_buff) +			goto fail; + +		ti_gp_buff_start_addr = ti_gp_buff; +		(*no_blks_reqd)++; +		debug("TIGPB %ld: %u\n", ti_gp_blockno, +		      *total_remaining_blocks); + +		/* for each 4 byte grand parent entry create one more block */ +		for (i = 0; i < (fs->blksz / sizeof(int)); i++) { +			ti_parent_blockno = ext4fs_get_new_blk_no(); +			if (ti_parent_blockno == -1) { +				printf("no block left to assign\n"); +				goto fail; +			} +			ti_parent_buff = zalloc(fs->blksz); +			if (!ti_parent_buff) +				goto fail; + +			ti_pbuff_start_addr = ti_parent_buff; +			*ti_gp_buff = ti_parent_blockno; +			ti_gp_buff++; +			(*no_blks_reqd)++; +			debug("TIPB %ld: %u\n", ti_parent_blockno, +			      *total_remaining_blocks); + +			/* for each 4 byte entry parent create one more block */ +			for (j = 0; j < (fs->blksz / sizeof(int)); j++) { +				ti_child_blockno = ext4fs_get_new_blk_no(); +				if (ti_child_blockno == -1) { +					printf("no block left assign\n"); +					goto fail; +				} +				ti_child_buff = zalloc(fs->blksz); +				if (!ti_child_buff) +					goto fail; + +				ti_cbuff_start_addr = ti_child_buff; +				*ti_parent_buff = ti_child_blockno; +				ti_parent_buff++; +				(*no_blks_reqd)++; +				debug("TICB %ld: %u\n", ti_parent_blockno, +				      *total_remaining_blocks); + +				/* fill actual datablocks for each child */ +				for (k = 0; k < (fs->blksz / sizeof(int)); +					k++) { +					actual_block_no = +					    ext4fs_get_new_blk_no(); +					if (actual_block_no == -1) { +						printf("no block left\n"); +						goto fail; +					} +					*ti_child_buff = actual_block_no; +					debug("TIAB %ld: %u\n", actual_block_no, +					      *total_remaining_blocks); + +					ti_child_buff++; +					(*total_remaining_blocks)--; +					if (*total_remaining_blocks == 0) +						break; +				} +				/* write the child block */ +				put_ext4(((uint64_t) (ti_child_blockno * +						      fs->blksz)), +					 ti_cbuff_start_addr, fs->blksz); +				free(ti_cbuff_start_addr); + +				if (*total_remaining_blocks == 0) +					break; +			} +			/* write the parent block */ +			put_ext4(((uint64_t) (ti_parent_blockno * fs->blksz)), +				 ti_pbuff_start_addr, fs->blksz); +			free(ti_pbuff_start_addr); + +			if (*total_remaining_blocks == 0) +				break; +		} +		/* write the grand parent block */ +		put_ext4(((uint64_t) (ti_gp_blockno * fs->blksz)), +			 ti_gp_buff_start_addr, fs->blksz); +		file_inode->b.blocks.triple_indir_block = ti_gp_blockno; +	} +fail: +	free(ti_gp_buff_start_addr); +} + +void ext4fs_allocate_blocks(struct ext2_inode *file_inode, +				unsigned int total_remaining_blocks, +				unsigned int *total_no_of_block) +{ +	short i; +	long int direct_blockno; +	unsigned int no_blks_reqd = 0; + +	/* allocation of direct blocks */ +	for (i = 0; i < INDIRECT_BLOCKS; i++) { +		direct_blockno = ext4fs_get_new_blk_no(); +		if (direct_blockno == -1) { +			printf("no block left to assign\n"); +			return; +		} +		file_inode->b.blocks.dir_blocks[i] = direct_blockno; +		debug("DB %ld: %u\n", direct_blockno, total_remaining_blocks); + +		total_remaining_blocks--; +		if (total_remaining_blocks == 0) +			break; +	} + +	alloc_single_indirect_block(file_inode, &total_remaining_blocks, +				    &no_blks_reqd); +	alloc_double_indirect_block(file_inode, &total_remaining_blocks, +				    &no_blks_reqd); +	alloc_triple_indirect_block(file_inode, &total_remaining_blocks, +				    &no_blks_reqd); +	*total_no_of_block += no_blks_reqd; +} + +#endif +  static struct ext4_extent_header *ext4fs_get_extent_block  	(struct ext2_data *data, char *buf,  		struct ext4_extent_header *ext_block, diff --git a/fs/ext4/ext4_common.h b/fs/ext4/ext4_common.h index 8e8bcbc0f..801b8b848 100644 --- a/fs/ext4/ext4_common.h +++ b/fs/ext4/ext4_common.h @@ -14,6 +14,8 @@   * GRUB  --  GRand Unified Bootloader   * Copyright (C) 2003, 2004  Free Software Foundation, Inc.   * + * ext4write : Based on generic ext4 protocol. + *   * 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 @@ -35,6 +37,10 @@  #include <ext4fs.h>  #include <malloc.h>  #include <asm/errno.h> +#if defined(CONFIG_CMD_EXT4_WRITE) +#include "ext4_journal.h" +#include "crc16.h" +#endif  #define YES		1  #define NO		0 @@ -60,4 +66,23 @@ int ext4fs_find_file(const char *path, struct ext2fs_node *rootnode,  			struct ext2fs_node **foundnode, int expecttype);  int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name,  			struct ext2fs_node **fnode, int *ftype); + +#if defined(CONFIG_CMD_EXT4_WRITE) +uint32_t ext4fs_div_roundup(uint32_t size, uint32_t n); +int ext4fs_checksum_update(unsigned int i); +int ext4fs_get_parent_inode_num(const char *dirname, char *dname, int flags); +void ext4fs_update_parent_dentry(char *filename, int *p_ino, int file_type); +long int ext4fs_get_new_blk_no(void); +int ext4fs_get_new_inode_no(void); +void ext4fs_reset_block_bmap(long int blockno, unsigned char *buffer, +					int index); +int ext4fs_set_block_bmap(long int blockno, unsigned char *buffer, int index); +int ext4fs_set_inode_bmap(int inode_no, unsigned char *buffer, int index); +void ext4fs_reset_inode_bmap(int inode_no, unsigned char *buffer, int index); +int ext4fs_iget(int inode_no, struct ext2_inode *inode); +void ext4fs_allocate_blocks(struct ext2_inode *file_inode, +				unsigned int total_remaining_blocks, +				unsigned int *total_no_of_block); +void put_ext4(uint64_t off, void *buf, uint32_t size); +#endif  #endif diff --git a/fs/ext4/ext4_journal.c b/fs/ext4/ext4_journal.c new file mode 100644 index 000000000..8a252d66c --- /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"); +} diff --git a/fs/ext4/ext4_journal.h b/fs/ext4/ext4_journal.h new file mode 100644 index 000000000..acc1c5163 --- /dev/null +++ b/fs/ext4/ext4_journal.h @@ -0,0 +1,141 @@ +/* + * (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. + */ + +#ifndef __EXT4_JRNL__ +#define __EXT4_JRNL__ + +#define EXT2_JOURNAL_INO		8	/* Journal inode */ +#define EXT2_JOURNAL_SUPERBLOCK	0	/* Journal  Superblock number */ + +#define JBD2_FEATURE_COMPAT_CHECKSUM	0x00000001 +#define EXT3_JOURNAL_MAGIC_NUMBER	0xc03b3998U +#define TRANSACTION_RUNNING		1 +#define TRANSACTION_COMPLETE		0 +#define EXT3_FEATURE_INCOMPAT_RECOVER	0x0004	/* Needs recovery */ +#define EXT3_JOURNAL_DESCRIPTOR_BLOCK	1 +#define EXT3_JOURNAL_COMMIT_BLOCK	2 +#define EXT3_JOURNAL_SUPERBLOCK_V1	3 +#define EXT3_JOURNAL_SUPERBLOCK_V2	4 +#define EXT3_JOURNAL_REVOKE_BLOCK	5 +#define EXT3_JOURNAL_FLAG_ESCAPE	1 +#define EXT3_JOURNAL_FLAG_SAME_UUID	2 +#define EXT3_JOURNAL_FLAG_DELETED	4 +#define EXT3_JOURNAL_FLAG_LAST_TAG	8 + +/* Maximum entries in 1 journal transaction */ +#define MAX_JOURNAL_ENTRIES 100 +struct journal_log { +	char *buf; +	int blknr; +}; + +struct dirty_blocks { +	char *buf; +	int blknr; +}; + +/* Standard header for all descriptor blocks: */ +struct journal_header_t { +	__u32 h_magic; +	__u32 h_blocktype; +	__u32 h_sequence; +}; + +/* The journal superblock.  All fields are in big-endian byte order. */ +struct journal_superblock_t { +	/* 0x0000 */ +	struct journal_header_t s_header; + +	/* Static information describing the journal */ +	__u32 s_blocksize;	/* journal device blocksize */ +	__u32 s_maxlen;		/* total blocks in journal file */ +	__u32 s_first;		/* first block of log information */ + +	/* Dynamic information describing the current state of the log */ +	__u32 s_sequence;	/* first commit ID expected in log */ +	__u32 s_start;		/* blocknr of start of log */ + +	/* Error value, as set by journal_abort(). */ +	__s32 s_errno; + +	/* Remaining fields are only valid in a version-2 superblock */ +	__u32 s_feature_compat;	/* compatible feature set */ +	__u32 s_feature_incompat;	/* incompatible feature set */ +	__u32 s_feature_ro_compat;	/* readonly-compatible feature set */ +	/* 0x0030 */ +	__u8 s_uuid[16];	/* 128-bit uuid for journal */ + +	/* 0x0040 */ +	__u32 s_nr_users;	/* Nr of filesystems sharing log */ + +	__u32 s_dynsuper;	/* Blocknr of dynamic superblock copy */ + +	/* 0x0048 */ +	__u32 s_max_transaction;	/* Limit of journal blocks per trans. */ +	__u32 s_max_trans_data;	/* Limit of data blocks per trans. */ + +	/* 0x0050 */ +	__u32 s_padding[44]; + +	/* 0x0100 */ +	__u8 s_users[16 * 48];	/* ids of all fs'es sharing the log */ +	/* 0x0400 */ +} ; + +struct ext3_journal_block_tag { +	uint32_t block; +	uint32_t flags; +}; + +struct journal_revoke_header_t { +	struct journal_header_t r_header; +	int r_count;		/* Count of bytes used in the block */ +}; + +struct revoke_blk_list { +	char *content;		/* revoke block itself */ +	struct revoke_blk_list *next; +}; + +extern struct ext2_data *ext4fs_root; + +int ext4fs_init_journal(void); +int ext4fs_log_gdt(char *gd_table); +int ext4fs_check_journal_state(int recovery_flag); +int ext4fs_log_journal(char *journal_buffer, long int blknr); +int ext4fs_put_metadata(char *metadata_buffer, long int blknr); +void ext4fs_update_journal(void); +void ext4fs_dump_metadata(void); +void ext4fs_push_revoke_blk(char *buffer); +void ext4fs_free_journal(void); +void ext4fs_free_revoke_blks(void); +#endif diff --git a/fs/ext4/ext4fs.c b/fs/ext4/ext4fs.c index 1287bf0e1..114c2a214 100644 --- a/fs/ext4/ext4fs.c +++ b/fs/ext4/ext4fs.c @@ -16,6 +16,8 @@   * GRUB  --  GRand Unified Bootloader   * Copyright (C) 2003, 2004  Free Software Foundation, Inc.   * + * ext4write : Based on generic ext4 protocol. + *   * 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 @@ -226,3 +228,962 @@ int ext4fs_read(char *buf, unsigned len)  	return ext4fs_read_file(ext4fs_file, 0, len, buf);  } + +#if defined(CONFIG_CMD_EXT4_WRITE) +static void ext4fs_update(void) +{ +	short i; +	ext4fs_update_journal(); +	struct ext_filesystem *fs = get_fs(); + +	/* update  super block */ +	put_ext4((uint64_t)(SUPERBLOCK_SIZE), +		 (struct ext2_sblock *)fs->sb, (uint32_t)SUPERBLOCK_SIZE); + +	/* update block groups */ +	for (i = 0; i < fs->no_blkgrp; i++) { +		fs->gd[i].bg_checksum = ext4fs_checksum_update(i); +		put_ext4((uint64_t)(fs->gd[i].block_id * fs->blksz), +			 fs->blk_bmaps[i], fs->blksz); +	} + +	/* update inode table groups */ +	for (i = 0; i < fs->no_blkgrp; i++) { +		put_ext4((uint64_t) (fs->gd[i].inode_id * fs->blksz), +			 fs->inode_bmaps[i], fs->blksz); +	} + +	/* update the block group descriptor table */ +	put_ext4((uint64_t)(fs->gdtable_blkno * fs->blksz), +		 (struct ext2_block_group *)fs->gdtable, +		 (fs->blksz * fs->no_blk_pergdt)); + +	ext4fs_dump_metadata(); + +	gindex = 0; +	gd_index = 0; +} + +int ext4fs_get_bgdtable(void) +{ +	int status; +	int grp_desc_size; +	struct ext_filesystem *fs = get_fs(); +	grp_desc_size = sizeof(struct ext2_block_group); +	fs->no_blk_pergdt = (fs->no_blkgrp * grp_desc_size) / fs->blksz; +	if ((fs->no_blkgrp * grp_desc_size) % fs->blksz) +		fs->no_blk_pergdt++; + +	/* allocate memory for gdtable */ +	fs->gdtable = zalloc(fs->blksz * fs->no_blk_pergdt); +	if (!fs->gdtable) +		return -ENOMEM; +	/* read the group descriptor table */ +	status = ext4fs_devread(fs->gdtable_blkno * fs->sect_perblk, 0, +				fs->blksz * fs->no_blk_pergdt, fs->gdtable); +	if (status == 0) +		goto fail; + +	if (ext4fs_log_gdt(fs->gdtable)) { +		printf("Error in ext4fs_log_gdt\n"); +		return -1; +	} + +	return 0; +fail: +	free(fs->gdtable); +	fs->gdtable = NULL; + +	return -1; +} + +static void delete_single_indirect_block(struct ext2_inode *inode) +{ +	struct ext2_block_group *gd = NULL; +	static int prev_bg_bmap_idx = -1; +	long int blknr; +	int remainder; +	int bg_idx; +	int status; +	unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group; +	struct ext_filesystem *fs = get_fs(); +	char *journal_buffer = zalloc(fs->blksz); +	if (!journal_buffer) { +		printf("No memory\n"); +		return; +	} +	/* get  block group descriptor table */ +	gd = (struct ext2_block_group *)fs->gdtable; + +	/* deleting the single indirect block associated with inode */ +	if (inode->b.blocks.indir_block != 0) { +		debug("SIPB releasing %u\n", inode->b.blocks.indir_block); +		blknr = inode->b.blocks.indir_block; +		if (fs->blksz != 1024) { +			bg_idx = blknr / blk_per_grp; +		} else { +			bg_idx = blknr / blk_per_grp; +			remainder = blknr % blk_per_grp; +			if (!remainder) +				bg_idx--; +		} +		ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx); +		gd[bg_idx].free_blocks++; +		fs->sb->free_blocks++; +		/* journal backup */ +		if (prev_bg_bmap_idx != bg_idx) { +			status = +			    ext4fs_devread(gd[bg_idx].block_id * +					   fs->sect_perblk, 0, fs->blksz, +					   journal_buffer); +			if (status == 0) +				goto fail; +			if (ext4fs_log_journal +			    (journal_buffer, gd[bg_idx].block_id)) +				goto fail; +			prev_bg_bmap_idx = bg_idx; +		} +	} +fail: +	free(journal_buffer); +} + +static void delete_double_indirect_block(struct ext2_inode *inode) +{ +	int i; +	short status; +	static int prev_bg_bmap_idx = -1; +	long int blknr; +	int remainder; +	int bg_idx; +	unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group; +	unsigned int *di_buffer = NULL; +	unsigned int *DIB_start_addr = NULL; +	struct ext2_block_group *gd = NULL; +	struct ext_filesystem *fs = get_fs(); +	char *journal_buffer = zalloc(fs->blksz); +	if (!journal_buffer) { +		printf("No memory\n"); +		return; +	} +	/* get the block group descriptor table */ +	gd = (struct ext2_block_group *)fs->gdtable; + +	if (inode->b.blocks.double_indir_block != 0) { +		di_buffer = zalloc(fs->blksz); +		if (!di_buffer) { +			printf("No memory\n"); +			return; +		} +		DIB_start_addr = (unsigned int *)di_buffer; +		blknr = inode->b.blocks.double_indir_block; +		status = ext4fs_devread(blknr * fs->sect_perblk, 0, fs->blksz, +					(char *)di_buffer); +		for (i = 0; i < fs->blksz / sizeof(int); i++) { +			if (*di_buffer == 0) +				break; + +			debug("DICB releasing %u\n", *di_buffer); +			if (fs->blksz != 1024) { +				bg_idx = (*di_buffer) / blk_per_grp; +			} else { +				bg_idx = (*di_buffer) / blk_per_grp; +				remainder = (*di_buffer) % blk_per_grp; +				if (!remainder) +					bg_idx--; +			} +			ext4fs_reset_block_bmap(*di_buffer, +					fs->blk_bmaps[bg_idx], bg_idx); +			di_buffer++; +			gd[bg_idx].free_blocks++; +			fs->sb->free_blocks++; +			/* journal backup */ +			if (prev_bg_bmap_idx != bg_idx) { +				status = ext4fs_devread(gd[bg_idx].block_id +							* fs->sect_perblk, 0, +							fs->blksz, +							journal_buffer); +				if (status == 0) +					goto fail; + +				if (ext4fs_log_journal(journal_buffer, +							gd[bg_idx].block_id)) +					goto fail; +				prev_bg_bmap_idx = bg_idx; +			} +		} + +		/* removing the parent double indirect block */ +		blknr = inode->b.blocks.double_indir_block; +		if (fs->blksz != 1024) { +			bg_idx = blknr / blk_per_grp; +		} else { +			bg_idx = blknr / blk_per_grp; +			remainder = blknr % blk_per_grp; +			if (!remainder) +				bg_idx--; +		} +		ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx); +		gd[bg_idx].free_blocks++; +		fs->sb->free_blocks++; +		/* journal backup */ +		if (prev_bg_bmap_idx != bg_idx) { +			memset(journal_buffer, '\0', fs->blksz); +			status = ext4fs_devread(gd[bg_idx].block_id * +						fs->sect_perblk, 0, fs->blksz, +						journal_buffer); +			if (status == 0) +				goto fail; + +			if (ext4fs_log_journal(journal_buffer, +						gd[bg_idx].block_id)) +				goto fail; +			prev_bg_bmap_idx = bg_idx; +		} +		debug("DIPB releasing %ld\n", blknr); +	} +fail: +	free(DIB_start_addr); +	free(journal_buffer); +} + +static void delete_triple_indirect_block(struct ext2_inode *inode) +{ +	int i, j; +	short status; +	static int prev_bg_bmap_idx = -1; +	long int blknr; +	int remainder; +	int bg_idx; +	unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group; +	unsigned int *tigp_buffer = NULL; +	unsigned int *tib_start_addr = NULL; +	unsigned int *tip_buffer = NULL; +	unsigned int *tipb_start_addr = NULL; +	struct ext2_block_group *gd = NULL; +	struct ext_filesystem *fs = get_fs(); +	char *journal_buffer = zalloc(fs->blksz); +	if (!journal_buffer) { +		printf("No memory\n"); +		return; +	} +	/* get block group descriptor table */ +	gd = (struct ext2_block_group *)fs->gdtable; + +	if (inode->b.blocks.triple_indir_block != 0) { +		tigp_buffer = zalloc(fs->blksz); +		if (!tigp_buffer) { +			printf("No memory\n"); +			return; +		} +		tib_start_addr = (unsigned int *)tigp_buffer; +		blknr = inode->b.blocks.triple_indir_block; +		status = ext4fs_devread(blknr * fs->sect_perblk, 0, fs->blksz, +					(char *)tigp_buffer); +		for (i = 0; i < fs->blksz / sizeof(int); i++) { +			if (*tigp_buffer == 0) +				break; +			debug("tigp buffer releasing %u\n", *tigp_buffer); + +			tip_buffer = zalloc(fs->blksz); +			if (!tip_buffer) +				goto fail; +			tipb_start_addr = (unsigned int *)tip_buffer; +			status = ext4fs_devread((*tigp_buffer) * +						fs->sect_perblk, 0, fs->blksz, +						(char *)tip_buffer); +			for (j = 0; j < fs->blksz / sizeof(int); j++) { +				if (*tip_buffer == 0) +					break; +				if (fs->blksz != 1024) { +					bg_idx = (*tip_buffer) / blk_per_grp; +				} else { +					bg_idx = (*tip_buffer) / blk_per_grp; + +					remainder = (*tip_buffer) % blk_per_grp; +					if (!remainder) +						bg_idx--; +				} + +				ext4fs_reset_block_bmap(*tip_buffer, +							fs->blk_bmaps[bg_idx], +							bg_idx); + +				tip_buffer++; +				gd[bg_idx].free_blocks++; +				fs->sb->free_blocks++; +				/* journal backup */ +				if (prev_bg_bmap_idx != bg_idx) { +					status = +					    ext4fs_devread(gd[bg_idx].block_id * +							   fs->sect_perblk, 0, +							   fs->blksz, +							   journal_buffer); +					if (status == 0) +						goto fail; + +					if (ext4fs_log_journal(journal_buffer, +							       gd[bg_idx]. +							       block_id)) +						goto fail; +					prev_bg_bmap_idx = bg_idx; +				} +			} +			free(tipb_start_addr); +			tipb_start_addr = NULL; + +			/* +			 * removing the grand parent blocks +			 * which is connected to inode +			 */ +			if (fs->blksz != 1024) { +				bg_idx = (*tigp_buffer) / blk_per_grp; +			} else { +				bg_idx = (*tigp_buffer) / blk_per_grp; + +				remainder = (*tigp_buffer) % blk_per_grp; +				if (!remainder) +					bg_idx--; +			} +			ext4fs_reset_block_bmap(*tigp_buffer, +						fs->blk_bmaps[bg_idx], bg_idx); + +			tigp_buffer++; +			gd[bg_idx].free_blocks++; +			fs->sb->free_blocks++; +			/* journal backup */ +			if (prev_bg_bmap_idx != bg_idx) { +				memset(journal_buffer, '\0', fs->blksz); +				status = +				    ext4fs_devread(gd[bg_idx].block_id * +						   fs->sect_perblk, 0, +						   fs->blksz, journal_buffer); +				if (status == 0) +					goto fail; + +				if (ext4fs_log_journal(journal_buffer, +							gd[bg_idx].block_id)) +					goto fail; +				prev_bg_bmap_idx = bg_idx; +			} +		} + +		/* removing the grand parent triple indirect block */ +		blknr = inode->b.blocks.triple_indir_block; +		if (fs->blksz != 1024) { +			bg_idx = blknr / blk_per_grp; +		} else { +			bg_idx = blknr / blk_per_grp; +			remainder = blknr % blk_per_grp; +			if (!remainder) +				bg_idx--; +		} +		ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx); +		gd[bg_idx].free_blocks++; +		fs->sb->free_blocks++; +		/* journal backup */ +		if (prev_bg_bmap_idx != bg_idx) { +			memset(journal_buffer, '\0', fs->blksz); +			status = ext4fs_devread(gd[bg_idx].block_id * +						fs->sect_perblk, 0, fs->blksz, +						journal_buffer); +			if (status == 0) +				goto fail; + +			if (ext4fs_log_journal(journal_buffer, +						gd[bg_idx].block_id)) +				goto fail; +			prev_bg_bmap_idx = bg_idx; +		} +		debug("tigp buffer itself releasing %ld\n", blknr); +	} +fail: +	free(tib_start_addr); +	free(tipb_start_addr); +	free(journal_buffer); +} + +static int ext4fs_delete_file(int inodeno) +{ +	struct ext2_inode inode; +	short status; +	int i; +	int remainder; +	long int blknr; +	int bg_idx; +	int ibmap_idx; +	char *read_buffer = NULL; +	char *start_block_address = NULL; +	unsigned int no_blocks; + +	static int prev_bg_bmap_idx = -1; +	unsigned int inodes_per_block; +	long int blkno; +	unsigned int blkoff; +	unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group; +	unsigned int inode_per_grp = ext4fs_root->sblock.inodes_per_group; +	struct ext2_inode *inode_buffer = NULL; +	struct ext2_block_group *gd = NULL; +	struct ext_filesystem *fs = get_fs(); +	char *journal_buffer = zalloc(fs->blksz); +	if (!journal_buffer) +		return -ENOMEM; +	/* get the block group descriptor table */ +	gd = (struct ext2_block_group *)fs->gdtable; +	status = ext4fs_read_inode(ext4fs_root, inodeno, &inode); +	if (status == 0) +		goto fail; + +	/* read the block no allocated to a file */ +	no_blocks = inode.size / fs->blksz; +	if (inode.size % fs->blksz) +		no_blocks++; + +	if (le32_to_cpu(inode.flags) & EXT4_EXTENTS_FL) { +		struct ext2fs_node *node_inode = +		    zalloc(sizeof(struct ext2fs_node)); +		if (!node_inode) +			goto fail; +		node_inode->data = ext4fs_root; +		node_inode->ino = inodeno; +		node_inode->inode_read = 0; +		memcpy(&(node_inode->inode), &inode, sizeof(struct ext2_inode)); + +		for (i = 0; i < no_blocks; i++) { +			blknr = read_allocated_block(&(node_inode->inode), i); +			if (fs->blksz != 1024) { +				bg_idx = blknr / blk_per_grp; +			} else { +				bg_idx = blknr / blk_per_grp; +				remainder = blknr % blk_per_grp; +				if (!remainder) +					bg_idx--; +			} +			ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], +						bg_idx); +			debug("EXT4_EXTENTS Block releasing %ld: %d\n", +			      blknr, bg_idx); + +			gd[bg_idx].free_blocks++; +			fs->sb->free_blocks++; + +			/* journal backup */ +			if (prev_bg_bmap_idx != bg_idx) { +				status = +				    ext4fs_devread(gd[bg_idx].block_id * +						   fs->sect_perblk, 0, +						   fs->blksz, journal_buffer); +				if (status == 0) +					goto fail; +				if (ext4fs_log_journal(journal_buffer, +							gd[bg_idx].block_id)) +					goto fail; +				prev_bg_bmap_idx = bg_idx; +			} +		} +		if (node_inode) { +			free(node_inode); +			node_inode = NULL; +		} +	} else { + +		delete_single_indirect_block(&inode); +		delete_double_indirect_block(&inode); +		delete_triple_indirect_block(&inode); + +		/* read the block no allocated to a file */ +		no_blocks = inode.size / fs->blksz; +		if (inode.size % fs->blksz) +			no_blocks++; +		for (i = 0; i < no_blocks; i++) { +			blknr = read_allocated_block(&inode, i); +			if (fs->blksz != 1024) { +				bg_idx = blknr / blk_per_grp; +			} else { +				bg_idx = blknr / blk_per_grp; +				remainder = blknr % blk_per_grp; +				if (!remainder) +					bg_idx--; +			} +			ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], +						bg_idx); +			debug("ActualB releasing %ld: %d\n", blknr, bg_idx); + +			gd[bg_idx].free_blocks++; +			fs->sb->free_blocks++; +			/* journal backup */ +			if (prev_bg_bmap_idx != bg_idx) { +				memset(journal_buffer, '\0', fs->blksz); +				status = ext4fs_devread(gd[bg_idx].block_id +							* fs->sect_perblk, +							0, fs->blksz, +							journal_buffer); +				if (status == 0) +					goto fail; +				if (ext4fs_log_journal(journal_buffer, +						gd[bg_idx].block_id)) +					goto fail; +				prev_bg_bmap_idx = bg_idx; +			} +		} +	} + +	/* from the inode no to blockno */ +	inodes_per_block = fs->blksz / fs->inodesz; +	ibmap_idx = inodeno / inode_per_grp; + +	/* get the block no */ +	inodeno--; +	blkno = __le32_to_cpu(gd[ibmap_idx].inode_table_id) + +		(inodeno % __le32_to_cpu(inode_per_grp)) / inodes_per_block; + +	/* get the offset of the inode */ +	blkoff = ((inodeno) % inodes_per_block) * fs->inodesz; + +	/* read the block no containing the inode */ +	read_buffer = zalloc(fs->blksz); +	if (!read_buffer) +		goto fail; +	start_block_address = read_buffer; +	status = ext4fs_devread(blkno * fs->sect_perblk, +				0, fs->blksz, read_buffer); +	if (status == 0) +		goto fail; + +	if (ext4fs_log_journal(read_buffer, blkno)) +		goto fail; + +	read_buffer = read_buffer + blkoff; +	inode_buffer = (struct ext2_inode *)read_buffer; +	memset(inode_buffer, '\0', sizeof(struct ext2_inode)); + +	/* write the inode to original position in inode table */ +	if (ext4fs_put_metadata(start_block_address, blkno)) +		goto fail; + +	/* update the respective inode bitmaps */ +	inodeno++; +	ext4fs_reset_inode_bmap(inodeno, fs->inode_bmaps[ibmap_idx], ibmap_idx); +	gd[ibmap_idx].free_inodes++; +	fs->sb->free_inodes++; +	/* journal backup */ +	memset(journal_buffer, '\0', fs->blksz); +	status = ext4fs_devread(gd[ibmap_idx].inode_id * +				fs->sect_perblk, 0, fs->blksz, journal_buffer); +	if (status == 0) +		goto fail; +	if (ext4fs_log_journal(journal_buffer, gd[ibmap_idx].inode_id)) +		goto fail; + +	ext4fs_update(); +	ext4fs_deinit(); + +	if (ext4fs_init() != 0) { +		printf("error in File System init\n"); +		goto fail; +	} + +	free(start_block_address); +	free(journal_buffer); + +	return 0; +fail: +	free(start_block_address); +	free(journal_buffer); + +	return -1; +} + +int ext4fs_init(void) +{ +	short status; +	int i; +	unsigned int real_free_blocks = 0; +	struct ext_filesystem *fs = get_fs(); + +	/* populate fs */ +	fs->blksz = EXT2_BLOCK_SIZE(ext4fs_root); +	fs->inodesz = INODE_SIZE_FILESYSTEM(ext4fs_root); +	fs->sect_perblk = fs->blksz / SECTOR_SIZE; + +	/* get the superblock */ +	fs->sb = zalloc(SUPERBLOCK_SIZE); +	if (!fs->sb) +		return -ENOMEM; +	if (!ext4fs_devread(SUPERBLOCK_SECTOR, 0, SUPERBLOCK_SIZE, +			(char *)fs->sb)) +		goto fail; + +	/* init journal */ +	if (ext4fs_init_journal()) +		goto fail; + +	/* get total no of blockgroups */ +	fs->no_blkgrp = (uint32_t)ext4fs_div_roundup( +			(ext4fs_root->sblock.total_blocks - +			ext4fs_root->sblock.first_data_block), +			ext4fs_root->sblock.blocks_per_group); + +	/* get the block group descriptor table */ +	fs->gdtable_blkno = ((EXT2_MIN_BLOCK_SIZE == fs->blksz) + 1); +	if (ext4fs_get_bgdtable() == -1) { +		printf("Error in getting the block group descriptor table\n"); +		goto fail; +	} +	fs->gd = (struct ext2_block_group *)fs->gdtable; + +	/* load all the available bitmap block of the partition */ +	fs->blk_bmaps = zalloc(fs->no_blkgrp * sizeof(char *)); +	if (!fs->blk_bmaps) +		goto fail; +	for (i = 0; i < fs->no_blkgrp; i++) { +		fs->blk_bmaps[i] = zalloc(fs->blksz); +		if (!fs->blk_bmaps[i]) +			goto fail; +	} + +	for (i = 0; i < fs->no_blkgrp; i++) { +		status = +		    ext4fs_devread(fs->gd[i].block_id * fs->sect_perblk, 0, +				   fs->blksz, (char *)fs->blk_bmaps[i]); +		if (status == 0) +			goto fail; +	} + +	/* load all the available inode bitmap of the partition */ +	fs->inode_bmaps = zalloc(fs->no_blkgrp * sizeof(unsigned char *)); +	if (!fs->inode_bmaps) +		goto fail; +	for (i = 0; i < fs->no_blkgrp; i++) { +		fs->inode_bmaps[i] = zalloc(fs->blksz); +		if (!fs->inode_bmaps[i]) +			goto fail; +	} + +	for (i = 0; i < fs->no_blkgrp; i++) { +		status = ext4fs_devread(fs->gd[i].inode_id * fs->sect_perblk, +					0, fs->blksz, +					(char *)fs->inode_bmaps[i]); +		if (status == 0) +			goto fail; +	} + +	/* +	 * check filesystem consistency with free blocks of file system +	 * some time we observed that superblock freeblocks does not match +	 * with the  blockgroups freeblocks when improper +	 * reboot of a linux kernel +	 */ +	for (i = 0; i < fs->no_blkgrp; i++) +		real_free_blocks = real_free_blocks + fs->gd[i].free_blocks; +	if (real_free_blocks != fs->sb->free_blocks) +		fs->sb->free_blocks = real_free_blocks; + +	return 0; +fail: +	ext4fs_deinit(); + +	return -1; +} + +void ext4fs_deinit(void) +{ +	int i; +	struct ext2_inode inode_journal; +	struct journal_superblock_t *jsb; +	long int blknr; +	struct ext_filesystem *fs = get_fs(); + +	/* free journal */ +	char *temp_buff = zalloc(fs->blksz); +	if (temp_buff) { +		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; +		jsb->s_start = cpu_to_be32(0); +		put_ext4((uint64_t) (blknr * fs->blksz), +			 (struct journal_superblock_t *)temp_buff, fs->blksz); +		free(temp_buff); +	} +	ext4fs_free_journal(); + +	/* get the superblock */ +	ext4fs_devread(SUPERBLOCK_SECTOR, 0, SUPERBLOCK_SIZE, (char *)fs->sb); +	fs->sb->feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER; +	put_ext4((uint64_t)(SUPERBLOCK_SIZE), +		 (struct ext2_sblock *)fs->sb, (uint32_t)SUPERBLOCK_SIZE); +	free(fs->sb); +	fs->sb = NULL; + +	if (fs->blk_bmaps) { +		for (i = 0; i < fs->no_blkgrp; i++) { +			free(fs->blk_bmaps[i]); +			fs->blk_bmaps[i] = NULL; +		} +		free(fs->blk_bmaps); +		fs->blk_bmaps = NULL; +	} + +	if (fs->inode_bmaps) { +		for (i = 0; i < fs->no_blkgrp; i++) { +			free(fs->inode_bmaps[i]); +			fs->inode_bmaps[i] = NULL; +		} +		free(fs->inode_bmaps); +		fs->inode_bmaps = NULL; +	} + + +	free(fs->gdtable); +	fs->gdtable = NULL; +	fs->gd = NULL; +	/* +	 * reinitiliazed the global inode and +	 * block bitmap first execution check variables +	 */ +	fs->first_pass_ibmap = 0; +	fs->first_pass_bbmap = 0; +	fs->curr_inode_no = 0; +	fs->curr_blkno = 0; +} + +static int ext4fs_write_file(struct ext2_inode *file_inode, +			     int pos, unsigned int len, char *buf) +{ +	int i; +	int blockcnt; +	int log2blocksize = LOG2_EXT2_BLOCK_SIZE(ext4fs_root); +	unsigned int filesize = __le32_to_cpu(file_inode->size); +	struct ext_filesystem *fs = get_fs(); +	int previous_block_number = -1; +	int delayed_start = 0; +	int delayed_extent = 0; +	int delayed_skipfirst = 0; +	int delayed_next = 0; +	char *delayed_buf = NULL; + +	/* Adjust len so it we can't read past the end of the file. */ +	if (len > filesize) +		len = filesize; + +	blockcnt = ((len + pos) + fs->blksz - 1) / fs->blksz; + +	for (i = pos / fs->blksz; i < blockcnt; i++) { +		long int blknr; +		int blockend = fs->blksz; +		int skipfirst = 0; +		blknr = read_allocated_block(file_inode, i); +		if (blknr < 0) +			return -1; + +		blknr = blknr << log2blocksize; + +		if (blknr) { +			if (previous_block_number != -1) { +				if (delayed_next == blknr) { +					delayed_extent += blockend; +					delayed_next += blockend >> SECTOR_BITS; +				} else {	/* spill */ +					put_ext4((uint64_t) (delayed_start * +							     SECTOR_SIZE), +						 delayed_buf, +						 (uint32_t) delayed_extent); +					previous_block_number = blknr; +					delayed_start = blknr; +					delayed_extent = blockend; +					delayed_skipfirst = skipfirst; +					delayed_buf = buf; +					delayed_next = blknr + +					    (blockend >> SECTOR_BITS); +				} +			} else { +				previous_block_number = blknr; +				delayed_start = blknr; +				delayed_extent = blockend; +				delayed_skipfirst = skipfirst; +				delayed_buf = buf; +				delayed_next = blknr + +				    (blockend >> SECTOR_BITS); +			} +		} else { +			if (previous_block_number != -1) { +				/* spill */ +				put_ext4((uint64_t) (delayed_start * +						     SECTOR_SIZE), delayed_buf, +					 (uint32_t) delayed_extent); +				previous_block_number = -1; +			} +			memset(buf, 0, fs->blksz - skipfirst); +		} +		buf += fs->blksz - skipfirst; +	} +	if (previous_block_number != -1) { +		/* spill */ +		put_ext4((uint64_t) (delayed_start * SECTOR_SIZE), +			 delayed_buf, (uint32_t) delayed_extent); +		previous_block_number = -1; +	} + +	return len; +} + +int ext4fs_write(const char *fname, unsigned char *buffer, +					unsigned long sizebytes) +{ +	int ret = 0; +	struct ext2_inode *file_inode = NULL; +	unsigned char *inode_buffer = NULL; +	int parent_inodeno; +	int inodeno; +	time_t timestamp = 0; + +	uint64_t bytes_reqd_for_file; +	unsigned int blks_reqd_for_file; +	unsigned int blocks_remaining; +	int existing_file_inodeno; +	char filename[256]; + +	char *temp_ptr = NULL; +	long int itable_blkno; +	long int parent_itable_blkno; +	long int blkoff; +	struct ext2_sblock *sblock = &(ext4fs_root->sblock); +	unsigned int inodes_per_block; +	unsigned int ibmap_idx; +	struct ext_filesystem *fs = get_fs(); +	g_parent_inode = zalloc(sizeof(struct ext2_inode)); +	if (!g_parent_inode) +		goto fail; + +	if (ext4fs_init() != 0) { +		printf("error in File System init\n"); +		return -1; +	} +	inodes_per_block = fs->blksz / fs->inodesz; +	parent_inodeno = ext4fs_get_parent_inode_num(fname, filename, F_FILE); +	if (parent_inodeno == -1) +		goto fail; +	if (ext4fs_iget(parent_inodeno, g_parent_inode)) +		goto fail; +	/* check if the filename is already present in root */ +	existing_file_inodeno = ext4fs_filename_check(filename); +	if (existing_file_inodeno != -1) { +		ret = ext4fs_delete_file(existing_file_inodeno); +		fs->first_pass_bbmap = 0; +		fs->curr_blkno = 0; + +		fs->first_pass_ibmap = 0; +		fs->curr_inode_no = 0; +		if (ret) +			goto fail; +	} +	/* calucalate how many blocks required */ +	bytes_reqd_for_file = sizebytes; +	blks_reqd_for_file = bytes_reqd_for_file / fs->blksz; +	if (bytes_reqd_for_file % fs->blksz != 0) { +		blks_reqd_for_file++; +		debug("total bytes for a file %u\n", blks_reqd_for_file); +	} +	blocks_remaining = blks_reqd_for_file; +	/* test for available space in partition */ +	if (fs->sb->free_blocks < blks_reqd_for_file) { +		printf("Not enough space on partition !!!\n"); +		goto fail; +	} + +	ext4fs_update_parent_dentry(filename, &inodeno, FILETYPE_REG); +	/* prepare file inode */ +	inode_buffer = zalloc(fs->inodesz); +	if (!inode_buffer) +		goto fail; +	file_inode = (struct ext2_inode *)inode_buffer; +	file_inode->mode = S_IFREG | S_IRWXU | +	    S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH; +	/* ToDo: Update correct time */ +	file_inode->mtime = timestamp; +	file_inode->atime = timestamp; +	file_inode->ctime = timestamp; +	file_inode->nlinks = 1; +	file_inode->size = sizebytes; + +	/* Allocate data blocks */ +	ext4fs_allocate_blocks(file_inode, blocks_remaining, +			       &blks_reqd_for_file); +	file_inode->blockcnt = (blks_reqd_for_file * fs->blksz) / SECTOR_SIZE; + +	temp_ptr = zalloc(fs->blksz); +	if (!temp_ptr) +		goto fail; +	ibmap_idx = inodeno / ext4fs_root->sblock.inodes_per_group; +	inodeno--; +	itable_blkno = __le32_to_cpu(fs->gd[ibmap_idx].inode_table_id) + +			(inodeno % __le32_to_cpu(sblock->inodes_per_group)) / +			inodes_per_block; +	blkoff = (inodeno % inodes_per_block) * fs->inodesz; +	ext4fs_devread(itable_blkno * fs->sect_perblk, 0, fs->blksz, temp_ptr); +	if (ext4fs_log_journal(temp_ptr, itable_blkno)) +		goto fail; + +	memcpy(temp_ptr + blkoff, inode_buffer, fs->inodesz); +	if (ext4fs_put_metadata(temp_ptr, itable_blkno)) +		goto fail; +	/* copy the file content into data blocks */ +	if (ext4fs_write_file(file_inode, 0, sizebytes, (char *)buffer) == -1) { +		printf("Error in copying content\n"); +		goto fail; +	} +	ibmap_idx = parent_inodeno / ext4fs_root->sblock.inodes_per_group; +	parent_inodeno--; +	parent_itable_blkno = __le32_to_cpu(fs->gd[ibmap_idx].inode_table_id) + +	    (parent_inodeno % +	     __le32_to_cpu(sblock->inodes_per_group)) / inodes_per_block; +	blkoff = (parent_inodeno % inodes_per_block) * fs->inodesz; +	if (parent_itable_blkno != itable_blkno) { +		memset(temp_ptr, '\0', fs->blksz); +		ext4fs_devread(parent_itable_blkno * fs->sect_perblk, +			       0, fs->blksz, temp_ptr); +		if (ext4fs_log_journal(temp_ptr, parent_itable_blkno)) +			goto fail; + +		memcpy(temp_ptr + blkoff, g_parent_inode, +			sizeof(struct ext2_inode)); +		if (ext4fs_put_metadata(temp_ptr, parent_itable_blkno)) +			goto fail; +		free(temp_ptr); +	} else { +		/* +		 * If parent and child fall in same inode table block +		 * both should be kept in 1 buffer +		 */ +		memcpy(temp_ptr + blkoff, g_parent_inode, +		       sizeof(struct ext2_inode)); +		gd_index--; +		if (ext4fs_put_metadata(temp_ptr, itable_blkno)) +			goto fail; +		free(temp_ptr); +	} +	ext4fs_update(); +	ext4fs_deinit(); + +	fs->first_pass_bbmap = 0; +	fs->curr_blkno = 0; +	fs->first_pass_ibmap = 0; +	fs->curr_inode_no = 0; +	free(inode_buffer); +	free(g_parent_inode); +	g_parent_inode = NULL; + +	return 0; +fail: +	ext4fs_deinit(); +	free(inode_buffer); +	free(g_parent_inode); +	g_parent_inode = NULL; + +	return -1; +} +#endif |