diff options
Diffstat (limited to 'fs/ext4/ext4_common.c')
| -rw-r--r-- | fs/ext4/ext4_common.c | 1353 | 
1 files changed, 1353 insertions, 0 deletions
| 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, |