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)
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);
}
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))
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
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 * 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;
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
}
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("/"));
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
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
684
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
/* 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);
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;
if (le16_to_cpu(bgd[bg_idx].bg_flags) & EXT4_BG_BLOCK_UNINIT) {
uint16_t new_flags;
put_ext4((uint64_t)le32_to_cpu(bgd[bg_idx].block_id) * fs->blksz,
zero_buffer, fs->blksz);
memcpy(fs->blk_bmaps[bg_idx], zero_buffer, fs->blksz);
new_flags = le16_to_cpu(bgd[bg_idx].bg_flags) & ~EXT4_BG_BLOCK_UNINIT;
bgd[bg_idx].bg_flags = cpu_to_le16(new_flags);
}
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);
fs->curr_blkno++;
goto restart;
}
/* journal backup */
if (prev_bg_bitmap_index != bg_idx) {
status = ext4fs_devread(
(lbaint_t)le32_to_cpu(bgd[bg_idx].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[bg_idx].block_id)))
goto fail;
prev_bg_bitmap_index = bg_idx;
}
ext4fs_bg_free_blocks_dec(&bgd[bg_idx]);
ext4fs_sb_free_blocks_dec(fs->sb);
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 = le32_to_cpu(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;
int has_gdt_chksum = le32_to_cpu(fs->sb->feature_ro_compat) &
EXT4_FEATURE_RO_COMPAT_GDT_CSUM ? 1 : 0;
if (fs->first_pass_ibmap == 0) {
for (i = 0; i < fs->no_blkgrp; i++) {
if (bgd[i].free_inodes) {
if (has_gdt_chksum)
bgd[i].bg_itable_unused =
bgd[i].free_inodes;
if (le16_to_cpu(bgd[i].bg_flags) & EXT4_BG_INODE_UNINIT) {