@@ -663,6 +663,70 @@ int ext4l_size(const char *filename, loff_t *sizep)
return 0;
}
+int ext4l_read(const char *filename, void *buf, loff_t offset, loff_t len,
+ loff_t *actread)
+{
+ uint copy_len, blk_off, blksize;
+ loff_t bytes_left, file_size;
+ struct buffer_head *bh;
+ struct inode *inode;
+ ext4_lblk_t block;
+ char *dst;
+ int ret;
+
+ *actread = 0;
+
+ ret = ext4l_resolve_path(filename, &inode);
+ if (ret) {
+ printf("** File not found %s **\n", filename);
+ return ret;
+ }
+
+ file_size = inode->i_size;
+ if (offset >= file_size)
+ return 0;
+
+ /* If len is 0, read the whole file from offset */
+ if (!len)
+ len = file_size - offset;
+
+ /* Clamp to file size */
+ if (offset + len > file_size)
+ len = file_size - offset;
+
+ blksize = inode->i_sb->s_blocksize;
+ bytes_left = len;
+ dst = buf;
+
+ while (bytes_left > 0) {
+ /* Calculate logical block number and offset within block */
+ block = offset / blksize;
+ blk_off = offset % blksize;
+
+ /* Read the block */
+ bh = ext4_bread(NULL, inode, block, 0);
+ if (IS_ERR(bh))
+ return PTR_ERR(bh);
+ if (!bh)
+ return -EIO;
+
+ /* Calculate how much to copy from this block */
+ copy_len = blksize - blk_off;
+ if (copy_len > bytes_left)
+ copy_len = bytes_left;
+
+ memcpy(dst, bh->b_data + blk_off, copy_len);
+ brelse(bh);
+
+ dst += copy_len;
+ offset += copy_len;
+ bytes_left -= copy_len;
+ *actread += copy_len;
+ }
+
+ return 0;
+}
+
void ext4l_close(void)
{
if (ext4l_open_dirs > 0)
@@ -268,7 +268,7 @@ static struct fstype_info fstypes[] = {
.ls = ext4l_ls,
.exists = ext4l_exists,
.size = ext4l_size,
- .read = fs_read_unsupported,
+ .read = ext4l_read,
.write = fs_write_unsupported,
.uuid = fs_uuid_unsupported,
.opendir = ext4l_opendir,
@@ -55,6 +55,19 @@ int ext4l_exists(const char *filename);
*/
int ext4l_size(const char *filename, loff_t *sizep);
+/**
+ * ext4l_read() - Read data from a file
+ *
+ * @filename: Path to file
+ * @buf: Buffer to read data into
+ * @offset: Byte offset to start reading from
+ * @len: Number of bytes to read (0 = read entire file from offset)
+ * @actread: Returns actual bytes read
+ * Return: 0 on success, negative on error
+ */
+int ext4l_read(const char *filename, void *buf, loff_t offset, loff_t len,
+ loff_t *actread);
+
/**
* ext4l_get_uuid() - Get the filesystem UUID
*
@@ -257,3 +257,41 @@ static int fs_test_ext4l_size_norun(struct unit_test_state *uts)
}
FS_TEST_ARGS(fs_test_ext4l_size_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
{ "fs_image", UT_ARG_STR });
+
+/**
+ * fs_test_ext4l_read_norun() - Test ext4l_read function
+ *
+ * Verifies that ext4l can read file contents.
+ *
+ * Arguments:
+ * fs_image: Path to the ext4 filesystem image
+ */
+static int fs_test_ext4l_read_norun(struct unit_test_state *uts)
+{
+ const char *fs_image = ut_str(EXT4L_ARG_IMAGE);
+ loff_t actread;
+ char buf[32];
+
+ ut_assertnonnull(fs_image);
+ ut_assertok(run_commandf("host bind 0 %s", fs_image));
+ ut_assertok(fs_set_blk_dev("host", "0", FS_TYPE_ANY));
+
+ /* Read the test file - contains "hello world\n" (12 bytes) */
+ memset(buf, '\0', sizeof(buf));
+ ut_assertok(ext4l_read("/testfile.txt", buf, 0, 0, &actread));
+ ut_asserteq(12, actread);
+ ut_asserteq_str("hello world\n", buf);
+
+ /* Test partial read with offset */
+ memset(buf, '\0', sizeof(buf));
+ ut_assertok(ext4l_read("/testfile.txt", buf, 6, 5, &actread));
+ ut_asserteq(5, actread);
+ ut_asserteq_str("world", buf);
+
+ /* Verify read returns error for non-existent path */
+ ut_asserteq(-ENOENT, ext4l_read("/no/such/file", buf, 0, 10, &actread));
+
+ return 0;
+}
+FS_TEST_ARGS(fs_test_ext4l_read_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
+ { "fs_image", UT_ARG_STR });
@@ -110,3 +110,10 @@ class TestExt4l:
output = ubman.run_command(
f'ut -f fs fs_test_ext4l_size_norun fs_image={ext4_image}')
assert 'failures: 0' in output
+
+ def test_read(self, ubman, ext4_image):
+ """Test that ext4l can read file contents."""
+ with ubman.log.section('Test ext4l read'):
+ output = ubman.run_command(
+ f'ut -f fs fs_test_ext4l_read_norun fs_image={ext4_image}')
+ assert 'failures: 0' in output