From patchwork Sat Dec 27 20:43:07 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 1089 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1766868282; bh=7EVVz5F6VrM/5Yd5DQj+1cqQkVvYsVpYJcgjbUwHyKo=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=wA8V5qzZdUv+BeRM418MKW9fm0aXGp1tpuSjGHOR6k/LcE8ZCpd76Yrj4WfX2lHNq +UNBx0pRyhm8fBD0bZKhwLwGFqVODta8wAXCzn/o7wqYjOfdbfwXpfVb1fBw2Dai+4 6hgncwF3itP06x0b/WDNSFMjvfzAcLskNVnOxOSPx07UM79JRiIiwZwmYCCiqYwHy3 CjnErGjA8ctcygXcNp2Efy/f9PQ5+XtaDakrUHuktmS2Pv+rq47dG0dd4/M0xr+ffF aBUvHIHyBDTmBOAoxKMLEKRXjkRUzg7tHsPjyuv3K2fW7g2cREhNrxhiEx3uUSubfe et8gOxf9zkuAQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 0535B68BF3 for ; Sat, 27 Dec 2025 13:44:42 -0700 (MST) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id WgFPFUy2PA-r for ; Sat, 27 Dec 2025 13:44:41 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1766868281; bh=7EVVz5F6VrM/5Yd5DQj+1cqQkVvYsVpYJcgjbUwHyKo=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=VxX52S+SxIxryhITfjV+YEtHPt+9ILWIYsvxoWQCmA52d/gitrqQ97oMDlul1s1mY 5oI070fN1Oh//FIIFv8b5NUu/Py3I/CAOWzQd4sGjOJTBAb9pc0lgTrbz3P5ouRrSS XJhQZsfPoCKZnbHPWo/7VcDF1qRz82VL6wYRmcRgnFG8UkvDmTRQ+kX9Gq1fH/jE+8 RSh2ADSazZol2USlJAqt1OK1gYio5oQQPdB8XRhT7sijCe5dGI/ScSJyivw8ULb3KL FcZzl5AVUeXHyA5okbQ3S8HfQxIeo9LkmcoefyQEogkxtljFfzH7cc94JSYvY1QciE KzVmZITsLVTHA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id E976F64E4D for ; Sat, 27 Dec 2025 13:44:41 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1766868280; bh=PQKJKkVwzxIBH673tO5BbNZgvz/yVbJfFcs+DKkYigU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=uqGRq5DrR87t4NMeprCV7XNt/xNR3fexd2rYNMkBoLxklx9bnNWfA/pvppTNktaoP M/DQf1rhi/ukoehZPLTBwcSHRJvvL6j09ESsEfOkl30CGl/k5lBUyIrLpOdI9pLTx/ KzmIjrYo7FqTY1sgtWgF3k5su4zXHQ124yBNaaa+IOzEaDDub+ZOOLv6Dy0MJEsGMP K+vQZNuzd3OXL5jxkOpqWJe2VLJQJO7biN1bkhmx+iuXAFKOpSjPieepJw/2yVFlOw g7u+umt+d3qPzYgxAF4rhBPDIFBNLNNrc2ksQHw8HRhDTmiQyIcHryiGfJmk9xKTpq MWD/m3QLmzpiA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id ED24964E49; Sat, 27 Dec 2025 13:44:40 -0700 (MST) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id BmUDRNtTdqvC; Sat, 27 Dec 2025 13:44:40 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1766868276; bh=iYned3bvF4UHyQ/kvVEQeSJppe2yO/NswFvI1ZJ235o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=tcIsbrH3q4xSjlegsR4IWUHAQRcMka51EEwTQPhRlMF+4p8H32Rdmx+Tq+hsGhCt9 A6g9IK5BRW8qi0Hhf6iXTp1/2GxrluDpICza7O3VmXjkk7l236jNo3hB1ow4DQeXvZ ABV/P9mvSVfYxD4N7He/TiRL09CyGmVcJKpy70Hb/Kr9XXp5MTcLiRm4aFZznr5E16 4QDo7mQFgKPSzE2aZ1MNuHoEyPdehANCJDV4N3c+raLPBh+9/eLdGpFVXqJ6jwF9ap hP/GRl982WQ5r9PdhABW650cA+MyiU0ySE0fnOWCuMmBYEtN8IEQUd0KD4ZDjkKrCo njz3dcMRxdDmA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 5F31E64E42; Sat, 27 Dec 2025 13:44:35 -0700 (MST) From: Simon Glass To: U-Boot Concept Date: Sat, 27 Dec 2025 13:43:07 -0700 Message-ID: <20251227204318.886983-13-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251227204318.886983-1-sjg@u-boot.org> References: <20251227204318.886983-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: PZWEZDOYLED57M44INXLNQ2P4KCQSCHU X-Message-ID-Hash: PZWEZDOYLED57M44INXLNQ2P4KCQSCHU X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Heinrich Schuchardt , Simon Glass , Simon Glass , "Claude Opus 4 . 5" X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 12/16] ext4l: Add read() support List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Add ext4l_read() function to read file contents. The function resolves the path to an inode, then reads the file block by block using ext4_bread() and copies the data to the output buffer. Signed-off-by: Simon Glass Co-developed-by: Claude Opus 4.5 Signed-off-by: Simon Glass --- fs/ext4l/interface.c | 64 +++++++++++++++++++++++++++++ fs/fs_legacy.c | 2 +- include/ext4l.h | 13 ++++++ test/fs/ext4l.c | 38 +++++++++++++++++ test/py/tests/test_fs/test_ext4l.py | 7 ++++ 5 files changed, 123 insertions(+), 1 deletion(-) diff --git a/fs/ext4l/interface.c b/fs/ext4l/interface.c index 17648a59077..34e659cd28b 100644 --- a/fs/ext4l/interface.c +++ b/fs/ext4l/interface.c @@ -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) diff --git a/fs/fs_legacy.c b/fs/fs_legacy.c index 5edc35c4cdb..27a2d7be220 100644 --- a/fs/fs_legacy.c +++ b/fs/fs_legacy.c @@ -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, diff --git a/include/ext4l.h b/include/ext4l.h index 6fee701f335..643060ee44c 100644 --- a/include/ext4l.h +++ b/include/ext4l.h @@ -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 * diff --git a/test/fs/ext4l.c b/test/fs/ext4l.c index f58a91893cc..1bea9186d5a 100644 --- a/test/fs/ext4l.c +++ b/test/fs/ext4l.c @@ -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 }); diff --git a/test/py/tests/test_fs/test_ext4l.py b/test/py/tests/test_fs/test_ext4l.py index 922fa37a7d8..4064a6c53ff 100644 --- a/test/py/tests/test_fs/test_ext4l.py +++ b/test/py/tests/test_fs/test_ext4l.py @@ -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