u-boot/fs/ext4/ext4_common.c
Tom Rini 715b56fe2b Revert "ext4fs: Add ext4 extent cache for read operations"
This reverts commit fc0fc50f38.

The author has asked on the mailing list that we revert this for now as
it breaks write support.

Reported-by: Łukasz Majewski <l.majewski@samsung.com>
Signed-off-by: Tom Rini <trini@ti.com>
2014-02-26 08:18:58 -05:00

2236 lines
54 KiB
C

/*
* (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.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#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_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;
struct ext_filesystem *fs = get_fs();
int log2blksz = fs->dev_desc->log2blksz;
ALLOC_CACHE_ALIGN_BUFFER(unsigned char, sec_buf, fs->dev_desc->blksz);
startblock = off >> log2blksz;
startblock += part_offset;
remainder = off & (uint64_t)(fs->dev_desc->blksz - 1);
if (fs->dev_desc == NULL)
return;
if ((startblock + (size >> log2blksz)) >
(part_offset + fs->total_sect)) {
printf("part_offset is " LBAFU "\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 >> log2blksz != 0) {
fs->dev_desc->block_write(fs->dev_desc->dev,
startblock,
size >> log2blksz,
(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->bgd[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 *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((lbaint_t)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;
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)
((uint64_t)g_parent_inode->b.
blocks.dir_blocks[direct_blk_idx] *
(uint64_t)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);
}
/* make a pointer ready for creating next directory entry */
templength = dir->direntlen;
totalbytes = totalbytes + templength;
dir = (struct ext2_dirent *)((char *)dir + templength);
/* 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((lbaint_t)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((lbaint_t)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 *bgd = (struct ext2_block_group *)fs->gdtable;
if (fs->first_pass_bbmap == 0) {
for (i = 0; i < fs->no_blkgrp; i++) {
if (bgd[i].free_blocks) {
if (bgd[i].bg_flags & EXT4_BG_BLOCK_UNINIT) {
put_ext4(((uint64_t) ((uint64_t)bgd[i].block_id *
(uint64_t)fs->blksz)),
zero_buffer, fs->blksz);
bgd[i].bg_flags =
bgd[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++;
bgd[i].free_blocks--;
fs->sb->free_blocks--;
status = ext4fs_devread((lbaint_t)
bgd[i].block_id *
fs->sect_perblk, 0,
fs->blksz,
journal_buffer);
if (status == 0)
goto fail;
if (ext4fs_log_journal(journal_buffer,
bgd[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 (bgd[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 (bgd[bg_idx].bg_flags & EXT4_BG_BLOCK_UNINIT) {
memset(zero_buffer, '\0', fs->blksz);
put_ext4(((uint64_t) ((uint64_t)bgd[bg_idx].block_id *
(uint64_t)fs->blksz)), zero_buffer, fs->blksz);
memcpy(fs->blk_bmaps[bg_idx], zero_buffer, fs->blksz);
bgd[bg_idx].bg_flags = bgd[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((lbaint_t)bgd[bg_idx].block_id
* fs->sect_perblk,
0, fs->blksz, journal_buffer);
if (status == 0)
goto fail;
if (ext4fs_log_journal(journal_buffer,
bgd[bg_idx].block_id))
goto fail;
prev_bg_bitmap_index = bg_idx;
}
bgd[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 *bgd = (struct ext2_block_group *)fs->gdtable;
if (fs->first_pass_ibmap == 0) {
for (i = 0; i < fs->no_blkgrp; i++) {
if (bgd[i].free_inodes) {
if (bgd[i].bg_itable_unused !=
bgd[i].free_inodes)
bgd[i].bg_itable_unused =
bgd[i].free_inodes;
if (bgd[i].bg_flags & EXT4_BG_INODE_UNINIT) {
put_ext4(((uint64_t)
((uint64_t)bgd[i].inode_id *
(uint64_t)fs->blksz)),
zero_buffer, fs->blksz);
bgd[i].bg_flags = bgd[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++;
bgd[i].free_inodes--;
bgd[i].bg_itable_unused--;
fs->sb->free_inodes--;
status = ext4fs_devread((lbaint_t)
bgd[i].inode_id *
fs->sect_perblk, 0,
fs->blksz,
journal_buffer);
if (status == 0)
goto fail;
if (ext4fs_log_journal(journal_buffer,
bgd[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 (bgd[ibmap_idx].bg_flags & EXT4_BG_INODE_UNINIT) {
memset(zero_buffer, '\0', fs->blksz);
put_ext4(((uint64_t) ((uint64_t)bgd[ibmap_idx].inode_id *
(uint64_t)fs->blksz)), zero_buffer,
fs->blksz);
bgd[ibmap_idx].bg_flags =
bgd[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((lbaint_t)
bgd[ibmap_idx].inode_id
* fs->sect_perblk,
0, fs->blksz, journal_buffer);
if (status == 0)
goto fail;
if (ext4fs_log_journal(journal_buffer,
bgd[ibmap_idx].inode_id))
goto fail;
prev_inode_bitmap_index = ibmap_idx;
}
if (bgd[ibmap_idx].bg_itable_unused !=
bgd[ibmap_idx].free_inodes)
bgd[ibmap_idx].bg_itable_unused =
bgd[ibmap_idx].free_inodes;
bgd[ibmap_idx].free_inodes--;
bgd[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((lbaint_t)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) ((uint64_t)si_blockno * (uint64_t)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((lbaint_t)di_blockno_parent *
fs->sect_perblk, 0,
fs->blksz, (char *)di_parent_buffer);
if (!status) {
printf("%s: Device read error!\n", __func__);
goto fail;
}
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((lbaint_t)di_blockno_child *
fs->sect_perblk, 0,
fs->blksz,
(char *)di_child_buff);
if (!status) {
printf("%s: Device read error!\n", __func__);
goto fail;
}
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) ((uint64_t)di_blockno_child * (uint64_t)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) ((uint64_t)di_blockno_parent * (uint64_t)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) ((uint64_t)ti_child_blockno *
(uint64_t)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) ((uint64_t)ti_parent_blockno * (uint64_t)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) ((uint64_t)ti_gp_blockno * (uint64_t)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;
int blksz = EXT2_BLOCK_SIZE(data);
int i;
while (1) {
index = (struct ext4_extent_idx *)(ext_block + 1);
if (le16_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 >= le16_to_cpu(ext_block->eh_entries))
break;
} while (fileblock >= le32_to_cpu(index[i].ei_block));
if (--i < 0)
return 0;
block = le16_to_cpu(index[i].ei_leaf_hi);
block = (block << 32) + le32_to_cpu(index[i].ei_leaf_lo);
if (ext4fs_devread((lbaint_t)block << log2_blksz, 0, 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;
int log2blksz = get_fs()->dev_desc->log2blksz;
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((lbaint_t)blkno <<
(LOG2_BLOCK_SIZE(data) - log2blksz),
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 log2blksz = get_fs()->dev_desc->log2blksz;
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((lbaint_t)blkno << (LOG2_BLOCK_SIZE(data) -
log2blksz), 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_BLOCK_SIZE(ext4fs_root)
- get_fs()->dev_desc->log2blksz;
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 >= le16_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 >= le16_to_cpu(extent[i].ee_len)) {
free(buf);
return 0;
}
start = le16_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((lbaint_t)__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((lbaint_t)__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((lbaint_t)__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
((lbaint_t)
__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((lbaint_t)__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((lbaint_t)__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("read_allocated_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(SUPERBLOCK_SIZE);
if (!data)
return 0;
/* Read the superblock. */
status = ext4_read_superblock((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;
}