diff options
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/Makefile | 5 | ||||
| -rw-r--r-- | fs/ext2/dev.c | 131 | ||||
| -rw-r--r-- | fs/ext2/ext2fs.c | 919 | ||||
| -rw-r--r-- | fs/ext4/Makefile (renamed from fs/ext2/Makefile) | 9 | ||||
| -rw-r--r-- | fs/ext4/crc16.c | 62 | ||||
| -rw-r--r-- | fs/ext4/crc16.h | 16 | ||||
| -rw-r--r-- | fs/ext4/dev.c | 145 | ||||
| -rw-r--r-- | fs/ext4/ext4_common.c | 2228 | ||||
| -rw-r--r-- | fs/ext4/ext4_common.h | 93 | ||||
| -rw-r--r-- | fs/ext4/ext4_journal.c | 667 | ||||
| -rw-r--r-- | fs/ext4/ext4_journal.h | 141 | ||||
| -rw-r--r-- | fs/ext4/ext4fs.c | 1189 | ||||
| -rw-r--r-- | fs/fat/fat.c | 172 | ||||
| -rw-r--r-- | fs/ubifs/ubifs.c | 15 | ||||
| -rw-r--r-- | fs/yaffs2/Makefile | 2 | ||||
| -rw-r--r-- | fs/yaffs2/stdio.h | 1 | ||||
| -rw-r--r-- | fs/yaffs2/stdlib.h | 1 | ||||
| -rw-r--r-- | fs/yaffs2/string.h | 4 | ||||
| -rw-r--r-- | fs/yaffs2/yaffs_hweight.c | 52 | ||||
| -rw-r--r-- | fs/yaffs2/yaffs_hweight.h | 24 | ||||
| -rw-r--r-- | fs/yaffs2/yaffs_mtdif2.c | 3 | ||||
| -rw-r--r-- | fs/yaffs2/yaffsfs.c | 2 | ||||
| -rw-r--r-- | fs/yaffs2/ydirectenv.h | 16 | ||||
| -rw-r--r-- | fs/yaffs2/yportenv.h | 1 | 
24 files changed, 4656 insertions, 1242 deletions
| diff --git a/fs/Makefile b/fs/Makefile index 28da76e95..901e1894b 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -23,7 +23,10 @@  #  subdirs-$(CONFIG_CMD_CRAMFS) := cramfs -subdirs-$(CONFIG_CMD_EXT2) += ext2 +subdirs-$(CONFIG_CMD_EXT4) += ext4 +ifndef CONFIG_CMD_EXT4 +subdirs-$(CONFIG_CMD_EXT2) += ext4 +endif  subdirs-$(CONFIG_CMD_FAT) += fat  subdirs-$(CONFIG_CMD_FDOS) += fdos  subdirs-$(CONFIG_CMD_JFFS2) += jffs2 diff --git a/fs/ext2/dev.c b/fs/ext2/dev.c deleted file mode 100644 index 874e21161..000000000 --- a/fs/ext2/dev.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * (C) Copyright 2004 - *  esd gmbh <www.esd-electronics.com> - *  Reinhard Arlt <reinhard.arlt@esd-electronics.com> - * - *  based on code of fs/reiserfs/dev.c by - * - *  (C) Copyright 2003 - 2004 - *  Sysgo AG, <www.elinos.com>, Pavel Bartusek <pba@sysgo.com> - * - *  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 <config.h> -#include <ext2fs.h> - -static block_dev_desc_t *ext2fs_block_dev_desc; -static disk_partition_t part_info; - -int ext2fs_set_blk_dev(block_dev_desc_t *rbdd, int part) -{ -	ext2fs_block_dev_desc = rbdd; - -	if (part == 0) { -		/* disk doesn't use partition table */ -		part_info.start = 0; -		part_info.size = rbdd->lba; -		part_info.blksz = rbdd->blksz; -	} else { -		if (get_partition_info -		    (ext2fs_block_dev_desc, part, &part_info)) { -			return 0; -		} -	} -	return part_info.size; -} - - -int ext2fs_devread(int sector, int byte_offset, int byte_len, char *buf) -{ -	ALLOC_CACHE_ALIGN_BUFFER(char, sec_buf, SECTOR_SIZE); -	unsigned sectors; - -	/* -	 *  Check partition boundaries -	 */ -	if ((sector < 0) || -	    ((sector + ((byte_offset + byte_len - 1) >> SECTOR_BITS)) >= -		part_info.size)) { -		/* errnum = ERR_OUTSIDE_PART; */ -		printf(" ** %s read outside partition sector %d\n", -		       __func__, -		       sector); -		return 0; -	} - -	/* -	 *  Get the read to the beginning of a partition. -	 */ -	sector += byte_offset >> SECTOR_BITS; -	byte_offset &= SECTOR_SIZE - 1; - -	debug(" <%d, %d, %d>\n", sector, byte_offset, byte_len); - -	if (ext2fs_block_dev_desc == NULL) { -		printf(" ** %s Invalid Block Device Descriptor (NULL)\n", -		       __func__); -		return 0; -	} - -	if (byte_offset != 0) { -		/* read first part which isn't aligned with start of sector */ -		if (ext2fs_block_dev_desc-> -		    block_read(ext2fs_block_dev_desc->dev, -			       part_info.start + sector, 1, -			       (unsigned long *) sec_buf) != 1) { -			printf(" ** %s read error **\n", __func__); -			return 0; -		} -		memcpy(buf, sec_buf + byte_offset, -		       min(SECTOR_SIZE - byte_offset, byte_len)); -		buf += min(SECTOR_SIZE - byte_offset, byte_len); -		byte_len -= min(SECTOR_SIZE - byte_offset, byte_len); -		sector++; -	} - -	/*  read sector aligned part */ -	sectors = byte_len / SECTOR_SIZE; - -	if (sectors > 0) { -		if (ext2fs_block_dev_desc->block_read( -			ext2fs_block_dev_desc->dev, -			part_info.start + sector, -			sectors, -			(unsigned long *) buf) != sectors) { -			printf(" ** %s read error - block\n", __func__); -			return 0; -		} - -		buf += sectors * SECTOR_SIZE; -		byte_len -= sectors * SECTOR_SIZE; -		sector += sectors; -	} - -	if (byte_len != 0) { -		/* read rest of data which are not in whole sector */ -		if (ext2fs_block_dev_desc-> -		    block_read(ext2fs_block_dev_desc->dev, -			       part_info.start + sector, 1, -			       (unsigned long *) sec_buf) != 1) { -			printf(" ** %s read error - last part\n", __func__); -			return 0; -		} -		memcpy(buf, sec_buf, byte_len); -	} -	return 1; -} diff --git a/fs/ext2/ext2fs.c b/fs/ext2/ext2fs.c deleted file mode 100644 index 418404e60..000000000 --- a/fs/ext2/ext2fs.c +++ /dev/null @@ -1,919 +0,0 @@ -/* - * (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. - * - *  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 <ext2fs.h> -#include <malloc.h> -#include <asm/byteorder.h> - -extern int ext2fs_devread (int sector, int byte_offset, int byte_len, -			   char *buf); - -/* Magic value used to identify an ext2 filesystem.  */ -#define	EXT2_MAGIC		0xEF53 -/* Amount of indirect blocks in an inode.  */ -#define INDIRECT_BLOCKS		12 -/* Maximum lenght of a pathname.  */ -#define EXT2_PATH_MAX		4096 -/* Maximum nesting of symlinks, used to prevent a loop.  */ -#define	EXT2_MAX_SYMLINKCNT	8 - -/* Filetype used in directory entry.  */ -#define	FILETYPE_UNKNOWN	0 -#define	FILETYPE_REG		1 -#define	FILETYPE_DIRECTORY	2 -#define	FILETYPE_SYMLINK	7 - -/* Filetype information as used in inodes.  */ -#define FILETYPE_INO_MASK	0170000 -#define FILETYPE_INO_REG	0100000 -#define FILETYPE_INO_DIRECTORY	0040000 -#define FILETYPE_INO_SYMLINK	0120000 - -/* Bits used as offset in sector */ -#define DISK_SECTOR_BITS        9 - -/* Log2 size of ext2 block in 512 blocks.  */ -#define LOG2_EXT2_BLOCK_SIZE(data) (__le32_to_cpu (data->sblock.log2_block_size) + 1) - -/* Log2 size of ext2 block in bytes.  */ -#define LOG2_BLOCK_SIZE(data)	   (__le32_to_cpu (data->sblock.log2_block_size) + 10) - -/* The size of an ext2 block in bytes.  */ -#define EXT2_BLOCK_SIZE(data)	   (1 << LOG2_BLOCK_SIZE(data)) - -/* The ext2 superblock.  */ -struct ext2_sblock { -	uint32_t total_inodes; -	uint32_t total_blocks; -	uint32_t reserved_blocks; -	uint32_t free_blocks; -	uint32_t free_inodes; -	uint32_t first_data_block; -	uint32_t log2_block_size; -	uint32_t log2_fragment_size; -	uint32_t blocks_per_group; -	uint32_t fragments_per_group; -	uint32_t inodes_per_group; -	uint32_t mtime; -	uint32_t utime; -	uint16_t mnt_count; -	uint16_t max_mnt_count; -	uint16_t magic; -	uint16_t fs_state; -	uint16_t error_handling; -	uint16_t minor_revision_level; -	uint32_t lastcheck; -	uint32_t checkinterval; -	uint32_t creator_os; -	uint32_t revision_level; -	uint16_t uid_reserved; -	uint16_t gid_reserved; -	uint32_t first_inode; -	uint16_t inode_size; -	uint16_t block_group_number; -	uint32_t feature_compatibility; -	uint32_t feature_incompat; -	uint32_t feature_ro_compat; -	uint32_t unique_id[4]; -	char volume_name[16]; -	char last_mounted_on[64]; -	uint32_t compression_info; -}; - -/* The ext2 blockgroup.  */ -struct ext2_block_group { -	uint32_t block_id; -	uint32_t inode_id; -	uint32_t inode_table_id; -	uint16_t free_blocks; -	uint16_t free_inodes; -	uint16_t used_dir_cnt; -	uint32_t reserved[3]; -}; - -/* The ext2 inode.  */ -struct ext2_inode { -	uint16_t mode; -	uint16_t uid; -	uint32_t size; -	uint32_t atime; -	uint32_t ctime; -	uint32_t mtime; -	uint32_t dtime; -	uint16_t gid; -	uint16_t nlinks; -	uint32_t blockcnt;	/* Blocks of 512 bytes!! */ -	uint32_t flags; -	uint32_t osd1; -	union { -		struct datablocks { -			uint32_t dir_blocks[INDIRECT_BLOCKS]; -			uint32_t indir_block; -			uint32_t double_indir_block; -			uint32_t tripple_indir_block; -		} blocks; -		char symlink[60]; -	} b; -	uint32_t version; -	uint32_t acl; -	uint32_t dir_acl; -	uint32_t fragment_addr; -	uint32_t osd2[3]; -}; - -/* The header of an ext2 directory entry.  */ -struct ext2_dirent { -	uint32_t inode; -	uint16_t direntlen; -	uint8_t namelen; -	uint8_t filetype; -}; - -struct ext2fs_node { -	struct ext2_data *data; -	struct ext2_inode inode; -	int ino; -	int inode_read; -}; - -/* Information about a "mounted" ext2 filesystem.  */ -struct ext2_data { -	struct ext2_sblock sblock; -	struct ext2_inode *inode; -	struct ext2fs_node diropen; -}; - - -typedef struct ext2fs_node *ext2fs_node_t; - -struct ext2_data *ext2fs_root = NULL; -ext2fs_node_t ext2fs_file = NULL; -int symlinknest = 0; -uint32_t *indir1_block = NULL; -int indir1_size = 0; -int indir1_blkno = -1; -uint32_t *indir2_block = NULL; -int indir2_size = 0; -int indir2_blkno = -1; -static unsigned int inode_size; - - -static int ext2fs_blockgroup -	(struct ext2_data *data, int group, struct ext2_block_group *blkgrp) { -	unsigned int blkno; -	unsigned int blkoff; -	unsigned int 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); -#ifdef DEBUG -	printf ("ext2fs read %d group descriptor (blkno %d blkoff %d)\n", -		group, blkno, blkoff); -#endif -	return (ext2fs_devread (blkno << LOG2_EXT2_BLOCK_SIZE(data), -		blkoff, sizeof(struct ext2_block_group), (char *)blkgrp)); - -} - - -static int ext2fs_read_inode -	(struct ext2_data *data, int ino, struct ext2_inode *inode) { -	struct ext2_block_group blkgrp; -	struct ext2_sblock *sblock = &data->sblock; -	int inodes_per_block; -	int status; - -	unsigned int blkno; -	unsigned int blkoff; - -#ifdef DEBUG -	printf ("ext2fs read inode %d, inode_size %d\n", ino, inode_size); -#endif -	/* It is easier to calculate if the first inode is 0.  */ -	ino--; -	status = ext2fs_blockgroup (data, ino / __le32_to_cpu -				    (sblock->inodes_per_group), &blkgrp); -	if (status == 0) { -		return (0); -	} - -	inodes_per_block = EXT2_BLOCK_SIZE(data) / inode_size; - -	blkno = __le32_to_cpu (blkgrp.inode_table_id) + -		(ino % __le32_to_cpu (sblock->inodes_per_group)) -		/ inodes_per_block; -	blkoff = (ino % inodes_per_block) * inode_size; -#ifdef DEBUG -	printf ("ext2fs read inode blkno %d blkoff %d\n", blkno, blkoff); -#endif -	/* Read the inode.  */ -	status = ext2fs_devread (blkno << LOG2_EXT2_BLOCK_SIZE (data), blkoff, -				 sizeof (struct ext2_inode), (char *) inode); -	if (status == 0) { -		return (0); -	} - -	return (1); -} - - -void ext2fs_free_node (ext2fs_node_t node, ext2fs_node_t currroot) { -	if ((node != &ext2fs_root->diropen) && (node != currroot)) { -		free (node); -	} -} - - -static int ext2fs_read_block (ext2fs_node_t node, int fileblock) { -	struct ext2_data *data = node->data; -	struct ext2_inode *inode = &node->inode; -	int blknr; -	int blksz = EXT2_BLOCK_SIZE (data); -	int log2_blksz = LOG2_EXT2_BLOCK_SIZE (data); -	int status; - -	/* 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 (indir1_block == NULL) { -			indir1_block = (uint32_t *) memalign(ARCH_DMA_MINALIGN, -							     blksz); -			if (indir1_block == NULL) { -				printf ("** ext2fs read block (indir 1) malloc failed. **\n"); -				return (-1); -			} -			indir1_size = blksz; -			indir1_blkno = -1; -		} -		if (blksz != indir1_size) { -			free (indir1_block); -			indir1_block = NULL; -			indir1_size = 0; -			indir1_blkno = -1; -			indir1_block = (uint32_t *) memalign(ARCH_DMA_MINALIGN, -							     blksz); -			if (indir1_block == NULL) { -				printf ("** ext2fs read block (indir 1) malloc failed. **\n"); -				return (-1); -			} -			indir1_size = blksz; -		} -		if ((__le32_to_cpu (inode->b.blocks.indir_block) << -		     log2_blksz) != indir1_blkno) { -			status = ext2fs_devread (__le32_to_cpu(inode->b.blocks.indir_block) << log2_blksz, -						 0, blksz, -						 (char *) indir1_block); -			if (status == 0) { -				printf ("** ext2fs read block (indir 1) failed. **\n"); -				return (0); -			} -			indir1_blkno = -				__le32_to_cpu (inode->b.blocks. -					       indir_block) << log2_blksz; -		} -		blknr = __le32_to_cpu (indir1_block -				       [fileblock - INDIRECT_BLOCKS]); -	} -	/* Double indirect.  */ -	else if (fileblock < -		 (INDIRECT_BLOCKS + (blksz / 4 * (blksz / 4 + 1)))) { -		unsigned int perblock = blksz / 4; -		unsigned int rblock = fileblock - (INDIRECT_BLOCKS -						   + blksz / 4); - -		if (indir1_block == NULL) { -			indir1_block = (uint32_t *) memalign(ARCH_DMA_MINALIGN, -							     blksz); -			if (indir1_block == NULL) { -				printf ("** ext2fs read block (indir 2 1) malloc failed. **\n"); -				return (-1); -			} -			indir1_size = blksz; -			indir1_blkno = -1; -		} -		if (blksz != indir1_size) { -			free (indir1_block); -			indir1_block = NULL; -			indir1_size = 0; -			indir1_blkno = -1; -			indir1_block = (uint32_t *) memalign(ARCH_DMA_MINALIGN, -							     blksz); -			if (indir1_block == NULL) { -				printf ("** ext2fs read block (indir 2 1) malloc failed. **\n"); -				return (-1); -			} -			indir1_size = blksz; -		} -		if ((__le32_to_cpu (inode->b.blocks.double_indir_block) << -		     log2_blksz) != indir1_blkno) { -			status = ext2fs_devread (__le32_to_cpu(inode->b.blocks.double_indir_block) << log2_blksz, -						0, blksz, -						(char *) indir1_block); -			if (status == 0) { -				printf ("** ext2fs read block (indir 2 1) failed. **\n"); -				return (-1); -			} -			indir1_blkno = -				__le32_to_cpu (inode->b.blocks.double_indir_block) << log2_blksz; -		} - -		if (indir2_block == NULL) { -			indir2_block = (uint32_t *) memalign(ARCH_DMA_MINALIGN, -							     blksz); -			if (indir2_block == NULL) { -				printf ("** ext2fs read block (indir 2 2) malloc failed. **\n"); -				return (-1); -			} -			indir2_size = blksz; -			indir2_blkno = -1; -		} -		if (blksz != indir2_size) { -			free (indir2_block); -			indir2_block = NULL; -			indir2_size = 0; -			indir2_blkno = -1; -			indir2_block = (uint32_t *) memalign(ARCH_DMA_MINALIGN, -							     blksz); -			if (indir2_block == NULL) { -				printf ("** ext2fs read block (indir 2 2) malloc failed. **\n"); -				return (-1); -			} -			indir2_size = blksz; -		} -		if ((__le32_to_cpu (indir1_block[rblock / perblock]) << -		     log2_blksz) != indir2_blkno) { -			status = ext2fs_devread (__le32_to_cpu(indir1_block[rblock / perblock]) << log2_blksz, -						 0, blksz, -						 (char *) indir2_block); -			if (status == 0) { -				printf ("** ext2fs read block (indir 2 2) failed. **\n"); -				return (-1); -			} -			indir2_blkno = -				__le32_to_cpu (indir1_block[rblock / perblock]) << log2_blksz; -		} -		blknr = __le32_to_cpu (indir2_block[rblock % perblock]); -	} -	/* Tripple indirect.  */ -	else { -		printf ("** ext2fs doesn't support tripple indirect blocks. **\n"); -		return (-1); -	} -#ifdef DEBUG -	printf ("ext2fs_read_block %08x\n", blknr); -#endif -	return (blknr); -} - - -int ext2fs_read_file -	(ext2fs_node_t node, int pos, unsigned int len, char *buf) { -	int i; -	int blockcnt; -	int log2blocksize = LOG2_EXT2_BLOCK_SIZE (node->data); -	int blocksize = 1 << (log2blocksize + DISK_SECTOR_BITS); -	unsigned int filesize = __le32_to_cpu(node->inode.size); - -	/* Adjust len so it we can't read past the end of the file.  */ -	if (len > filesize) { -		len = filesize; -	} -	blockcnt = ((len + pos) + blocksize - 1) / blocksize; - -	for (i = pos / blocksize; i < blockcnt; i++) { -		int blknr; -		int blockoff = pos % blocksize; -		int blockend = blocksize; - -		int skipfirst = 0; - -		blknr = ext2fs_read_block (node, i); -		if (blknr < 0) { -			return (-1); -		} - -		/* Last block.  */ -		if (i == blockcnt - 1) { -			blockend = (len + pos) % blocksize; - -			/* The last portion is exactly blocksize.  */ -			if (!blockend) { -				blockend = blocksize; -			} -		} - -		/* First block.  */ -		if (i == pos / blocksize) { -			skipfirst = blockoff; -			blockend -= skipfirst; -		} - -		/* grab middle blocks in one go */ -		if (i != pos / blocksize && i < blockcnt - 1 && blockcnt > 3) { -			int oldblk = blknr; -			int blocknxt = ext2fs_read_block(node, i + 1); -			while (i < blockcnt - 1) { -				if (blocknxt == (oldblk + 1)) { -					oldblk = blocknxt; -					i++; -				} else { -					blocknxt = ext2fs_read_block(node, i); -					break; -				} -				blocknxt = ext2fs_read_block(node, i); -			} - -			if (oldblk == blknr) -				blockend = blocksize; -			else -				blockend = (1 + blocknxt - blknr) * blocksize; -		} - -		blknr = blknr << log2blocksize; - -		/* If the block number is 0 this block is not stored on disk but -		   is zero filled instead.  */ -		if (blknr) { -			int status; - -			status = ext2fs_devread (blknr, skipfirst, blockend, buf); -			if (status == 0) { -				return (-1); -			} -		} else { -			memset (buf, 0, blocksize - skipfirst); -		} -		buf += blockend - skipfirst; -	} -	return (len); -} - - -static int ext2fs_iterate_dir (ext2fs_node_t dir, char *name, ext2fs_node_t * 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 = ext2fs_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 = ext2fs_read_file (diro, fpos, -					   sizeof (struct ext2_dirent), -					   (char *) &dirent); -		if (status < 1) { -			return (0); -		} -		if (dirent.namelen != 0) { -			char filename[dirent.namelen + 1]; -			ext2fs_node_t fdiro; -			int type = FILETYPE_UNKNOWN; - -			status = ext2fs_read_file (diro, -						   fpos + sizeof (struct ext2_dirent), -						   dirent.namelen, filename); -			if (status < 1) { -				return (0); -			} -			fdiro = malloc (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 { -				/* The filetype can not be read from the dirent, get it from inode */ - -				status = ext2fs_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 = ext2fs_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 *ext2fs_read_symlink (ext2fs_node_t node) { -	char *symlink; -	struct ext2fs_node *diro = node; -	int status; - -	if (!diro->inode_read) { -		status = ext2fs_read_inode (diro->data, diro->ino, -					    &diro->inode); -		if (status == 0) { -			return (0); -		} -	} -	symlink = malloc (__le32_to_cpu (diro->inode.size) + 1); -	if (!symlink) { -		return (0); -	} -	/* If the filesize of the symlink is bigger than -	   60 the symlink is stored in a separate block, -	   otherwise it is stored in the inode.  */ -	if (__le32_to_cpu (diro->inode.size) <= 60) { -		strncpy (symlink, diro->inode.b.symlink, -			 __le32_to_cpu (diro->inode.size)); -	} else { -		status = ext2fs_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); -} - - -int ext2fs_find_file1 -	(const char *currpath, -	 ext2fs_node_t currroot, ext2fs_node_t * currfound, int *foundtype) { -	char fpath[strlen (currpath) + 1]; -	char *name = fpath; -	char *next; -	int status; -	int type = FILETYPE_DIRECTORY; -	ext2fs_node_t currnode = currroot; -	ext2fs_node_t 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'; -			} -		} - -		/* At this point it is expected that the current node is a directory, check if this is true.  */ -		if (type != FILETYPE_DIRECTORY) { -			ext2fs_free_node (currnode, currroot); -			return (0); -		} - -		oldnode = currnode; - -		/* Iterate over the directory.  */ -		found = ext2fs_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) { -				ext2fs_free_node (currnode, currroot); -				ext2fs_free_node (oldnode, currroot); -				return (0); -			} - -			symlink = ext2fs_read_symlink (currnode); -			ext2fs_free_node (currnode, currroot); - -			if (!symlink) { -				ext2fs_free_node (oldnode, currroot); -				return (0); -			} -#ifdef DEBUG -			printf ("Got symlink >%s<\n", symlink); -#endif /* of DEBUG */ -			/* The symlink is an absolute path, go back to the root inode.  */ -			if (symlink[0] == '/') { -				ext2fs_free_node (oldnode, currroot); -				oldnode = &ext2fs_root->diropen; -			} - -			/* Lookup the node the symlink points to.  */ -			status = ext2fs_find_file1 (symlink, oldnode, -						    &currnode, &type); - -			free (symlink); - -			if (status == 0) { -				ext2fs_free_node (oldnode, currroot); -				return (0); -			} -		} - -		ext2fs_free_node (oldnode, currroot); - -		/* Found the node!  */ -		if (!next || *next == '\0') { -			*currfound = currnode; -			*foundtype = type; -			return (1); -		} -		name = next; -	} -	return (-1); -} - - -int ext2fs_find_file -	(const char *path, -	 ext2fs_node_t rootnode, ext2fs_node_t * foundnode, int expecttype) { -	int status; -	int foundtype = FILETYPE_DIRECTORY; - - -	symlinknest = 0; -	if (!path) { -		return (0); -	} - -	status = ext2fs_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 ext2fs_ls (const char *dirname) { -	ext2fs_node_t dirnode; -	int status; - -	if (ext2fs_root == NULL) { -		return (0); -	} - -	status = ext2fs_find_file (dirname, &ext2fs_root->diropen, &dirnode, -				   FILETYPE_DIRECTORY); -	if (status != 1) { -		printf ("** Can not find directory. **\n"); -		return (1); -	} -	ext2fs_iterate_dir (dirnode, NULL, NULL, NULL); -	ext2fs_free_node (dirnode, &ext2fs_root->diropen); -	return (0); -} - - -int ext2fs_open (const char *filename) { -	ext2fs_node_t fdiro = NULL; -	int status; -	int len; - -	if (ext2fs_root == NULL) { -		return (-1); -	} -	ext2fs_file = NULL; -	status = ext2fs_find_file (filename, &ext2fs_root->diropen, &fdiro, -				   FILETYPE_REG); -	if (status == 0) { -		goto fail; -	} -	if (!fdiro->inode_read) { -		status = ext2fs_read_inode (fdiro->data, fdiro->ino, -					    &fdiro->inode); -		if (status == 0) { -			goto fail; -		} -	} -	len = __le32_to_cpu (fdiro->inode.size); -	ext2fs_file = fdiro; -	return (len); - -fail: -	ext2fs_free_node (fdiro, &ext2fs_root->diropen); -	return (-1); -} - - -int ext2fs_close (void -	) { -	if ((ext2fs_file != NULL) && (ext2fs_root != NULL)) { -		ext2fs_free_node (ext2fs_file, &ext2fs_root->diropen); -		ext2fs_file = NULL; -	} -	if (ext2fs_root != NULL) { -		free (ext2fs_root); -		ext2fs_root = NULL; -	} -	if (indir1_block != NULL) { -		free (indir1_block); -		indir1_block = NULL; -		indir1_size = 0; -		indir1_blkno = -1; -	} -	if (indir2_block != NULL) { -		free (indir2_block); -		indir2_block = NULL; -		indir2_size = 0; -		indir2_blkno = -1; -	} -	return (0); -} - - -int ext2fs_read (char *buf, unsigned len) { -	int status; - -	if (ext2fs_root == NULL) { -		return (0); -	} - -	if (ext2fs_file == NULL) { -		return (0); -	} - -	status = ext2fs_read_file (ext2fs_file, 0, len, buf); -	return (status); -} - - -int ext2fs_mount (unsigned part_length) { -	struct ext2_data *data; -	int status; - -	data = malloc (sizeof (struct ext2_data)); -	if (!data) { -		return (0); -	} -	/* Read the superblock.  */ -	status = ext2fs_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)) { -		inode_size = 128; -	} else { -		inode_size = __le16_to_cpu(data->sblock.inode_size); -	} -#ifdef DEBUG -	printf("EXT2 rev %d, inode_size %d\n", -			__le32_to_cpu(data->sblock.revision_level), inode_size); -#endif -	data->diropen.data = data; -	data->diropen.ino = 2; -	data->diropen.inode_read = 1; -	data->inode = &data->diropen.inode; - -	status = ext2fs_read_inode (data, 2, data->inode); -	if (status == 0) { -		goto fail; -	} - -	ext2fs_root = data; - -	return (1); - -fail: -	printf ("Failed to mount ext2 filesystem...\n"); -	free (data); -	ext2fs_root = NULL; -	return (0); -} diff --git a/fs/ext2/Makefile b/fs/ext4/Makefile index 3c65d252f..82cd9ae16 100644 --- a/fs/ext2/Makefile +++ b/fs/ext4/Makefile @@ -27,15 +27,18 @@  include $(TOPDIR)/config.mk -LIB	= $(obj)libext2fs.o +LIB	= $(obj)libext4fs.o  AOBJS	= -COBJS-$(CONFIG_CMD_EXT2) := ext2fs.o dev.o +COBJS-$(CONFIG_CMD_EXT4) := ext4fs.o ext4_common.o dev.o +ifndef CONFIG_CMD_EXT4 +COBJS-$(CONFIG_CMD_EXT2) := ext4fs.o ext4_common.o dev.o +endif +COBJS-$(CONFIG_CMD_EXT4_WRITE) += ext4_journal.o crc16.o  SRCS	:= $(AOBJS:.o=.S) $(COBJS-y:.o=.c)  OBJS	:= $(addprefix $(obj),$(AOBJS) $(COBJS-y)) -#CPPFLAGS +=  all:	$(LIB) $(AOBJS) diff --git a/fs/ext4/crc16.c b/fs/ext4/crc16.c new file mode 100644 index 000000000..3afb34dae --- /dev/null +++ b/fs/ext4/crc16.c @@ -0,0 +1,62 @@ +/* + *      crc16.c + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <common.h> +#include <asm/byteorder.h> +#include <linux/stat.h> +#include "crc16.h" + +/** CRC table for the CRC-16. The poly is 0x8005 (x16 + x15 + x2 + 1) */ +static __u16 const crc16_table[256] = { +	0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, +	0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, +	0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, +	0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, +	0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, +	0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, +	0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, +	0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, +	0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, +	0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, +	0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, +	0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, +	0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, +	0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, +	0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, +	0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, +	0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, +	0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, +	0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, +	0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, +	0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, +	0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, +	0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, +	0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, +	0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, +	0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, +	0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, +	0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, +	0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, +	0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, +	0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, +	0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 +}; + +/** + * Compute the CRC-16 for the data buffer +*/ + +unsigned int ext2fs_crc16(unsigned int crc, +	const void *buffer, unsigned int len) +{ +	const unsigned char *cp = buffer; + +	while (len--) +		crc = (((crc >> 8) & 0xffU) ^ +		       crc16_table[(crc ^ *cp++) & 0xffU]) & 0x0000ffffU; +	return crc; +} diff --git a/fs/ext4/crc16.h b/fs/ext4/crc16.h new file mode 100644 index 000000000..5fd113a56 --- /dev/null +++ b/fs/ext4/crc16.h @@ -0,0 +1,16 @@ +/* + * crc16.h - CRC-16 routine + * Implements the standard CRC-16: + *  Width 16 + *  Poly  0x8005 (x16 + x15 + x2 + 1) + *  Init  0 + * + * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com> + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ +#ifndef __CRC16_H +#define __CRC16_H +extern unsigned int ext2fs_crc16(unsigned int crc, +	const void *buffer, unsigned int len); +#endif diff --git a/fs/ext4/dev.c b/fs/ext4/dev.c new file mode 100644 index 000000000..9e85228ed --- /dev/null +++ b/fs/ext4/dev.c @@ -0,0 +1,145 @@ +/* + * (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> + * + * made from existing ext2/dev.c file of Uboot + * (C) Copyright 2004 + * esd gmbh <www.esd-electronics.com> + * Reinhard Arlt <reinhard.arlt@esd-electronics.com> + * + * based on code of fs/reiserfs/dev.c by + * + * (C) Copyright 2003 - 2004 + * Sysgo AG, <www.elinos.com>, Pavel Bartusek <pba@sysgo.com> + * + * 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. + * + */ + +/* + * Changelog: + *	0.1 - Newly created file for ext4fs support. Taken from + *		fs/ext2/dev.c file in uboot. + */ + +#include <common.h> +#include <config.h> +#include <ext_common.h> + +static block_dev_desc_t *ext4fs_block_dev_desc; +static disk_partition_t part_info; + +int ext4fs_set_blk_dev(block_dev_desc_t *rbdd, int part) +{ +	ext4fs_block_dev_desc = rbdd; + +	if (part == 0) { +		/* disk doesn't use partition table */ +		part_info.start = 0; +		part_info.size = rbdd->lba; +		part_info.blksz = rbdd->blksz; +	} else { +		if (get_partition_info(ext4fs_block_dev_desc, +					part, &part_info)) +			return 0; +	} +	return part_info.size; +} + +int ext4fs_devread(int sector, int byte_offset, int byte_len, char *buf) +{ +	ALLOC_CACHE_ALIGN_BUFFER(char, sec_buf, SECTOR_SIZE); +	unsigned block_len; + +	/* Check partition boundaries */ +	if ((sector < 0) +	    || ((sector + ((byte_offset + byte_len - 1) >> SECTOR_BITS)) >= +		part_info.size)) { +		printf("%s read outside partition %d\n", __func__, sector); +		return 0; +	} + +	/* Get the read to the beginning of a partition */ +	sector += byte_offset >> SECTOR_BITS; +	byte_offset &= SECTOR_SIZE - 1; + +	debug(" <%d, %d, %d>\n", sector, byte_offset, byte_len); + +	if (ext4fs_block_dev_desc == NULL) { +		printf("** Invalid Block Device Descriptor (NULL)\n"); +		return 0; +	} + +	if (byte_offset != 0) { +		/* read first part which isn't aligned with start of sector */ +		if (ext4fs_block_dev_desc-> +		    block_read(ext4fs_block_dev_desc->dev, +				part_info.start + sector, 1, +				(unsigned long *) sec_buf) != 1) { +			printf(" ** ext2fs_devread() read error **\n"); +			return 0; +		} +		memcpy(buf, sec_buf + byte_offset, +			min(SECTOR_SIZE - byte_offset, byte_len)); +		buf += min(SECTOR_SIZE - byte_offset, byte_len); +		byte_len -= min(SECTOR_SIZE - byte_offset, byte_len); +		sector++; +	} + +	if (byte_len == 0) +		return 1; + +	/* read sector aligned part */ +	block_len = byte_len & ~(SECTOR_SIZE - 1); + +	if (block_len == 0) { +		ALLOC_CACHE_ALIGN_BUFFER(u8, p, SECTOR_SIZE); + +		block_len = SECTOR_SIZE; +		ext4fs_block_dev_desc->block_read(ext4fs_block_dev_desc->dev, +						  part_info.start + sector, +						  1, (unsigned long *)p); +		memcpy(buf, p, byte_len); +		return 1; +	} + +	if (ext4fs_block_dev_desc->block_read(ext4fs_block_dev_desc->dev, +					       part_info.start + sector, +					       block_len / SECTOR_SIZE, +					       (unsigned long *) buf) != +					       block_len / SECTOR_SIZE) { +		printf(" ** %s read error - block\n", __func__); +		return 0; +	} +	block_len = byte_len & ~(SECTOR_SIZE - 1); +	buf += block_len; +	byte_len -= block_len; +	sector += block_len / SECTOR_SIZE; + +	if (byte_len != 0) { +		/* read rest of data which are not in whole sector */ +		if (ext4fs_block_dev_desc-> +		    block_read(ext4fs_block_dev_desc->dev, +				part_info.start + sector, 1, +				(unsigned long *) sec_buf) != 1) { +			printf("* %s read error - last part\n", __func__); +			return 0; +		} +		memcpy(buf, sec_buf, byte_len); +	} +	return 1; +} 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; +} diff --git a/fs/ext4/ext4_common.h b/fs/ext4/ext4_common.h new file mode 100644 index 000000000..0af625db2 --- /dev/null +++ b/fs/ext4/ext4_common.h @@ -0,0 +1,93 @@ +/* + * (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. + */ + +#ifndef __EXT4_COMMON__ +#define __EXT4_COMMON__ +#include <ext_common.h> +#include <ext4fs.h> +#include <malloc.h> +#include <asm/errno.h> +#if defined(CONFIG_CMD_EXT4_WRITE) +#include "ext4_journal.h" +#include "crc16.h" +#endif + +#define YES		1 +#define NO		0 +#define TRUE		1 +#define FALSE		0 +#define RECOVER	1 +#define SCAN		0 + +#define S_IFLNK		0120000		/* symbolic link */ +#define BLOCK_NO_ONE		1 +#define SUPERBLOCK_SECTOR	2 +#define SUPERBLOCK_SIZE	1024 +#define F_FILE			1 + +static inline void *zalloc(size_t size) +{ +	void *p = memalign(ARCH_DMA_MINALIGN, size); +	memset(p, 0, size); +	return p; +} + +extern unsigned long part_offset; +int ext4fs_read_inode(struct ext2_data *data, int ino, +		      struct ext2_inode *inode); +int ext4fs_read_file(struct ext2fs_node *node, int pos, +		unsigned int len, char *buf); +int ext4fs_find_file(const char *path, struct ext2fs_node *rootnode, +			struct ext2fs_node **foundnode, int expecttype); +int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name, +			struct ext2fs_node **fnode, int *ftype); + +#if defined(CONFIG_CMD_EXT4_WRITE) +uint32_t ext4fs_div_roundup(uint32_t size, uint32_t n); +int ext4fs_checksum_update(unsigned int i); +int ext4fs_get_parent_inode_num(const char *dirname, char *dname, int flags); +void ext4fs_update_parent_dentry(char *filename, int *p_ino, int file_type); +long int ext4fs_get_new_blk_no(void); +int ext4fs_get_new_inode_no(void); +void ext4fs_reset_block_bmap(long int blockno, unsigned char *buffer, +					int index); +int ext4fs_set_block_bmap(long int blockno, unsigned char *buffer, int index); +int ext4fs_set_inode_bmap(int inode_no, unsigned char *buffer, int index); +void ext4fs_reset_inode_bmap(int inode_no, unsigned char *buffer, int index); +int ext4fs_iget(int inode_no, struct ext2_inode *inode); +void ext4fs_allocate_blocks(struct ext2_inode *file_inode, +				unsigned int total_remaining_blocks, +				unsigned int *total_no_of_block); +void put_ext4(uint64_t off, void *buf, uint32_t size); +#endif +#endif diff --git a/fs/ext4/ext4_journal.c b/fs/ext4/ext4_journal.c new file mode 100644 index 000000000..8a252d66c --- /dev/null +++ b/fs/ext4/ext4_journal.c @@ -0,0 +1,667 @@ +/* + * (C) Copyright 2011 - 2012 Samsung Electronics + * EXT4 filesystem implementation in Uboot by + * Uma Shankar <uma.shankar@samsung.com> + * Manjunatha C Achar <a.manjunatha@samsung.com> + * + * Journal data structures and headers for Journaling feature of ext4 + * have been referred from JBD2 (Journaling Block device 2) + * implementation in Linux Kernel. + * Written by Stephen C. Tweedie <sct@redhat.com> + * + * Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved + * This file is part of the Linux kernel and is made available under + * the terms of the GNU General Public License, version 2, or at your + * option, any later version, incorporated herein by reference. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <common.h> +#include <ext4fs.h> +#include <malloc.h> +#include <ext_common.h> +#include "ext4_common.h" + +static struct revoke_blk_list *revk_blk_list; +static struct revoke_blk_list *prev_node; +static int first_node = TRUE; + +int gindex; +int gd_index; +int jrnl_blk_idx; +struct journal_log *journal_ptr[MAX_JOURNAL_ENTRIES]; +struct dirty_blocks *dirty_block_ptr[MAX_JOURNAL_ENTRIES]; + +int ext4fs_init_journal(void) +{ +	int i; +	char *temp = NULL; +	struct ext_filesystem *fs = get_fs(); + +	/* init globals */ +	revk_blk_list = NULL; +	prev_node = NULL; +	gindex = 0; +	gd_index = 0; +	jrnl_blk_idx = 1; + +	for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) { +		journal_ptr[i] = zalloc(sizeof(struct journal_log)); +		if (!journal_ptr[i]) +			goto fail; +		dirty_block_ptr[i] = zalloc(sizeof(struct dirty_blocks)); +		if (!dirty_block_ptr[i]) +			goto fail; +		journal_ptr[i]->buf = NULL; +		journal_ptr[i]->blknr = -1; + +		dirty_block_ptr[i]->buf = NULL; +		dirty_block_ptr[i]->blknr = -1; +	} + +	if (fs->blksz == 4096) { +		temp = zalloc(fs->blksz); +		if (!temp) +			goto fail; +		journal_ptr[gindex]->buf = zalloc(fs->blksz); +		if (!journal_ptr[gindex]->buf) +			goto fail; +		ext4fs_devread(0, 0, fs->blksz, temp); +		memcpy(temp + SUPERBLOCK_SIZE, fs->sb, SUPERBLOCK_SIZE); +		memcpy(journal_ptr[gindex]->buf, temp, fs->blksz); +		journal_ptr[gindex++]->blknr = 0; +		free(temp); +	} else { +		journal_ptr[gindex]->buf = zalloc(fs->blksz); +		if (!journal_ptr[gindex]->buf) +			goto fail; +		memcpy(journal_ptr[gindex]->buf, fs->sb, SUPERBLOCK_SIZE); +		journal_ptr[gindex++]->blknr = 1; +	} + +	/* Check the file system state using journal super block */ +	if (ext4fs_check_journal_state(SCAN)) +		goto fail; +	/* Check the file system state using journal super block */ +	if (ext4fs_check_journal_state(RECOVER)) +		goto fail; + +	return 0; +fail: +	return -1; +} + +void ext4fs_dump_metadata(void) +{ +	struct ext_filesystem *fs = get_fs(); +	int i; +	for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) { +		if (dirty_block_ptr[i]->blknr == -1) +			break; +		put_ext4((uint64_t) ((uint64_t)dirty_block_ptr[i]->blknr * +				(uint64_t)fs->blksz), dirty_block_ptr[i]->buf, +								fs->blksz); +	} +} + +void ext4fs_free_journal(void) +{ +	int i; +	for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) { +		if (dirty_block_ptr[i]->blknr == -1) +			break; +		if (dirty_block_ptr[i]->buf) +			free(dirty_block_ptr[i]->buf); +	} + +	for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) { +		if (journal_ptr[i]->blknr == -1) +			break; +		if (journal_ptr[i]->buf) +			free(journal_ptr[i]->buf); +	} + +	for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) { +		if (journal_ptr[i]) +			free(journal_ptr[i]); +		if (dirty_block_ptr[i]) +			free(dirty_block_ptr[i]); +	} +	gindex = 0; +	gd_index = 0; +	jrnl_blk_idx = 1; +} + +int ext4fs_log_gdt(char *gd_table) +{ +	struct ext_filesystem *fs = get_fs(); +	short i; +	long int var = fs->gdtable_blkno; +	for (i = 0; i < fs->no_blk_pergdt; i++) { +		journal_ptr[gindex]->buf = zalloc(fs->blksz); +		if (!journal_ptr[gindex]->buf) +			return -ENOMEM; +		memcpy(journal_ptr[gindex]->buf, gd_table, fs->blksz); +		gd_table += fs->blksz; +		journal_ptr[gindex++]->blknr = var++; +	} + +	return 0; +} + +/* + * This function stores the backup copy of meta data in RAM + * journal_buffer -- Buffer containing meta data + * blknr -- Block number on disk of the meta data buffer + */ +int ext4fs_log_journal(char *journal_buffer, long int blknr) +{ +	struct ext_filesystem *fs = get_fs(); +	short i; + +	if (!journal_buffer) { +		printf("Invalid input arguments %s\n", __func__); +		return -EINVAL; +	} + +	for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) { +		if (journal_ptr[i]->blknr == -1) +			break; +		if (journal_ptr[i]->blknr == blknr) +			return 0; +	} + +	journal_ptr[gindex]->buf = zalloc(fs->blksz); +	if (!journal_ptr[gindex]->buf) +		return -ENOMEM; + +	memcpy(journal_ptr[gindex]->buf, journal_buffer, fs->blksz); +	journal_ptr[gindex++]->blknr = blknr; + +	return 0; +} + +/* + * This function stores the modified meta data in RAM + * metadata_buffer -- Buffer containing meta data + * blknr -- Block number on disk of the meta data buffer + */ +int ext4fs_put_metadata(char *metadata_buffer, long int blknr) +{ +	struct ext_filesystem *fs = get_fs(); +	if (!metadata_buffer) { +		printf("Invalid input arguments %s\n", __func__); +		return -EINVAL; +	} +	dirty_block_ptr[gd_index]->buf = zalloc(fs->blksz); +	if (!dirty_block_ptr[gd_index]->buf) +		return -ENOMEM; +	memcpy(dirty_block_ptr[gd_index]->buf, metadata_buffer, fs->blksz); +	dirty_block_ptr[gd_index++]->blknr = blknr; + +	return 0; +} + +void print_revoke_blks(char *revk_blk) +{ +	int offset; +	int max; +	long int blocknr; +	struct journal_revoke_header_t *header; + +	if (revk_blk == NULL) +		return; + +	header = (struct journal_revoke_header_t *) revk_blk; +	offset = sizeof(struct journal_revoke_header_t); +	max = be32_to_cpu(header->r_count); +	printf("total bytes %d\n", max); + +	while (offset < max) { +		blocknr = be32_to_cpu(*((long int *)(revk_blk + offset))); +		printf("revoke blknr is %ld\n", blocknr); +		offset += 4; +	} +} + +static struct revoke_blk_list *_get_node(void) +{ +	struct revoke_blk_list *tmp_node; +	tmp_node = zalloc(sizeof(struct revoke_blk_list)); +	if (tmp_node == NULL) +		return NULL; +	tmp_node->content = NULL; +	tmp_node->next = NULL; + +	return tmp_node; +} + +void ext4fs_push_revoke_blk(char *buffer) +{ +	struct revoke_blk_list *node = NULL; +	struct ext_filesystem *fs = get_fs(); +	if (buffer == NULL) { +		printf("buffer ptr is NULL\n"); +		return; +	} +	node = _get_node(); +	if (!node) { +		printf("_get_node: malloc failed\n"); +		return; +	} + +	node->content = zalloc(fs->blksz); +	if (node->content == NULL) +		return; +	memcpy(node->content, buffer, fs->blksz); + +	if (first_node == TRUE) { +		revk_blk_list = node; +		prev_node = node; +		 first_node = FALSE; +	} else { +		prev_node->next = node; +		prev_node = node; +	} +} + +void ext4fs_free_revoke_blks(void) +{ +	struct revoke_blk_list *tmp_node = revk_blk_list; +	struct revoke_blk_list *next_node = NULL; + +	while (tmp_node != NULL) { +		if (tmp_node->content) +			free(tmp_node->content); +		tmp_node = tmp_node->next; +	} + +	tmp_node = revk_blk_list; +	while (tmp_node != NULL) { +		next_node = tmp_node->next; +		free(tmp_node); +		tmp_node = next_node; +	} + +	revk_blk_list = NULL; +	prev_node = NULL; +	first_node = TRUE; +} + +int check_blknr_for_revoke(long int blknr, int sequence_no) +{ +	struct journal_revoke_header_t *header; +	int offset; +	int max; +	long int blocknr; +	char *revk_blk; +	struct revoke_blk_list *tmp_revk_node = revk_blk_list; +	while (tmp_revk_node != NULL) { +		revk_blk = tmp_revk_node->content; + +		header = (struct journal_revoke_header_t *) revk_blk; +		if (sequence_no < be32_to_cpu(header->r_header.h_sequence)) { +			offset = sizeof(struct journal_revoke_header_t); +			max = be32_to_cpu(header->r_count); + +			while (offset < max) { +				blocknr = be32_to_cpu(*((long int *) +						  (revk_blk + offset))); +				if (blocknr == blknr) +					goto found; +				offset += 4; +			} +		} +		tmp_revk_node = tmp_revk_node->next; +	} + +	return -1; + +found: +	return 0; +} + +/* + * This function parses the journal blocks and replays the + * suceessful transactions. A transaction is successfull + * if commit block is found for a descriptor block + * The tags in descriptor block contain the disk block + * numbers of the metadata  to be replayed + */ +void recover_transaction(int prev_desc_logical_no) +{ +	struct ext2_inode inode_journal; +	struct ext_filesystem *fs = get_fs(); +	struct journal_header_t *jdb; +	long int blknr; +	char *p_jdb; +	int ofs, flags; +	int i; +	struct ext3_journal_block_tag *tag; +	char *temp_buff = zalloc(fs->blksz); +	char *metadata_buff = zalloc(fs->blksz); +	if (!temp_buff || !metadata_buff) +		goto fail; +	i = prev_desc_logical_no; +	ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO, +			  (struct ext2_inode *)&inode_journal); +	blknr = read_allocated_block((struct ext2_inode *) +				     &inode_journal, i); +	ext4fs_devread(blknr * fs->sect_perblk, 0, fs->blksz, temp_buff); +	p_jdb = (char *)temp_buff; +	jdb = (struct journal_header_t *) temp_buff; +	ofs = sizeof(struct journal_header_t); + +	do { +		tag = (struct ext3_journal_block_tag *)&p_jdb[ofs]; +		ofs += sizeof(struct ext3_journal_block_tag); + +		if (ofs > fs->blksz) +			break; + +		flags = be32_to_cpu(tag->flags); +		if (!(flags & EXT3_JOURNAL_FLAG_SAME_UUID)) +			ofs += 16; + +		i++; +		debug("\t\ttag %u\n", be32_to_cpu(tag->block)); +		if (revk_blk_list != NULL) { +			if (check_blknr_for_revoke(be32_to_cpu(tag->block), +				be32_to_cpu(jdb->h_sequence)) == 0) +				continue; +		} +		blknr = read_allocated_block(&inode_journal, i); +		ext4fs_devread(blknr * fs->sect_perblk, 0, +			       fs->blksz, metadata_buff); +		put_ext4((uint64_t)(be32_to_cpu(tag->block) * fs->blksz), +			 metadata_buff, (uint32_t) fs->blksz); +	} while (!(flags & EXT3_JOURNAL_FLAG_LAST_TAG)); +fail: +	free(temp_buff); +	free(metadata_buff); +} + +void print_jrnl_status(int recovery_flag) +{ +	if (recovery_flag == RECOVER) +		printf("Journal Recovery Completed\n"); +	else +		printf("Journal Scan Completed\n"); +} + +int ext4fs_check_journal_state(int recovery_flag) +{ +	int i; +	int DB_FOUND = NO; +	long int blknr; +	int transaction_state = TRANSACTION_COMPLETE; +	int prev_desc_logical_no = 0; +	int curr_desc_logical_no = 0; +	int ofs, flags, block; +	struct ext2_inode inode_journal; +	struct journal_superblock_t *jsb = NULL; +	struct journal_header_t *jdb = NULL; +	char *p_jdb = NULL; +	struct ext3_journal_block_tag *tag = NULL; +	char *temp_buff = NULL; +	char *temp_buff1 = NULL; +	struct ext_filesystem *fs = get_fs(); + +	temp_buff = zalloc(fs->blksz); +	if (!temp_buff) +		return -ENOMEM; +	temp_buff1 = zalloc(fs->blksz); +	if (!temp_buff1) { +		free(temp_buff); +		return -ENOMEM; +	} + +	ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO, &inode_journal); +	blknr = read_allocated_block(&inode_journal, EXT2_JOURNAL_SUPERBLOCK); +	ext4fs_devread(blknr * fs->sect_perblk, 0, fs->blksz, temp_buff); +	jsb = (struct journal_superblock_t *) temp_buff; + +	if (fs->sb->feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) { +		if (recovery_flag == RECOVER) +			printf("Recovery required\n"); +	} else { +		if (recovery_flag == RECOVER) +			printf("File System is consistent\n"); +		goto end; +	} + +	if (be32_to_cpu(jsb->s_start) == 0) +		goto end; + +	if (!(jsb->s_feature_compat & +				cpu_to_be32(JBD2_FEATURE_COMPAT_CHECKSUM))) +		jsb->s_feature_compat |= +				cpu_to_be32(JBD2_FEATURE_COMPAT_CHECKSUM); + +	i = be32_to_cpu(jsb->s_first); +	while (1) { +		block = be32_to_cpu(jsb->s_first); +		blknr = read_allocated_block(&inode_journal, i); +		memset(temp_buff1, '\0', fs->blksz); +		ext4fs_devread(blknr * fs->sect_perblk, +			       0, fs->blksz, temp_buff1); +		jdb = (struct journal_header_t *) temp_buff1; + +		if (be32_to_cpu(jdb->h_blocktype) == +		    EXT3_JOURNAL_DESCRIPTOR_BLOCK) { +			if (be32_to_cpu(jdb->h_sequence) != +			    be32_to_cpu(jsb->s_sequence)) { +				print_jrnl_status(recovery_flag); +				break; +			} + +			curr_desc_logical_no = i; +			if (transaction_state == TRANSACTION_COMPLETE) +				transaction_state = TRANSACTION_RUNNING; +			else +				return -1; +			p_jdb = (char *)temp_buff1; +			ofs = sizeof(struct journal_header_t); +			do { +				tag = (struct ext3_journal_block_tag *) +				    &p_jdb[ofs]; +				ofs += sizeof(struct ext3_journal_block_tag); +				if (ofs > fs->blksz) +					break; +				flags = be32_to_cpu(tag->flags); +				if (!(flags & EXT3_JOURNAL_FLAG_SAME_UUID)) +					ofs += 16; +				i++; +				debug("\t\ttag %u\n", be32_to_cpu(tag->block)); +			} while (!(flags & EXT3_JOURNAL_FLAG_LAST_TAG)); +			i++; +			DB_FOUND = YES; +		} else if (be32_to_cpu(jdb->h_blocktype) == +				EXT3_JOURNAL_COMMIT_BLOCK) { +			if (be32_to_cpu(jdb->h_sequence) != +			     be32_to_cpu(jsb->s_sequence)) { +				print_jrnl_status(recovery_flag); +				break; +			} + +			if (transaction_state == TRANSACTION_RUNNING || +					(DB_FOUND == NO)) { +				transaction_state = TRANSACTION_COMPLETE; +				i++; +				jsb->s_sequence = +					cpu_to_be32(be32_to_cpu( +						jsb->s_sequence) + 1); +			} +			prev_desc_logical_no = curr_desc_logical_no; +			if ((recovery_flag == RECOVER) && (DB_FOUND == YES)) +				recover_transaction(prev_desc_logical_no); + +			DB_FOUND = NO; +		} else if (be32_to_cpu(jdb->h_blocktype) == +				EXT3_JOURNAL_REVOKE_BLOCK) { +			if (be32_to_cpu(jdb->h_sequence) != +			    be32_to_cpu(jsb->s_sequence)) { +				print_jrnl_status(recovery_flag); +				break; +			} +			if (recovery_flag == SCAN) +				ext4fs_push_revoke_blk((char *)jdb); +			i++; +		} else { +			debug("Else Case\n"); +			if (be32_to_cpu(jdb->h_sequence) != +			    be32_to_cpu(jsb->s_sequence)) { +				print_jrnl_status(recovery_flag); +				break; +			} +		} +	} + +end: +	if (recovery_flag == RECOVER) { +		jsb->s_start = cpu_to_be32(1); +		jsb->s_sequence = cpu_to_be32(be32_to_cpu(jsb->s_sequence) + 1); +		/* get the superblock */ +		ext4fs_devread(SUPERBLOCK_SECTOR, 0, SUPERBLOCK_SIZE, +			       (char *)fs->sb); +		fs->sb->feature_incompat |= EXT3_FEATURE_INCOMPAT_RECOVER; + +		/* Update the super block */ +		put_ext4((uint64_t) (SUPERBLOCK_SIZE), +			 (struct ext2_sblock *)fs->sb, +			 (uint32_t) SUPERBLOCK_SIZE); +		ext4fs_devread(SUPERBLOCK_SECTOR, 0, SUPERBLOCK_SIZE, +			       (char *)fs->sb); + +		blknr = read_allocated_block(&inode_journal, +					 EXT2_JOURNAL_SUPERBLOCK); +		put_ext4((uint64_t) (blknr * fs->blksz), +			 (struct journal_superblock_t *)temp_buff, +			 (uint32_t) fs->blksz); +		ext4fs_free_revoke_blks(); +	} +	free(temp_buff); +	free(temp_buff1); + +	return 0; +} + +static void update_descriptor_block(long int blknr) +{ +	int i; +	long int jsb_blknr; +	struct journal_header_t jdb; +	struct ext3_journal_block_tag tag; +	struct ext2_inode inode_journal; +	struct journal_superblock_t *jsb = NULL; +	char *buf = NULL; +	char *temp = NULL; +	struct ext_filesystem *fs = get_fs(); +	char *temp_buff = zalloc(fs->blksz); +	if (!temp_buff) +		return; + +	ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO, &inode_journal); +	jsb_blknr = read_allocated_block(&inode_journal, +					 EXT2_JOURNAL_SUPERBLOCK); +	ext4fs_devread(jsb_blknr * fs->sect_perblk, 0, fs->blksz, temp_buff); +	jsb = (struct journal_superblock_t *) temp_buff; + +	jdb.h_blocktype = cpu_to_be32(EXT3_JOURNAL_DESCRIPTOR_BLOCK); +	jdb.h_magic = cpu_to_be32(EXT3_JOURNAL_MAGIC_NUMBER); +	jdb.h_sequence = jsb->s_sequence; +	buf = zalloc(fs->blksz); +	if (!buf) { +		free(temp_buff); +		return; +	} +	temp = buf; +	memcpy(buf, &jdb, sizeof(struct journal_header_t)); +	temp += sizeof(struct journal_header_t); + +	for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) { +		if (journal_ptr[i]->blknr == -1) +			break; + +		tag.block = cpu_to_be32(journal_ptr[i]->blknr); +		tag.flags = cpu_to_be32(EXT3_JOURNAL_FLAG_SAME_UUID); +		memcpy(temp, &tag, sizeof(struct ext3_journal_block_tag)); +		temp = temp + sizeof(struct ext3_journal_block_tag); +	} + +	tag.block = cpu_to_be32(journal_ptr[--i]->blknr); +	tag.flags = cpu_to_be32(EXT3_JOURNAL_FLAG_LAST_TAG); +	memcpy(temp - sizeof(struct ext3_journal_block_tag), &tag, +	       sizeof(struct ext3_journal_block_tag)); +	put_ext4((uint64_t) (blknr * fs->blksz), buf, (uint32_t) fs->blksz); + +	free(temp_buff); +	free(buf); +} + +static void update_commit_block(long int blknr) +{ +	struct journal_header_t jdb; +	struct ext_filesystem *fs = get_fs(); +	char *buf = NULL; +	struct ext2_inode inode_journal; +	struct journal_superblock_t *jsb; +	long int jsb_blknr; +	char *temp_buff = zalloc(fs->blksz); +	if (!temp_buff) +		return; + +	ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO, &inode_journal); +	jsb_blknr = read_allocated_block(&inode_journal, +					 EXT2_JOURNAL_SUPERBLOCK); +	ext4fs_devread(jsb_blknr * fs->sect_perblk, 0, fs->blksz, temp_buff); +	jsb = (struct journal_superblock_t *) temp_buff; + +	jdb.h_blocktype = cpu_to_be32(EXT3_JOURNAL_COMMIT_BLOCK); +	jdb.h_magic = cpu_to_be32(EXT3_JOURNAL_MAGIC_NUMBER); +	jdb.h_sequence = jsb->s_sequence; +	buf = zalloc(fs->blksz); +	if (!buf) { +		free(temp_buff); +		return; +	} +	memcpy(buf, &jdb, sizeof(struct journal_header_t)); +	put_ext4((uint64_t) (blknr * fs->blksz), buf, (uint32_t) fs->blksz); + +	free(temp_buff); +	free(buf); +} + +void ext4fs_update_journal(void) +{ +	struct ext2_inode inode_journal; +	struct ext_filesystem *fs = get_fs(); +	long int blknr; +	int i; +	ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO, &inode_journal); +	blknr = read_allocated_block(&inode_journal, jrnl_blk_idx++); +	update_descriptor_block(blknr); +	for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) { +		if (journal_ptr[i]->blknr == -1) +			break; +		blknr = read_allocated_block(&inode_journal, jrnl_blk_idx++); +		put_ext4((uint64_t) ((uint64_t)blknr * (uint64_t)fs->blksz), +			 journal_ptr[i]->buf, fs->blksz); +	} +	blknr = read_allocated_block(&inode_journal, jrnl_blk_idx++); +	update_commit_block(blknr); +	printf("update journal finished\n"); +} diff --git a/fs/ext4/ext4_journal.h b/fs/ext4/ext4_journal.h new file mode 100644 index 000000000..acc1c5163 --- /dev/null +++ b/fs/ext4/ext4_journal.h @@ -0,0 +1,141 @@ +/* + * (C) Copyright 2011 - 2012 Samsung Electronics + * EXT4 filesystem implementation in Uboot by + * Uma Shankar <uma.shankar@samsung.com> + * Manjunatha C Achar <a.manjunatha@samsung.com> + * + * Journal data structures and headers for Journaling feature of ext4 + * have been referred from JBD2 (Journaling Block device 2) + * implementation in Linux Kernel. + * + * Written by Stephen C. Tweedie <sct@redhat.com> + * + * Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved + * This file is part of the Linux kernel and is made available under + * the terms of the GNU General Public License, version 2, or at your + * option, any later version, incorporated herein by reference. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __EXT4_JRNL__ +#define __EXT4_JRNL__ + +#define EXT2_JOURNAL_INO		8	/* Journal inode */ +#define EXT2_JOURNAL_SUPERBLOCK	0	/* Journal  Superblock number */ + +#define JBD2_FEATURE_COMPAT_CHECKSUM	0x00000001 +#define EXT3_JOURNAL_MAGIC_NUMBER	0xc03b3998U +#define TRANSACTION_RUNNING		1 +#define TRANSACTION_COMPLETE		0 +#define EXT3_FEATURE_INCOMPAT_RECOVER	0x0004	/* Needs recovery */ +#define EXT3_JOURNAL_DESCRIPTOR_BLOCK	1 +#define EXT3_JOURNAL_COMMIT_BLOCK	2 +#define EXT3_JOURNAL_SUPERBLOCK_V1	3 +#define EXT3_JOURNAL_SUPERBLOCK_V2	4 +#define EXT3_JOURNAL_REVOKE_BLOCK	5 +#define EXT3_JOURNAL_FLAG_ESCAPE	1 +#define EXT3_JOURNAL_FLAG_SAME_UUID	2 +#define EXT3_JOURNAL_FLAG_DELETED	4 +#define EXT3_JOURNAL_FLAG_LAST_TAG	8 + +/* Maximum entries in 1 journal transaction */ +#define MAX_JOURNAL_ENTRIES 100 +struct journal_log { +	char *buf; +	int blknr; +}; + +struct dirty_blocks { +	char *buf; +	int blknr; +}; + +/* Standard header for all descriptor blocks: */ +struct journal_header_t { +	__u32 h_magic; +	__u32 h_blocktype; +	__u32 h_sequence; +}; + +/* The journal superblock.  All fields are in big-endian byte order. */ +struct journal_superblock_t { +	/* 0x0000 */ +	struct journal_header_t s_header; + +	/* Static information describing the journal */ +	__u32 s_blocksize;	/* journal device blocksize */ +	__u32 s_maxlen;		/* total blocks in journal file */ +	__u32 s_first;		/* first block of log information */ + +	/* Dynamic information describing the current state of the log */ +	__u32 s_sequence;	/* first commit ID expected in log */ +	__u32 s_start;		/* blocknr of start of log */ + +	/* Error value, as set by journal_abort(). */ +	__s32 s_errno; + +	/* Remaining fields are only valid in a version-2 superblock */ +	__u32 s_feature_compat;	/* compatible feature set */ +	__u32 s_feature_incompat;	/* incompatible feature set */ +	__u32 s_feature_ro_compat;	/* readonly-compatible feature set */ +	/* 0x0030 */ +	__u8 s_uuid[16];	/* 128-bit uuid for journal */ + +	/* 0x0040 */ +	__u32 s_nr_users;	/* Nr of filesystems sharing log */ + +	__u32 s_dynsuper;	/* Blocknr of dynamic superblock copy */ + +	/* 0x0048 */ +	__u32 s_max_transaction;	/* Limit of journal blocks per trans. */ +	__u32 s_max_trans_data;	/* Limit of data blocks per trans. */ + +	/* 0x0050 */ +	__u32 s_padding[44]; + +	/* 0x0100 */ +	__u8 s_users[16 * 48];	/* ids of all fs'es sharing the log */ +	/* 0x0400 */ +} ; + +struct ext3_journal_block_tag { +	uint32_t block; +	uint32_t flags; +}; + +struct journal_revoke_header_t { +	struct journal_header_t r_header; +	int r_count;		/* Count of bytes used in the block */ +}; + +struct revoke_blk_list { +	char *content;		/* revoke block itself */ +	struct revoke_blk_list *next; +}; + +extern struct ext2_data *ext4fs_root; + +int ext4fs_init_journal(void); +int ext4fs_log_gdt(char *gd_table); +int ext4fs_check_journal_state(int recovery_flag); +int ext4fs_log_journal(char *journal_buffer, long int blknr); +int ext4fs_put_metadata(char *metadata_buffer, long int blknr); +void ext4fs_update_journal(void); +void ext4fs_dump_metadata(void); +void ext4fs_push_revoke_blk(char *buffer); +void ext4fs_free_journal(void); +void ext4fs_free_revoke_blks(void); +#endif diff --git a/fs/ext4/ext4fs.c b/fs/ext4/ext4fs.c new file mode 100644 index 000000000..114c2a214 --- /dev/null +++ b/fs/ext4/ext4fs.c @@ -0,0 +1,1189 @@ +/* + * (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 and load support in Uboot. + *		       Ext4 read optimization taken from Open-Moko + *		       Qi bootloader + * + * (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 <malloc.h> +#include <ext_common.h> +#include <ext4fs.h> +#include <linux/stat.h> +#include <linux/time.h> +#include <asm/byteorder.h> +#include "ext4_common.h" + +int ext4fs_symlinknest; +block_dev_desc_t *ext4_dev_desc; + +struct ext_filesystem *get_fs(void) +{ +	if (ext4_dev_desc == NULL || ext4_dev_desc->priv == NULL) +		printf("Invalid Input Arguments %s\n", __func__); + +	return ext4_dev_desc->priv; +} + +int init_fs(block_dev_desc_t *dev_desc) +{ +	struct ext_filesystem *fs; +	if (dev_desc == NULL) { +		printf("Invalid Input Arguments %s\n", __func__); +		return -EINVAL; +	} + +	fs = zalloc(sizeof(struct ext_filesystem)); +	if (fs == NULL) { +		printf("malloc failed: %s\n", __func__); +		return -ENOMEM; +	} + +	fs->dev_desc = dev_desc; +	dev_desc->priv = fs; + +	return 0; +} + +void deinit_fs(block_dev_desc_t *dev_desc) +{ +	if (dev_desc == NULL) { +		printf("Invalid Input Arguments %s\n", __func__); +		return; +	} +	free(dev_desc->priv); +	dev_desc->priv = NULL; +} + +void ext4fs_free_node(struct ext2fs_node *node, struct ext2fs_node *currroot) +{ +	if ((node != &ext4fs_root->diropen) && (node != currroot)) +		free(node); +} + +/* + * Taken from openmoko-kernel mailing list: By Andy green + * Optimized read file API : collects and defers contiguous sector + * reads into one potentially more efficient larger sequential read action + */ +int ext4fs_read_file(struct ext2fs_node *node, int pos, +		unsigned int len, char *buf) +{ +	int i; +	int blockcnt; +	int log2blocksize = LOG2_EXT2_BLOCK_SIZE(node->data); +	int blocksize = 1 << (log2blocksize + DISK_SECTOR_BITS); +	unsigned int filesize = __le32_to_cpu(node->inode.size); +	int previous_block_number = -1; +	int delayed_start = 0; +	int delayed_extent = 0; +	int delayed_skipfirst = 0; +	int delayed_next = 0; +	char *delayed_buf = NULL; +	short status; + +	/* Adjust len so it we can't read past the end of the file. */ +	if (len > filesize) +		len = filesize; + +	blockcnt = ((len + pos) + blocksize - 1) / blocksize; + +	for (i = pos / blocksize; i < blockcnt; i++) { +		int blknr; +		int blockoff = pos % blocksize; +		int blockend = blocksize; +		int skipfirst = 0; +		blknr = read_allocated_block(&(node->inode), i); +		if (blknr < 0) +			return -1; + +		blknr = blknr << log2blocksize; + +		/* Last block.  */ +		if (i == blockcnt - 1) { +			blockend = (len + pos) % blocksize; + +			/* The last portion is exactly blocksize. */ +			if (!blockend) +				blockend = blocksize; +		} + +		/* First block. */ +		if (i == pos / blocksize) { +			skipfirst = blockoff; +			blockend -= skipfirst; +		} +		if (blknr) { +			int status; + +			if (previous_block_number != -1) { +				if (delayed_next == blknr) { +					delayed_extent += blockend; +					delayed_next += blockend >> SECTOR_BITS; +				} else {	/* spill */ +					status = ext4fs_devread(delayed_start, +							delayed_skipfirst, +							delayed_extent, +							delayed_buf); +					if (status == 0) +						return -1; +					previous_block_number = blknr; +					delayed_start = blknr; +					delayed_extent = blockend; +					delayed_skipfirst = skipfirst; +					delayed_buf = buf; +					delayed_next = blknr + +						(blockend >> SECTOR_BITS); +				} +			} else { +				previous_block_number = blknr; +				delayed_start = blknr; +				delayed_extent = blockend; +				delayed_skipfirst = skipfirst; +				delayed_buf = buf; +				delayed_next = blknr + +					(blockend >> SECTOR_BITS); +			} +		} else { +			if (previous_block_number != -1) { +				/* spill */ +				status = ext4fs_devread(delayed_start, +							delayed_skipfirst, +							delayed_extent, +							delayed_buf); +				if (status == 0) +					return -1; +				previous_block_number = -1; +			} +			memset(buf, 0, blocksize - skipfirst); +		} +		buf += blocksize - skipfirst; +	} +	if (previous_block_number != -1) { +		/* spill */ +		status = ext4fs_devread(delayed_start, +					delayed_skipfirst, delayed_extent, +					delayed_buf); +		if (status == 0) +			return -1; +		previous_block_number = -1; +	} + +	return len; +} + +int ext4fs_ls(const char *dirname) +{ +	struct ext2fs_node *dirnode; +	int status; + +	if (dirname == NULL) +		return 0; + +	status = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode, +				  FILETYPE_DIRECTORY); +	if (status != 1) { +		printf("** Can not find directory. **\n"); +		return 1; +	} + +	ext4fs_iterate_dir(dirnode, NULL, NULL, NULL); +	ext4fs_free_node(dirnode, &ext4fs_root->diropen); + +	return 0; +} + +int ext4fs_read(char *buf, unsigned len) +{ +	if (ext4fs_root == NULL || ext4fs_file == NULL) +		return 0; + +	return ext4fs_read_file(ext4fs_file, 0, len, buf); +} + +#if defined(CONFIG_CMD_EXT4_WRITE) +static void ext4fs_update(void) +{ +	short i; +	ext4fs_update_journal(); +	struct ext_filesystem *fs = get_fs(); + +	/* update  super block */ +	put_ext4((uint64_t)(SUPERBLOCK_SIZE), +		 (struct ext2_sblock *)fs->sb, (uint32_t)SUPERBLOCK_SIZE); + +	/* update block groups */ +	for (i = 0; i < fs->no_blkgrp; i++) { +		fs->gd[i].bg_checksum = ext4fs_checksum_update(i); +		put_ext4((uint64_t)(fs->gd[i].block_id * fs->blksz), +			 fs->blk_bmaps[i], fs->blksz); +	} + +	/* update inode table groups */ +	for (i = 0; i < fs->no_blkgrp; i++) { +		put_ext4((uint64_t) (fs->gd[i].inode_id * fs->blksz), +			 fs->inode_bmaps[i], fs->blksz); +	} + +	/* update the block group descriptor table */ +	put_ext4((uint64_t)(fs->gdtable_blkno * fs->blksz), +		 (struct ext2_block_group *)fs->gdtable, +		 (fs->blksz * fs->no_blk_pergdt)); + +	ext4fs_dump_metadata(); + +	gindex = 0; +	gd_index = 0; +} + +int ext4fs_get_bgdtable(void) +{ +	int status; +	int grp_desc_size; +	struct ext_filesystem *fs = get_fs(); +	grp_desc_size = sizeof(struct ext2_block_group); +	fs->no_blk_pergdt = (fs->no_blkgrp * grp_desc_size) / fs->blksz; +	if ((fs->no_blkgrp * grp_desc_size) % fs->blksz) +		fs->no_blk_pergdt++; + +	/* allocate memory for gdtable */ +	fs->gdtable = zalloc(fs->blksz * fs->no_blk_pergdt); +	if (!fs->gdtable) +		return -ENOMEM; +	/* read the group descriptor table */ +	status = ext4fs_devread(fs->gdtable_blkno * fs->sect_perblk, 0, +				fs->blksz * fs->no_blk_pergdt, fs->gdtable); +	if (status == 0) +		goto fail; + +	if (ext4fs_log_gdt(fs->gdtable)) { +		printf("Error in ext4fs_log_gdt\n"); +		return -1; +	} + +	return 0; +fail: +	free(fs->gdtable); +	fs->gdtable = NULL; + +	return -1; +} + +static void delete_single_indirect_block(struct ext2_inode *inode) +{ +	struct ext2_block_group *gd = NULL; +	static int prev_bg_bmap_idx = -1; +	long int blknr; +	int remainder; +	int bg_idx; +	int status; +	unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group; +	struct ext_filesystem *fs = get_fs(); +	char *journal_buffer = zalloc(fs->blksz); +	if (!journal_buffer) { +		printf("No memory\n"); +		return; +	} +	/* get  block group descriptor table */ +	gd = (struct ext2_block_group *)fs->gdtable; + +	/* deleting the single indirect block associated with inode */ +	if (inode->b.blocks.indir_block != 0) { +		debug("SIPB releasing %u\n", inode->b.blocks.indir_block); +		blknr = inode->b.blocks.indir_block; +		if (fs->blksz != 1024) { +			bg_idx = blknr / blk_per_grp; +		} else { +			bg_idx = blknr / blk_per_grp; +			remainder = blknr % blk_per_grp; +			if (!remainder) +				bg_idx--; +		} +		ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx); +		gd[bg_idx].free_blocks++; +		fs->sb->free_blocks++; +		/* journal backup */ +		if (prev_bg_bmap_idx != bg_idx) { +			status = +			    ext4fs_devread(gd[bg_idx].block_id * +					   fs->sect_perblk, 0, fs->blksz, +					   journal_buffer); +			if (status == 0) +				goto fail; +			if (ext4fs_log_journal +			    (journal_buffer, gd[bg_idx].block_id)) +				goto fail; +			prev_bg_bmap_idx = bg_idx; +		} +	} +fail: +	free(journal_buffer); +} + +static void delete_double_indirect_block(struct ext2_inode *inode) +{ +	int i; +	short status; +	static int prev_bg_bmap_idx = -1; +	long int blknr; +	int remainder; +	int bg_idx; +	unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group; +	unsigned int *di_buffer = NULL; +	unsigned int *DIB_start_addr = NULL; +	struct ext2_block_group *gd = NULL; +	struct ext_filesystem *fs = get_fs(); +	char *journal_buffer = zalloc(fs->blksz); +	if (!journal_buffer) { +		printf("No memory\n"); +		return; +	} +	/* get the block group descriptor table */ +	gd = (struct ext2_block_group *)fs->gdtable; + +	if (inode->b.blocks.double_indir_block != 0) { +		di_buffer = zalloc(fs->blksz); +		if (!di_buffer) { +			printf("No memory\n"); +			return; +		} +		DIB_start_addr = (unsigned int *)di_buffer; +		blknr = inode->b.blocks.double_indir_block; +		status = ext4fs_devread(blknr * fs->sect_perblk, 0, fs->blksz, +					(char *)di_buffer); +		for (i = 0; i < fs->blksz / sizeof(int); i++) { +			if (*di_buffer == 0) +				break; + +			debug("DICB releasing %u\n", *di_buffer); +			if (fs->blksz != 1024) { +				bg_idx = (*di_buffer) / blk_per_grp; +			} else { +				bg_idx = (*di_buffer) / blk_per_grp; +				remainder = (*di_buffer) % blk_per_grp; +				if (!remainder) +					bg_idx--; +			} +			ext4fs_reset_block_bmap(*di_buffer, +					fs->blk_bmaps[bg_idx], bg_idx); +			di_buffer++; +			gd[bg_idx].free_blocks++; +			fs->sb->free_blocks++; +			/* journal backup */ +			if (prev_bg_bmap_idx != bg_idx) { +				status = ext4fs_devread(gd[bg_idx].block_id +							* fs->sect_perblk, 0, +							fs->blksz, +							journal_buffer); +				if (status == 0) +					goto fail; + +				if (ext4fs_log_journal(journal_buffer, +							gd[bg_idx].block_id)) +					goto fail; +				prev_bg_bmap_idx = bg_idx; +			} +		} + +		/* removing the parent double indirect block */ +		blknr = inode->b.blocks.double_indir_block; +		if (fs->blksz != 1024) { +			bg_idx = blknr / blk_per_grp; +		} else { +			bg_idx = blknr / blk_per_grp; +			remainder = blknr % blk_per_grp; +			if (!remainder) +				bg_idx--; +		} +		ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx); +		gd[bg_idx].free_blocks++; +		fs->sb->free_blocks++; +		/* journal backup */ +		if (prev_bg_bmap_idx != bg_idx) { +			memset(journal_buffer, '\0', fs->blksz); +			status = ext4fs_devread(gd[bg_idx].block_id * +						fs->sect_perblk, 0, fs->blksz, +						journal_buffer); +			if (status == 0) +				goto fail; + +			if (ext4fs_log_journal(journal_buffer, +						gd[bg_idx].block_id)) +				goto fail; +			prev_bg_bmap_idx = bg_idx; +		} +		debug("DIPB releasing %ld\n", blknr); +	} +fail: +	free(DIB_start_addr); +	free(journal_buffer); +} + +static void delete_triple_indirect_block(struct ext2_inode *inode) +{ +	int i, j; +	short status; +	static int prev_bg_bmap_idx = -1; +	long int blknr; +	int remainder; +	int bg_idx; +	unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group; +	unsigned int *tigp_buffer = NULL; +	unsigned int *tib_start_addr = NULL; +	unsigned int *tip_buffer = NULL; +	unsigned int *tipb_start_addr = NULL; +	struct ext2_block_group *gd = NULL; +	struct ext_filesystem *fs = get_fs(); +	char *journal_buffer = zalloc(fs->blksz); +	if (!journal_buffer) { +		printf("No memory\n"); +		return; +	} +	/* get block group descriptor table */ +	gd = (struct ext2_block_group *)fs->gdtable; + +	if (inode->b.blocks.triple_indir_block != 0) { +		tigp_buffer = zalloc(fs->blksz); +		if (!tigp_buffer) { +			printf("No memory\n"); +			return; +		} +		tib_start_addr = (unsigned int *)tigp_buffer; +		blknr = inode->b.blocks.triple_indir_block; +		status = ext4fs_devread(blknr * fs->sect_perblk, 0, fs->blksz, +					(char *)tigp_buffer); +		for (i = 0; i < fs->blksz / sizeof(int); i++) { +			if (*tigp_buffer == 0) +				break; +			debug("tigp buffer releasing %u\n", *tigp_buffer); + +			tip_buffer = zalloc(fs->blksz); +			if (!tip_buffer) +				goto fail; +			tipb_start_addr = (unsigned int *)tip_buffer; +			status = ext4fs_devread((*tigp_buffer) * +						fs->sect_perblk, 0, fs->blksz, +						(char *)tip_buffer); +			for (j = 0; j < fs->blksz / sizeof(int); j++) { +				if (*tip_buffer == 0) +					break; +				if (fs->blksz != 1024) { +					bg_idx = (*tip_buffer) / blk_per_grp; +				} else { +					bg_idx = (*tip_buffer) / blk_per_grp; + +					remainder = (*tip_buffer) % blk_per_grp; +					if (!remainder) +						bg_idx--; +				} + +				ext4fs_reset_block_bmap(*tip_buffer, +							fs->blk_bmaps[bg_idx], +							bg_idx); + +				tip_buffer++; +				gd[bg_idx].free_blocks++; +				fs->sb->free_blocks++; +				/* journal backup */ +				if (prev_bg_bmap_idx != bg_idx) { +					status = +					    ext4fs_devread(gd[bg_idx].block_id * +							   fs->sect_perblk, 0, +							   fs->blksz, +							   journal_buffer); +					if (status == 0) +						goto fail; + +					if (ext4fs_log_journal(journal_buffer, +							       gd[bg_idx]. +							       block_id)) +						goto fail; +					prev_bg_bmap_idx = bg_idx; +				} +			} +			free(tipb_start_addr); +			tipb_start_addr = NULL; + +			/* +			 * removing the grand parent blocks +			 * which is connected to inode +			 */ +			if (fs->blksz != 1024) { +				bg_idx = (*tigp_buffer) / blk_per_grp; +			} else { +				bg_idx = (*tigp_buffer) / blk_per_grp; + +				remainder = (*tigp_buffer) % blk_per_grp; +				if (!remainder) +					bg_idx--; +			} +			ext4fs_reset_block_bmap(*tigp_buffer, +						fs->blk_bmaps[bg_idx], bg_idx); + +			tigp_buffer++; +			gd[bg_idx].free_blocks++; +			fs->sb->free_blocks++; +			/* journal backup */ +			if (prev_bg_bmap_idx != bg_idx) { +				memset(journal_buffer, '\0', fs->blksz); +				status = +				    ext4fs_devread(gd[bg_idx].block_id * +						   fs->sect_perblk, 0, +						   fs->blksz, journal_buffer); +				if (status == 0) +					goto fail; + +				if (ext4fs_log_journal(journal_buffer, +							gd[bg_idx].block_id)) +					goto fail; +				prev_bg_bmap_idx = bg_idx; +			} +		} + +		/* removing the grand parent triple indirect block */ +		blknr = inode->b.blocks.triple_indir_block; +		if (fs->blksz != 1024) { +			bg_idx = blknr / blk_per_grp; +		} else { +			bg_idx = blknr / blk_per_grp; +			remainder = blknr % blk_per_grp; +			if (!remainder) +				bg_idx--; +		} +		ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], bg_idx); +		gd[bg_idx].free_blocks++; +		fs->sb->free_blocks++; +		/* journal backup */ +		if (prev_bg_bmap_idx != bg_idx) { +			memset(journal_buffer, '\0', fs->blksz); +			status = ext4fs_devread(gd[bg_idx].block_id * +						fs->sect_perblk, 0, fs->blksz, +						journal_buffer); +			if (status == 0) +				goto fail; + +			if (ext4fs_log_journal(journal_buffer, +						gd[bg_idx].block_id)) +				goto fail; +			prev_bg_bmap_idx = bg_idx; +		} +		debug("tigp buffer itself releasing %ld\n", blknr); +	} +fail: +	free(tib_start_addr); +	free(tipb_start_addr); +	free(journal_buffer); +} + +static int ext4fs_delete_file(int inodeno) +{ +	struct ext2_inode inode; +	short status; +	int i; +	int remainder; +	long int blknr; +	int bg_idx; +	int ibmap_idx; +	char *read_buffer = NULL; +	char *start_block_address = NULL; +	unsigned int no_blocks; + +	static int prev_bg_bmap_idx = -1; +	unsigned int inodes_per_block; +	long int blkno; +	unsigned int blkoff; +	unsigned int blk_per_grp = ext4fs_root->sblock.blocks_per_group; +	unsigned int inode_per_grp = ext4fs_root->sblock.inodes_per_group; +	struct ext2_inode *inode_buffer = NULL; +	struct ext2_block_group *gd = NULL; +	struct ext_filesystem *fs = get_fs(); +	char *journal_buffer = zalloc(fs->blksz); +	if (!journal_buffer) +		return -ENOMEM; +	/* get the block group descriptor table */ +	gd = (struct ext2_block_group *)fs->gdtable; +	status = ext4fs_read_inode(ext4fs_root, inodeno, &inode); +	if (status == 0) +		goto fail; + +	/* read the block no allocated to a file */ +	no_blocks = inode.size / fs->blksz; +	if (inode.size % fs->blksz) +		no_blocks++; + +	if (le32_to_cpu(inode.flags) & EXT4_EXTENTS_FL) { +		struct ext2fs_node *node_inode = +		    zalloc(sizeof(struct ext2fs_node)); +		if (!node_inode) +			goto fail; +		node_inode->data = ext4fs_root; +		node_inode->ino = inodeno; +		node_inode->inode_read = 0; +		memcpy(&(node_inode->inode), &inode, sizeof(struct ext2_inode)); + +		for (i = 0; i < no_blocks; i++) { +			blknr = read_allocated_block(&(node_inode->inode), i); +			if (fs->blksz != 1024) { +				bg_idx = blknr / blk_per_grp; +			} else { +				bg_idx = blknr / blk_per_grp; +				remainder = blknr % blk_per_grp; +				if (!remainder) +					bg_idx--; +			} +			ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], +						bg_idx); +			debug("EXT4_EXTENTS Block releasing %ld: %d\n", +			      blknr, bg_idx); + +			gd[bg_idx].free_blocks++; +			fs->sb->free_blocks++; + +			/* journal backup */ +			if (prev_bg_bmap_idx != bg_idx) { +				status = +				    ext4fs_devread(gd[bg_idx].block_id * +						   fs->sect_perblk, 0, +						   fs->blksz, journal_buffer); +				if (status == 0) +					goto fail; +				if (ext4fs_log_journal(journal_buffer, +							gd[bg_idx].block_id)) +					goto fail; +				prev_bg_bmap_idx = bg_idx; +			} +		} +		if (node_inode) { +			free(node_inode); +			node_inode = NULL; +		} +	} else { + +		delete_single_indirect_block(&inode); +		delete_double_indirect_block(&inode); +		delete_triple_indirect_block(&inode); + +		/* read the block no allocated to a file */ +		no_blocks = inode.size / fs->blksz; +		if (inode.size % fs->blksz) +			no_blocks++; +		for (i = 0; i < no_blocks; i++) { +			blknr = read_allocated_block(&inode, i); +			if (fs->blksz != 1024) { +				bg_idx = blknr / blk_per_grp; +			} else { +				bg_idx = blknr / blk_per_grp; +				remainder = blknr % blk_per_grp; +				if (!remainder) +					bg_idx--; +			} +			ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx], +						bg_idx); +			debug("ActualB releasing %ld: %d\n", blknr, bg_idx); + +			gd[bg_idx].free_blocks++; +			fs->sb->free_blocks++; +			/* journal backup */ +			if (prev_bg_bmap_idx != bg_idx) { +				memset(journal_buffer, '\0', fs->blksz); +				status = ext4fs_devread(gd[bg_idx].block_id +							* fs->sect_perblk, +							0, fs->blksz, +							journal_buffer); +				if (status == 0) +					goto fail; +				if (ext4fs_log_journal(journal_buffer, +						gd[bg_idx].block_id)) +					goto fail; +				prev_bg_bmap_idx = bg_idx; +			} +		} +	} + +	/* from the inode no to blockno */ +	inodes_per_block = fs->blksz / fs->inodesz; +	ibmap_idx = inodeno / inode_per_grp; + +	/* get the block no */ +	inodeno--; +	blkno = __le32_to_cpu(gd[ibmap_idx].inode_table_id) + +		(inodeno % __le32_to_cpu(inode_per_grp)) / inodes_per_block; + +	/* get the offset of the inode */ +	blkoff = ((inodeno) % inodes_per_block) * fs->inodesz; + +	/* read the block no containing the inode */ +	read_buffer = zalloc(fs->blksz); +	if (!read_buffer) +		goto fail; +	start_block_address = read_buffer; +	status = ext4fs_devread(blkno * fs->sect_perblk, +				0, fs->blksz, read_buffer); +	if (status == 0) +		goto fail; + +	if (ext4fs_log_journal(read_buffer, blkno)) +		goto fail; + +	read_buffer = read_buffer + blkoff; +	inode_buffer = (struct ext2_inode *)read_buffer; +	memset(inode_buffer, '\0', sizeof(struct ext2_inode)); + +	/* write the inode to original position in inode table */ +	if (ext4fs_put_metadata(start_block_address, blkno)) +		goto fail; + +	/* update the respective inode bitmaps */ +	inodeno++; +	ext4fs_reset_inode_bmap(inodeno, fs->inode_bmaps[ibmap_idx], ibmap_idx); +	gd[ibmap_idx].free_inodes++; +	fs->sb->free_inodes++; +	/* journal backup */ +	memset(journal_buffer, '\0', fs->blksz); +	status = ext4fs_devread(gd[ibmap_idx].inode_id * +				fs->sect_perblk, 0, fs->blksz, journal_buffer); +	if (status == 0) +		goto fail; +	if (ext4fs_log_journal(journal_buffer, gd[ibmap_idx].inode_id)) +		goto fail; + +	ext4fs_update(); +	ext4fs_deinit(); + +	if (ext4fs_init() != 0) { +		printf("error in File System init\n"); +		goto fail; +	} + +	free(start_block_address); +	free(journal_buffer); + +	return 0; +fail: +	free(start_block_address); +	free(journal_buffer); + +	return -1; +} + +int ext4fs_init(void) +{ +	short status; +	int i; +	unsigned int real_free_blocks = 0; +	struct ext_filesystem *fs = get_fs(); + +	/* populate fs */ +	fs->blksz = EXT2_BLOCK_SIZE(ext4fs_root); +	fs->inodesz = INODE_SIZE_FILESYSTEM(ext4fs_root); +	fs->sect_perblk = fs->blksz / SECTOR_SIZE; + +	/* get the superblock */ +	fs->sb = zalloc(SUPERBLOCK_SIZE); +	if (!fs->sb) +		return -ENOMEM; +	if (!ext4fs_devread(SUPERBLOCK_SECTOR, 0, SUPERBLOCK_SIZE, +			(char *)fs->sb)) +		goto fail; + +	/* init journal */ +	if (ext4fs_init_journal()) +		goto fail; + +	/* get total no of blockgroups */ +	fs->no_blkgrp = (uint32_t)ext4fs_div_roundup( +			(ext4fs_root->sblock.total_blocks - +			ext4fs_root->sblock.first_data_block), +			ext4fs_root->sblock.blocks_per_group); + +	/* get the block group descriptor table */ +	fs->gdtable_blkno = ((EXT2_MIN_BLOCK_SIZE == fs->blksz) + 1); +	if (ext4fs_get_bgdtable() == -1) { +		printf("Error in getting the block group descriptor table\n"); +		goto fail; +	} +	fs->gd = (struct ext2_block_group *)fs->gdtable; + +	/* load all the available bitmap block of the partition */ +	fs->blk_bmaps = zalloc(fs->no_blkgrp * sizeof(char *)); +	if (!fs->blk_bmaps) +		goto fail; +	for (i = 0; i < fs->no_blkgrp; i++) { +		fs->blk_bmaps[i] = zalloc(fs->blksz); +		if (!fs->blk_bmaps[i]) +			goto fail; +	} + +	for (i = 0; i < fs->no_blkgrp; i++) { +		status = +		    ext4fs_devread(fs->gd[i].block_id * fs->sect_perblk, 0, +				   fs->blksz, (char *)fs->blk_bmaps[i]); +		if (status == 0) +			goto fail; +	} + +	/* load all the available inode bitmap of the partition */ +	fs->inode_bmaps = zalloc(fs->no_blkgrp * sizeof(unsigned char *)); +	if (!fs->inode_bmaps) +		goto fail; +	for (i = 0; i < fs->no_blkgrp; i++) { +		fs->inode_bmaps[i] = zalloc(fs->blksz); +		if (!fs->inode_bmaps[i]) +			goto fail; +	} + +	for (i = 0; i < fs->no_blkgrp; i++) { +		status = ext4fs_devread(fs->gd[i].inode_id * fs->sect_perblk, +					0, fs->blksz, +					(char *)fs->inode_bmaps[i]); +		if (status == 0) +			goto fail; +	} + +	/* +	 * check filesystem consistency with free blocks of file system +	 * some time we observed that superblock freeblocks does not match +	 * with the  blockgroups freeblocks when improper +	 * reboot of a linux kernel +	 */ +	for (i = 0; i < fs->no_blkgrp; i++) +		real_free_blocks = real_free_blocks + fs->gd[i].free_blocks; +	if (real_free_blocks != fs->sb->free_blocks) +		fs->sb->free_blocks = real_free_blocks; + +	return 0; +fail: +	ext4fs_deinit(); + +	return -1; +} + +void ext4fs_deinit(void) +{ +	int i; +	struct ext2_inode inode_journal; +	struct journal_superblock_t *jsb; +	long int blknr; +	struct ext_filesystem *fs = get_fs(); + +	/* free journal */ +	char *temp_buff = zalloc(fs->blksz); +	if (temp_buff) { +		ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO, +				  &inode_journal); +		blknr = read_allocated_block(&inode_journal, +					EXT2_JOURNAL_SUPERBLOCK); +		ext4fs_devread(blknr * fs->sect_perblk, 0, fs->blksz, +			       temp_buff); +		jsb = (struct journal_superblock_t *)temp_buff; +		jsb->s_start = cpu_to_be32(0); +		put_ext4((uint64_t) (blknr * fs->blksz), +			 (struct journal_superblock_t *)temp_buff, fs->blksz); +		free(temp_buff); +	} +	ext4fs_free_journal(); + +	/* get the superblock */ +	ext4fs_devread(SUPERBLOCK_SECTOR, 0, SUPERBLOCK_SIZE, (char *)fs->sb); +	fs->sb->feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER; +	put_ext4((uint64_t)(SUPERBLOCK_SIZE), +		 (struct ext2_sblock *)fs->sb, (uint32_t)SUPERBLOCK_SIZE); +	free(fs->sb); +	fs->sb = NULL; + +	if (fs->blk_bmaps) { +		for (i = 0; i < fs->no_blkgrp; i++) { +			free(fs->blk_bmaps[i]); +			fs->blk_bmaps[i] = NULL; +		} +		free(fs->blk_bmaps); +		fs->blk_bmaps = NULL; +	} + +	if (fs->inode_bmaps) { +		for (i = 0; i < fs->no_blkgrp; i++) { +			free(fs->inode_bmaps[i]); +			fs->inode_bmaps[i] = NULL; +		} +		free(fs->inode_bmaps); +		fs->inode_bmaps = NULL; +	} + + +	free(fs->gdtable); +	fs->gdtable = NULL; +	fs->gd = NULL; +	/* +	 * reinitiliazed the global inode and +	 * block bitmap first execution check variables +	 */ +	fs->first_pass_ibmap = 0; +	fs->first_pass_bbmap = 0; +	fs->curr_inode_no = 0; +	fs->curr_blkno = 0; +} + +static int ext4fs_write_file(struct ext2_inode *file_inode, +			     int pos, unsigned int len, char *buf) +{ +	int i; +	int blockcnt; +	int log2blocksize = LOG2_EXT2_BLOCK_SIZE(ext4fs_root); +	unsigned int filesize = __le32_to_cpu(file_inode->size); +	struct ext_filesystem *fs = get_fs(); +	int previous_block_number = -1; +	int delayed_start = 0; +	int delayed_extent = 0; +	int delayed_skipfirst = 0; +	int delayed_next = 0; +	char *delayed_buf = NULL; + +	/* Adjust len so it we can't read past the end of the file. */ +	if (len > filesize) +		len = filesize; + +	blockcnt = ((len + pos) + fs->blksz - 1) / fs->blksz; + +	for (i = pos / fs->blksz; i < blockcnt; i++) { +		long int blknr; +		int blockend = fs->blksz; +		int skipfirst = 0; +		blknr = read_allocated_block(file_inode, i); +		if (blknr < 0) +			return -1; + +		blknr = blknr << log2blocksize; + +		if (blknr) { +			if (previous_block_number != -1) { +				if (delayed_next == blknr) { +					delayed_extent += blockend; +					delayed_next += blockend >> SECTOR_BITS; +				} else {	/* spill */ +					put_ext4((uint64_t) (delayed_start * +							     SECTOR_SIZE), +						 delayed_buf, +						 (uint32_t) delayed_extent); +					previous_block_number = blknr; +					delayed_start = blknr; +					delayed_extent = blockend; +					delayed_skipfirst = skipfirst; +					delayed_buf = buf; +					delayed_next = blknr + +					    (blockend >> SECTOR_BITS); +				} +			} else { +				previous_block_number = blknr; +				delayed_start = blknr; +				delayed_extent = blockend; +				delayed_skipfirst = skipfirst; +				delayed_buf = buf; +				delayed_next = blknr + +				    (blockend >> SECTOR_BITS); +			} +		} else { +			if (previous_block_number != -1) { +				/* spill */ +				put_ext4((uint64_t) (delayed_start * +						     SECTOR_SIZE), delayed_buf, +					 (uint32_t) delayed_extent); +				previous_block_number = -1; +			} +			memset(buf, 0, fs->blksz - skipfirst); +		} +		buf += fs->blksz - skipfirst; +	} +	if (previous_block_number != -1) { +		/* spill */ +		put_ext4((uint64_t) (delayed_start * SECTOR_SIZE), +			 delayed_buf, (uint32_t) delayed_extent); +		previous_block_number = -1; +	} + +	return len; +} + +int ext4fs_write(const char *fname, unsigned char *buffer, +					unsigned long sizebytes) +{ +	int ret = 0; +	struct ext2_inode *file_inode = NULL; +	unsigned char *inode_buffer = NULL; +	int parent_inodeno; +	int inodeno; +	time_t timestamp = 0; + +	uint64_t bytes_reqd_for_file; +	unsigned int blks_reqd_for_file; +	unsigned int blocks_remaining; +	int existing_file_inodeno; +	char filename[256]; + +	char *temp_ptr = NULL; +	long int itable_blkno; +	long int parent_itable_blkno; +	long int blkoff; +	struct ext2_sblock *sblock = &(ext4fs_root->sblock); +	unsigned int inodes_per_block; +	unsigned int ibmap_idx; +	struct ext_filesystem *fs = get_fs(); +	g_parent_inode = zalloc(sizeof(struct ext2_inode)); +	if (!g_parent_inode) +		goto fail; + +	if (ext4fs_init() != 0) { +		printf("error in File System init\n"); +		return -1; +	} +	inodes_per_block = fs->blksz / fs->inodesz; +	parent_inodeno = ext4fs_get_parent_inode_num(fname, filename, F_FILE); +	if (parent_inodeno == -1) +		goto fail; +	if (ext4fs_iget(parent_inodeno, g_parent_inode)) +		goto fail; +	/* check if the filename is already present in root */ +	existing_file_inodeno = ext4fs_filename_check(filename); +	if (existing_file_inodeno != -1) { +		ret = ext4fs_delete_file(existing_file_inodeno); +		fs->first_pass_bbmap = 0; +		fs->curr_blkno = 0; + +		fs->first_pass_ibmap = 0; +		fs->curr_inode_no = 0; +		if (ret) +			goto fail; +	} +	/* calucalate how many blocks required */ +	bytes_reqd_for_file = sizebytes; +	blks_reqd_for_file = bytes_reqd_for_file / fs->blksz; +	if (bytes_reqd_for_file % fs->blksz != 0) { +		blks_reqd_for_file++; +		debug("total bytes for a file %u\n", blks_reqd_for_file); +	} +	blocks_remaining = blks_reqd_for_file; +	/* test for available space in partition */ +	if (fs->sb->free_blocks < blks_reqd_for_file) { +		printf("Not enough space on partition !!!\n"); +		goto fail; +	} + +	ext4fs_update_parent_dentry(filename, &inodeno, FILETYPE_REG); +	/* prepare file inode */ +	inode_buffer = zalloc(fs->inodesz); +	if (!inode_buffer) +		goto fail; +	file_inode = (struct ext2_inode *)inode_buffer; +	file_inode->mode = S_IFREG | S_IRWXU | +	    S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH; +	/* ToDo: Update correct time */ +	file_inode->mtime = timestamp; +	file_inode->atime = timestamp; +	file_inode->ctime = timestamp; +	file_inode->nlinks = 1; +	file_inode->size = sizebytes; + +	/* Allocate data blocks */ +	ext4fs_allocate_blocks(file_inode, blocks_remaining, +			       &blks_reqd_for_file); +	file_inode->blockcnt = (blks_reqd_for_file * fs->blksz) / SECTOR_SIZE; + +	temp_ptr = zalloc(fs->blksz); +	if (!temp_ptr) +		goto fail; +	ibmap_idx = inodeno / ext4fs_root->sblock.inodes_per_group; +	inodeno--; +	itable_blkno = __le32_to_cpu(fs->gd[ibmap_idx].inode_table_id) + +			(inodeno % __le32_to_cpu(sblock->inodes_per_group)) / +			inodes_per_block; +	blkoff = (inodeno % inodes_per_block) * fs->inodesz; +	ext4fs_devread(itable_blkno * fs->sect_perblk, 0, fs->blksz, temp_ptr); +	if (ext4fs_log_journal(temp_ptr, itable_blkno)) +		goto fail; + +	memcpy(temp_ptr + blkoff, inode_buffer, fs->inodesz); +	if (ext4fs_put_metadata(temp_ptr, itable_blkno)) +		goto fail; +	/* copy the file content into data blocks */ +	if (ext4fs_write_file(file_inode, 0, sizebytes, (char *)buffer) == -1) { +		printf("Error in copying content\n"); +		goto fail; +	} +	ibmap_idx = parent_inodeno / ext4fs_root->sblock.inodes_per_group; +	parent_inodeno--; +	parent_itable_blkno = __le32_to_cpu(fs->gd[ibmap_idx].inode_table_id) + +	    (parent_inodeno % +	     __le32_to_cpu(sblock->inodes_per_group)) / inodes_per_block; +	blkoff = (parent_inodeno % inodes_per_block) * fs->inodesz; +	if (parent_itable_blkno != itable_blkno) { +		memset(temp_ptr, '\0', fs->blksz); +		ext4fs_devread(parent_itable_blkno * fs->sect_perblk, +			       0, fs->blksz, temp_ptr); +		if (ext4fs_log_journal(temp_ptr, parent_itable_blkno)) +			goto fail; + +		memcpy(temp_ptr + blkoff, g_parent_inode, +			sizeof(struct ext2_inode)); +		if (ext4fs_put_metadata(temp_ptr, parent_itable_blkno)) +			goto fail; +		free(temp_ptr); +	} else { +		/* +		 * If parent and child fall in same inode table block +		 * both should be kept in 1 buffer +		 */ +		memcpy(temp_ptr + blkoff, g_parent_inode, +		       sizeof(struct ext2_inode)); +		gd_index--; +		if (ext4fs_put_metadata(temp_ptr, itable_blkno)) +			goto fail; +		free(temp_ptr); +	} +	ext4fs_update(); +	ext4fs_deinit(); + +	fs->first_pass_bbmap = 0; +	fs->curr_blkno = 0; +	fs->first_pass_ibmap = 0; +	fs->curr_inode_no = 0; +	free(inode_buffer); +	free(g_parent_inode); +	g_parent_inode = NULL; + +	return 0; +fail: +	ext4fs_deinit(); +	free(inode_buffer); +	free(g_parent_inode); +	g_parent_inode = NULL; + +	return -1; +} +#endif diff --git a/fs/fat/fat.c b/fs/fat/fat.c index bc46cc5d2..19f6a8c0a 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -37,7 +37,7 @@  /*   * Convert a string to lowercase.   */ -static void downcase (char *str) +static void downcase(char *str)  {  	while (*str != '\0') {  		TOLOWER(*str); @@ -62,7 +62,7 @@ static int disk_read(__u32 block, __u32 nr_blocks, void *buf)  			cur_part_info.start + block, nr_blocks, buf);  } -int fat_register_device (block_dev_desc_t * dev_desc, int part_no) +int fat_register_device(block_dev_desc_t * dev_desc, int part_no)  {  	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz); @@ -127,7 +127,7 @@ int fat_register_device (block_dev_desc_t * dev_desc, int part_no)   * Get the first occurence of a directory delimiter ('/' or '\') in a string.   * Return index into string if found, -1 otherwise.   */ -static int dirdelim (char *str) +static int dirdelim(char *str)  {  	char *start = str; @@ -142,7 +142,7 @@ static int dirdelim (char *str)  /*   * Extract zero terminated short name from a directory entry.   */ -static void get_name (dir_entry *dirent, char *s_name) +static void get_name(dir_entry *dirent, char *s_name)  {  	char *ptr; @@ -171,7 +171,7 @@ static void get_name (dir_entry *dirent, char *s_name)   * Get the entry at index 'entry' in a FAT (12/16/32) table.   * On failure 0x00 is returned.   */ -static __u32 get_fatent (fsdata *mydata, __u32 entry) +static __u32 get_fatent(fsdata *mydata, __u32 entry)  {  	__u32 bufnum;  	__u32 off16, offset; @@ -207,10 +207,9 @@ static __u32 get_fatent (fsdata *mydata, __u32 entry)  		__u32 fatlength = mydata->fatlength;  		__u32 startblock = bufnum * FATBUFBLOCKS; -		if (getsize > fatlength) -			getsize = fatlength; +		if (startblock + getsize > fatlength) +			getsize = fatlength - startblock; -		fatlength *= mydata->sect_size;	/* We want it in bytes now */  		startblock += mydata->fat_sect;	/* Offset from start of disk */  		if (disk_read(startblock, getsize, bufptr) < 0) { @@ -270,12 +269,10 @@ static __u32 get_fatent (fsdata *mydata, __u32 entry)   * Return 0 on success, -1 otherwise.   */  static int -get_cluster (fsdata *mydata, __u32 clustnum, __u8 *buffer, -	     unsigned long size) +get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)  {  	__u32 idx = 0;  	__u32 startsect; -	__u32 nr_sect;  	int ret;  	if (clustnum > 0) { @@ -287,25 +284,44 @@ get_cluster (fsdata *mydata, __u32 clustnum, __u8 *buffer,  	debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect); -	nr_sect = size / mydata->sect_size; -	ret = disk_read(startsect, nr_sect, buffer); -	if (ret != nr_sect) { -		debug("Error reading data (got %d)\n", ret); -		return -1; -	} -	if (size % mydata->sect_size) { +	if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {  		ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size); +		printf("FAT: Misaligned buffer address (%p)\n", buffer); + +		while (size >= mydata->sect_size) { +			ret = disk_read(startsect++, 1, tmpbuf); +			if (ret != 1) { +				debug("Error reading data (got %d)\n", ret); +				return -1; +			} + +			memcpy(buffer, tmpbuf, mydata->sect_size); +			buffer += mydata->sect_size; +			size -= mydata->sect_size; +		} +	} else {  		idx = size / mydata->sect_size; -		ret = disk_read(startsect + idx, 1, tmpbuf); +		ret = disk_read(startsect, idx, buffer); +		if (ret != idx) { +			debug("Error reading data (got %d)\n", ret); +			return -1; +		} +		startsect += idx; +		idx *= mydata->sect_size; +		buffer += idx; +		size -= idx; +	} +	if (size) { +		ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size); + +		ret = disk_read(startsect, 1, tmpbuf);  		if (ret != 1) {  			debug("Error reading data (got %d)\n", ret);  			return -1;  		} -		buffer += idx * mydata->sect_size; -		memcpy(buffer, tmpbuf, size % mydata->sect_size); -		return 0; +		memcpy(buffer, tmpbuf, size);  	}  	return 0; @@ -317,8 +333,8 @@ get_cluster (fsdata *mydata, __u32 clustnum, __u8 *buffer,   * Return the number of bytes read or -1 on fatal errors.   */  static long -get_contents (fsdata *mydata, dir_entry *dentptr, __u8 *buffer, -	      unsigned long maxsize) +get_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer, +	     unsigned long maxsize)  {  	unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0;  	unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; @@ -351,21 +367,9 @@ get_contents (fsdata *mydata, dir_entry *dentptr, __u8 *buffer,  			actsize += bytesperclust;  		} -		/* actsize >= file size */ -		actsize -= bytesperclust; - -		/* get remaining clusters */ -		if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) { -			printf("Error reading cluster\n"); -			return -1; -		} -  		/* get remaining bytes */ -		gotsize += (int)actsize; -		filesize -= actsize; -		buffer += actsize;  		actsize = filesize; -		if (get_cluster(mydata, endclust, buffer, (int)actsize) != 0) { +		if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {  			printf("Error reading cluster\n");  			return -1;  		} @@ -397,7 +401,7 @@ getit:   * starting at l_name[*idx].   * Return 1 if terminator (zero byte) is found, 0 otherwise.   */ -static int slot2str (dir_slot *slotptr, char *l_name, int *idx) +static int slot2str(dir_slot *slotptr, char *l_name, int *idx)  {  	int j; @@ -433,8 +437,8 @@ __u8 get_vfatname_block[MAX_CLUSTSIZE]  	__aligned(ARCH_DMA_MINALIGN);  static int -get_vfatname (fsdata *mydata, int curclust, __u8 *cluster, -	      dir_entry *retdent, char *l_name) +get_vfatname(fsdata *mydata, int curclust, __u8 *cluster, +	     dir_entry *retdent, char *l_name)  {  	dir_entry *realdent;  	dir_slot *slotptr = (dir_slot *)retdent; @@ -516,7 +520,7 @@ get_vfatname (fsdata *mydata, int curclust, __u8 *cluster,  }  /* Calculate short name checksum */ -static __u8 mkcksum (const char *str) +static __u8 mkcksum(const char *str)  {  	int i; @@ -537,9 +541,9 @@ static __u8 mkcksum (const char *str)  __u8 get_dentfromdir_block[MAX_CLUSTSIZE]  	__aligned(ARCH_DMA_MINALIGN); -static dir_entry *get_dentfromdir (fsdata *mydata, int startsect, -				   char *filename, dir_entry *retdent, -				   int dols) +static dir_entry *get_dentfromdir(fsdata *mydata, int startsect, +				  char *filename, dir_entry *retdent, +				  int dols)  {  	__u16 prevcksum = 0xffff;  	__u32 curclust = START(retdent); @@ -699,7 +703,7 @@ static dir_entry *get_dentfromdir (fsdata *mydata, int startsect,   * Read boot sector and volume info from a FAT filesystem   */  static int -read_bootsectandvi (boot_sector *bs, volume_info *volinfo, int *fatsize) +read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)  {  	__u8 *block;  	volume_info *vistart; @@ -716,7 +720,7 @@ read_bootsectandvi (boot_sector *bs, volume_info *volinfo, int *fatsize)  		return -1;  	} -	if (disk_read (0, 1, block) < 0) { +	if (disk_read(0, 1, block) < 0) {  		debug("Error: reading block\n");  		goto fail;  	} @@ -770,15 +774,14 @@ __u8 do_fat_read_block[MAX_CLUSTSIZE]  	__aligned(ARCH_DMA_MINALIGN);  long -do_fat_read (const char *filename, void *buffer, unsigned long maxsize, -	     int dols) +do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)  {  	char fnamecopy[2048];  	boot_sector bs;  	volume_info volinfo;  	fsdata datablock;  	fsdata *mydata = &datablock; -	dir_entry *dentptr; +	dir_entry *dentptr = NULL;  	__u16 prevcksum = 0xffff;  	char *subname = "";  	__u32 cursect; @@ -877,19 +880,21 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize,  	while (1) {  		int i; -		debug("FAT read sect=%d, clust_size=%d, DIRENTSPERBLOCK=%zd\n", -			cursect, mydata->clust_size, DIRENTSPERBLOCK); +		if (j == 0) { +			debug("FAT read sect=%d, clust_size=%d, DIRENTSPERBLOCK=%zd\n", +				cursect, mydata->clust_size, DIRENTSPERBLOCK); -		if (disk_read(cursect, -				(mydata->fatsize == 32) ? -				(mydata->clust_size) : -				PREFETCH_BLOCKS, -				do_fat_read_block) < 0) { -			debug("Error: reading rootdir block\n"); -			goto exit; -		} +			if (disk_read(cursect, +					(mydata->fatsize == 32) ? +					(mydata->clust_size) : +					PREFETCH_BLOCKS, +					do_fat_read_block) < 0) { +				debug("Error: reading rootdir block\n"); +				goto exit; +			} -		dentptr = (dir_entry *) do_fat_read_block; +			dentptr = (dir_entry *) do_fat_read_block; +		}  		for (i = 0; i < DIRENTSPERBLOCK; i++) {  			char s_name[14], l_name[VFAT_MAXLEN_BYTES]; @@ -1031,28 +1036,33 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize,  		 * completely processed.  		 */  		++j; -		int fat32_end = 0; -		if ((mydata->fatsize == 32) && (j == mydata->clust_size)) { -			int nxtsect = 0; -			int nxt_clust = 0; +		int rootdir_end = 0; +		if (mydata->fatsize == 32) { +			if (j == mydata->clust_size) { +				int nxtsect = 0; +				int nxt_clust = 0; -			nxt_clust = get_fatent(mydata, root_cluster); -			fat32_end = CHECK_CLUST(nxt_clust, 32); +				nxt_clust = get_fatent(mydata, root_cluster); +				rootdir_end = CHECK_CLUST(nxt_clust, 32); -			nxtsect = mydata->data_begin + -				(nxt_clust * mydata->clust_size); +				nxtsect = mydata->data_begin + +					(nxt_clust * mydata->clust_size); -			root_cluster = nxt_clust; +				root_cluster = nxt_clust; -			cursect = nxtsect; -			j = 0; +				cursect = nxtsect; +				j = 0; +			}  		} else { -			cursect++; +			if (j == PREFETCH_BLOCKS) +				j = 0; + +			rootdir_end = (++cursect - mydata->rootdir_sect >= +				       rootdir_size);  		}  		/* If end of rootdir reached */ -		if ((mydata->fatsize == 32 && fat32_end) || -		    (mydata->fatsize != 32 && j == rootdir_size)) { +		if (rootdir_end) {  			if (dols == LS_ROOT) {  				printf("\n%d file(s), %d dir(s)\n\n",  				       files, dirs); @@ -1099,11 +1109,11 @@ rootdir_done:  			goto exit;  		} -		if (idx >= 0) { -			if (!(dentptr->attr & ATTR_DIR)) -				goto exit; +		if (isdir && !(dentptr->attr & ATTR_DIR)) +			goto exit; + +		if (idx >= 0)  			subname = nextname; -		}  	}  	ret = get_contents(mydata, dentptr, buffer, maxsize); @@ -1114,7 +1124,7 @@ exit:  	return ret;  } -int file_fat_detectfs (void) +int file_fat_detectfs(void)  {  	boot_sector bs;  	volume_info volinfo; @@ -1177,12 +1187,12 @@ int file_fat_detectfs (void)  	return 0;  } -int file_fat_ls (const char *dir) +int file_fat_ls(const char *dir)  {  	return do_fat_read(dir, NULL, 0, LS_YES);  } -long file_fat_read (const char *filename, void *buffer, unsigned long maxsize) +long file_fat_read(const char *filename, void *buffer, unsigned long maxsize)  {  	printf("reading %s\n", filename);  	return do_fat_read(filename, buffer, maxsize, LS_NO); diff --git a/fs/ubifs/ubifs.c b/fs/ubifs/ubifs.c index 604eb8fdc..44be3f53e 100644 --- a/fs/ubifs/ubifs.c +++ b/fs/ubifs/ubifs.c @@ -37,8 +37,8 @@ DECLARE_GLOBAL_DATA_PTR;  static int gzip_decompress(const unsigned char *in, size_t in_len,  			   unsigned char *out, size_t *out_len)  { -	unsigned long len = in_len; -	return zunzip(out, *out_len, (unsigned char *)in, &len, 0, 0); +	return zunzip(out, *out_len, (unsigned char *)in, +		      (unsigned long *)out_len, 0, 0);  }  /* Fake description object for the "none" compressor */ @@ -295,6 +295,7 @@ static int ubifs_finddir(struct super_block *sb, char *dirname,  	struct file *file;  	struct dentry *dentry;  	struct inode *dir; +	int ret = 0;  	file = kzalloc(sizeof(struct file), 0);  	dentry = kzalloc(sizeof(struct dentry), 0); @@ -336,7 +337,8 @@ static int ubifs_finddir(struct super_block *sb, char *dirname,  		if ((strncmp(dirname, (char *)dent->name, nm.len) == 0) &&  		    (strlen(dirname) == nm.len)) {  			*inum = le64_to_cpu(dent->inum); -			return 1; +			ret = 1; +			goto out_free;  		}  		/* Switch to the next entry */ @@ -355,11 +357,10 @@ static int ubifs_finddir(struct super_block *sb, char *dirname,  	}  out: -	if (err != -ENOENT) { +	if (err != -ENOENT)  		ubifs_err("cannot find next direntry, error %d", err); -		return err; -	} +out_free:  	if (file->private_data)  		kfree(file->private_data);  	if (file) @@ -369,7 +370,7 @@ out:  	if (dir)  		free(dir); -	return 0; +	return ret;  }  static unsigned long ubifs_findfile(struct super_block *sb, char *filename) diff --git a/fs/yaffs2/Makefile b/fs/yaffs2/Makefile index 31c12af67..9b29d225a 100644 --- a/fs/yaffs2/Makefile +++ b/fs/yaffs2/Makefile @@ -23,7 +23,7 @@ LIB = $(obj)libyaffs2.o  COBJS-$(CONFIG_YAFFS2) := \  	yaffs_allocator.o yaffs_attribs.o yaffs_bitmap.o yaffs_uboot_glue.o\  	yaffs_checkptrw.o yaffs_ecc.o yaffs_error.o \ -	yaffsfs.o yaffs_guts.o yaffs_hweight.o yaffs_nameval.o yaffs_nand.o\ +	yaffsfs.o yaffs_guts.o yaffs_nameval.o yaffs_nand.o\  	yaffs_packedtags1.o yaffs_packedtags2.o yaffs_qsort.o \  	yaffs_summary.o yaffs_tagscompat.o yaffs_verify.o yaffs_yaffs1.o \  	yaffs_yaffs2.o yaffs_mtdif.o yaffs_mtdif2.o diff --git a/fs/yaffs2/stdio.h b/fs/yaffs2/stdio.h deleted file mode 100644 index 9f379d7a6..000000000 --- a/fs/yaffs2/stdio.h +++ /dev/null @@ -1 +0,0 @@ -/* Dummy header for u-boot */ diff --git a/fs/yaffs2/stdlib.h b/fs/yaffs2/stdlib.h deleted file mode 100644 index 9f379d7a6..000000000 --- a/fs/yaffs2/stdlib.h +++ /dev/null @@ -1 +0,0 @@ -/* Dummy header for u-boot */ diff --git a/fs/yaffs2/string.h b/fs/yaffs2/string.h deleted file mode 100644 index 6501fa7d9..000000000 --- a/fs/yaffs2/string.h +++ /dev/null @@ -1,4 +0,0 @@ -#include <linux/stddef.h> -#include <linux/string.h> -#include <linux/stat.h> -#include <common.h> diff --git a/fs/yaffs2/yaffs_hweight.c b/fs/yaffs2/yaffs_hweight.c deleted file mode 100644 index d96773eb1..000000000 --- a/fs/yaffs2/yaffs_hweight.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. - * - * Copyright (C) 2002-2011 Aleph One Ltd. - *   for Toby Churchill Ltd and Brightstar Engineering - * - * Created by Charles Manning <charles@aleph1.co.uk> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* These functions have been renamed to hweightxx to match the - * equivaqlent functions in the Linux kernel. - */ - -#include "yaffs_hweight.h" - -static const char yaffs_count_bits_table[256] = { -	0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, -	1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, -	1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, -	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, -	1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, -	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, -	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, -	3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, -	1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, -	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, -	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, -	3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, -	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, -	3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, -	3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, -	4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 -}; - -int yaffs_hweight8(u8 x) -{ -	int ret_val; -	ret_val = yaffs_count_bits_table[x]; -	return ret_val; -} - -int yaffs_hweight32(u32 x) -{ -	return yaffs_hweight8(x & 0xff) + -		yaffs_hweight8((x >> 8) & 0xff) + -		yaffs_hweight8((x >> 16) & 0xff) + -		yaffs_hweight8((x >> 24) & 0xff); -} diff --git a/fs/yaffs2/yaffs_hweight.h b/fs/yaffs2/yaffs_hweight.h deleted file mode 100644 index 3f7c6c33e..000000000 --- a/fs/yaffs2/yaffs_hweight.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * YAFFS: Yet another Flash File System . A NAND-flash specific file system. - * - * Copyright (C) 2002-2011 Aleph One Ltd. - *   for Toby Churchill Ltd and Brightstar Engineering - * - * Created by Charles Manning <charles@aleph1.co.uk> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 2.1 as - * published by the Free Software Foundation. - * - * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. - */ - -#ifndef __YAFFS_HWEIGHT_H__ -#define __YAFFS_HWEIGHT_H__ - -#include "yportenv.h" - -int yaffs_hweight8(u8 x); -int yaffs_hweight32(u32 x); - -#endif diff --git a/fs/yaffs2/yaffs_mtdif2.c b/fs/yaffs2/yaffs_mtdif2.c index b27fe5621..8135bcc0f 100644 --- a/fs/yaffs2/yaffs_mtdif2.c +++ b/fs/yaffs2/yaffs_mtdif2.c @@ -28,7 +28,6 @@  #include "yaffs_trace.h"  #include "yaffs_packedtags2.h" -#include "string.h"  #define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context))  #define yaffs_dev_to_lc(dev) ((struct yaffs_linux_context *)((dev)->os_context)) @@ -46,9 +45,7 @@ int nandmtd2_write_chunk_tags(struct yaffs_dev *dev, int nand_chunk,  	struct mtd_oob_ops ops;  	int retval = 0; -  	loff_t addr; -	u8 local_spare[128];  	struct yaffs_packed_tags2 pt; diff --git a/fs/yaffs2/yaffsfs.c b/fs/yaffs2/yaffsfs.c index 9f1397b57..ac4a010bd 100644 --- a/fs/yaffs2/yaffsfs.c +++ b/fs/yaffs2/yaffsfs.c @@ -17,8 +17,6 @@  #include "yportenv.h"  #include "yaffs_trace.h" -#include "string.h" -  #define YAFFSFS_MAX_SYMLINK_DEREFERENCES 5  #ifndef NULL diff --git a/fs/yaffs2/ydirectenv.h b/fs/yaffs2/ydirectenv.h index df0b8fb1c..c912b09a4 100644 --- a/fs/yaffs2/ydirectenv.h +++ b/fs/yaffs2/ydirectenv.h @@ -20,15 +20,14 @@  #ifndef __YDIRECTENV_H__  #define __YDIRECTENV_H__ -#include "stdlib.h" -#include "stdio.h" -#include "string.h" +#include <common.h> +#include <malloc.h> +#include <linux/compat.h> +  #include "yaffs_osglue.h" -#include "yaffs_hweight.h"  void yaffs_bug_fn(const char *file_name, int line_no); -#define BUG() do { yaffs_bug_fn(__FILE__, __LINE__); } while (0)  #define YCHAR char @@ -47,8 +46,6 @@ void yaffs_bug_fn(const char *file_name, int line_no);  #define yaffs_strncmp(a, b, c)	strncmp(a, b, c)  #endif -#define hweight8(x)	yaffs_hweight8(x) -#define hweight32(x)	yaffs_hweight32(x)  void yaffs_qsort(void *aa, size_t n, size_t es,  		int (*cmp)(const void *, const void *)); @@ -63,11 +60,6 @@ void yaffs_qsort(void *aa, size_t n, size_t es,  #define inline __inline__  #endif -#define kmalloc(x, flags) yaffsfs_malloc(x) -#define kfree(x)   yaffsfs_free(x) -#define vmalloc(x) yaffsfs_malloc(x) -#define vfree(x) yaffsfs_free(x) -  #define cond_resched()  do {} while (0)  #define yaffs_trace(msk, fmt, ...) do { \ diff --git a/fs/yaffs2/yportenv.h b/fs/yaffs2/yportenv.h index b47a4d601..251eba079 100644 --- a/fs/yaffs2/yportenv.h +++ b/fs/yaffs2/yportenv.h @@ -17,6 +17,7 @@  #ifndef __YPORTENV_H__  #define __YPORTENV_H__ +#include <linux/types.h>  /* Definition of types */  #ifdef CONFIG_YAFFS_DEFINES_TYPES |