+// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2011 - 2012 Samsung Electronics
* EXT4 filesystem implementation in Uboot by
* Copyright (C) 2003, 2004 Free Software Foundation, Inc.
*
* ext4write : Based on generic ext4 protocol.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <ext4fs.h>
#include "ext4_common.h"
#include <div64.h>
+#include <malloc.h>
int ext4fs_symlinknest;
struct ext_filesystem ext_fs;
lbaint_t delayed_skipfirst = 0;
lbaint_t delayed_next = 0;
char *delayed_buf = NULL;
+ char *start_buf = buf;
short status;
+ struct ext_block_cache cache;
- if (blocksize <= 0)
- return -1;
+ ext_cache_init(&cache);
/* Adjust len so it we can't read past the end of the file. */
if (len + pos > filesize)
len = (filesize - pos);
+ if (blocksize <= 0 || len <= 0) {
+ ext_cache_fini(&cache);
+ return -1;
+ }
+
blockcnt = lldiv(((len + pos) + blocksize - 1), blocksize);
for (i = lldiv(pos, blocksize); i < blockcnt; i++) {
int blockoff = pos - (blocksize * i);
int blockend = blocksize;
int skipfirst = 0;
- blknr = read_allocated_block(&(node->inode), i);
- if (blknr < 0)
+ blknr = read_allocated_block(&node->inode, i, &cache);
+ if (blknr < 0) {
+ ext_cache_fini(&cache);
return -1;
+ }
blknr = blknr << log2_fs_blocksize;
delayed_skipfirst,
delayed_extent,
delayed_buf);
- if (status == 0)
+ if (status == 0) {
+ ext_cache_fini(&cache);
return -1;
+ }
previous_block_number = blknr;
delayed_start = blknr;
delayed_extent = blockend;
}
} else {
int n;
+ int n_left;
if (previous_block_number != -1) {
/* spill */
status = ext4fs_devread(delayed_start,
delayed_skipfirst,
delayed_extent,
delayed_buf);
- if (status == 0)
+ if (status == 0) {
+ ext_cache_fini(&cache);
return -1;
+ }
previous_block_number = -1;
}
/* Zero no more than `len' bytes. */
n = blocksize - skipfirst;
- if (n > len)
- n = len;
+ n_left = len - ( buf - start_buf );
+ if (n > n_left)
+ n = n_left;
memset(buf, 0, n);
}
buf += blocksize - skipfirst;
status = ext4fs_devread(delayed_start,
delayed_skipfirst, delayed_extent,
delayed_buf);
- if (status == 0)
+ if (status == 0) {
+ ext_cache_fini(&cache);
return -1;
+ }
previous_block_number = -1;
}
*actread = len;
+ ext_cache_fini(&cache);
return 0;
}
int ext4fs_ls(const char *dirname)
{
- struct ext2fs_node *dirnode;
+ struct ext2fs_node *dirnode = NULL;
int status;
if (dirname == NULL)
FILETYPE_DIRECTORY);
if (status != 1) {
printf("** Can not find directory. **\n");
- ext4fs_free_node(dirnode, &ext4fs_root->diropen);
+ if (dirnode)
+ ext4fs_free_node(dirnode, &ext4fs_root->diropen);
return 1;
}
return -ENOSYS;
#endif
}
+
+void ext_cache_init(struct ext_block_cache *cache)
+{
+ memset(cache, 0, sizeof(*cache));
+}
+
+void ext_cache_fini(struct ext_block_cache *cache)
+{
+ free(cache->buf);
+ ext_cache_init(cache);
+}
+
+int ext_cache_read(struct ext_block_cache *cache, lbaint_t block, int size)
+{
+ /* This could be more lenient, but this is simple and enough for now */
+ if (cache->buf && cache->block == block && cache->size == size)
+ return 1;
+ ext_cache_fini(cache);
+ cache->buf = malloc(size);
+ if (!cache->buf)
+ return 0;
+ if (!ext4fs_devread(block, 0, size, cache->buf)) {
+ ext_cache_fini(cache);
+ return 0;
+ }
+ cache->block = block;
+ cache->size = size;
+ return 1;
+}