ext4fs write support

Signed-off-by: Uma Shankar <uma.shankar@samsung.com>
Signed-off-by: Manjunatha C Achar <a.manjunatha@samsung.com>
Signed-off-by: Iqbal Shareef <iqbal.ams@samsung.com>
Signed-off-by: Hakgoo Lee <goodguy.lee@samsung.com>
This commit is contained in:
Uma Shankar 2012-05-25 21:22:49 +05:30 committed by Wolfgang Denk
parent a1596438a6
commit ed34f34dba
12 changed files with 3427 additions and 0 deletions

View file

@ -62,6 +62,10 @@
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
@ -84,6 +88,143 @@ int do_ext4_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
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"

46
doc/README.ext4 Normal file
View file

@ -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

View file

@ -34,6 +34,7 @@ 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))

62
fs/ext4/crc16.c Normal file
View file

@ -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;
}

16
fs/ext4/crc16.h Normal file
View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -14,6 +14,8 @@
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2003, 2004 Free Software Foundation, Inc.
*
* ext4write : Based on generic ext4 protocol.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@ -35,6 +37,10 @@
#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
@ -60,4 +66,23 @@ 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

667
fs/ext4/ext4_journal.c Normal file
View file

@ -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");
}

141
fs/ext4/ext4_journal.h Normal file
View file

@ -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

View file

@ -16,6 +16,8 @@
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2003, 2004 Free Software Foundation, Inc.
*
* ext4write : Based on generic ext4 protocol.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@ -226,3 +228,962 @@ int ext4fs_read(char *buf, unsigned len)
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

View file

@ -117,6 +117,18 @@ 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);

View file

@ -191,6 +191,8 @@ 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[]);