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) | 8 | ||||
| -rw-r--r-- | fs/ext4/dev.c | 145 | ||||
| -rw-r--r-- | fs/ext4/ext4_common.c | 875 | ||||
| -rw-r--r-- | fs/ext4/ext4_common.h | 63 | ||||
| -rw-r--r-- | fs/ext4/ext4fs.c | 228 | 
8 files changed, 1320 insertions, 1054 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..7b7fcbd9f 100644 --- a/fs/ext2/Makefile +++ b/fs/ext4/Makefile @@ -27,15 +27,17 @@  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  SRCS	:= $(AOBJS:.o=.S) $(COBJS-y:.o=.c)  OBJS	:= $(addprefix $(obj),$(AOBJS) $(COBJS-y)) -#CPPFLAGS +=  all:	$(LIB) $(AOBJS) diff --git a/fs/ext4/dev.c b/fs/ext4/dev.c new file mode 100644 index 000000000..fb62f241e --- /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) +{ +	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) { +		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..2ddbb50e8 --- /dev/null +++ b/fs/ext4/ext4_common.c @@ -0,0 +1,875 @@ +/* + * (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. + * + * 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; + +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..8e8bcbc0f --- /dev/null +++ b/fs/ext4/ext4_common.h @@ -0,0 +1,63 @@ +/* + * (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. + * + * 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> + +#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 + +#define zalloc(size) calloc(1, size) + +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); +#endif diff --git a/fs/ext4/ext4fs.c b/fs/ext4/ext4fs.c new file mode 100644 index 000000000..1287bf0e1 --- /dev/null +++ b/fs/ext4/ext4fs.c @@ -0,0 +1,228 @@ +/* + * (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. + * + * 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); +} |