diff options
Diffstat (limited to 'fs/ext4/ext4_common.c')
| -rw-r--r-- | fs/ext4/ext4_common.c | 2228 | 
1 files changed, 2228 insertions, 0 deletions
| diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c new file mode 100644 index 000000000..3deffd523 --- /dev/null +++ b/fs/ext4/ext4_common.c @@ -0,0 +1,2228 @@ +/* + * (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> + * + * ext4ls and ext4load : Based on ext2 ls load support in Uboot. + * + * (C) Copyright 2004 + * esd gmbh <www.esd-electronics.com> + * Reinhard Arlt <reinhard.arlt@esd-electronics.com> + * + * based on code from grub2 fs/ext2.c and fs/fshelp.c by + * 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 + * (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 <ext_common.h> +#include <ext4fs.h> +#include <malloc.h> +#include <stddef.h> +#include <linux/stat.h> +#include <linux/time.h> +#include <asm/byteorder.h> +#include "ext4_common.h" + +struct ext2_data *ext4fs_root; +struct ext2fs_node *ext4fs_file; +uint32_t *ext4fs_indir1_block; +int ext4fs_indir1_size; +int ext4fs_indir1_blkno = -1; +uint32_t *ext4fs_indir2_block; +int ext4fs_indir2_size; +int ext4fs_indir2_blkno = -1; + +uint32_t *ext4fs_indir3_block; +int ext4fs_indir3_size; +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; +	ALLOC_CACHE_ALIGN_BUFFER(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, +		uint32_t fileblock, int log2_blksz) +{ +	struct ext4_extent_idx *index; +	unsigned long long block; +	struct ext_filesystem *fs = get_fs(); +	int i; + +	while (1) { +		index = (struct ext4_extent_idx *)(ext_block + 1); + +		if (le32_to_cpu(ext_block->eh_magic) != EXT4_EXT_MAGIC) +			return 0; + +		if (ext_block->eh_depth == 0) +			return ext_block; +		i = -1; +		do { +			i++; +			if (i >= le32_to_cpu(ext_block->eh_entries)) +				break; +		} while (fileblock > le32_to_cpu(index[i].ei_block)); + +		if (--i < 0) +			return 0; + +		block = le32_to_cpu(index[i].ei_leaf_hi); +		block = (block << 32) + le32_to_cpu(index[i].ei_leaf_lo); + +		if (ext4fs_devread(block << log2_blksz, 0, fs->blksz, buf)) +			ext_block = (struct ext4_extent_header *)buf; +		else +			return 0; +	} +} + +static int ext4fs_blockgroup +	(struct ext2_data *data, int group, struct ext2_block_group *blkgrp) +{ +	long int blkno; +	unsigned int blkoff, desc_per_blk; + +	desc_per_blk = EXT2_BLOCK_SIZE(data) / sizeof(struct ext2_block_group); + +	blkno = __le32_to_cpu(data->sblock.first_data_block) + 1 + +			group / desc_per_blk; +	blkoff = (group % desc_per_blk) * sizeof(struct ext2_block_group); + +	debug("ext4fs read %d group descriptor (blkno %ld blkoff %u)\n", +	      group, blkno, blkoff); + +	return ext4fs_devread(blkno << LOG2_EXT2_BLOCK_SIZE(data), +			      blkoff, sizeof(struct ext2_block_group), +			      (char *)blkgrp); +} + +int ext4fs_read_inode(struct ext2_data *data, int ino, struct ext2_inode *inode) +{ +	struct ext2_block_group blkgrp; +	struct ext2_sblock *sblock = &data->sblock; +	struct ext_filesystem *fs = get_fs(); +	int inodes_per_block, status; +	long int blkno; +	unsigned int blkoff; + +	/* It is easier to calculate if the first inode is 0. */ +	ino--; +	status = ext4fs_blockgroup(data, ino / __le32_to_cpu +				   (sblock->inodes_per_group), &blkgrp); +	if (status == 0) +		return 0; + +	inodes_per_block = EXT2_BLOCK_SIZE(data) / fs->inodesz; +	blkno = __le32_to_cpu(blkgrp.inode_table_id) + +	    (ino % __le32_to_cpu(sblock->inodes_per_group)) / inodes_per_block; +	blkoff = (ino % inodes_per_block) * fs->inodesz; +	/* Read the inode. */ +	status = ext4fs_devread(blkno << LOG2_EXT2_BLOCK_SIZE(data), blkoff, +				sizeof(struct ext2_inode), (char *)inode); +	if (status == 0) +		return 0; + +	return 1; +} + +long int read_allocated_block(struct ext2_inode *inode, int fileblock) +{ +	long int blknr; +	int blksz; +	int log2_blksz; +	int status; +	long int rblock; +	long int perblock_parent; +	long int perblock_child; +	unsigned long long start; +	/* get the blocksize of the filesystem */ +	blksz = EXT2_BLOCK_SIZE(ext4fs_root); +	log2_blksz = LOG2_EXT2_BLOCK_SIZE(ext4fs_root); +	if (le32_to_cpu(inode->flags) & EXT4_EXTENTS_FL) { +		char *buf = zalloc(blksz); +		if (!buf) +			return -ENOMEM; +		struct ext4_extent_header *ext_block; +		struct ext4_extent *extent; +		int i = -1; +		ext_block = ext4fs_get_extent_block(ext4fs_root, buf, +						    (struct ext4_extent_header +						     *)inode->b. +						    blocks.dir_blocks, +						    fileblock, log2_blksz); +		if (!ext_block) { +			printf("invalid extent block\n"); +			free(buf); +			return -EINVAL; +		} + +		extent = (struct ext4_extent *)(ext_block + 1); + +		do { +			i++; +			if (i >= le32_to_cpu(ext_block->eh_entries)) +				break; +		} while (fileblock >= le32_to_cpu(extent[i].ee_block)); +		if (--i >= 0) { +			fileblock -= le32_to_cpu(extent[i].ee_block); +			if (fileblock >= le32_to_cpu(extent[i].ee_len)) { +				free(buf); +				return 0; +			} + +			start = le32_to_cpu(extent[i].ee_start_hi); +			start = (start << 32) + +					le32_to_cpu(extent[i].ee_start_lo); +			free(buf); +			return fileblock + start; +		} + +		printf("Extent Error\n"); +		free(buf); +		return -1; +	} + +	/* Direct blocks. */ +	if (fileblock < INDIRECT_BLOCKS) +		blknr = __le32_to_cpu(inode->b.blocks.dir_blocks[fileblock]); + +	/* Indirect. */ +	else if (fileblock < (INDIRECT_BLOCKS + (blksz / 4))) { +		if (ext4fs_indir1_block == NULL) { +			ext4fs_indir1_block = zalloc(blksz); +			if (ext4fs_indir1_block == NULL) { +				printf("** SI ext2fs read block (indir 1)" +					"malloc failed. **\n"); +				return -1; +			} +			ext4fs_indir1_size = blksz; +			ext4fs_indir1_blkno = -1; +		} +		if (blksz != ext4fs_indir1_size) { +			free(ext4fs_indir1_block); +			ext4fs_indir1_block = NULL; +			ext4fs_indir1_size = 0; +			ext4fs_indir1_blkno = -1; +			ext4fs_indir1_block = zalloc(blksz); +			if (ext4fs_indir1_block == NULL) { +				printf("** SI ext2fs read block (indir 1):" +					"malloc failed. **\n"); +				return -1; +			} +			ext4fs_indir1_size = blksz; +		} +		if ((__le32_to_cpu(inode->b.blocks.indir_block) << +		     log2_blksz) != ext4fs_indir1_blkno) { +			status = +			    ext4fs_devread(__le32_to_cpu +					   (inode->b.blocks. +					    indir_block) << log2_blksz, 0, +					   blksz, (char *)ext4fs_indir1_block); +			if (status == 0) { +				printf("** SI ext2fs read block (indir 1)" +					"failed. **\n"); +				return 0; +			} +			ext4fs_indir1_blkno = +				__le32_to_cpu(inode->b.blocks. +					       indir_block) << log2_blksz; +		} +		blknr = __le32_to_cpu(ext4fs_indir1_block +				      [fileblock - INDIRECT_BLOCKS]); +	} +	/* Double indirect. */ +	else if (fileblock < (INDIRECT_BLOCKS + (blksz / 4 * +					(blksz / 4 + 1)))) { + +		long int perblock = blksz / 4; +		long int rblock = fileblock - (INDIRECT_BLOCKS + blksz / 4); + +		if (ext4fs_indir1_block == NULL) { +			ext4fs_indir1_block = zalloc(blksz); +			if (ext4fs_indir1_block == NULL) { +				printf("** DI ext2fs read block (indir 2 1)" +					"malloc failed. **\n"); +				return -1; +			} +			ext4fs_indir1_size = blksz; +			ext4fs_indir1_blkno = -1; +		} +		if (blksz != ext4fs_indir1_size) { +			free(ext4fs_indir1_block); +			ext4fs_indir1_block = NULL; +			ext4fs_indir1_size = 0; +			ext4fs_indir1_blkno = -1; +			ext4fs_indir1_block = zalloc(blksz); +			if (ext4fs_indir1_block == NULL) { +				printf("** DI ext2fs read block (indir 2 1)" +					"malloc failed. **\n"); +				return -1; +			} +			ext4fs_indir1_size = blksz; +		} +		if ((__le32_to_cpu(inode->b.blocks.double_indir_block) << +		     log2_blksz) != ext4fs_indir1_blkno) { +			status = +			    ext4fs_devread(__le32_to_cpu +					   (inode->b.blocks. +					    double_indir_block) << log2_blksz, +					   0, blksz, +					   (char *)ext4fs_indir1_block); +			if (status == 0) { +				printf("** DI ext2fs read block (indir 2 1)" +					"failed. **\n"); +				return -1; +			} +			ext4fs_indir1_blkno = +			    __le32_to_cpu(inode->b.blocks.double_indir_block) << +			    log2_blksz; +		} + +		if (ext4fs_indir2_block == NULL) { +			ext4fs_indir2_block = zalloc(blksz); +			if (ext4fs_indir2_block == NULL) { +				printf("** DI ext2fs read block (indir 2 2)" +					"malloc failed. **\n"); +				return -1; +			} +			ext4fs_indir2_size = blksz; +			ext4fs_indir2_blkno = -1; +		} +		if (blksz != ext4fs_indir2_size) { +			free(ext4fs_indir2_block); +			ext4fs_indir2_block = NULL; +			ext4fs_indir2_size = 0; +			ext4fs_indir2_blkno = -1; +			ext4fs_indir2_block = zalloc(blksz); +			if (ext4fs_indir2_block == NULL) { +				printf("** DI ext2fs read block (indir 2 2)" +					"malloc failed. **\n"); +				return -1; +			} +			ext4fs_indir2_size = blksz; +		} +		if ((__le32_to_cpu(ext4fs_indir1_block[rblock / perblock]) << +		     log2_blksz) != ext4fs_indir2_blkno) { +			status = ext4fs_devread(__le32_to_cpu +						(ext4fs_indir1_block +						 [rblock / +						  perblock]) << log2_blksz, 0, +						blksz, +						(char *)ext4fs_indir2_block); +			if (status == 0) { +				printf("** DI ext2fs read block (indir 2 2)" +					"failed. **\n"); +				return -1; +			} +			ext4fs_indir2_blkno = +			    __le32_to_cpu(ext4fs_indir1_block[rblock +							      / +							      perblock]) << +			    log2_blksz; +		} +		blknr = __le32_to_cpu(ext4fs_indir2_block[rblock % perblock]); +	} +	/* Tripple indirect. */ +	else { +		rblock = fileblock - (INDIRECT_BLOCKS + blksz / 4 + +				      (blksz / 4 * blksz / 4)); +		perblock_child = blksz / 4; +		perblock_parent = ((blksz / 4) * (blksz / 4)); + +		if (ext4fs_indir1_block == NULL) { +			ext4fs_indir1_block = zalloc(blksz); +			if (ext4fs_indir1_block == NULL) { +				printf("** TI ext2fs read block (indir 2 1)" +					"malloc failed. **\n"); +				return -1; +			} +			ext4fs_indir1_size = blksz; +			ext4fs_indir1_blkno = -1; +		} +		if (blksz != ext4fs_indir1_size) { +			free(ext4fs_indir1_block); +			ext4fs_indir1_block = NULL; +			ext4fs_indir1_size = 0; +			ext4fs_indir1_blkno = -1; +			ext4fs_indir1_block = zalloc(blksz); +			if (ext4fs_indir1_block == NULL) { +				printf("** TI ext2fs read block (indir 2 1)" +					"malloc failed. **\n"); +				return -1; +			} +			ext4fs_indir1_size = blksz; +		} +		if ((__le32_to_cpu(inode->b.blocks.triple_indir_block) << +		     log2_blksz) != ext4fs_indir1_blkno) { +			status = ext4fs_devread +			    (__le32_to_cpu(inode->b.blocks.triple_indir_block) +			     << log2_blksz, 0, blksz, +			     (char *)ext4fs_indir1_block); +			if (status == 0) { +				printf("** TI ext2fs read block (indir 2 1)" +					"failed. **\n"); +				return -1; +			} +			ext4fs_indir1_blkno = +			    __le32_to_cpu(inode->b.blocks.triple_indir_block) << +			    log2_blksz; +		} + +		if (ext4fs_indir2_block == NULL) { +			ext4fs_indir2_block = zalloc(blksz); +			if (ext4fs_indir2_block == NULL) { +				printf("** TI ext2fs read block (indir 2 2)" +					"malloc failed. **\n"); +				return -1; +			} +			ext4fs_indir2_size = blksz; +			ext4fs_indir2_blkno = -1; +		} +		if (blksz != ext4fs_indir2_size) { +			free(ext4fs_indir2_block); +			ext4fs_indir2_block = NULL; +			ext4fs_indir2_size = 0; +			ext4fs_indir2_blkno = -1; +			ext4fs_indir2_block = zalloc(blksz); +			if (ext4fs_indir2_block == NULL) { +				printf("** TI ext2fs read block (indir 2 2)" +					"malloc failed. **\n"); +				return -1; +			} +			ext4fs_indir2_size = blksz; +		} +		if ((__le32_to_cpu(ext4fs_indir1_block[rblock / +						       perblock_parent]) << +		     log2_blksz) +		    != ext4fs_indir2_blkno) { +			status = ext4fs_devread(__le32_to_cpu +						(ext4fs_indir1_block +						 [rblock / +						  perblock_parent]) << +						log2_blksz, 0, blksz, +						(char *)ext4fs_indir2_block); +			if (status == 0) { +				printf("** TI ext2fs read block (indir 2 2)" +					"failed. **\n"); +				return -1; +			} +			ext4fs_indir2_blkno = +			    __le32_to_cpu(ext4fs_indir1_block[rblock / +							      perblock_parent]) +			    << log2_blksz; +		} + +		if (ext4fs_indir3_block == NULL) { +			ext4fs_indir3_block = zalloc(blksz); +			if (ext4fs_indir3_block == NULL) { +				printf("** TI ext2fs read block (indir 2 2)" +					"malloc failed. **\n"); +				return -1; +			} +			ext4fs_indir3_size = blksz; +			ext4fs_indir3_blkno = -1; +		} +		if (blksz != ext4fs_indir3_size) { +			free(ext4fs_indir3_block); +			ext4fs_indir3_block = NULL; +			ext4fs_indir3_size = 0; +			ext4fs_indir3_blkno = -1; +			ext4fs_indir3_block = zalloc(blksz); +			if (ext4fs_indir3_block == NULL) { +				printf("** TI ext2fs read block (indir 2 2)" +					"malloc failed. **\n"); +				return -1; +			} +			ext4fs_indir3_size = blksz; +		} +		if ((__le32_to_cpu(ext4fs_indir2_block[rblock +						       / +						       perblock_child]) << +		     log2_blksz) != ext4fs_indir3_blkno) { +			status = +			    ext4fs_devread(__le32_to_cpu +					   (ext4fs_indir2_block +					    [(rblock / perblock_child) +					     % (blksz / 4)]) << log2_blksz, 0, +					   blksz, (char *)ext4fs_indir3_block); +			if (status == 0) { +				printf("** TI ext2fs read block (indir 2 2)" +				       "failed. **\n"); +				return -1; +			} +			ext4fs_indir3_blkno = +			    __le32_to_cpu(ext4fs_indir2_block[(rblock / +							       perblock_child) % +							      (blksz / +							       4)]) << +			    log2_blksz; +		} + +		blknr = __le32_to_cpu(ext4fs_indir3_block +				      [rblock % perblock_child]); +	} +	debug("ext4fs_read_block %ld\n", blknr); + +	return blknr; +} + +void ext4fs_close(void) +{ +	if ((ext4fs_file != NULL) && (ext4fs_root != NULL)) { +		ext4fs_free_node(ext4fs_file, &ext4fs_root->diropen); +		ext4fs_file = NULL; +	} +	if (ext4fs_root != NULL) { +		free(ext4fs_root); +		ext4fs_root = NULL; +	} +	if (ext4fs_indir1_block != NULL) { +		free(ext4fs_indir1_block); +		ext4fs_indir1_block = NULL; +		ext4fs_indir1_size = 0; +		ext4fs_indir1_blkno = -1; +	} +	if (ext4fs_indir2_block != NULL) { +		free(ext4fs_indir2_block); +		ext4fs_indir2_block = NULL; +		ext4fs_indir2_size = 0; +		ext4fs_indir2_blkno = -1; +	} +	if (ext4fs_indir3_block != NULL) { +		free(ext4fs_indir3_block); +		ext4fs_indir3_block = NULL; +		ext4fs_indir3_size = 0; +		ext4fs_indir3_blkno = -1; +	} +} + +int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name, +				struct ext2fs_node **fnode, int *ftype) +{ +	unsigned int fpos = 0; +	int status; +	struct ext2fs_node *diro = (struct ext2fs_node *) dir; + +#ifdef DEBUG +	if (name != NULL) +		printf("Iterate dir %s\n", name); +#endif /* of DEBUG */ +	if (!diro->inode_read) { +		status = ext4fs_read_inode(diro->data, diro->ino, &diro->inode); +		if (status == 0) +			return 0; +	} +	/* Search the file.  */ +	while (fpos < __le32_to_cpu(diro->inode.size)) { +		struct ext2_dirent dirent; + +		status = ext4fs_read_file(diro, fpos, +					   sizeof(struct ext2_dirent), +					   (char *) &dirent); +		if (status < 1) +			return 0; + +		if (dirent.namelen != 0) { +			char filename[dirent.namelen + 1]; +			struct ext2fs_node *fdiro; +			int type = FILETYPE_UNKNOWN; + +			status = ext4fs_read_file(diro, +						  fpos + +						  sizeof(struct ext2_dirent), +						  dirent.namelen, filename); +			if (status < 1) +				return 0; + +			fdiro = zalloc(sizeof(struct ext2fs_node)); +			if (!fdiro) +				return 0; + +			fdiro->data = diro->data; +			fdiro->ino = __le32_to_cpu(dirent.inode); + +			filename[dirent.namelen] = '\0'; + +			if (dirent.filetype != FILETYPE_UNKNOWN) { +				fdiro->inode_read = 0; + +				if (dirent.filetype == FILETYPE_DIRECTORY) +					type = FILETYPE_DIRECTORY; +				else if (dirent.filetype == FILETYPE_SYMLINK) +					type = FILETYPE_SYMLINK; +				else if (dirent.filetype == FILETYPE_REG) +					type = FILETYPE_REG; +			} else { +				status = ext4fs_read_inode(diro->data, +							   __le32_to_cpu +							   (dirent.inode), +							   &fdiro->inode); +				if (status == 0) { +					free(fdiro); +					return 0; +				} +				fdiro->inode_read = 1; + +				if ((__le16_to_cpu(fdiro->inode.mode) & +				     FILETYPE_INO_MASK) == +				    FILETYPE_INO_DIRECTORY) { +					type = FILETYPE_DIRECTORY; +				} else if ((__le16_to_cpu(fdiro->inode.mode) +					    & FILETYPE_INO_MASK) == +					   FILETYPE_INO_SYMLINK) { +					type = FILETYPE_SYMLINK; +				} else if ((__le16_to_cpu(fdiro->inode.mode) +					    & FILETYPE_INO_MASK) == +					   FILETYPE_INO_REG) { +					type = FILETYPE_REG; +				} +			} +#ifdef DEBUG +			printf("iterate >%s<\n", filename); +#endif /* of DEBUG */ +			if ((name != NULL) && (fnode != NULL) +			    && (ftype != NULL)) { +				if (strcmp(filename, name) == 0) { +					*ftype = type; +					*fnode = fdiro; +					return 1; +				} +			} else { +				if (fdiro->inode_read == 0) { +					status = ext4fs_read_inode(diro->data, +								 __le32_to_cpu( +								 dirent.inode), +								 &fdiro->inode); +					if (status == 0) { +						free(fdiro); +						return 0; +					} +					fdiro->inode_read = 1; +				} +				switch (type) { +				case FILETYPE_DIRECTORY: +					printf("<DIR> "); +					break; +				case FILETYPE_SYMLINK: +					printf("<SYM> "); +					break; +				case FILETYPE_REG: +					printf("      "); +					break; +				default: +					printf("< ? > "); +					break; +				} +				printf("%10d %s\n", +					__le32_to_cpu(fdiro->inode.size), +					filename); +			} +			free(fdiro); +		} +		fpos += __le16_to_cpu(dirent.direntlen); +	} +	return 0; +} + +static char *ext4fs_read_symlink(struct ext2fs_node *node) +{ +	char *symlink; +	struct ext2fs_node *diro = node; +	int status; + +	if (!diro->inode_read) { +		status = ext4fs_read_inode(diro->data, diro->ino, &diro->inode); +		if (status == 0) +			return 0; +	} +	symlink = zalloc(__le32_to_cpu(diro->inode.size) + 1); +	if (!symlink) +		return 0; + +	if (__le32_to_cpu(diro->inode.size) <= 60) { +		strncpy(symlink, diro->inode.b.symlink, +			 __le32_to_cpu(diro->inode.size)); +	} else { +		status = ext4fs_read_file(diro, 0, +					   __le32_to_cpu(diro->inode.size), +					   symlink); +		if (status == 0) { +			free(symlink); +			return 0; +		} +	} +	symlink[__le32_to_cpu(diro->inode.size)] = '\0'; +	return symlink; +} + +static int ext4fs_find_file1(const char *currpath, +			     struct ext2fs_node *currroot, +			     struct ext2fs_node **currfound, int *foundtype) +{ +	char fpath[strlen(currpath) + 1]; +	char *name = fpath; +	char *next; +	int status; +	int type = FILETYPE_DIRECTORY; +	struct ext2fs_node *currnode = currroot; +	struct ext2fs_node *oldnode = currroot; + +	strncpy(fpath, currpath, strlen(currpath) + 1); + +	/* Remove all leading slashes. */ +	while (*name == '/') +		name++; + +	if (!*name) { +		*currfound = currnode; +		return 1; +	} + +	for (;;) { +		int found; + +		/* Extract the actual part from the pathname. */ +		next = strchr(name, '/'); +		if (next) { +			/* Remove all leading slashes. */ +			while (*next == '/') +				*(next++) = '\0'; +		} + +		if (type != FILETYPE_DIRECTORY) { +			ext4fs_free_node(currnode, currroot); +			return 0; +		} + +		oldnode = currnode; + +		/* Iterate over the directory. */ +		found = ext4fs_iterate_dir(currnode, name, &currnode, &type); +		if (found == 0) +			return 0; + +		if (found == -1) +			break; + +		/* Read in the symlink and follow it. */ +		if (type == FILETYPE_SYMLINK) { +			char *symlink; + +			/* Test if the symlink does not loop. */ +			if (++symlinknest == 8) { +				ext4fs_free_node(currnode, currroot); +				ext4fs_free_node(oldnode, currroot); +				return 0; +			} + +			symlink = ext4fs_read_symlink(currnode); +			ext4fs_free_node(currnode, currroot); + +			if (!symlink) { +				ext4fs_free_node(oldnode, currroot); +				return 0; +			} + +			debug("Got symlink >%s<\n", symlink); + +			if (symlink[0] == '/') { +				ext4fs_free_node(oldnode, currroot); +				oldnode = &ext4fs_root->diropen; +			} + +			/* Lookup the node the symlink points to. */ +			status = ext4fs_find_file1(symlink, oldnode, +						    &currnode, &type); + +			free(symlink); + +			if (status == 0) { +				ext4fs_free_node(oldnode, currroot); +				return 0; +			} +		} + +		ext4fs_free_node(oldnode, currroot); + +		/* Found the node! */ +		if (!next || *next == '\0') { +			*currfound = currnode; +			*foundtype = type; +			return 1; +		} +		name = next; +	} +	return -1; +} + +int ext4fs_find_file(const char *path, struct ext2fs_node *rootnode, +	struct ext2fs_node **foundnode, int expecttype) +{ +	int status; +	int foundtype = FILETYPE_DIRECTORY; + +	symlinknest = 0; +	if (!path) +		return 0; + +	status = ext4fs_find_file1(path, rootnode, foundnode, &foundtype); +	if (status == 0) +		return 0; + +	/* Check if the node that was found was of the expected type. */ +	if ((expecttype == FILETYPE_REG) && (foundtype != expecttype)) +		return 0; +	else if ((expecttype == FILETYPE_DIRECTORY) +		   && (foundtype != expecttype)) +		return 0; + +	return 1; +} + +int ext4fs_open(const char *filename) +{ +	struct ext2fs_node *fdiro = NULL; +	int status; +	int len; + +	if (ext4fs_root == NULL) +		return -1; + +	ext4fs_file = NULL; +	status = ext4fs_find_file(filename, &ext4fs_root->diropen, &fdiro, +				  FILETYPE_REG); +	if (status == 0) +		goto fail; + +	if (!fdiro->inode_read) { +		status = ext4fs_read_inode(fdiro->data, fdiro->ino, +				&fdiro->inode); +		if (status == 0) +			goto fail; +	} +	len = __le32_to_cpu(fdiro->inode.size); +	ext4fs_file = fdiro; + +	return len; +fail: +	ext4fs_free_node(fdiro, &ext4fs_root->diropen); + +	return -1; +} + +int ext4fs_mount(unsigned part_length) +{ +	struct ext2_data *data; +	int status; +	struct ext_filesystem *fs = get_fs(); +	data = zalloc(sizeof(struct ext2_data)); +	if (!data) +		return 0; + +	/* Read the superblock. */ +	status = ext4fs_devread(1 * 2, 0, sizeof(struct ext2_sblock), +				(char *)&data->sblock); + +	if (status == 0) +		goto fail; + +	/* Make sure this is an ext2 filesystem. */ +	if (__le16_to_cpu(data->sblock.magic) != EXT2_MAGIC) +		goto fail; + +	if (__le32_to_cpu(data->sblock.revision_level == 0)) +		fs->inodesz = 128; +	else +		fs->inodesz = __le16_to_cpu(data->sblock.inode_size); + +	debug("EXT2 rev %d, inode_size %d\n", +	       __le32_to_cpu(data->sblock.revision_level), fs->inodesz); + +	data->diropen.data = data; +	data->diropen.ino = 2; +	data->diropen.inode_read = 1; +	data->inode = &data->diropen.inode; + +	status = ext4fs_read_inode(data, 2, data->inode); +	if (status == 0) +		goto fail; + +	ext4fs_root = data; + +	return 1; +fail: +	printf("Failed to mount ext2 filesystem...\n"); +	free(data); +	ext4fs_root = NULL; + +	return 0; +} |