diff options
| author | Tom Rini <trini@ti.com> | 2012-09-20 11:27:35 -0700 | 
|---|---|---|
| committer | Tom Rini <trini@ti.com> | 2012-09-20 11:27:35 -0700 | 
| commit | 5fb29f3c48d26981b117b08286bc16ec99d4ca0b (patch) | |
| tree | b4a4c5b5cc88fedc568bcb49e248e9561a884ef4 | |
| parent | 30a3f3881e44187f21eb08bf8601ed4adaf4ca0e (diff) | |
| parent | 55b523b7d4ab885142f77d388007eb5490ba6bf4 (diff) | |
| download | olio-uboot-2014.01-5fb29f3c48d26981b117b08286bc16ec99d4ca0b.tar.xz olio-uboot-2014.01-5fb29f3c48d26981b117b08286bc16ec99d4ca0b.zip | |
Merge branch 'ext4'
Update Makefile change for LIBS -> LIBS-y change.
Conflicts:
	Makefile
Signed-off-by: Tom Rini <trini@ti.com>
| -rw-r--r-- | Makefile | 12 | ||||
| -rw-r--r-- | common/Makefile | 6 | ||||
| -rw-r--r-- | common/cmd_ext2.c | 219 | ||||
| -rw-r--r-- | common/cmd_ext4.c | 237 | ||||
| -rw-r--r-- | common/cmd_ext_common.c | 259 | ||||
| -rw-r--r-- | doc/README.ext4 | 46 | ||||
| -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-- | include/ext2fs.h | 81 | ||||
| -rw-r--r-- | include/ext4fs.h | 144 | ||||
| -rw-r--r-- | include/ext_common.h | 199 | 
21 files changed, 5468 insertions, 1340 deletions
| @@ -242,9 +242,15 @@ LIBS-y += arch/arm/cpu/ixp/npe/libnpe.o  endif  LIBS-$(CONFIG_OF_EMBED) += dts/libdts.o  LIBS-y += arch/$(ARCH)/lib/lib$(ARCH).o -LIBS-y += fs/cramfs/libcramfs.o fs/fat/libfat.o fs/fdos/libfdos.o fs/jffs2/libjffs2.o \ -	fs/reiserfs/libreiserfs.o fs/ext2/libext2fs.o fs/yaffs2/libyaffs2.o \ -	fs/ubifs/libubifs.o fs/zfs/libzfs.o +LIBS-y += fs/cramfs/libcramfs.o \ +	fs/ext4/libext4fs.o \ +	fs/fat/libfat.o \ +	fs/fdos/libfdos.o \ +	fs/jffs2/libjffs2.o \ +	fs/reiserfs/libreiserfs.o \ +	fs/ubifs/libubifs.o \ +	fs/yaffs2/libyaffs2.o \ +	fs/zfs/libzfs.o  LIBS-y += net/libnet.o  LIBS-y += disk/libdisk.o  LIBS-y += drivers/bios_emulator/libatibiosemu.o diff --git a/common/Makefile b/common/Makefile index 57da76f90..22e8a6fb3 100644 --- a/common/Makefile +++ b/common/Makefile @@ -86,7 +86,13 @@ COBJS-$(CONFIG_ENV_IS_IN_EEPROM) += cmd_eeprom.o  COBJS-$(CONFIG_CMD_EEPROM) += cmd_eeprom.o  COBJS-$(CONFIG_CMD_ELF) += cmd_elf.o  COBJS-$(CONFIG_SYS_HUSH_PARSER) += cmd_exit.o +COBJS-$(CONFIG_CMD_EXT4) += cmd_ext4.o  COBJS-$(CONFIG_CMD_EXT2) += cmd_ext2.o +ifdef CONFIG_CMD_EXT4 +COBJS-y	+= cmd_ext_common.o +else +COBJS-$(CONFIG_CMD_EXT2) += cmd_ext_common.o +endif  COBJS-$(CONFIG_CMD_FAT) += cmd_fat.o  COBJS-$(CONFIG_CMD_FDC)$(CONFIG_CMD_FDOS) += cmd_fdc.o  COBJS-$(CONFIG_OF_LIBFDT) += cmd_fdt.o fdt_support.o diff --git a/common/cmd_ext2.c b/common/cmd_ext2.c index 79b1e2fb6..c27d9c7ed 100644 --- a/common/cmd_ext2.c +++ b/common/cmd_ext2.c @@ -1,4 +1,9 @@  /* + * (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> +   * (C) Copyright 2004   * esd gmbh <www.esd-electronics.com>   * Reinhard Arlt <reinhard.arlt@esd-electronics.com> @@ -33,225 +38,35 @@   * Ext2fs support   */  #include <common.h> -#include <part.h> -#include <config.h> -#include <command.h> -#include <image.h> -#include <linux/ctype.h> -#include <asm/byteorder.h> -#include <ext2fs.h> -#if defined(CONFIG_CMD_USB) && defined(CONFIG_USB_STORAGE) -#include <usb.h> -#endif - -#if !defined(CONFIG_DOS_PARTITION) && !defined(CONFIG_EFI_PARTITION) -#error DOS or EFI partition support must be selected -#endif - -/* #define	EXT2_DEBUG */ - -#ifdef	EXT2_DEBUG -#define	PRINTF(fmt,args...)	printf (fmt ,##args) -#else -#define PRINTF(fmt,args...) -#endif +#include <ext_common.h>  int do_ext2ls (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])  { -	char *filename = "/"; -	int dev=0; -	int part=1; -	char *ep; -	block_dev_desc_t *dev_desc=NULL; -	int part_length; - -	if (argc < 3) -		return CMD_RET_USAGE; - -	dev = (int)simple_strtoul (argv[2], &ep, 16); -	dev_desc = get_dev(argv[1],dev); - -	if (dev_desc == NULL) { -		printf ("\n** Block device %s %d not supported\n", argv[1], dev); -		return 1; -	} - -	if (*ep) { -		if (*ep != ':') { -			puts ("\n** Invalid boot device, use `dev[:part]' **\n"); -			return 1; -		} -		part = (int)simple_strtoul(++ep, NULL, 16); -	} - -	if (argc == 4) -		filename = argv[3]; - -	PRINTF("Using device %s %d:%d, directory: %s\n", argv[1], dev, part, filename); - -	if ((part_length = ext2fs_set_blk_dev(dev_desc, part)) == 0) { -		printf ("** Bad partition - %s %d:%d **\n",  argv[1], dev, part); -		ext2fs_close(); -		return 1; -	} - -	if (!ext2fs_mount(part_length)) { -		printf ("** Bad ext2 partition or disk - %s %d:%d **\n",  argv[1], dev, part); -		ext2fs_close(); -		return 1; -	} - -	if (ext2fs_ls (filename)) { -		printf ("** Error ext2fs_ls() **\n"); -		ext2fs_close(); -		return 1; -	}; - -	ext2fs_close(); +	if (do_ext_ls(cmdtp, flag, argc, argv)) +		return -1;  	return 0;  } -U_BOOT_CMD( -	ext2ls,	4,	1,	do_ext2ls, -	"list files in a directory (default /)", -	"<interface> <dev[:part]> [directory]\n" -	"    - list files from 'dev' on 'interface' in a 'directory'" -); -  /******************************************************************************   * Ext2fs boot command intepreter. Derived from diskboot   */  int do_ext2load (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])  { -	char *filename = NULL; -	char *ep; -	int dev, part = 1; -	ulong addr = 0, part_length; -	int filelen; -	disk_partition_t info; -	block_dev_desc_t *dev_desc = NULL; -	char buf [12]; -	unsigned long count; -	char *addr_str; - -	switch (argc) { -	case 3: -		addr_str = getenv("loadaddr"); -		if (addr_str != NULL) -			addr = simple_strtoul (addr_str, NULL, 16); -		else -			addr = CONFIG_SYS_LOAD_ADDR; - -		filename = getenv ("bootfile"); -		count = 0; -		break; -	case 4: -		addr = simple_strtoul (argv[3], NULL, 16); -		filename = getenv ("bootfile"); -		count = 0; -		break; -	case 5: -		addr = simple_strtoul (argv[3], NULL, 16); -		filename = argv[4]; -		count = 0; -		break; -	case 6: -		addr = simple_strtoul (argv[3], NULL, 16); -		filename = argv[4]; -		count = simple_strtoul (argv[5], NULL, 16); -		break; - -	default: -		return CMD_RET_USAGE; -	} - -	if (!filename) { -		puts ("** No boot file defined **\n"); -		return 1; -	} - -	dev = (int)simple_strtoul (argv[2], &ep, 16); -	dev_desc = get_dev(argv[1],dev); -	if (dev_desc==NULL) { -		printf ("** Block device %s %d not supported\n", argv[1], dev); -		return 1; -	} -	if (*ep) { -		if (*ep != ':') { -			puts ("** Invalid boot device, use `dev[:part]' **\n"); -			return 1; -		} -		part = (int)simple_strtoul(++ep, NULL, 16); -	} - -	PRINTF("Using device %s%d, partition %d\n", argv[1], dev, part); - -	if (part != 0) { -		if (get_partition_info (dev_desc, part, &info)) { -			printf ("** Bad partition %d **\n", part); -			return 1; -		} - -		if (strncmp((char *)info.type, BOOT_PART_TYPE, sizeof(info.type)) != 0) { -			printf ("** Invalid partition type \"%.32s\"" -				" (expect \"" BOOT_PART_TYPE "\")\n", -				info.type); -			return 1; -		} -		printf ("Loading file \"%s\" " -			"from %s device %d:%d (%.32s)\n", -			filename, -			argv[1], dev, part, info.name); -	} else { -		printf ("Loading file \"%s\" from %s device %d\n", -			filename, argv[1], dev); -	} - - -	if ((part_length = ext2fs_set_blk_dev(dev_desc, part)) == 0) { -		printf ("** Bad partition - %s %d:%d **\n",  argv[1], dev, part); -		ext2fs_close(); -		return 1; -	} - -	if (!ext2fs_mount(part_length)) { -		printf ("** Bad ext2 partition or disk - %s %d:%d **\n", -			argv[1], dev, part); -		ext2fs_close(); -		return 1; -	} - -	filelen = ext2fs_open(filename); -	if (filelen < 0) { -		printf("** File not found %s\n", filename); -		ext2fs_close(); -		return 1; -	} -	if ((count < filelen) && (count != 0)) { -	    filelen = count; -	} - -	if (ext2fs_read((char *)addr, filelen) != filelen) { -		printf("** Unable to read \"%s\" from %s %d:%d **\n", -			filename, argv[1], dev, part); -		ext2fs_close(); -		return 1; -	} - -	ext2fs_close(); - -	/* Loading ok, update default load address */ -	load_addr = addr; - -	printf ("%d bytes read\n", filelen); -	sprintf(buf, "%X", filelen); -	setenv("filesize", buf); +	if (do_ext_load(cmdtp, flag, argc, argv)) +		return -1;  	return 0;  }  U_BOOT_CMD( +	ext2ls,	4,	1,	do_ext2ls, +	"list files in a directory (default /)", +	"<interface> <dev[:part]> [directory]\n" +	"    - list files from 'dev' on 'interface' in a 'directory'" +); + +U_BOOT_CMD(  	ext2load,	6,	0,	do_ext2load,  	"load binary file from a Ext2 filesystem",  	"<interface> <dev[:part]> [addr] [filename] [bytes]\n" diff --git a/common/cmd_ext4.c b/common/cmd_ext4.c new file mode 100644 index 000000000..77094c4eb --- /dev/null +++ b/common/cmd_ext4.c @@ -0,0 +1,237 @@ +/* + * (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> + * + * Ext4fs support + * made from existing cmd_ext2.c file of Uboot + * + * (C) Copyright 2004 + * esd gmbh <www.esd-electronics.com> + * Reinhard Arlt <reinhard.arlt@esd-electronics.com> + * + * made from cmd_reiserfs by + * + * (C) Copyright 2003 - 2004 + * Sysgo Real-Time Solutions, 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +/* + * Changelog: + *	0.1 - Newly created file for ext4fs support. Taken from cmd_ext2.c + *	        file in uboot. Added ext4fs ls load and write support. + */ + +#include <common.h> +#include <part.h> +#include <config.h> +#include <command.h> +#include <image.h> +#include <linux/ctype.h> +#include <asm/byteorder.h> +#include <ext_common.h> +#include <ext4fs.h> +#include <linux/stat.h> +#include <malloc.h> + +#if defined(CONFIG_CMD_USB) && defined(CONFIG_USB_STORAGE) +#include <usb.h> +#endif + +#if !defined(CONFIG_DOS_PARTITION) && !defined(CONFIG_EFI_PARTITION) +#error DOS or EFI partition support must be selected +#endif + +uint64_t total_sector; +uint64_t part_offset; +#if defined(CONFIG_CMD_EXT4_WRITE) +static uint64_t part_size; +static uint16_t cur_part = 1; +#endif + +#define DOS_PART_MAGIC_OFFSET		0x1fe +#define DOS_FS_TYPE_OFFSET		0x36 +#define DOS_FS32_TYPE_OFFSET		0x52 + +int do_ext4_load(cmd_tbl_t *cmdtp, int flag, int argc, +						char *const argv[]) +{ +	if (do_ext_load(cmdtp, flag, argc, argv)) +		return -1; + +	return 0; +} + +int do_ext4_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ +	if (do_ext_ls(cmdtp, flag, argc, argv)) +		return -1; + +	return 0; +} + +#if defined(CONFIG_CMD_EXT4_WRITE) +static int ext4_register_device(block_dev_desc_t *dev_desc, int part_no) +{ +	unsigned char buffer[SECTOR_SIZE]; +	disk_partition_t info; + +	if (!dev_desc->block_read) +		return -1; + +	/* check if we have a MBR (on floppies we have only a PBR) */ +	if (dev_desc->block_read(dev_desc->dev, 0, 1, (ulong *) buffer) != 1) { +		printf("** Can't read from device %d **\n", dev_desc->dev); +		return -1; +	} +	if (buffer[DOS_PART_MAGIC_OFFSET] != 0x55 || +	    buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) { +		/* no signature found */ +		return -1; +	} + +	/* First we assume there is a MBR */ +	if (!get_partition_info(dev_desc, part_no, &info)) { +		part_offset = info.start; +		cur_part = part_no; +		part_size = info.size; +	} else if ((strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET], +			    "FAT", 3) == 0) || (strncmp((char *)&buffer +							[DOS_FS32_TYPE_OFFSET], +							"FAT32", 5) == 0)) { +		/* ok, we assume we are on a PBR only */ +		cur_part = 1; +		part_offset = 0; +	} else { +		printf("** Partition %d not valid on device %d **\n", +		       part_no, dev_desc->dev); +		return -1; +	} + +	return 0; +} + +int do_ext4_write(cmd_tbl_t *cmdtp, int flag, int argc, +				char *const argv[]) +{ +	const char *filename = "/"; +	int part_length; +	unsigned long part = 1; +	int dev; +	char *ep; +	unsigned long ram_address; +	unsigned long file_size; +	disk_partition_t info; +	struct ext_filesystem *fs; + +	if (argc < 6) +		return cmd_usage(cmdtp); + +	dev = (int)simple_strtoul(argv[2], &ep, 16); +	ext4_dev_desc = get_dev(argv[1], dev); +	if (ext4_dev_desc == NULL) { +		printf("Block device %s %d not supported\n", argv[1], dev); +		return 1; +	} +	if (init_fs(ext4_dev_desc)) +		return 1; + +	fs = get_fs(); +	if (*ep) { +		if (*ep != ':') { +			puts("Invalid boot device, use `dev[:part]'\n"); +			goto fail; +		} +		part = simple_strtoul(++ep, NULL, 16); +	} + +	/* get the filename */ +	filename = argv[3]; + +	/* get the address in hexadecimal format (string to int) */ +	ram_address = simple_strtoul(argv[4], NULL, 16); + +	/* get the filesize in base 10 format */ +	file_size = simple_strtoul(argv[5], NULL, 10); + +	/* set the device as block device */ +	part_length = ext4fs_set_blk_dev(fs->dev_desc, part); +	if (part_length == 0) { +		printf("Bad partition - %s %d:%lu\n", argv[1], dev, part); +		goto fail; +	} + +	/* register the device and partition */ +	if (ext4_register_device(fs->dev_desc, part) != 0) { +		printf("Unable to use %s %d:%lu for fattable\n", +		       argv[1], dev, part); +		goto fail; +	} + +	/* get the partition information */ +	if (!get_partition_info(fs->dev_desc, part, &info)) { +		total_sector = (info.size * info.blksz) / SECTOR_SIZE; +		fs->total_sect = total_sector; +	} else { +		printf("error : get partition info\n"); +		goto fail; +	} + +	/* mount the filesystem */ +	if (!ext4fs_mount(part_length)) { +		printf("Bad ext4 partition %s %d:%lu\n", argv[1], dev, part); +		goto fail; +	} + +	/* start write */ +	if (ext4fs_write(filename, (unsigned char *)ram_address, file_size)) { +		printf("** Error ext4fs_write() **\n"); +		goto fail; +	} +	ext4fs_close(); +	deinit_fs(fs->dev_desc); + +	return 0; + +fail: +	ext4fs_close(); +	deinit_fs(fs->dev_desc); + +	return 1; +} + +U_BOOT_CMD(ext4write, 6, 1, do_ext4_write, +	"create a file in the root directory", +	"<interface> <dev[:part]> [Absolute filename path] [Address] [sizebytes]\n" +	"	  - create a file in / directory"); + +#endif + +U_BOOT_CMD(ext4ls, 4, 1, do_ext4_ls, +	   "list files in a directory (default /)", +	   "<interface> <dev[:part]> [directory]\n" +	   "	  - list files from 'dev' on 'interface' in a 'directory'"); + +U_BOOT_CMD(ext4load, 6, 0, do_ext4_load, +	   "load binary file from a Ext4 filesystem", +	   "<interface> <dev[:part]> [addr] [filename] [bytes]\n" +	   "	  - load binary file 'filename' from 'dev' on 'interface'\n" +	   "		 to address 'addr' from ext4 filesystem"); diff --git a/common/cmd_ext_common.c b/common/cmd_ext_common.c new file mode 100644 index 000000000..56ee9a55b --- /dev/null +++ b/common/cmd_ext_common.c @@ -0,0 +1,259 @@ +/* + * (C) Copyright 2011 - 2012 Samsung Electronics + * EXT2/4 filesystem implementation in Uboot by + * Uma Shankar <uma.shankar@samsung.com> + * Manjunatha C Achar <a.manjunatha@samsung.com> + * + * Ext4fs support + * made from existing cmd_ext2.c file of Uboot + * + * (C) Copyright 2004 + * esd gmbh <www.esd-electronics.com> + * Reinhard Arlt <reinhard.arlt@esd-electronics.com> + * + * made from cmd_reiserfs by + * + * (C) Copyright 2003 - 2004 + * Sysgo Real-Time Solutions, 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +/* + * Changelog: + *	0.1 - Newly created file for ext4fs support. Taken from cmd_ext2.c + *	        file in uboot. Added ext4fs ls load and write support. + */ + +#include <common.h> +#include <part.h> +#include <config.h> +#include <command.h> +#include <image.h> +#include <linux/ctype.h> +#include <asm/byteorder.h> +#include <ext_common.h> +#include <ext4fs.h> +#include <linux/stat.h> +#include <malloc.h> + +#if defined(CONFIG_CMD_USB) && defined(CONFIG_USB_STORAGE) +#include <usb.h> +#endif + +#if !defined(CONFIG_DOS_PARTITION) && !defined(CONFIG_EFI_PARTITION) +#error DOS or EFI partition support must be selected +#endif + +#define DOS_PART_MAGIC_OFFSET		0x1fe +#define DOS_FS_TYPE_OFFSET		0x36 +#define DOS_FS32_TYPE_OFFSET		0x52 + +int do_ext_load(cmd_tbl_t *cmdtp, int flag, int argc, +						char *const argv[]) +{ +	char *filename = NULL; +	char *ep; +	int dev; +	unsigned long part = 1; +	ulong addr = 0; +	ulong part_length; +	int filelen; +	disk_partition_t info; +	struct ext_filesystem *fs; +	char buf[12]; +	unsigned long count; +	const char *addr_str; + +	count = 0; +	addr = simple_strtoul(argv[3], NULL, 16); +	filename = getenv("bootfile"); +	switch (argc) { +	case 3: +		addr_str = getenv("loadaddr"); +		if (addr_str != NULL) +			addr = simple_strtoul(addr_str, NULL, 16); +		else +			addr = CONFIG_SYS_LOAD_ADDR; + +		break; +	case 4: +		break; +	case 5: +		filename = argv[4]; +		break; +	case 6: +		filename = argv[4]; +		count = simple_strtoul(argv[5], NULL, 16); +		break; + +	default: +		return cmd_usage(cmdtp); +	} + +	if (!filename) { +		puts("** No boot file defined **\n"); +		return 1; +	} + +	dev = (int)simple_strtoul(argv[2], &ep, 16); +	ext4_dev_desc = get_dev(argv[1], dev); +	if (ext4_dev_desc == NULL) { +		printf("** Block device %s %d not supported\n", argv[1], dev); +		return 1; +	} +	if (init_fs(ext4_dev_desc)) +		return 1; + +	fs = get_fs(); +	if (*ep) { +		if (*ep != ':') { +			puts("** Invalid boot device, use `dev[:part]' **\n"); +			goto fail; +		} +		part = simple_strtoul(++ep, NULL, 16); +	} + +	if (part != 0) { +		if (get_partition_info(fs->dev_desc, part, &info)) { +			printf("** Bad partition %lu **\n", part); +			goto fail; +		} + +		if (strncmp((char *)info.type, BOOT_PART_TYPE, +			    strlen(BOOT_PART_TYPE)) != 0) { +			printf("** Invalid partition type \"%s\"" +			       " (expect \"" BOOT_PART_TYPE "\")\n", info.type); +			goto fail; +		} +		printf("Loading file \"%s\" " +		       "from %s device %d:%lu %s\n", +		       filename, argv[1], dev, part, info.name); +	} else { +		printf("Loading file \"%s\" from %s device %d\n", +		       filename, argv[1], dev); +	} + +	part_length = ext4fs_set_blk_dev(fs->dev_desc, part); +	if (part_length == 0) { +		printf("**Bad partition - %s %d:%lu **\n", argv[1], dev, part); +		ext4fs_close(); +		goto fail; +	} + +	if (!ext4fs_mount(part_length)) { +		printf("** Bad ext2 partition or disk - %s %d:%lu **\n", +		       argv[1], dev, part); +		ext4fs_close(); +		goto fail; +	} + +	filelen = ext4fs_open(filename); +	if (filelen < 0) { +		printf("** File not found %s\n", filename); +		ext4fs_close(); +		goto fail; +	} +	if ((count < filelen) && (count != 0)) +		filelen = count; + +	if (ext4fs_read((char *)addr, filelen) != filelen) { +		printf("** Unable to read \"%s\" from %s %d:%lu **\n", +		       filename, argv[1], dev, part); +		ext4fs_close(); +		goto fail; +	} + +	ext4fs_close(); +	deinit_fs(fs->dev_desc); +	/* Loading ok, update default load address */ +	load_addr = addr; + +	printf("%d bytes read\n", filelen); +	sprintf(buf, "%X", filelen); +	setenv("filesize", buf); + +	return 0; +fail: +	deinit_fs(fs->dev_desc); +	return 1; +} + +int do_ext_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ +	const char *filename = "/"; +	int dev; +	unsigned long part = 1; +	char *ep; +	struct ext_filesystem *fs; +	int part_length; +	if (argc < 3) +		return cmd_usage(cmdtp); + +	dev = (int)simple_strtoul(argv[2], &ep, 16); + +	ext4_dev_desc = get_dev(argv[1], dev); + +	if (ext4_dev_desc == NULL) { +		printf("\n** Block device %s %d not supported\n", argv[1], dev); +		return 1; +	} + +	if (init_fs(ext4_dev_desc)) +		return 1; + +	fs = get_fs(); +	if (*ep) { +		if (*ep != ':') { +			puts("\n** Invalid boot device, use `dev[:part]' **\n"); +			goto fail; +		} +		part = simple_strtoul(++ep, NULL, 16); +	} + +	if (argc == 4) +		filename = argv[3]; + +	part_length = ext4fs_set_blk_dev(fs->dev_desc, part); +	if (part_length == 0) { +		printf("** Bad partition - %s %d:%lu **\n", argv[1], dev, part); +		ext4fs_close(); +		goto fail; +	} + +	if (!ext4fs_mount(part_length)) { +		printf("** Bad ext2 partition or disk - %s %d:%lu **\n", +		       argv[1], dev, part); +		ext4fs_close(); +		goto fail; +	} + +	if (ext4fs_ls(filename)) { +		printf("** Error extfs_ls() **\n"); +		ext4fs_close(); +		goto fail; +	}; + +	ext4fs_close(); +	deinit_fs(fs->dev_desc); +	return 0; + +fail: +	deinit_fs(fs->dev_desc); +	return 1; +} diff --git a/doc/README.ext4 b/doc/README.ext4 new file mode 100644 index 000000000..b3ea8b776 --- /dev/null +++ b/doc/README.ext4 @@ -0,0 +1,46 @@ +This patch series adds support for ext4 ls,load and write features in uboot +Journaling is supported for write feature. + +To Enable ext2 ls and load commands, modify the board specific config file with +#define CONFIG_CMD_EXT2 + +To Enable ext4 ls and load commands, modify the board specific config file with +#define CONFIG_CMD_EXT4 + +To enable ext4 write command, modify the board specific config file with +#define CONFIG_CMD_EXT4 +#define CONFIG_CMD_EXT4_WRITE + +Steps to test: + +1. After applying the patch, ext4 specific commands can be seen +   in the boot loader prompt using +        UBOOT #help + +        ext4load- load binary file from a Ext4 file system +        ext4ls  - list files in a directory (default /) +        ext4write- create a file in ext4 formatted partition + +2. To list the files in ext4 formatted partition, execute +        ext4ls <interface> <dev[:part]> [directory] +        For example: +        UBOOT #ext4ls mmc 0:5 /usr/lib + +3. To read and load a file from an ext4 formatted partition to RAM, execute +        ext4load <interface> <dev[:part]> [addr] [filename] [bytes] +        For example: +        UBOOT #ext4load mmc 2:2 0x30007fc0 uImage + +4. To write a file to a ext4 formatted partition. +        a) First load a file to RAM at a particular address for example 0x30007fc0. +        Now execute ext4write command +        ext4write <interface> <dev[:part]> [filename] [Address] [sizebytes] +        For example: +        UBOOT #ext4write mmc 2:2 /boot/uImage 0x30007fc0 6183120 +        (here 6183120 is the size of the file to be written) +        Note: Absolute path is required for the file to be written + +References : +	-- ext4 implementation in Linux Kernel +	-- Uboot existing ext2 load and ls implementation +	-- Journaling block device JBD2 implementation in linux Kernel 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/include/ext2fs.h b/include/ext2fs.h deleted file mode 100644 index 163a9bbc0..000000000 --- a/include/ext2fs.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - *  GRUB  --  GRand Unified Bootloader - *  Copyright (C) 2000, 2001  Free Software Foundation, Inc. - * - *  (C) Copyright 2003 Sysgo Real-Time Solutions, AG <www.elinos.com> - *  Pavel Bartusek <pba@sysgo.de> - * - *  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. - */ - -/* An implementation for the Ext2FS filesystem ported from GRUB. - * Some parts of this code (mainly the structures and defines) are - * from the original ext2 fs code, as found in the linux kernel. - */ - - -#define SECTOR_SIZE		0x200 -#define SECTOR_BITS		9 - -/* Error codes */ -typedef enum -{ -  ERR_NONE = 0, -  ERR_BAD_FILENAME, -  ERR_BAD_FILETYPE, -  ERR_BAD_GZIP_DATA, -  ERR_BAD_GZIP_HEADER, -  ERR_BAD_PART_TABLE, -  ERR_BAD_VERSION, -  ERR_BELOW_1MB, -  ERR_BOOT_COMMAND, -  ERR_BOOT_FAILURE, -  ERR_BOOT_FEATURES, -  ERR_DEV_FORMAT, -  ERR_DEV_VALUES, -  ERR_EXEC_FORMAT, -  ERR_FILELENGTH, -  ERR_FILE_NOT_FOUND, -  ERR_FSYS_CORRUPT, -  ERR_FSYS_MOUNT, -  ERR_GEOM, -  ERR_NEED_LX_KERNEL, -  ERR_NEED_MB_KERNEL, -  ERR_NO_DISK, -  ERR_NO_PART, -  ERR_NUMBER_PARSING, -  ERR_OUTSIDE_PART, -  ERR_READ, -  ERR_SYMLINK_LOOP, -  ERR_UNRECOGNIZED, -  ERR_WONT_FIT, -  ERR_WRITE, -  ERR_BAD_ARGUMENT, -  ERR_UNALIGNED, -  ERR_PRIVILEGED, -  ERR_DEV_NEED_INIT, -  ERR_NO_DISK_SPACE, -  ERR_NUMBER_OVERFLOW, - -  MAX_ERR_NUM -} ext2fs_error_t; - - -extern int ext2fs_set_blk_dev(block_dev_desc_t *rbdd, int part); -extern int ext2fs_ls (const char *dirname); -extern int ext2fs_open (const char *filename); -extern int ext2fs_read (char *buf, unsigned len); -extern int ext2fs_mount (unsigned part_length); -extern int ext2fs_close(void); diff --git a/include/ext4fs.h b/include/ext4fs.h new file mode 100644 index 000000000..ab2983ceb --- /dev/null +++ b/include/ext4fs.h @@ -0,0 +1,144 @@ +/* + * (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> + * + * Ext4 Extent data structures are taken from  original ext4 fs code + * as found in the linux kernel. + * + * Copyright (c) 2003-2006, Cluster File Systems, Inc, info@clusterfs.com + * Written by Alex Tomas <alex@clusterfs.com> + * + * 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. + * + * 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__ +#define __EXT4__ +#include <ext_common.h> + +#define EXT4_EXTENTS_FL		0x00080000 /* Inode uses extents */ +#define EXT4_EXT_MAGIC			0xf30a +#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM	0x0010 +#define EXT4_FEATURE_INCOMPAT_EXTENTS	0x0040 +#define EXT4_INDIRECT_BLOCKS		12 + +#define EXT4_BG_INODE_UNINIT		0x0001 +#define EXT4_BG_BLOCK_UNINIT		0x0002 +#define EXT4_BG_INODE_ZEROED		0x0004 + +/* + * ext4_inode has i_block array (60 bytes total). + * The first 12 bytes store ext4_extent_header; + * the remainder stores an array of ext4_extent. + */ + +/* + * This is the extent on-disk structure. + * It's used at the bottom of the tree. + */ +struct ext4_extent { +	__le32	ee_block;	/* first logical block extent covers */ +	__le16	ee_len;		/* number of blocks covered by extent */ +	__le16	ee_start_hi;	/* high 16 bits of physical block */ +	__le32	ee_start_lo;	/* low 32 bits of physical block */ +}; + +/* + * This is index on-disk structure. + * It's used at all the levels except the bottom. + */ +struct ext4_extent_idx { +	__le32	ei_block;	/* index covers logical blocks from 'block' */ +	__le32	ei_leaf_lo;	/* pointer to the physical block of the next * +				 * level. leaf or next index could be there */ +	__le16	ei_leaf_hi;	/* high 16 bits of physical block */ +	__u16	ei_unused; +}; + +/* Each block (leaves and indexes), even inode-stored has header. */ +struct ext4_extent_header { +	__le16	eh_magic;	/* probably will support different formats */ +	__le16	eh_entries;	/* number of valid entries */ +	__le16	eh_max;		/* capacity of store in entries */ +	__le16	eh_depth;	/* has tree real underlying blocks? */ +	__le32	eh_generation;	/* generation of the tree */ +}; + +struct ext_filesystem { +	/* Total Sector of partition */ +	uint64_t total_sect; +	/* Block size  of partition */ +	uint32_t blksz; +	/* Inode size of partition */ +	uint32_t inodesz; +	/* Sectors per Block */ +	uint32_t sect_perblk; +	/* Group Descriptor Block Number */ +	uint32_t gdtable_blkno; +	/* Total block groups of partition */ +	uint32_t no_blkgrp; +	/* No of blocks required for bgdtable */ +	uint32_t no_blk_pergdt; +	/* Superblock */ +	struct ext2_sblock *sb; +	/* Block group descritpor table */ +	struct ext2_block_group *gd; +	char *gdtable; + +	/* Block Bitmap Related */ +	unsigned char **blk_bmaps; +	long int curr_blkno; +	uint16_t first_pass_bbmap; + +	/* Inode Bitmap Related */ +	unsigned char **inode_bmaps; +	int curr_inode_no; +	uint16_t first_pass_ibmap; + +	/* Journal Related */ + +	/* Block Device Descriptor */ +	block_dev_desc_t *dev_desc; +}; + +extern block_dev_desc_t *ext4_dev_desc; +extern struct ext2_data *ext4fs_root; +extern struct ext2fs_node *ext4fs_file; + +#if defined(CONFIG_CMD_EXT4_WRITE) +extern struct ext2_inode *g_parent_inode; +extern int gd_index; +extern int gindex; + +int ext4fs_init(void); +void ext4fs_deinit(void); +int ext4fs_filename_check(char *filename); +int ext4fs_write(const char *fname, unsigned char *buffer, +				unsigned long sizebytes); +#endif + +struct ext_filesystem *get_fs(void); +int init_fs(block_dev_desc_t *dev_desc); +void deinit_fs(block_dev_desc_t *dev_desc); +int ext4fs_open(const char *filename); +int ext4fs_read(char *buf, unsigned len); +int ext4fs_mount(unsigned part_length); +void ext4fs_close(void); +int ext4fs_ls(const char *dirname); +void ext4fs_free_node(struct ext2fs_node *node, struct ext2fs_node *currroot); +int ext4fs_devread(int sector, int byte_offset, int byte_len, char *buf); +int ext4fs_set_blk_dev(block_dev_desc_t *rbdd, int part); +long int read_allocated_block(struct ext2_inode *inode, int fileblock); +#endif diff --git a/include/ext_common.h b/include/ext_common.h new file mode 100644 index 000000000..9b97522c8 --- /dev/null +++ b/include/ext_common.h @@ -0,0 +1,199 @@ +/* + * (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> + * + * Data structures and headers for ext4 support have been taken from + * 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 __EXT_COMMON__ +#define __EXT_COMMON__ +#include <command.h> +#define SECTOR_SIZE		0x200 +#define SECTOR_BITS		9 + +/* 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 +#define EXT2_ROOT_INO			2 /* Root inode */ + +/* Bits used as offset in sector */ +#define DISK_SECTOR_BITS		9 +/* The size of an ext2 block in bytes.  */ +#define EXT2_BLOCK_SIZE(data)	   (1 << LOG2_BLOCK_SIZE(data)) + +/* 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) +#define INODE_SIZE_FILESYSTEM(data)	(__le32_to_cpu \ +			(data->sblock.inode_size)) + +#define EXT2_FT_DIR	2 +#define SUCCESS	1 + +/* Macro-instructions used to manage several block sizes  */ +#define EXT2_MIN_BLOCK_LOG_SIZE	10 /* 1024 */ +#define EXT2_MAX_BLOCK_LOG_SIZE	16 /* 65536 */ +#define EXT2_MIN_BLOCK_SIZE		(1 << EXT2_MIN_BLOCK_LOG_SIZE) +#define EXT2_MAX_BLOCK_SIZE		(1 << EXT2_MAX_BLOCK_LOG_SIZE) + +/* 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; +}; + +struct ext2_block_group { +	__u32 block_id;	/* Blocks bitmap block */ +	__u32 inode_id;	/* Inodes bitmap block */ +	__u32 inode_table_id;	/* Inodes table block */ +	__u16 free_blocks;	/* Free blocks count */ +	__u16 free_inodes;	/* Free inodes count */ +	__u16 used_dir_cnt;	/* Directories count */ +	__u16 bg_flags; +	__u32 bg_reserved[2]; +	__u16 bg_itable_unused; /* Unused inodes count */ +	__u16 bg_checksum;	/* crc16(s_uuid+grouo_num+group_desc)*/ +}; + +/* 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 triple_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; +}; + +int do_ext2ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); +int do_ext2load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); +int do_ext4_load(cmd_tbl_t *cmdtp, int flag, int argc, +					char *const argv[]); +int do_ext4_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]); +int do_ext4_write(cmd_tbl_t *cmdtp, int flag, int argc, +				char *const argv[]); +int do_ext_load(cmd_tbl_t *cmdtp, int flag, int argc, +					char *const argv[]); +int do_ext_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]); +#endif |