Newer
Older
/*
* (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.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <ext_common.h>
#include <ext4fs.h>
#include <memalign.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;
__le32 *ext4fs_indir1_block;
int ext4fs_indir1_size;
int ext4fs_indir1_blkno = -1;
__le32 *ext4fs_indir2_block;
int ext4fs_indir2_size;
int ext4fs_indir2_blkno = -1;
__le32 *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)
struct ext2_block_group *ext4fs_get_group_descriptor
(const struct ext_filesystem *fs, uint32_t bg_idx)
{
return (struct ext2_block_group *)(fs->gdtable + (bg_idx * fs->gdsize));
}
static inline void ext4fs_sb_free_inodes_dec(struct ext2_sblock *sb)
{
sb->free_inodes = cpu_to_le32(le32_to_cpu(sb->free_inodes) - 1);
}
static inline void ext4fs_sb_free_blocks_dec(struct ext2_sblock *sb)
{
sb->free_blocks = cpu_to_le32(le32_to_cpu(sb->free_blocks) - 1);
}
static inline void ext4fs_bg_free_inodes_dec(struct ext2_block_group *bg)
{
bg->free_inodes = cpu_to_le16(le16_to_cpu(bg->free_inodes) - 1);
}
static inline void ext4fs_bg_free_blocks_dec(struct ext2_block_group *bg)
{
bg->free_blocks = cpu_to_le16(le16_to_cpu(bg->free_blocks) - 1);
}
static inline void ext4fs_bg_itable_unused_dec(struct ext2_block_group *bg)
{
bg->bg_itable_unused = cpu_to_le16(le16_to_cpu(bg->bg_itable_unused) - 1);
}
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
uint64_t ext4fs_sb_get_free_blocks(const struct ext2_sblock *sb)
{
uint64_t free_blocks = le32_to_cpu(sb->free_blocks);
free_blocks += (uint64_t)le32_to_cpu(sb->free_blocks_high) << 32;
return free_blocks;
}
void ext4fs_sb_set_free_blocks(struct ext2_sblock *sb, uint64_t free_blocks)
{
sb->free_blocks = cpu_to_le32(free_blocks & 0xffffffff);
sb->free_blocks_high = cpu_to_le16(free_blocks >> 32);
}
uint32_t ext4fs_bg_get_free_blocks(const struct ext2_block_group *bg,
const struct ext_filesystem *fs)
{
uint32_t free_blocks = le16_to_cpu(bg->free_blocks);
if (fs->gdsize == 64)
free_blocks += le16_to_cpu(bg->free_blocks_high) << 16;
return free_blocks;
}
static inline
uint32_t ext4fs_bg_get_free_inodes(const struct ext2_block_group *bg,
const struct ext_filesystem *fs)
{
uint32_t free_inodes = le16_to_cpu(bg->free_inodes);
if (fs->gdsize == 64)
free_inodes += le16_to_cpu(bg->free_inodes_high) << 16;
return free_inodes;
}
static inline uint16_t ext4fs_bg_get_flags(const struct ext2_block_group *bg)
{
return le16_to_cpu(bg->bg_flags);
}
static inline void ext4fs_bg_set_flags(struct ext2_block_group *bg,
uint16_t flags)
{
bg->bg_flags = cpu_to_le16(flags);
}
/* Block number of the block bitmap */
uint64_t ext4fs_bg_get_block_id(const struct ext2_block_group *bg,
const struct ext_filesystem *fs)
{
uint64_t block_nr = le32_to_cpu(bg->block_id);
if (fs->gdsize == 64)
block_nr += (uint64_t)le32_to_cpu(bg->block_id_high) << 32;
return block_nr;
}
/* Block number of the inode bitmap */
uint64_t ext4fs_bg_get_inode_id(const struct ext2_block_group *bg,
const struct ext_filesystem *fs)
{
uint64_t block_nr = le32_to_cpu(bg->inode_id);
if (fs->gdsize == 64)
block_nr += (uint64_t)le32_to_cpu(bg->inode_id_high) << 32;
return block_nr;
}
#endif
/* Block number of the inode table */
uint64_t ext4fs_bg_get_inode_table_id(const struct ext2_block_group *bg,
const struct ext_filesystem *fs)
{
uint64_t block_nr = le32_to_cpu(bg->inode_table_id);
if (fs->gdsize == 64)
block_nr +=
(uint64_t)le32_to_cpu(bg->inode_table_id_high) << 32;
return block_nr;
}
#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;
remainder = off & (uint64_t)(fs->dev_desc->blksz - 1);
if ((startblock + (size >> log2blksz)) >
printf("part_offset is " LBAFU "\n", part_offset);
printf("total_sector is %" PRIu64 "\n", fs->total_sect);
printf("error: overflow occurs\n");
return;
}
if (remainder) {
blk_dread(fs->dev_desc, startblock, 1, sec_buf);
temp_ptr = sec_buf;
memcpy((temp_ptr + remainder), (unsigned char *)buf, size);
blk_dwrite(fs->dev_desc, startblock, 1, sec_buf);
if (size >> log2blksz != 0) {
blk_dwrite(fs->dev_desc, startblock, size >> log2blksz,
(unsigned long *)buf);
blk_dread(fs->dev_desc, startblock, 1, sec_buf);
blk_dwrite(fs->dev_desc, 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 > le32_to_cpu(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 *ptr = buffer;
struct ext_filesystem *fs = get_fs();
while (*ptr == 255) {
ptr++;
count += 8;
if (count == (fs->blksz * 8))
return -1;
}
if (fs->blksz == 1024)
count += 1;
for (i = 0; i <= 7; i++) {
operand = 1 << i;
if (*ptr & operand) {
count++;
} else {
*ptr |= operand;
return count;
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
}
}
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 * le32_to_cpu(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 * le32_to_cpu(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);
}
uint16_t ext4fs_checksum_update(uint32_t i)
{
struct ext2_block_group *desc;
struct ext_filesystem *fs = get_fs();
uint16_t crc = 0;
__le32 le32_i = cpu_to_le32(i);
desc = (struct ext2_block_group *)&fs->bgd[i];
if (le32_to_cpu(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, &le32_i, sizeof(le32_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 = le16_to_cpu(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 = cpu_to_le16(dentry_length);
return sizeof_void_space;
}
return 0;
}
int ext4fs_update_parent_dentry(char *filename, int file_type)
{
unsigned int *zero_buffer = NULL;
char *root_first_block_buffer = NULL;
int blk_idx;
long int first_block_no_of_root = 0;
int totalbytes = 0;
unsigned int new_entry_byte_reqd;
int sizeof_void_space = 0;
int templength = 0;
int inodeno = -1;
int status;
struct ext_filesystem *fs = get_fs();
/* directory entry */
struct ext2_dirent *dir;
char *temp_dir = NULL;
uint32_t new_blk_no;
uint32_t new_size;
uint32_t new_blockcnt;
uint32_t directory_blocks;
zero_buffer = zalloc(fs->blksz);
if (!zero_buffer) {
printf("No Memory\n");
}
root_first_block_buffer = zalloc(fs->blksz);
if (!root_first_block_buffer) {
free(zero_buffer);
printf("No Memory\n");
new_entry_byte_reqd = ROUND(strlen(filename) +
sizeof(struct ext2_dirent), 4);
directory_blocks = le32_to_cpu(g_parent_inode->size) >>
LOG2_BLOCK_SIZE(ext4fs_root);
blk_idx = directory_blocks - 1;
restart_read:
first_block_no_of_root = read_allocated_block(g_parent_inode, blk_idx);
if (first_block_no_of_root <= 0)
goto fail;
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 (le16_to_cpu(dir->direntlen) > 0) {
unsigned short used_len = ROUND(dir->namelen +
sizeof(struct ext2_dirent), 4);
/* last entry of block */
if (fs->blksz - totalbytes == le16_to_cpu(dir->direntlen)) {
/* check if new entry fits */
if ((used_len + new_entry_byte_reqd) <=
le16_to_cpu(dir->direntlen)) {
dir->direntlen = cpu_to_le16(used_len);
break;
} else {
if (blk_idx > 0) {
printf("Block full, trying previous\n");
blk_idx--;
goto restart_read;
}
printf("All blocks full: Allocate new\n");
if (le32_to_cpu(g_parent_inode->flags) &
EXT4_EXTENTS_FL) {
printf("Directory uses extents\n");
goto fail;
}
if (directory_blocks >= INDIRECT_BLOCKS) {
printf("Directory exceeds limit\n");
goto fail;
}
new_blk_no = ext4fs_get_new_blk_no();
if (new_blk_no == -1) {
printf("no block left to assign\n");
goto fail;
}
put_ext4((uint64_t)new_blk_no * fs->blksz, zero_buffer, fs->blksz);
g_parent_inode->b.blocks.
dir_blocks[directory_blocks] =
cpu_to_le32(new_blk_no);
new_size = le32_to_cpu(g_parent_inode->size);
new_size += fs->blksz;
g_parent_inode->size = cpu_to_le32(new_size);
new_blockcnt = le32_to_cpu(g_parent_inode->blockcnt);
new_blockcnt += fs->sect_perblk;
g_parent_inode->blockcnt = cpu_to_le32(new_blockcnt);
if (ext4fs_put_metadata
(root_first_block_buffer,
first_block_no_of_root))
goto fail;
goto restart;
}
}
templength = le16_to_cpu(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 = le16_to_cpu(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 = cpu_to_le32(inodeno);
dir->direntlen = cpu_to_le16(sizeof_void_space);
dir->direntlen = cpu_to_le16(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));
/* 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);
return inodeno;
}
static int search_dir(struct ext2_inode *parent_inode, char *dirname)
{
int status;
int inodeno = 0;
int offset;
int blk_idx;
char *block_buffer = NULL;
struct ext2_dirent *dir = NULL;
struct ext_filesystem *fs = get_fs();
uint32_t directory_blocks;
char *direntname;
directory_blocks = le32_to_cpu(parent_inode->size) >>
LOG2_BLOCK_SIZE(ext4fs_root);
block_buffer = zalloc(fs->blksz);
if (!block_buffer)
goto fail;
/* get the block no allocated to a file */
for (blk_idx = 0; blk_idx < directory_blocks; blk_idx++) {
blknr = read_allocated_block(parent_inode, blk_idx);
if (blknr <= 0)
/* read the directory block */
status = ext4fs_devread((lbaint_t)blknr * fs->sect_perblk,
0, fs->blksz, (char *)block_buffer);
if (status == 0)
goto fail;
offset = 0;
do {
dir = (struct ext2_dirent *)(block_buffer + offset);
direntname = (char*)(dir) + sizeof(struct ext2_dirent);
int direntlen = le16_to_cpu(dir->direntlen);
if (direntlen < sizeof(struct ext2_dirent))
if (dir->inode && (strlen(dirname) == dir->namelen) &&
(strncmp(dirname, direntname, dir->namelen) == 0)) {
inodeno = le32_to_cpu(dir->inode);
break;
}
offset += direntlen;
} while (offset < fs->blksz);
if (inodeno > 0) {
free(block_buffer);
return inodeno;
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
}
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;
memcpy(arr[i++], "/", strlen("/"));
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
/* 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 (le16_to_cpu(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);
for (i = 0; i < depth; i++) {
if (!ptr[i])
break;
free(ptr[i]);
}
free(ptr);
free(parent_inode);
free(first_inode);
return result_inode_no;
}
static int unlink_filename(char *filename, unsigned int blknr)
{
int totalbytes = 0;
int templength = 0;
int status, inodeno;
int found = 0;
char *root_first_block_buffer = 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 */
root_first_block_buffer = zalloc(fs->blksz);
if (!root_first_block_buffer)
return -ENOMEM;
status = ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0,
fs->blksz, root_first_block_buffer);
if (status == 0)
goto fail;
if (ext4fs_log_journal(root_first_block_buffer, blknr))
goto fail;
dir = (struct ext2_dirent *)root_first_block_buffer;
ptr = (char *)dir;
totalbytes = 0;
while (le16_to_cpu(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 (dir->inode && (strlen(filename) == dir->namelen) &&
(strncmp(ptr + sizeof(struct ext2_dirent),
filename, dir->namelen) == 0)) {
printf("file found, deleting\n");
inodeno = le32_to_cpu(dir->inode);
if (previous_dir) {
uint16_t new_len;
new_len = le16_to_cpu(previous_dir->direntlen);
new_len += le16_to_cpu(dir->direntlen);
previous_dir->direntlen = cpu_to_le16(new_len);
} else {
found = 1;
break;
if (fs->blksz - totalbytes == le16_to_cpu(dir->direntlen))
break;
/* traversing the each directory entry */
templength = le16_to_cpu(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_buffer, blknr))
int ext4fs_filename_unlink(char *filename)
uint32_t directory_blocks;
directory_blocks = le32_to_cpu(g_parent_inode->size) >>
LOG2_BLOCK_SIZE(ext4fs_root);
for (blk_idx = 0; blk_idx < directory_blocks; blk_idx++) {
blknr = read_allocated_block(g_parent_inode, blk_idx);
if (blknr <= 0)
inodeno = unlink_filename(filename, blknr);
if (inodeno != -1)
return inodeno;
}
return -1;
}
uint32_t 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 = le32_to_cpu(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 (le16_to_cpu(bgd[i].free_blocks)) {
if (le16_to_cpu(bgd[i].bg_flags) & EXT4_BG_BLOCK_UNINIT) {
uint16_t new_flags;
put_ext4((uint64_t)le32_to_cpu(bgd[i].block_id) * fs->blksz,
new_flags = le16_to_cpu(bgd[i].bg_flags) & ~EXT4_BG_BLOCK_UNINIT;
bgd[i].bg_flags = cpu_to_le16(new_flags);
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++;
ext4fs_bg_free_blocks_dec(&bgd[i]);
ext4fs_sb_free_blocks_dec(fs->sb);
status = ext4fs_devread(
(lbaint_t)le32_to_cpu(bgd[i].block_id) *
fs->sect_perblk, 0,
fs->blksz,
journal_buffer);
if (status == 0)
goto fail;
if (ext4fs_log_journal(journal_buffer,
le32_to_cpu(bgd[i].block_id)))
goto fail;
goto success;
} else {
debug("no space left on block group %d\n", i);
}
}
goto fail;
} else {
fs->curr_blkno++;
restart:
/* get the blockbitmap index respective to blockno */
bg_idx = fs->curr_blkno / blk_per_grp;
if (fs->blksz == 1024) {
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) {
fs->curr_blkno = (bg_idx + 1) * blk_per_grp;
if (fs->blksz == 1024)
fs->curr_blkno += 1;