From patchwork Fri Apr 3 14:04:23 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2086 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=1775225150; bh=nteHJXwoRdhLLoItsHnGNFMKJ6Xi5IhnwlTOSWPiavI=; 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=VaRsYomIFBmB5or991PZwZjquMh8BdHvhKh2evxWhXvifBAOTlwEZuJc+7pwF4pzX mPt7NHGFfT0cAeGom81iTjlL8OaUlgWYYqMO3nB3CiOMYVJQRcBeE2hyvR7pY78dub 5ifxf/t9OQ6IU/jZSw4sR2A9ZHnaLUhh/MK2o+GAz7R2Kllng3oev8uPJN9XaDDHaX IV7cL7VcHCTtkUw6Ggslfz3UUcvc4FngRABhgIxBRlZTgWgHAwwDtxPw1ZEooiw1cF l45Oo2c3ubngw1zIpW0+oDWk3Xj1xmEPeJhLPY+bod0IgW6WuEHgFlc/afTbCniH5Z b38MdYPNWHlqg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id BB0986A34F for ; Fri, 3 Apr 2026 08:05:50 -0600 (MDT) 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 iM33afcKojag for ; Fri, 3 Apr 2026 08:05:50 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225150; bh=nteHJXwoRdhLLoItsHnGNFMKJ6Xi5IhnwlTOSWPiavI=; 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=VaRsYomIFBmB5or991PZwZjquMh8BdHvhKh2evxWhXvifBAOTlwEZuJc+7pwF4pzX mPt7NHGFfT0cAeGom81iTjlL8OaUlgWYYqMO3nB3CiOMYVJQRcBeE2hyvR7pY78dub 5ifxf/t9OQ6IU/jZSw4sR2A9ZHnaLUhh/MK2o+GAz7R2Kllng3oev8uPJN9XaDDHaX IV7cL7VcHCTtkUw6Ggslfz3UUcvc4FngRABhgIxBRlZTgWgHAwwDtxPw1ZEooiw1cF l45Oo2c3ubngw1zIpW0+oDWk3Xj1xmEPeJhLPY+bod0IgW6WuEHgFlc/afTbCniH5Z b38MdYPNWHlqg== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id AB1376A34C for ; Fri, 3 Apr 2026 08:05:50 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225148; bh=PJhQlGAdpjkZfyiUvW/O5yjg5yIAOweCdTfHLB2W+Xg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VdvvHHHD1Hnfr4SGvqPXWKwRwucJzYbc9r4jQ+jsqaJdaK6ilLi6TpfgF7nb7iW3R OV+VzxIpA075zdcn+j9CTHs/DIVH61X6Wmij9/itAl7L2v1waMHLjGn8aLaM3DIkh0 iAtMKJTF8D8D3VRcxNjcA9bQ2si61Sp1Ac1jH7hsdlmBd2ZSHwXIsafz8uhkc4NPQm e3/v3mgLfiA0H5BHLAyxDy6s4+fKtiLzYQECvZ4nTXg0siuZZRS88MTZKma39gLqBn GRIMq8L7P/UPM7YK3NhnEPnSQ0GiU4L41NhcwMCs5xzvUMfgKCN4nVd3Ob37p4bFHM HUTSA62ho0Glg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 9900C6A34E; Fri, 3 Apr 2026 08:05:48 -0600 (MDT) 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 D2K5VQdP3rSf; Fri, 3 Apr 2026 08:05:48 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225143; bh=4H7S0xXl91Mu6Esi4XQjQ2Dfp+lks0jAB180CkGGAdo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RJliIuEChCTIyZRlD5PdOy9ZajfgObSLsITvDxHB6YXJ8qmAP2ptG/mVl+ElWoYpO io3InQbAPcq/f/l4/j/1BQEZsT76rnEiPrIcH7FyXil5xx38g+VF+PafIfJ6d+UByy P989OXvbP/10n+g0s12oIIgCDZ7Al54vTMFerLquAHeM9jmSe/3FHxiagNBm0bIbaN pk+fcxTmvqLCZ0j46vanGNURBWh9gZW0qRFIHVL5bQfoymDNB4q/DawkkNrCgrQ2tO hpdvVxmP3Dy6lz9+nQWxP2CSqTwRrxaxg7NeoYdH0oVhj0Yx8qDHjYEr7cf3t/d4PC bcSQfwG4F0GZw== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 5E32F67EE6; Fri, 3 Apr 2026 08:05:43 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:23 -0600 Message-ID: <20260403140523.1998228-2-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: FDLEYLQ4TVN2FHZ5VIGYF5MIAPB5W674 X-Message-ID-Hash: FDLEYLQ4TVN2FHZ5VIGYF5MIAPB5W674 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 01/34] vfs: Fix NULL check after strdup() in dir_add_probe() 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 The NULL check after strdup(path) tests the wrong variable (str instead of dup_path), so a failed allocation is not caught. Signed-off-by: Simon Glass --- fs/dir-uclass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/dir-uclass.c b/fs/dir-uclass.c index c5d7ef45d3b..0b55713cbdc 100644 --- a/fs/dir-uclass.c +++ b/fs/dir-uclass.c @@ -27,7 +27,7 @@ int dir_add_probe(struct udevice *fsdev, struct driver *drv, const char *path, if (!str) goto no_dev_name; dup_path = strdup(path); - if (!str) + if (!dup_path) goto no_dev_path; ret = device_bind_with_driver_data(fsdev, drv, str, 0 /* data */, From patchwork Fri Apr 3 14:04:24 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2087 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=1775225153; bh=WEytO3NksblilTmjbUvaJQAE7p/WXxBhZAwPAWdXMFk=; 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=IfPRAxUH1PPQXifQMQVvjbCTFh2yuJwXHu69HaBassNykS6w+F2/gKG+rrs5BDp/h 2ZVxKcX5/DAmb8elZq7PXWvjUncHpPOfK6HTkXH74pC5/UopBSN9JLFwt+QQhYf6YS CJw5piWRZS2u4rCmeOnYdPNT7VBBZISOz7LFoZIvFje1jDqfmnDbvBfSzryazzqryG P4t9oQd0OlclUfoKHS0+DAw7t7tM4crL5MkmefAhhhFg5/m0w/JpTYo4bkV/Xxeuzs UK156G2NJMAzOVevn5QVpXWkIWk1vJJJVDcGep1vZi1zwh76H7lZmpKXxOhTxlge0C o50b3BDMd1I7A== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 1DC826A34E for ; Fri, 3 Apr 2026 08:05:53 -0600 (MDT) 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 WUvMXsPDwyxD for ; Fri, 3 Apr 2026 08:05:53 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225151; bh=WEytO3NksblilTmjbUvaJQAE7p/WXxBhZAwPAWdXMFk=; 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=i/uEINhbw0+7p6D0RWSS4gRg3LLo2p070COutkUmSQ9Ym+gNs1sxupDcfCwIde3pj Ild0+ppVaPk2f4fiU16wNinkRkCOVPWPjg8H2M66JbPFYnIktxKuLYvH+3Cd8kv3r7 sZ7gc8Cy6TmSJoPOstR3ZvbE51k+ilnF8xwupX6X81p0f4YIy7S9N3nRwPvgqIiS2e lHafHUO36GfQWH3lMEzchpYtA2/GcUteH8C7ZckhelJ5H+dmgp7Q6C0okc5xWk3vdx ZZ1oQ58zo2DRfqMnFrL3qbhz1wfPXbvJKix1RK2IKKRRRlRQ+NOcfggOfa+7pilky7 aJlsceSFv81NA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 989226A347 for ; Fri, 3 Apr 2026 08:05:51 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225148; bh=q/tvbT82iEHH3Nm8P5KQ0JM2FQx+oMGfLXrcyqWrn5Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=D+fshww5IpwV+SWMTEV6Syb2Ad9NOMMquPWe9d3PWiH3OQ5drlKPOkI72KVv3LxRz mseQ1ZSnHerIJ3EY0yDYixMsHXcSIIK+VY9C0kSSRynIAe9Ivt+43jgulne3Nsdkka Y9YdSuORT+vCCHy3Vp6BDfiAiMPXhbyl9j5HqR7HZ/yUeJ88VGkvbit8uzS+WkZtJn 5lLfORnkL6InWguSZ1JldoVJXFEacDr1S+y9BDq3f59rYk4JIDC3JxcFj96aW7HbQT U1gw+vRWQnzsRlB6HaiJ0pfE1RCxewTzN+jyIG6lU1piW8pnFJFVMQvdK76vY5zfdJ 8VdC/wIQ7ojIQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id AACCF6A34F; Fri, 3 Apr 2026 08:05:48 -0600 (MDT) 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 ZDJqcID68Gmy; Fri, 3 Apr 2026 08:05:48 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225148; bh=1BFE58sMWbSkD/Is8qsRe1r3wPLkAgRsTWONqotcEwc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ZAjXPhg4Vtqxnd7RZt6v1zaJzT/hO6ym3Or/oEqIQljdt9b+xGyEbDD3Z8jgxmuFQ 6K+Xyai3M6N2Mw4/8EyD9adyyn1oZ8X058nSA/3pED24LNTy4MGIek1QkX8ritoKZy 9Kii+buHutMK8ZT2dq/PtAGME3w+lK5xGJmNVrOTB+G/XlPEaAU3GSkY3TIbwO+XEp qTP1g/ynI7uabU/CoCtW+sL1GmODeIdKlHRWUBWMEu3aQEn+JLoninMkXN1CpEgaMu 0KEEDLvwKMnReLaCOvZ5+g647KyE650pwynrooqVLfkepwdgOa5WcFkHf5HWM2lkOo cx/kWJvlXyPzw== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id E26916A347; Fri, 3 Apr 2026 08:05:47 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:24 -0600 Message-ID: <20260403140523.1998228-3-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 4ZS6SEBYJYO6NDKFMRKFWU6TPZG55PLQ X-Message-ID-Hash: 4ZS6SEBYJYO6NDKFMRKFWU6TPZG55PLQ 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 02/34] sandbox: Rename the sandbox filesystem 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 The name for this comes from the devicetree node. The 'fs' name is to generic, so rename it to 'hostfs'. Signed-off-by: Simon Glass --- arch/sandbox/dts/test.dts | 2 +- fs/sandbox/sandboxfs.c | 5 +++++ test/dm/fs.c | 6 +++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 68e3d35a183..c49dcdf4a23 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -589,7 +589,7 @@ compatible = "denx,u-boot-devres-test"; }; - fs { + hostfs { compatible = "sandbox,fs"; }; diff --git a/fs/sandbox/sandboxfs.c b/fs/sandbox/sandboxfs.c index 5b8f4f50a8e..494af0d373a 100644 --- a/fs/sandbox/sandboxfs.c +++ b/fs/sandbox/sandboxfs.c @@ -179,10 +179,15 @@ int fs_write_sandbox(const char *filename, void *buf, loff_t offset, static int sandbox_fs_mount(struct udevice *dev) { struct fs_priv *uc_priv = dev_get_uclass_priv(dev); + struct fs_plat *plat = dev_get_uclass_plat(dev); if (uc_priv->mounted) return log_msg_ret("vfi", -EISCONN); + /* Reject block-device mounts - sandboxfs uses the host OS */ + if (plat->desc) + return log_msg_ret("vfb", -ENODEV); + uc_priv->mounted = true; return 0; diff --git a/test/dm/fs.c b/test/dm/fs.c index 31712617d09..6cb5f74db35 100644 --- a/test/dm/fs.c +++ b/test/dm/fs.c @@ -46,7 +46,7 @@ static int dm_test_fs_dir(struct unit_test_state *uts) ut_asserteq(-ENOENT, fs_lookup_dir(fsdev, "does-not-exit", &dir)); ut_assertok(fs_lookup_dir(fsdev, "", &dir)); ut_assertnonnull(dir); - ut_asserteq_str("fs.dir", dir->name); + ut_asserteq_str("hostfs.dir", dir->name); ut_assertok(dir_open(dir, &strm)); found = 0; @@ -81,12 +81,12 @@ static int dm_test_fs_file(struct unit_test_state *uts) ut_assertok(fs_lookup_dir(fsdev, "", &dir)); ut_assertnonnull(dir); - ut_asserteq_str("fs.dir", dir->name); + ut_asserteq_str("hostfs.dir", dir->name); /* check the start and end of the README, which perhaps won't change */ ut_assertok(dir_open_file(dir, "README", DIR_O_RDONLY, &fil)); ut_assertnonnull(fil); - ut_asserteq_str("fs.dir.file.1", fil->name); + ut_asserteq_str("hostfs.dir.file.1", fil->name); uc_priv = dev_get_uclass_priv(fil); ut_asserteq_str("README", uc_priv->leaf); ut_asserteq(0, uc_priv->pos); From patchwork Fri Apr 3 14:04:25 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2088 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=1775225157; bh=vmHkOfUcZJk1UEBD1B2NTQqtxAdDVlYriKoAloCRnBE=; 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=kpgPoO9hINCmUg/PuQqOGJBHVHwBam16CCXQVjSSQtWcPEJ284wLRXXlue8gyx4xY hFCM7QoFqBQAfQ9xUhRb+i4ItVo9pVSp+SA26flOK/NqCcwONm8ICVY790um5tR/6t GLLBu5+RRQJS75abYbNi9oIWOyjmv6cLluALuE+b3SodiFTYjocN7gNwRuiLmOYSkC t396LsPy5KqSN9KDg4J6tM+nO0fYWYc6oW5rjzkuAMSTxarqyXaLP9jLd1PCMHhQ0m UlAQxCwaqLhCpF8Qjhc2vEDCepis6PU8RKOjt8G/60uZrjAGB33SB0gZeCTBPVPR3h 4is6hzujj6ZQw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id AA6886A347 for ; Fri, 3 Apr 2026 08:05:57 -0600 (MDT) 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 HtkKckHAY3NH for ; Fri, 3 Apr 2026 08:05:57 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225157; bh=vmHkOfUcZJk1UEBD1B2NTQqtxAdDVlYriKoAloCRnBE=; 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=kpgPoO9hINCmUg/PuQqOGJBHVHwBam16CCXQVjSSQtWcPEJ284wLRXXlue8gyx4xY hFCM7QoFqBQAfQ9xUhRb+i4ItVo9pVSp+SA26flOK/NqCcwONm8ICVY790um5tR/6t GLLBu5+RRQJS75abYbNi9oIWOyjmv6cLluALuE+b3SodiFTYjocN7gNwRuiLmOYSkC t396LsPy5KqSN9KDg4J6tM+nO0fYWYc6oW5rjzkuAMSTxarqyXaLP9jLd1PCMHhQ0m UlAQxCwaqLhCpF8Qjhc2vEDCepis6PU8RKOjt8G/60uZrjAGB33SB0gZeCTBPVPR3h 4is6hzujj6ZQw== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 7EF476A34C for ; Fri, 3 Apr 2026 08:05:57 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225155; bh=kroBI61HTMtWd3hXWvw733ZfYz7Da9B/ePzWehs8jUs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=OoMHSIDAJ1Uc0l+08exLrxaiwTNNNYpAqEfVWkA7zsGsGciHgp5bXIwhM5ZWccTvZ yUz2s5wlZm/D4VLWOPqaxBxYoihJN1qi70DyGGy98qEbUogQDBOGoK/0lcU5XQAdMm 6/p1rTbnU1lV6VxyCLxSLNiHZKOySmQdPNwqRFXrfB0VrKpeMyxWhAUkbz9bPmG8s3 YH9AUoj6/XKFspB28cRYI2lu81ejy/aoaoF34Z7xVlC0AxEP6FPGF8o6DtIcepyRkS ShkMEtIC77zGzlwiMfitRUQT56KcH7933ch83QVibM2mX+hCkFBt2yEz1kPbWPAVgN tJxkc8YssOI4g== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 6C1776A351; Fri, 3 Apr 2026 08:05:55 -0600 (MDT) 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 eaeD3hQIGAwT; Fri, 3 Apr 2026 08:05:55 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225148; bh=i1yYDg0HFTG6pgnn36OQnWk5QBxXlQxXKCiI7skQvQo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NgiKbjyjrQqoDEZGSoDiQv2Qx6uHB9z6Gbfk30VgCjMAu/dofxFyl23Utxh2bsg17 9Bp48Bdl0cQL0MoRWY4iJ5FT20X8/yF+AFWJSfiCUnfoIYMWw84A2K/E0nyp0rTSms nTqol3sjldeZFokk3n40IzvlzQcjGOd77syUHL8GwA2IASUgXCRpTeO5GTxn2j9yvd AAwbTf7OnrcFcFwvIcNfljOdQ8Jb1QCFQQF/YPF9qv+W6QVTVAtS9TrNfmRkeWDPkd 2oMoTtXQARNEQb90mv9eSbnyoo1rRy8RxI5aWuzsjOrA2RnZTlsluc9v9sAdVfwrfO kUbuydCSWPLQA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id A0F1B67EE6; Fri, 3 Apr 2026 08:05:48 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:25 -0600 Message-ID: <20260403140523.1998228-4-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: JOWA4AZRJYOTB37RECEY5EQZTHXJD5BC X-Message-ID-Hash: JOWA4AZRJYOTB37RECEY5EQZTHXJD5BC 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 03/34] sandbox: Add hostfs node to sandbox.dtsi 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 the hostfs device tree node to sandbox.dtsi so that the sandbox host filesystem is available in both sandbox and sandbox64 when running with -D (normal mode), not just with -T (test mode). Signed-off-by: Simon Glass --- arch/sandbox/dts/sandbox.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index e5d6be633fb..0bbbad96738 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -515,4 +515,8 @@ keyboard-controller { bootph-some-ram; }; + + hostfs { + compatible = "sandbox,fs"; + }; }; From patchwork Fri Apr 3 14:04:26 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2089 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=1775225160; bh=CtqnGyt3tbgyARFq5lYYaNidvz5DU2Eb0C+eeEsBILQ=; 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=WZ0wDsVZltGN2AreLYBFxaRJAhbaRQjrwHngCpKEP3/uDrO+tWM+MgrzZR1KHNynq ZgvD99q2auBQGADTI1yClUWdEkofV9wULkuK75Kv5kWt4XMmY54akN7vfE1762Tk14 4nSN6wlJ/MaPfnHe4tB//EgDJs5tHsc6+loT0te3wVOUzW8tulSk7ay/QOSR/RKRwS vX8SjtcIBcSDEBanqNsNxt+3GqUqKgYmHbHMnMoUJTUBRQJ4OIC2VnVXKTplYoVurw 71vdym9SwpxovHWoTm85scfg6yPGo9PE1Vm8N1HOk2eyHteLFOIDty/cGrJHfQtXhy 0yUaQkPqdBNqA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 32B4D6A375 for ; Fri, 3 Apr 2026 08:06:00 -0600 (MDT) 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 2lsg7raT3SXY for ; Fri, 3 Apr 2026 08:06:00 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225158; bh=CtqnGyt3tbgyARFq5lYYaNidvz5DU2Eb0C+eeEsBILQ=; 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=dBNaU9/ANdg+I/ugrzxwLc+G8jTeboTOULaZ0wK87CPgVyLDKNlM8UHdTyHiUfwtS xmmhr3EzCdq6BQn1qQBQ+VbgU0WUQxHN05kVLPjrlPZErpee8HBeeNZQV+2AheYDce FLgEt4Ad3rIeGqdK++eYtS4WiKDyLIh3/fKINm44fTVl3CGXqCeZgDDMGT4JAsJHWR YRWSQDaVqa9Py0ejkr6bTn3ra6ocobr77fXy0eWKptW5/NjJsctLwa3KrlVvWQXf1a cCkbwkxGg25dGIUBjDzJP5367eb2JW9FbJu6QIRz8yq/ZznwXRZRwMN3FycHI0c74C R5DnLxdN+EGow== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 4D3DF6A354 for ; Fri, 3 Apr 2026 08:05:58 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225155; bh=hFHqjHoK5F0albQGQLfMwYoMMD3920W6fqqx93oQvR4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=UhcmOlGqW1KTxiT2li1D7y152qYiVSH6bxrnk2mpU4Uf3L3N/w4i1pB15BEeFFiHR UygdxTlqc/Th/60AnkvMh/A/90Is24LJWa6WKkd8neek3emUha8moYX+QjAqJwpixF relnOHk1IQ+S3n0lu9riOB2nSMDGX7+s5zddqjgr1McNxPrMpWQu5Lw4+cj0nBuWHw t6WvpYqkS9Nk+2TtsftleKnPQYKHnBRsPnP9/4GFEd8allTLKidLH1pcmtfuTphVyV 59fbBlmhaP3yT+8TxjksazF3suKYc86MI6VfZ44h5bRAe+98WBm3PiTaUm6T2q7hh3 vJCfCw8LGt1FQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 6C08B6A350; Fri, 3 Apr 2026 08:05:55 -0600 (MDT) 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 HZzyMnOpNyDQ; Fri, 3 Apr 2026 08:05:55 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225153; bh=+sdbz/xhaRXVQOPUWh74EUBv4rhWtlTuUqVVEBAG4xo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=woiciE3iU6X1MB2ohjQKPA+r41pLeHcrhvwRmuAhPCivOOYFPXQ33UKpd23Hi6a1/ nuWG3e6vT0SZEf2pkh38umuUr/ybo++qPqO9JWil6ci724Sf3PEaVBS3xa2ePZemsS v3tMQmQ2Z3CaB7zPVLC1vbV7THu+39AMOluau6nb9rawOUzr9TB2nngGbYPmIyjmXs N3+5ysNBP5Nnw5UDJozwwU4APkU8aFjTn2sznEUO61PJLyvVzrLiJH5CdqkyrmaSVR gXxfA1S/n98OA3Sc2dB2+LVJLRje8kUOCjYKl6WhyLk9XsVTb4zQT3axG3rzEE/xjz HaiK486KlWXWA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 2E5EE6A347; Fri, 3 Apr 2026 08:05:53 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:26 -0600 Message-ID: <20260403140523.1998228-5-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 2NZZPHDPQO6V2MBLFI6YPRJ6PJ6C23O4 X-Message-ID-Hash: 2NZZPHDPQO6V2MBLFI6YPRJ6PJ6C23O4 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 04/34] sandbox: Add various helpers for mkdir, rmdir, etc. 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 os_mkdir(), os_rmdir(), os_rename(), os_symlink() and os_readlink() wrappers around POSIX calls for sandbox use. Signed-off-by: Simon Glass --- arch/sandbox/cpu/os.c | 44 ++++++++++++++++++++++++++++++++++++++++++ include/os.h | 45 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c index 8f751d69cf0..7088d8eb5e3 100644 --- a/arch/sandbox/cpu/os.c +++ b/arch/sandbox/cpu/os.c @@ -152,6 +152,50 @@ int os_unlink(const char *pathname) return unlink(pathname); } +int os_mkdir(const char *pathname, int mode) +{ + if (mkdir(pathname, mode)) + return -errno; + + return 0; +} + +int os_rmdir(const char *pathname) +{ + if (rmdir(pathname)) + return -errno; + + return 0; +} + +int os_rename(const char *old_path, const char *new_path) +{ + if (rename(old_path, new_path)) + return -errno; + + return 0; +} + +int os_symlink(const char *target, const char *linkpath) +{ + if (symlink(target, linkpath)) + return -errno; + + return 0; +} + +int os_readlink(const char *pathname, char *buf, int size) +{ + ssize_t len; + + len = readlink(pathname, buf, size - 1); + if (len < 0) + return -errno; + buf[len] = '\0'; + + return len; +} + char *os_fgets(char *str, int size, int fd) { char *s = str; diff --git a/include/os.h b/include/os.h index 41e3022657c..495ab3a9d0a 100644 --- a/include/os.h +++ b/include/os.h @@ -108,6 +108,51 @@ int os_isatty(int fd); */ int os_unlink(const char *pathname); +/** + * os_mkdir() - Create a directory + * + * @pathname: Path of directory to create + * @mode: Permissions (e.g. 0755) + * Return: 0 for success, -errno on error + */ +int os_mkdir(const char *pathname, int mode); + +/** + * os_rmdir() - Remove an empty directory + * + * @pathname: Path of directory to remove + * Return: 0 for success, -errno on error + */ +int os_rmdir(const char *pathname); + +/** + * os_readlink() - Read the target of a symbolic link + * + * @pathname: Path of the symbolic link + * @buf: Buffer to receive the target path + * @size: Size of buffer + * Return: length of target on success, -errno on error + */ +int os_readlink(const char *pathname, char *buf, int size); + +/** + * os_symlink() - Create a symbolic link + * + * @target: Target path + * @linkpath: Path of the symbolic link to create + * Return: 0 for success, -errno on error + */ +int os_symlink(const char *target, const char *linkpath); + +/** + * os_rename() - Rename or move a file or directory + * + * @old_path: Current path + * @new_path: New path + * Return: 0 for success, -errno on error + */ +int os_rename(const char *old_path, const char *new_path); + /** * os_fgets() - read a string from a file stream * From patchwork Fri Apr 3 14:04:27 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2091 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=1775225163; bh=EhcH9Y2Q1Vj2/22keGrc6V4PwlcoGo6D+YTEVVPZtzg=; 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=pxaKW4/hHGKJGNA3tKKO2ti5W1/4GrA+arUn8IikeCbaR6i1Iw5I1Gl/ezRRXKwgE Dvq8wz7U9+1NxT267wxnUnI8CptNy3a9/PVrlyJlzfuoEVFMB6MslSl5A5Xq3+hEmH 0uUsLVgPIIBjQsDzDQXw4prsGO6Z/t03EkyweiSJBnZeR32Z1R06w9Sz1OIEE0iWy1 CXiomFAt5xoegIKTdcNcDOuUWWelP80u4IADlWCJoBHXURUFs6RkP2bYcz+MsGKnZV zQlWDX1jHcTzHywDhHwQIWRK37wB5o3ISYDrACVR8cIM2w1ZbzQkoIMb6UgkEw8moY 0tS9HeB4ejtJw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 7B82B6A363 for ; Fri, 3 Apr 2026 08:06:03 -0600 (MDT) 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 k5jVCdfDuuQj for ; Fri, 3 Apr 2026 08:06:03 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225159; bh=EhcH9Y2Q1Vj2/22keGrc6V4PwlcoGo6D+YTEVVPZtzg=; 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=SyvmrL3F0KPIxcRnWw1ELGJeqw8B5wpB7fqWRT2uB8BieCMDFjXiFANWqyQiiReSP ZAUzNQwlvnHAhzC7GaKuuFoVEigcqrJKxNcIX/2rlZmIYV5m5kmJgYgcoJO+vz17F6 fvJtsqdySRplZwMZJl6beoVZgtExrI9zxcqDs7BiaZWj+G+ygwo28lHLwRVRsAb90V +kdwyZoCKNvTIXzJ0nHAKvHHwOiHOX2m88woXX3VgvLh/uAHYFwgtGR6uY77QM44Kp LaZV+yBFsLdZ4sdIQtjldrVP24WIsb2OIxMIgeDyVfHBEo2BJtQLmTxUkPtsJsX4lI d+QEm9Dxy/B1Q== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 4A0406A367 for ; Fri, 3 Apr 2026 08:05:59 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225155; bh=i1QKN68+cNBpjlntljcoxqKr+iDLcLYyfQRCu0imYvs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=TLONNbikpcwr7XdZ21Bk7tfo/X8zkIjUmDBWvOrN74xg790vv8n8AeT3xxwOAFBed UbqwIV9FTxAlycnz8fiFnhFCrUrzfgL1HL0QgoVrSZLtx7qjIliSY92Qb91FNEWTzJ H2JzqYLmQ0iMp4Xw8TT6+op5tJ/vnrh0/BV47yZgbdpONPhpq0OMsORHt9wYOE2jNs Qpt9mVaO20kkMGOjcBZnDjTqmjtflZGWaGzLyiatLGVgyA6VT4uPFRFYb3jwPYQ/Bg 9w2FyaH9QcYxq0yHhCHAGiOgNvIW4EvsnAdV/NG5RHVbnbnasva82QJrsh6wHpvanQ fRi7eRQ8kyLjA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 7FCD767EE6; Fri, 3 Apr 2026 08:05:55 -0600 (MDT) 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 rHrytRHn5cjS; Fri, 3 Apr 2026 08:05:55 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225154; bh=WA0ZdeOTxMsK0vDFG42UyE+Fzbp630RYqlOh9ay5JqY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VKWNam8Pd1GBu/RHErUibEAdmMFpG9QRT1StLQDS3A7gaIJFcuHQZ6Hafkt066ZxC QvYEI3946TbcI3HFkyNbfXateJF4PGywS/RQc2pbruB6S7c8RxWYqK45ntllrnzjs8 eQe1nLeIGibfFPLzkQe59PLqAj1323A5OGmfghpBbDaXaYFktqJyxYH08lYdNVbC77 ioJSteOOga6DGIdHe+DjsEpLSUTQPNpadStS4ruB4wEQq90OaXz89Gyp9zvVbaU4Lr AaGfu1aXZ1kTBxKl9lDKsuQ+xu6QnW+iuhT2xEVATs72BeGdCpkhNwJmneEel8eZLP Pj4xF7hsnU9aA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id E20C26A34C; Fri, 3 Apr 2026 08:05:53 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:27 -0600 Message-ID: <20260403140523.1998228-6-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: JQDUEIO2CVJWEARCIAVW2FTTRTLPVEL7 X-Message-ID-Hash: JQDUEIO2CVJWEARCIAVW2FTTRTLPVEL7 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 05/34] vfs: Tidy fs_split_path() allocation 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 fs_split_path() duplicates the entire filename then truncates it to get the directory part. Instead, find the last slash first and only allocate the directory portion. When there is no slash at all, return an empty string rather than "/" since a bare filename has no directory component. Signed-off-by: Simon Glass --- fs/fs-uclass.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/fs/fs-uclass.c b/fs/fs-uclass.c index a335391bd74..6bb50bb2543 100644 --- a/fs/fs-uclass.c +++ b/fs/fs-uclass.c @@ -12,27 +12,34 @@ #include #include #include +#include #include int fs_split_path(const char *fname, char **subdirp, const char **leafp) { - char *subdir, *p; + const char *last_slash; + char *subdir; if (!*fname) return log_msg_ret("fsp", -EINVAL); - /* allocate space for the whole filename, for simplicity */ - subdir = strdup(fname); - if (!subdir) - return log_msg_ret("fsp", -ENOMEM); - - p = strrchr(subdir, '/'); - if (p) { - *leafp = p + 1; - *p = '\0'; + last_slash = strrchr(fname, '/'); + if (last_slash) { + int dir_len = last_slash - fname; + + if (!dir_len) + dir_len = 1; /* root "/" */ + subdir = malloc(dir_len + 1); + if (!subdir) + return log_msg_ret("fsp", -ENOMEM); + memcpy(subdir, fname, dir_len); + subdir[dir_len] = '\0'; + *leafp = last_slash + 1; } else { + subdir = strdup(""); + if (!subdir) + return log_msg_ret("fsp", -ENOMEM); *leafp = fname; - strcpy(subdir, "/"); } *subdirp = subdir; From patchwork Fri Apr 3 14:04:28 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2090 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=1775225160; bh=4+AFOhdrgBtpJDuoQ5AwLbINLHbeGVdOsIUHTKOt/CQ=; 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=CZpBxQj3h/y1yxCapdbwSuckIxhW/YbSWEIudoX0i+7dlPwLbwFn86mU35WfvPs0a 3WUbnyFX5KFyTg/6/DWqLCWjEFmlcxfNS5QZjVs/jByyFVQO4dmNnwvqmeQCebuK1p oLCeuPfXjCSYawhbu9OErzKZIKcikZa0CEHuB6hdzwP55x3Zt3Pi9QrsiPN+gPRdGo cdjS0ypsamcV1yu/LBolcp68E+lEOyD/datbC0i04DQhfhtIuAnob8HUwCuBc6HtsV EF4wnyF9KXnOa37XMw+HY0M/ko0aYaIIfcYp4dnvszN1WHuK8/8PfPNbvlOORGY+8S kohjhetK9l+Uw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id DC0CB6A35E for ; Fri, 3 Apr 2026 08:06:00 -0600 (MDT) 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 JNakDijMDLZB for ; Fri, 3 Apr 2026 08:06:00 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225158; bh=4+AFOhdrgBtpJDuoQ5AwLbINLHbeGVdOsIUHTKOt/CQ=; 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=J63Pnr5mSK8fSHZGa9hCnGGl9fREMTwkz2Eiie/gQ1O7nFzJh1Jc79mY425ZYf99i FYz0luLcj5/1EK051BNBlnwndr2TFwj5wHfOtmQ25cKZyWfK1x2oyEqif0A4Qw3/Xq tXb0zwkI1ow5dmlkAHq9420/McIXywQ93aesC2WX9ZW82COWNi0f8OiqxOeSmQ4cG5 tAvsC/0CKbni0/cY3Uco8ztu4YVxvtQLF/tLguKS9HjJhs/DUzy2NTqmGzEg1Z8rOU BmioODA74OFnWFTGY84xkiR/J21LfsngbuXTUVPpQUgDtjUHAnaWHaN5MV3zySUHBh lIHRt10jdnuFA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id C0E846A35F for ; Fri, 3 Apr 2026 08:05:58 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225155; bh=B1VBEzHcb54AV7T0jqO9Zo7/mwqELQftJ8qYKgwzK6c=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DNCM3YIyqXASNvnRnx5CBQm/9hHx6qZ7WmWSASbS40naM/thOgOyZBmDbtvlVm/HO jZvijXrpWAAHJomuFl3Pxm+54U3JHVRVC62BWEg/fWu+XyaHNCrw/UwBI3SPesueqh Kru4qGMLkegCvg/PnnwracJDKVnzLPCsA/QMDO4euRVH7RGDHdZku0cncx7RetIMxd sHqT3/UtdBEVKjsmYjA0DTeu8Ft7PBT9WPeyXx47MM2N6BmgHPliBXpt2SGB/gnqlY MO0eDEIvLZD4TuUinZ2tt1AdbwbpZENjsWG/A74tecabUn/RFC1Y8dzrAIQGKQml9X NFvHZVDF7gt8A== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 805716A347; Fri, 3 Apr 2026 08:05:55 -0600 (MDT) 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 yGq9uB3tuPSC; Fri, 3 Apr 2026 08:05:55 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225154; bh=S5VVvVsgs7dnYEUXiYuR8AX0k2o+ewgbN6878QEojok=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=eZJotTLCMRurDDfW7xX0lbqVNYNQn6gdrefy0/mj/sYwY0LQaasOTPyU2pugkrIzx NMocQ5MVIevBeQ0eZ4MDyxzJHTWv8wr/xOwnxPawO/QTrqWPtTU4R02yS3YXTryn0v kQpesr+BNrp2baJYJOJ5JIgDc+SSCED4wc2108JNkE3MXGR8ZtfEau3RZRauOw0KjA n7kwnV/8auhh3I7RCkBmZ41DPzdNwY/TlO0AiODfvKWhn2y9NTgPvCDGoE7Tpxz/V6 BmANLIpUrlr19AJXVVjn54SPA7JdmSZ/XPVev80i5w8xW41jwc0HWzrOpvVUOGjjnG oNBqq+r0oyeYg== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 9F8AD6A34E; Fri, 3 Apr 2026 08:05:54 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:28 -0600 Message-ID: <20260403140523.1998228-7-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: OQVPHKTEANZIGLUCLNBPOFCP3W7O7S7Z X-Message-ID-Hash: OQVPHKTEANZIGLUCLNBPOFCP3W7O7S7Z 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 06/34] vfs: Add UCLASS_MOUNT for the virtual filesystem 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 the mount uclass which tracks filesystem mount points. Each mount device holds a struct vfsmount with the mount point name and a reference to the mounted UCLASS_FS device. Signed-off-by: Simon Glass --- configs/sandbox_defconfig | 1 + fs/Kconfig | 7 +++++++ fs/Makefile | 1 + fs/mount-uclass.c | 18 ++++++++++++++++++ include/dm/uclass-id.h | 1 + include/vfs.h | 31 +++++++++++++++++++++++++++++++ 6 files changed, 59 insertions(+) create mode 100644 fs/mount-uclass.c create mode 100644 include/vfs.h diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index a08a838303b..76113ce711e 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -113,6 +113,7 @@ CONFIG_CMD_WDT=y CONFIG_CMD_WRITE=y CONFIG_CMD_AXI=y CONFIG_CMD_CAT=y +CONFIG_VFS=y CONFIG_CMD_SETEXPR_FMT=y CONFIG_CMD_XXD=y CONFIG_CMD_DHCP6=y diff --git a/fs/Kconfig b/fs/Kconfig index 13f7d084c09..6a55bd71adc 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -29,6 +29,13 @@ config FILE (UCLASS_FS). Note that a UCLASS_FILE device is only created when it is opened. +config VFS + bool "Virtual filesystem support" + depends on FS && DIR + help + Provides a virtual filesystem layer with a mount table and + unified path namespace. Includes a root filesystem at "/". + config FS_LEGACY def_bool y help diff --git a/fs/Makefile b/fs/Makefile index 969888a81da..ffb6bbce737 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_$(PHASE_)FS_LEGACY) += fs_legacy.o fs_internal.o obj-$(CONFIG_$(PHASE_)FS) += fs-uclass.o obj-$(CONFIG_$(PHASE_)DIR) += dir-uclass.o obj-$(CONFIG_$(PHASE_)FILE) += file-uclass.o +obj-$(CONFIG_$(PHASE_)VFS) += mount-uclass.o ifdef CONFIG_XPL_BUILD obj-$(CONFIG_SPL_FS_FAT) += fat/ diff --git a/fs/mount-uclass.c b/fs/mount-uclass.c new file mode 100644 index 00000000000..4cfc1d0ed02 --- /dev/null +++ b/fs/mount-uclass.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Virtual Filesystem layer + * + * Manages a mount table using UCLASS_MOUNT devices, providing unified + * path resolution across mounted filesystems. Inspired by the Linux VFS. + * + * Copyright 2026 Simon Glass + */ + +#include +#include + +UCLASS_DRIVER(mount) = { + .name = "mount", + .id = UCLASS_MOUNT, + .per_device_auto = sizeof(struct vfsmount), +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 6425ee88fa7..de79343904d 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -105,6 +105,7 @@ enum uclass_id { UCLASS_MISC, /* Miscellaneous device */ UCLASS_MMC, /* SD / MMC card or chip */ UCLASS_MOD_EXP, /* RSA Mod Exp device */ + UCLASS_MOUNT, /* Filesystem mount point */ UCLASS_MOUSE, /* Mouse, trackpad or other pointing device */ UCLASS_MTD, /* Memory Technology Device (MTD) device */ UCLASS_MUX, /* Multiplexer device */ diff --git a/include/vfs.h b/include/vfs.h new file mode 100644 index 00000000000..7c31caace25 --- /dev/null +++ b/include/vfs.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Virtual Filesystem layer for U-Boot + * + * Provides a unified path namespace with mount points, inspired by the + * Linux VFS but heavily cut down for U-Boot's needs. + * + * Copyright 2026 Simon Glass + */ + +#ifndef __VFS_H +#define __VFS_H + +struct udevice; + +/** + * struct vfsmount - A mount point in the VFS + * + * This is the UCLASS_MOUNT per-device uclass-private data. All mount + * devices are children of the VFS root FS device. Each links a mount-point + * directory to a UCLASS_FS device. + * + * @dir: UCLASS_DIR device that is the mount point + * @target: UCLASS_FS device that is mounted here + */ +struct vfsmount { + struct udevice *dir; + struct udevice *target; +}; + +#endif From patchwork Fri Apr 3 14:04:29 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2092 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=1775225163; bh=nJ0gLPipfkAp7qFifmriMpNkt27g9GhW9VaokzazqUE=; 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=D6w/VZPMMWFg6NA0/HMFbgXNHL7XFSe+h/PhUdNwGc8Ee1fa8mF5pmWw3QyCLcCXG tbIzvkginwGXKDTLYryQABGuXoSnwLqi8CQcUaGKxxk8QSBxuGDGwWqkLnBIP2SqEs mek64VXNukA9IZL/XETvl/7i7nyAmfUOIGTr9viLgdb9626zQ6tTjewdolMENCrSAr W6pzP3VtNxNTNZxh2REEKLIoStffB/AUggfD1gRJrCABbeXETOtyqfsfERjxW5Rlu1 Pc0i/5MJtSjoj4k9pbO0HWDxRt5fXhoquDroins4CQV4XgFRPCuLLeYqm5HIAC09Hv RnSFQQ9+0enlw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id CBE8B6A34F for ; Fri, 3 Apr 2026 08:06:03 -0600 (MDT) 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 tKyAlpAT_86e for ; Fri, 3 Apr 2026 08:06:03 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225159; bh=nJ0gLPipfkAp7qFifmriMpNkt27g9GhW9VaokzazqUE=; 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=PnwD91YcPSxfZJ+2T342IG0i7zhyIecaqn9mxtKkaqwAoiqP5/b/Nl2jGy2vD3N3x yHlGuDbG8pRbO1afqR3uOPP9xAFqITqzvsFODd9JTdG3NrkPEsMQvrmhg8VG6JkPc8 2E/GafDfC4PxBJf4Xg3mZvBSSVlEIPZpyL0x4qyWg0oeur/ncpnly0A/8DLZToe97K 6jXtYPfbWdZuK35pofQByDZ9i5/Tt+OtHB1keb9n+ppti97r2HmQf5wMXiwLKoo5c4 ZsaALz3smacyWtGOu21I7getXOY+PKd6Rv0zBaB9h4XFrNFQggUrR0dqeSH5UCjunO wNt/5SJdpri2w== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id BFE246A370 for ; Fri, 3 Apr 2026 08:05:59 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225157; bh=B0BLKCvYfHui3CEYa34GrEtcbziJJYld7T6aJOdhUP8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=PYsOCYUTn4PApvcFfTvEuA1TxMXfyXRyh0M/JEAfM2VUNWV4iy9nAdOfbd43POzDb zyGXN9+xRdaqKKOPK1AidLtFqxZxfZbtMPr/TAF6ArNLYA2lWpZETNMvgo/BvAfYlV ZP6PkPxxZVgqhHi6CtGsyhOPiz9DL4bxqXnLOuaffl957LA0A8xJoFG1atbnDsDyk+ Wa5xky53SKtey2MPJdO84UvcOVkOxu1FuVIoxCWUib/U71DBfREJlm1GAMmeOw3F9a yWjwYbsLxhkxj5oJfF5iPeFPLc1/30JkmMGeiuhbYQOytU4qmy/+oa24yV8mjJHpiq t54WzEJV8ds1Q== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 976FB6A350; Fri, 3 Apr 2026 08:05:57 -0600 (MDT) 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 3cTBOk0UBevS; Fri, 3 Apr 2026 08:05:57 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225155; bh=LJEiQHHZh4u28I13+2B2FzU54qMjb6W17ZkttXzSu3w=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Paai3yiQtK4Kf/sx6FSxPTQIVtuDvQmQVn9HXgI9gGbIEGw4jLB5Sa6GtfYgn9VxS 5cy6unAr44PKmeHt437QifBaMYWPzy/GxFZ7vhO0uVKEPH0gp9r/OoMTlXwh528cv0 JfAHs9Mw2oIqr6LTg5MLylJWzbaAEsABRb1lccHZmCMqZ/K9fiEJvD3iMCJFvWnm5J Z97vaffQE+ULB1YWb5vqRyBYFwDrkH5PN4rngoxDe0YnpOLjkvcBE9oKd3F59xmlkv /hcT4r9tnvNEyfEIogpMBLw/LH8bzyOzijjAJ+mNJ3vIlcb2Qz79Tr+YvA9lQKf4l7 F2UTWlSk3XNVA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 69B7B6A34F; Fri, 3 Apr 2026 08:05:55 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:29 -0600 Message-ID: <20260403140523.1998228-8-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 6BEZODSTB2STQYV363MF3S7V2J3XWJIJ X-Message-ID-Hash: 6BEZODSTB2STQYV363MF3S7V2J3XWJIJ 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 07/34] vfs: Add VFS root and init 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 the VFS root filesystem driver and initialisation. The rootfs is a minimal UCLASS_FS device that provides an empty root directory and creates mount-point directories on demand. The VFS root directory driver (in vfs_dir.c) lists mount-point directories as entries. It is initialised automatically via EVT_LAST_STAGE_INIT. Signed-off-by: Simon Glass --- fs/Kconfig | 1 + fs/Makefile | 2 +- fs/vfs.c | 124 +++++++++++++++++++++++++++++++ fs/vfs_dir.c | 88 ++++++++++++++++++++++ fs/vfs_internal.h | 22 ++++++ include/vfs.h | 18 +++++ test/dm/fs.c | 31 ++++++++ test/py/tests/test_event_dump.py | 1 + 8 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 fs/vfs.c create mode 100644 fs/vfs_dir.c create mode 100644 fs/vfs_internal.h diff --git a/fs/Kconfig b/fs/Kconfig index 6a55bd71adc..b173b5814ed 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -32,6 +32,7 @@ config FILE config VFS bool "Virtual filesystem support" depends on FS && DIR + select EVENT help Provides a virtual filesystem layer with a mount table and unified path namespace. Includes a root filesystem at "/". diff --git a/fs/Makefile b/fs/Makefile index ffb6bbce737..704ac6e4866 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_$(PHASE_)FS_LEGACY) += fs_legacy.o fs_internal.o obj-$(CONFIG_$(PHASE_)FS) += fs-uclass.o obj-$(CONFIG_$(PHASE_)DIR) += dir-uclass.o obj-$(CONFIG_$(PHASE_)FILE) += file-uclass.o -obj-$(CONFIG_$(PHASE_)VFS) += mount-uclass.o +obj-$(CONFIG_$(PHASE_)VFS) += mount-uclass.o vfs.o vfs_dir.o ifdef CONFIG_XPL_BUILD obj-$(CONFIG_SPL_FS_FAT) += fat/ diff --git a/fs/vfs.c b/fs/vfs.c new file mode 100644 index 00000000000..439c9760829 --- /dev/null +++ b/fs/vfs.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Virtual Filesystem layer + * + * Manages a mount tree using UCLASS_MOUNT devices as children of + * UCLASS_DIR devices, providing unified path resolution across mounted + * filesystems. Inspired by the Linux VFS. + * + * Copyright 2026 Simon Glass + */ + +#define LOG_CATEGORY UCLASS_MOUNT + +#include +#include +#include +#include +#include +#include "vfs_internal.h" +#include +#include +#include +#include + +/* VFS root filesystem - provides an empty root directory */ + +static int vfs_rootfs_mount(struct udevice *dev) +{ + struct fs_priv *uc_priv = dev_get_uclass_priv(dev); + + if (uc_priv->mounted) + return log_msg_ret("rfm", -EISCONN); + + uc_priv->mounted = true; + + return 0; +} + +static int vfs_rootfs_unmount(struct udevice *dev) +{ + return log_msg_ret("rfu", -EBUSY); +} + +static int vfs_rootfs_lookup_dir(struct udevice *dev, const char *path, + struct udevice **dirp) +{ + struct dir_uc_priv *uc_priv; + struct udevice *child, *dir; + int ret; + + /* Check for an existing dir with this path */ + device_foreach_child(child, dev) { + if (device_get_uclass_id(child) == UCLASS_DIR) { + uc_priv = dev_get_uclass_priv(child); + if (uc_priv->path && !strcmp(uc_priv->path, path)) { + *dirp = child; + return 0; + } + } + } + + /* Create a new dir */ + ret = dir_add_probe(dev, DM_DRIVER_GET(vfs_rootfs_dir), path, &dir); + if (ret) + return log_msg_ret("rfD", ret); + + *dirp = dir; + + return 0; +} + +/* Exported functions */ + +struct udevice *vfs_root(void) +{ + struct udevice *dev; + + if (uclass_find_device_by_name(UCLASS_FS, "vfs_rootfs", &dev)) + return NULL; + if (!device_active(dev)) + return NULL; + + return dev; +} + +int vfs_init(void) +{ + struct udevice *dev; + int ret; + + /* Already initialised? */ + dev = vfs_root(); + if (dev) + return 0; + + ret = device_bind_driver(dm_root(), "vfs_rootfs", "vfs_rootfs", &dev); + if (ret) + return log_msg_ret("vib", ret); + + ret = device_probe(dev); + if (ret) + return log_msg_ret("vip", ret); + + ret = fs_mount(dev); + if (ret) + return log_msg_ret("vim", ret); + + return 0; +} + +static const struct fs_ops vfs_rootfs_ops = { + .mount = vfs_rootfs_mount, + .unmount = vfs_rootfs_unmount, + .lookup_dir = vfs_rootfs_lookup_dir, +}; + +U_BOOT_DRIVER(vfs_rootfs) = { + .name = "vfs_rootfs", + .id = UCLASS_FS, + .ops = &vfs_rootfs_ops, + .priv_auto = sizeof(struct vfs_priv), +}; + +EVENT_SPY_SIMPLE(EVT_LAST_STAGE_INIT, vfs_init); diff --git a/fs/vfs_dir.c b/fs/vfs_dir.c new file mode 100644 index 00000000000..87a4193516e --- /dev/null +++ b/fs/vfs_dir.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * VFS root directory driver + * + * Provides the directory driver for the VFS rootfs. The root directory + * (path "") lists all mount-point directories as entries. Mount-point + * directories themselves have no entries since access crosses into the + * mounted filesystem. + * + * Copyright 2026 Simon Glass + */ + +#include +#include +#include +#include + +static int vfs_rootfs_dir_open(struct udevice *dev, + struct fs_dir_stream *strm) +{ + strm->offset = 0; + + return 0; +} + +/** + * vfs_rootfs_dir_read() - Read the next directory entry + * + * For the root directory (path ""), iterates through sibling DIR devices + * that have non-empty paths, returning each as a directory entry. For + * mount-point directories, returns -ENOENT immediately since their + * contents come from the mounted filesystem. + */ +static int vfs_rootfs_dir_read(struct udevice *dev, + struct fs_dir_stream *strm, + struct fs_dirent *dent) +{ + struct dir_uc_priv *dir_priv = dev_get_uclass_priv(dev); + struct udevice *parent, *child; + int idx = 0; + + /* Only the root dir has entries to list */ + if (*dir_priv->path) + return -ENOENT; + + /* Walk children of the FS device, skipping to the current offset */ + parent = dev_get_parent(dev); + device_foreach_child(child, parent) { + struct dir_uc_priv *uc_priv; + + if (device_get_uclass_id(child) != UCLASS_DIR) + continue; + + uc_priv = dev_get_uclass_priv(child); + if (!uc_priv->path || !*uc_priv->path) + continue; + + if (idx++ < strm->offset) + continue; + + strlcpy(dent->name, uc_priv->path, sizeof(dent->name)); + dent->type = FS_DT_DIR; + dent->size = 0; + strm->offset++; + + return 0; + } + + return -ENOENT; +} + +static int vfs_rootfs_dir_close(struct udevice *dev, + struct fs_dir_stream *strm) +{ + return 0; +} + +static struct dir_ops vfs_rootfs_dir_ops = { + .open = vfs_rootfs_dir_open, + .read = vfs_rootfs_dir_read, + .close = vfs_rootfs_dir_close, +}; + +U_BOOT_DRIVER(vfs_rootfs_dir) = { + .name = "vfs_rootfs_dir", + .id = UCLASS_DIR, + .ops = &vfs_rootfs_dir_ops, +}; diff --git a/fs/vfs_internal.h b/fs/vfs_internal.h new file mode 100644 index 00000000000..b7e6b37d55d --- /dev/null +++ b/fs/vfs_internal.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Internal VFS helpers - not part of the public API + * + * Copyright 2026 Simon Glass + */ + +#ifndef __VFS_INTERNAL_H +#define __VFS_INTERNAL_H + +struct udevice; + +/** + * struct vfs_priv - VFS root FS device driver-private data + * + * @mount_count: Counter for unique mount device names + */ +struct vfs_priv { + int mount_count; +}; + +#endif diff --git a/include/vfs.h b/include/vfs.h index 7c31caace25..1bf1762ab52 100644 --- a/include/vfs.h +++ b/include/vfs.h @@ -28,4 +28,22 @@ struct vfsmount { struct udevice *target; }; +/** + * vfs_init() - Initialise the VFS + * + * Creates the VFS root directory device. Normally called automatically + * via EVT_LAST_STAGE_INIT during boot. May also be called directly in + * tests after a DM tree reset. + * + * Return: 0 if OK, -ve on error + */ +int vfs_init(void); + +/** + * vfs_root() - Get the VFS root FS device + * + * Return: VFS root FS device, or NULL if not initialised + */ +struct udevice *vfs_root(void); + #endif diff --git a/test/dm/fs.c b/test/dm/fs.c index 6cb5f74db35..1d395031dee 100644 --- a/test/dm/fs.c +++ b/test/dm/fs.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -104,3 +105,33 @@ static int dm_test_fs_file(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_fs_file, UTF_SCAN_FDT); + +#if IS_ENABLED(CONFIG_VFS) +/* Test VFS init and root directory operations */ +static int dm_test_vfs_init(struct unit_test_state *uts) +{ + struct udevice *vfs, *dir; + struct fs_dir_stream *strm; + struct fs_dirent dent; + + ut_assertok(vfs_init()); + + vfs = vfs_root(); + ut_assertnonnull(vfs); + + /* Look up the root directory */ + ut_assertok(fs_lookup_dir(vfs, "", &dir)); + ut_assertnonnull(dir); + + /* open should succeed, read should return -ENOENT (root is empty) */ + ut_assertok(dir_open(dir, &strm)); + ut_asserteq(-ENOENT, dir_read(dir, strm, &dent)); + ut_assertok(dir_close(dir, strm)); + + /* rootfs cannot be unmounted */ + ut_asserteq(-EBUSY, fs_unmount(vfs)); + + return 0; +} +DM_TEST(dm_test_vfs_init, UTF_SCAN_FDT); +#endif diff --git a/test/py/tests/test_event_dump.py b/test/py/tests/test_event_dump.py index ff0da82196b..dad0132eeed 100644 --- a/test/py/tests/test_event_dump.py +++ b/test/py/tests/test_event_dump.py @@ -21,6 +21,7 @@ EVT_LAST_STAGE_INIT alloc_write_acpi_tables .*lib/acpi/acpi_table.c:.* EVT_LAST_STAGE_INIT efi_block_device_create .*lib/efi_driver/efi_block_device.c:.* EVT_LAST_STAGE_INIT install_smbios_table .*lib/efi_loader/efi_smbios.c:.* EVT_LAST_STAGE_INIT last_stage_init .*arch/sandbox/cpu/start.c:.* +EVT_LAST_STAGE_INIT vfs_init .*fs/vfs.c:.* EVT_MISC_INIT_F sandbox_early_getopt_check .*arch/sandbox/cpu/start.c:.* EVT_SETTINGS_R sandbox_settings .*board/sandbox/sandbox.c:.* EVT_TEST h_adder_simple .*test/common/event.c:''' From patchwork Fri Apr 3 14:04:30 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2093 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=1775225164; bh=ZWRtrY2l6xsCEQUlhhCuCAS4oCEZ1eMtLYBA9Fetxtc=; 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=OhJyCX2i22x5+HM8RC5ad1ief5hOrS4pQBY65GzufbLgB/YzftTTCNyfC3Q1RbTmt 3oCRlyuyj1Jxjayr8GtaTUimkLTvcf3FERJTJMoRnkck2fZeAe46Y28ftbELVtE1P6 4cwhZWZtNeIVm1E8Z76WT/UzGfdE8yKMM+k6AH5L/3ouEG7+cEofC15qZjpTjVea04 jbVd1IKirIseUJRTHfRyWZ9aPNji/BcMXgXn+kgMYc7n65tepX7VDx/1NhRiwieLG7 qXdXd+hzkAUvf7lnLdCNF1D8Kc/AFaga4F7g7eLKuowltDD8Mo/AbOUoTP4d19etxz fhcu83oaerpKA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 6BE436A347 for ; Fri, 3 Apr 2026 08:06:04 -0600 (MDT) 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 nSc_20Tjhw37 for ; Fri, 3 Apr 2026 08:06:04 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225160; bh=ZWRtrY2l6xsCEQUlhhCuCAS4oCEZ1eMtLYBA9Fetxtc=; 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=FrM/wFoXXyMy/26Ac3VwXU9nWxy85MCD18ddtxReUGFcbaLMRDtr4BB1Oi/K1xGvm fzIA9ebctaLAHmPbFfZmEJCWbGYqE96j6gzH6Z/5YUKLhwaSnu1jCbV3Qg870tnmBI P/99ev6JIz5j6FZSuOzCJX0SiPYwgy1nf7oASaFudOli4vSxjMsB7fULhoEW+Ogrg4 E6b++dol419ly7HfXSH1A29rf4EXUdjRsaDVC49qi9klPHMin8Z2D4Xa+Qofr/gBvT Nf2Wp9R4NyQ08yl3S0tCxwhTYysDRGQEN3LCoIizJNAhDu64kCGxllCG+83lRXKUDE r1PhCnFuRgLXQ== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id D73B76A35D for ; Fri, 3 Apr 2026 08:06:00 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225157; bh=I5zr9bw0KA9nP4iZU1hSQSqtlnX7M3lvnTPVhabKZDU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kjJIWP9Htz6uipoy50nQ4ruJNBgz/xjtOXhczTxon15xSq+Z7Pt1CvnNsPL56YFWe Kok2xedED3vWcaBKUGJIFsV4gqAzghky4vAtbuQI6MZTgxh7GDO/vOeptpWx+M+5Gd NxmsdHXJKSBCLiOBssCpIC6kV71ZabKLblvT1uGFWNaG5TBe4xgt/T+C1ikf3Kvf6p goqtNzoCpKnEfTsWyVb3SvDYtih9/Q7lhYc32cVepoaWIE3pYIROtegLY/gACTv1oL aSBiNQPyhtdGikSoY6Y55NmtptBYORyuRP/a9x5awpG8Y3PdfQkzjMs2hWFjV2DsYf nBK/XArQgoc2w== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id B0F196A34C; Fri, 3 Apr 2026 08:05:57 -0600 (MDT) 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 Uh51TVW_VEUp; Fri, 3 Apr 2026 08:05:57 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225156; bh=RwSZ1I908Kmmuz+342qRnyH2Z2hTq3YabUNzFzTBvNE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kJRvkFmmNmT2CbFqHd6DnIIkyApzTXRyHcULPD32trtYwKU09Uo1vW+fL6VU5Ka13 NEd3fBoiIyZjLapnDP/sds9e1r0Xof+rRtu/+KrYIJ7pJ2EeJkWU/AAWwF43cF0z3j CptWfXzOa+HUXr7FxTjmjCNHJ3JnKPn9OUE9ZNyHhteBGB/OKPFq9lpvZLcs9oVy9v 6gRaM0KIj4dC1peHWO1i0HCj+gI/q/RKsGspT1VlapSU1Sn7w+tnAVCajVvQJCJtu7 nlgWbD8nMtL2ZPyi4KFexO610zQj3NSuw9ushh7q9k6wr2uLqJFBURCepd/bMLx4Ni 2OEMNESuD3BYg== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 5886067EE6; Fri, 3 Apr 2026 08:05:56 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:30 -0600 Message-ID: <20260403140523.1998228-9-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: Q4TM7S3TJXR6GRYR5WZHPMNKVSW564PL X-Message-ID-Hash: Q4TM7S3TJXR6GRYR5WZHPMNKVSW564PL 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 08/34] vfs: Add mount table and path resolution 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 the ability to mount filesystems at directories and resolve paths through the mount tree. Mounts are tracked as UCLASS_MOUNT devices, each linking a mount-point directory to a UCLASS_FS device. Path resolution walks each component, looking it up as a directory in the current filesystem and checking whether it is a mount point. This allows nested mounts and returns the remaining subpath within the matched filesystem. Signed-off-by: Simon Glass --- fs/Makefile | 2 +- fs/fs_mount.c | 69 +++++++++++++ fs/vfs.c | 253 ++++++++++++++++++++++++++++++++++++++++++++++ fs/vfs_internal.h | 22 ++++ include/vfs.h | 71 +++++++++++++ test/dm/fs.c | 113 +++++++++++++++++++++ 6 files changed, 529 insertions(+), 1 deletion(-) create mode 100644 fs/fs_mount.c diff --git a/fs/Makefile b/fs/Makefile index 704ac6e4866..345a4627241 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_$(PHASE_)FS_LEGACY) += fs_legacy.o fs_internal.o obj-$(CONFIG_$(PHASE_)FS) += fs-uclass.o obj-$(CONFIG_$(PHASE_)DIR) += dir-uclass.o obj-$(CONFIG_$(PHASE_)FILE) += file-uclass.o -obj-$(CONFIG_$(PHASE_)VFS) += mount-uclass.o vfs.o vfs_dir.o +obj-$(CONFIG_$(PHASE_)VFS) += fs_mount.o mount-uclass.o vfs.o vfs_dir.o ifdef CONFIG_XPL_BUILD obj-$(CONFIG_SPL_FS_FAT) += fat/ diff --git a/fs/fs_mount.c b/fs/fs_mount.c new file mode 100644 index 00000000000..11e7302eb18 --- /dev/null +++ b/fs/fs_mount.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Mount device driver for the VFS layer + * + * Copyright 2026 Simon Glass + */ + +#include +#include +#include +#include "vfs_internal.h" +#include +#include + +int fs_mount_init(struct udevice *vfs, struct udevice *dir, + struct udevice *fsdev) +{ + struct vfs_priv *priv = dev_get_priv(vfs); + struct vfsmount *mnt; + char dev_name[30]; + struct udevice *dev; + char *str; + int ret; + + snprintf(dev_name, sizeof(dev_name), "mount.%d", priv->mount_count); + str = strdup(dev_name); + if (!str) + return log_msg_ret("fms", -ENOMEM); + + ret = device_bind_driver(vfs, "mount", str, &dev); + if (ret) { + free(str); + return log_msg_ret("fmb", ret); + } + device_set_name_alloced(dev); + + ret = device_probe(dev); + if (ret) { + device_unbind(dev); + return log_msg_ret("fmp", ret); + } + + mnt = dev_get_uclass_priv(dev); + mnt->dir = dir; + mnt->target = fsdev; + priv->mount_count++; + + return 0; +} + +int fs_mount_uninit(struct udevice *mnt_dev) +{ + int ret; + + ret = device_remove(mnt_dev, DM_REMOVE_NORMAL); + if (ret) + return log_msg_ret("fdr", ret); + + ret = device_unbind(mnt_dev); + if (ret) + return log_msg_ret("fdb", ret); + + return 0; +} + +U_BOOT_DRIVER(mount) = { + .name = "mount", + .id = UCLASS_MOUNT, +}; diff --git a/fs/vfs.c b/fs/vfs.c index 439c9760829..c42e60b4dc6 100644 --- a/fs/vfs.c +++ b/fs/vfs.c @@ -14,7 +14,10 @@ #include #include #include +#include #include +#include +#include #include #include "vfs_internal.h" #include @@ -22,6 +25,150 @@ #include #include +#define vfs_foreach_mount(mnt, pos) \ + for (uclass_first_device(UCLASS_MOUNT, &(pos)); \ + (pos) && ((mnt) = dev_get_uclass_priv(pos)); \ + uclass_next_device(&(pos))) + +/** + * find_mount() - Check whether a directory is a mount point + * + * @dir: UCLASS_DIR device to check + * @mntp: Returns the UCLASS_MOUNT device if found + * Return: 0 if found, -ENOENT if not + */ +static int find_mount(struct udevice *dir, struct udevice **mntp) +{ + struct vfsmount *mnt; + struct udevice *dev; + + vfs_foreach_mount(mnt, dev) { + if (mnt->dir == dir) { + *mntp = dev; + return 0; + } + } + + return -ENOENT; +} + +/** + * find_mount_by_target() - Find the mount for a given FS device + * + * @fsdev: UCLASS_FS device to search for + * @mntp: Returns the UCLASS_MOUNT device if found + * Return: 0 if found, -ENOENT if not + */ +static int find_mount_by_target(struct udevice *fsdev, struct udevice **mntp) +{ + struct vfsmount *mnt; + struct udevice *dev; + + vfs_foreach_mount(mnt, dev) { + if (mnt->target == fsdev) { + *mntp = dev; + return 0; + } + } + + return -ENOENT; +} + +/** + * walk_path() - Walk a path following mount points + * + * For each path component, looks up the directory in the current + * filesystem and checks if it is a mount point. Stops when a component + * cannot be looked up or is not a mount point. + * + * @path: Path to walk (without leading '/') + * @start: Starting FS device + * @mntp: Returns the deepest mount found, or NULL if none + * @remainp: Returns pointer to the remaining unresolved path + * Return: The FS after the deepest mount crossed (same as @start if none) + */ +static struct udevice *walk_path(const char *path, struct udevice *start, + struct udevice **mntp, const char **remainp) +{ + struct udevice *best = NULL, *cur_fs = start; + const char *best_remain = path; + const char *p = path; + + while (*p) { + char component[FS_DIRENT_NAME_LEN]; + struct udevice *comp_dir, *mnt_dev; + struct vfsmount *mnt; + const char *slash; + int len; + + slash = strchr(p, '/'); + len = slash ? slash - p : strlen(p); + if (len >= sizeof(component)) + break; + + memcpy(component, p, len); + component[len] = '\0'; + + if (fs_lookup_dir(cur_fs, component, &comp_dir)) + break; + + if (find_mount(comp_dir, &mnt_dev)) + break; + + /* Found a mount - record it and cross into the FS */ + best = mnt_dev; + p += len; + if (*p == '/') + p++; + best_remain = p; + + mnt = dev_get_uclass_priv(mnt_dev); + cur_fs = mnt->target; + } + + *mntp = best; + *remainp = best_remain; + + return cur_fs; +} + +/** + * vfs_mount_path() - Build the full path for a mount device + * + * Walks up the device tree to reconstruct the absolute path, using + * dir_uc_priv->path from each mount's directory to get the component name. + * + * @mnt_dev: UCLASS_MOUNT device + * @buf: Buffer to write path into + * @size: Size of buffer + * Return: 0 if OK, -ve on error + */ +static int vfs_mount_path(struct udevice *mnt_dev, char *buf, int size) +{ + struct vfsmount *mnt = dev_get_uclass_priv(mnt_dev); + struct dir_uc_priv *uc_priv = dev_get_uclass_priv(mnt->dir); + struct udevice *parent_fs = dev_get_parent(mnt->dir); + + if (parent_fs == vfs_root()) { + snprintf(buf, size, "/%s", uc_priv->path); + } else { + char parent_path[FILE_MAX_PATH_LEN]; + struct udevice *pdev; + int ret; + + ret = find_mount_by_target(parent_fs, &pdev); + if (ret) + return ret; + + ret = vfs_mount_path(pdev, parent_path, sizeof(parent_path)); + if (ret) + return ret; + snprintf(buf, size, "%s/%s", parent_path, uc_priv->path); + } + + return 0; +} + /* VFS root filesystem - provides an empty root directory */ static int vfs_rootfs_mount(struct udevice *dev) @@ -71,6 +218,112 @@ static int vfs_rootfs_lookup_dir(struct udevice *dev, const char *path, /* Exported functions */ +int vfs_find_mount(struct udevice *vfs, const char *path, struct udevice **mntp, + const char **subpathp) +{ + struct udevice *best; + const char *p; + + p = path; + if (*p == '/') + p++; + + walk_path(p, vfs, &best, subpathp); + + if (!best) { + if (!*p) { + *mntp = NULL; + return 0; + } + return log_msg_ret("vfn", -ENOENT); + } + + *mntp = best; + + return 0; +} + +int vfs_resolve(struct udevice *vfs, const char *path, + struct udevice **dirp) +{ + struct udevice *cur_fs, *best; + const char *remain; + + if (!path || *path != '/') + return log_msg_ret("vrp", -EINVAL); + + cur_fs = walk_path(path + 1, vfs, &best, &remain); + + /* Remaining path must be at most one component (the target dir) */ + if (strchr(remain, '/')) + return log_msg_ret("vrm", -ENOENT); + + return fs_lookup_dir(cur_fs, remain, dirp); +} + +int vfs_mount(struct udevice *vfs, struct udevice *dir, struct udevice *fsdev) +{ + int ret; + + ret = fs_mount(fsdev); + if (ret && ret != -EISCONN) + return log_msg_ret("vmm", ret); + + ret = fs_mount_init(vfs, dir, fsdev); + if (ret) { + fs_unmount(fsdev); + return log_msg_ret("vmc", ret); + } + + return 0; +} + +int vfs_umount(struct udevice *mnt_dev) +{ + struct vfsmount *mnt = dev_get_uclass_priv(mnt_dev); + int ret; + + ret = fs_unmount(mnt->target); + if (ret && ret != -ENOTCONN) + return log_msg_ret("vuu", ret); + + ret = fs_mount_uninit(mnt_dev); + if (ret) + return log_msg_ret("vud", ret); + + return 0; +} + +int vfs_umount_path(struct udevice *vfs, const char *path) +{ + struct udevice *mnt_dev; + const char *subpath; + int ret; + + ret = vfs_find_mount(vfs, path, &mnt_dev, &subpath); + if (ret) + return log_msg_ret("vuf", ret); + + /* Make sure the entire path was consumed (exact match) */ + if (!mnt_dev || *subpath) + return log_msg_ret("vup", -ENOENT); + + return vfs_umount(mnt_dev); +} + +void vfs_print_mounts(void) +{ + struct vfsmount *mnt; + struct udevice *dev; + + vfs_foreach_mount(mnt, dev) { + char path[FILE_MAX_PATH_LEN]; + + if (!vfs_mount_path(dev, path, sizeof(path))) + printf("%-20s %s\n", path, mnt->target->name); + } +} + struct udevice *vfs_root(void) { struct udevice *dev; diff --git a/fs/vfs_internal.h b/fs/vfs_internal.h index b7e6b37d55d..ac7f756e24f 100644 --- a/fs/vfs_internal.h +++ b/fs/vfs_internal.h @@ -19,4 +19,26 @@ struct vfs_priv { int mount_count; }; +/** + * fs_mount_init() - Create a mount device + * + * Binds and probes a UCLASS_MOUNT device as a child of @vfs, linking + * @dir to @fsdev. + * + * @vfs: VFS root FS device + * @dir: UCLASS_DIR device that is the mount point + * @fsdev: UCLASS_FS device to mount + * Return: 0 if OK, -ve on error + */ +int fs_mount_init(struct udevice *vfs, struct udevice *dir, + struct udevice *fsdev); + +/** + * fs_mount_uninit() - Remove and unbind a mount device + * + * @mnt_dev: UCLASS_MOUNT device to destroy + * Return: 0 if OK, -ve on error + */ +int fs_mount_uninit(struct udevice *mnt_dev); + #endif diff --git a/include/vfs.h b/include/vfs.h index 1bf1762ab52..d7b6449b088 100644 --- a/include/vfs.h +++ b/include/vfs.h @@ -46,4 +46,75 @@ int vfs_init(void); */ struct udevice *vfs_root(void); +/** + * vfs_resolve() - Resolve a path to a directory + * + * Walks the path, following mount points along the way. For each + * component, looks up the directory in the current filesystem. If the + * directory does not exist (e.g. in the VFS rootfs), it is created. + * + * For "/host", looks up (or creates) "host" in the VFS rootfs. + * For "/mnt/data", follows the mount at /mnt, then looks up "data" + * in the mounted filesystem. + * + * @vfs: VFS root FS device + * @path: Absolute path (must start with '/') + * @dirp: Returns the UCLASS_DIR device for the final component + * Return: 0 if OK, -ve on error + */ +int vfs_resolve(struct udevice *vfs, const char *path, + struct udevice **dirp); + +/** + * vfs_mount() - Mount a filesystem at a directory + * + * Creates a UCLASS_MOUNT device linking @dir to @fsdev. + * + * @vfs: VFS root FS device + * @dir: UCLASS_DIR device for the mount point + * @fsdev: UCLASS_FS device to mount + * Return: 0 if OK, -ve on error + */ +int vfs_mount(struct udevice *vfs, struct udevice *dir, struct udevice *fsdev); + +/** + * vfs_umount() - Unmount a filesystem + * + * @mnt_dev: UCLASS_MOUNT device to unmount + * Return: 0 if OK, -ve on error + */ +int vfs_umount(struct udevice *mnt_dev); + +/** + * vfs_umount_path() - Unmount the filesystem at a path + * + * @vfs: VFS root FS device + * @path: Mount point to remove + * Return: 0 if OK, -ENOENT if not mounted, other -ve on error + */ +int vfs_umount_path(struct udevice *vfs, const char *path); + +/** + * vfs_find_mount() - Find the mount covering a path + * + * Walks the mount tree from the VFS root, following mount points for + * each path component. Returns the deepest mount and the remaining + * subpath. + * + * @vfs: VFS root FS device + * @path: Absolute path to resolve + * @mntp: Returns the UCLASS_MOUNT device + * @subpathp: Returns pointer into @path for the remaining path within the + * mounted filesystem + * Return: 0 if OK (with @mntp set to NULL if path is the VFS root), + * -ENOENT if no mount covers this path + */ +int vfs_find_mount(struct udevice *vfs, const char *path, + struct udevice **mntp, const char **subpathp); + +/** + * vfs_print_mounts() - Print all current mounts + */ +void vfs_print_mounts(void); + #endif diff --git a/test/dm/fs.c b/test/dm/fs.c index 1d395031dee..f31a11e90cb 100644 --- a/test/dm/fs.c +++ b/test/dm/fs.c @@ -5,6 +5,7 @@ * Copyright 2025 Simon Glass */ +#include #include #include #include @@ -128,10 +129,122 @@ static int dm_test_vfs_init(struct unit_test_state *uts) ut_asserteq(-ENOENT, dir_read(dir, strm, &dent)); ut_assertok(dir_close(dir, strm)); + /* vfs_resolve("/") should return the root dir */ + ut_assertok(vfs_resolve(vfs, "/", &dir)); + ut_assertnonnull(dir); + + /* vfs_resolve with bad paths should fail */ + ut_asserteq(-EINVAL, vfs_resolve(vfs, NULL, &dir)); + ut_asserteq(-EINVAL, vfs_resolve(vfs, "no_slash", &dir)); + /* rootfs cannot be unmounted */ ut_asserteq(-EBUSY, fs_unmount(vfs)); return 0; } DM_TEST(dm_test_vfs_init, UTF_SCAN_FDT); + +/* Test that the root directory lists mount points */ +static int dm_test_vfs_dir(struct unit_test_state *uts) +{ + struct udevice *vfs, *fsdev, *dir, *root_dir; + struct fs_dir_stream *strm; + struct fs_dirent dent; + + ut_assertok(vfs_init()); + vfs = vfs_root(); + ut_assertnonnull(vfs); + + /* Root dir should be empty before any mounts */ + ut_assertok(fs_lookup_dir(vfs, "", &root_dir)); + ut_assertok(dir_open(root_dir, &strm)); + ut_asserteq(-ENOENT, dir_read(root_dir, strm, &dent)); + ut_assertok(dir_close(root_dir, strm)); + + /* Mount the sandbox FS at /host */ + ut_assertok(uclass_get_device_by_name(UCLASS_FS, "hostfs", &fsdev)); + ut_assertok(vfs_resolve(vfs, "/host", &dir)); + ut_assertok(vfs_mount(vfs, dir, fsdev)); + + /* Root dir should now list "host" */ + ut_assertok(fs_lookup_dir(vfs, "", &root_dir)); + ut_assertok(dir_open(root_dir, &strm)); + ut_assertok(dir_read(root_dir, strm, &dent)); + ut_asserteq_str("host", dent.name); + ut_asserteq(FS_DT_DIR, dent.type); + ut_asserteq(-ENOENT, dir_read(root_dir, strm, &dent)); + ut_assertok(dir_close(root_dir, strm)); + + ut_assertok(vfs_umount_path(vfs, "/host")); + + return 0; +} +DM_TEST(dm_test_vfs_dir, UTF_SCAN_FDT); + +/* Test basic VFS mount, find_mount, ls and umount */ +static int dm_test_vfs_mount(struct unit_test_state *uts) +{ + struct udevice *vfs, *fsdev, *dir, *mnt; + const char *subpath; + + ut_assertok(vfs_init()); + vfs = vfs_root(); + ut_assertnonnull(vfs); + + /* Find the sandbox FS (not the vfs_rootfs) */ + ut_assertok(uclass_get_device_by_name(UCLASS_FS, "hostfs", &fsdev)); + + /* Resolve /host to a mount-point DIR */ + ut_assertok(vfs_resolve(vfs, "/host", &dir)); + + /* Mount the sandbox FS at /host */ + ut_assertok(vfs_mount(vfs, dir, fsdev)); + + /* Mounting same FS at another path is OK (-EISCONN ignored) */ + ut_assertok(vfs_resolve(vfs, "/other", &dir)); + ut_assertok(vfs_mount(vfs, dir, fsdev)); + ut_assertok(vfs_umount_path(vfs, "/other")); + + /* vfs_print_mounts() should show the /host mount */ + console_record_reset_enable(); + vfs_print_mounts(); + ut_assert_nextlinen("/host"); + ut_assert_console_end(); + + /* find_mount should resolve /host exactly */ + ut_assertok(vfs_find_mount(vfs, "/host", &mnt, &subpath)); + ut_asserteq_str("", subpath); + + /* find_mount should strip mount prefix from subpath */ + ut_assertok(vfs_find_mount(vfs, "/host/some/path", &mnt, &subpath)); + ut_asserteq_str("some/path", subpath); + + /* find_mount should handle trailing component */ + ut_assertok(vfs_find_mount(vfs, "/host/file.txt", &mnt, &subpath)); + ut_asserteq_str("file.txt", subpath); + + /* find_mount should fail for unmounted path */ + ut_asserteq(-ENOENT, vfs_find_mount(vfs, "/nowhere", &mnt, &subpath)); + + /* find_mount with partial prefix should not match */ + ut_asserteq(-ENOENT, vfs_find_mount(vfs, "/hostal", &mnt, &subpath)); + + /* vfs_resolve with intermediate non-mount should fail */ + ut_asserteq(-ENOENT, vfs_resolve(vfs, "/bogus/sub", &dir)); + + /* Unmount */ + ut_assertok(vfs_umount_path(vfs, "/host")); + + /* Should not be mounted any more */ + ut_asserteq(-ENOENT, vfs_find_mount(vfs, "/host", &mnt, &subpath)); + + /* Double umount should fail */ + ut_asserteq(-ENOENT, vfs_umount_path(vfs, "/host")); + + /* Umount of never-mounted path should fail */ + ut_asserteq(-ENOENT, vfs_umount_path(vfs, "/bogus")); + + return 0; +} +DM_TEST(dm_test_vfs_mount, UTF_SCAN_FDT); #endif From patchwork Fri Apr 3 14:04:31 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2094 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=1775225169; bh=bQF02IT9SoNd/U0uQC+k5TizGC1GRReClz+WK3OjzZg=; 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=RQuRXPZZE+2FTG1Pcun01PG36klh0LNUDCftnSZFbnVAXzmp7k0TmsO0php46pfxk Ue0bPgo6fX95BP/XtSFG5KdgH/z/dOIE+9Wfixw92tWj0LVBKlKUfKV4/oO0qX4deI NG1Io/NS3yInYAtVk+vZZSrJp6mBwaGVe/XRf6X+uWmbZNY8Ht5aVN6T2jxKuIdo3+ tiOKpJYPByWoiaO9OQC8iiYqivbKDJqeMM6c+2Kdvvl3gcuquzhTbqzLZRDrp31ktL SAIdVRBai/+0XPXI+5xH/51e2nPkfs+QYtBA8yeoXkLYM1vMCkXV0MXKs8W9bMXNAn DpTQkJK/HsVeQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 3AB966A369 for ; Fri, 3 Apr 2026 08:06:09 -0600 (MDT) 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 C5g2obCNk3Jq for ; Fri, 3 Apr 2026 08:06:09 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225165; bh=bQF02IT9SoNd/U0uQC+k5TizGC1GRReClz+WK3OjzZg=; 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=BuC0YxRzLNXUMO8ksOrJM+22XB2TR2iODzlvbWaqXgEcowXaixDjOHFNCl5dJPf9U uEnOoWoyd1Rq3y4+UtTJYblTwbXm5ErOH7jTUanNQvMj2ZpDA5McnMs26n0Os5Q28l e2trU8WwS3LiAGpFciDVf4+L+kv+mRAHHYLwbP5sxFNYxmVz1acGH8n1WCC05lht1+ i7twV+H3oC2H+DMruJJOhIrniBWE3Z0U2k/i1OrhYE1ES0bmHGAe9uAj4vnuDlSMvK 9oA5pR7H3NSwmPap8dP7OxZwmU5x9+YFlbhDQ+VcVdkrPf+2vakROghw2eGoEg0Z3u mbCzTiCT1uNUA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 7249E6A351 for ; Fri, 3 Apr 2026 08:06:05 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225163; bh=+AkuIzUPpilvKRgT6qBl/w/z5/lsOBpqHFKRURh4Gnk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=t+xENgP6MKgiMRFrboPtYl6sZW+hwuYcXGTRcQFAFiyjNZUzMW/hUW1yW+GHO+b86 v4lmsL4HP2efVnyxI+QfsO24k0kvx0pmdr6i1Hsbz7Kjzv9Juwra8qNH+slVwBhZvl iPGHRDL1OV6u98yLmZad7zsW/qvxHHL08x9FJ6IZICVkNwEPsw/Mui8GYfrAsah8/l 3YtjnRxTILoLyM9ZD6nJkpQ1vqkYwEtWxLExRITfjxeV8l80/FpNcfCgat6iX+g9tr Eo4/669KjpDopCy8AjTOovaS95HyplV2FLrJj1TB5jdfb4hjMgykUf3OthKHyHHa7a IWMprZFkCOe/A== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 413336A358; Fri, 3 Apr 2026 08:06:03 -0600 (MDT) 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 xdJT-uEkcST4; Fri, 3 Apr 2026 08:06:03 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225157; bh=TozoMReZCm4TZrlQiYDx1p4IbsJjNmtd3O6ZHIVcN28=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=PCnMSz0c4qSaith2qzpERm61yWfogi3bjayRBlCa6NTnAUTFugxQqzpw/lV3JDGXw 5cR3IqcgBDFznHNJRB4RMAw6Ea+k9fslnmo7lADG5tJ9tSjoDqE3frWC2uhZYwYQhZ o2r57wvRnzRF1CCooRTH9dyjbA3vQViYIR19rfKrDZexfqzAUfcchirvQppjGS8rFw nyu925yO4mTc7GMzo9VOvNc96D4jXeSWWruNpCZXOMdO5Rmm3wXkqdR/CSYHF+CO/8 HlEHf++7rBCoBaB05T/vHwqPbIUWRwJr2+QshJUIqDra5DHBuyojDhpDLD9+oTxcJB TTjTZHwjldwMg== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id AD2E96A34F; Fri, 3 Apr 2026 08:05:57 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:31 -0600 Message-ID: <20260403140523.1998228-10-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: KAMJ33ZN7BJ4X3OWXHX4MHNGOVK3MTCB X-Message-ID-Hash: KAMJ33ZN7BJ4X3OWXHX4MHNGOVK3MTCB 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 09/34] vfs: cmd: Move legacy filesystem commands to cmd/fs_legacy.c 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 The legacy filesystem commands (ls, load, save, size, mkdir, rm, ln, mv, fstype, fstypes) use the interface/dev:part syntax to access filesystems directly. Move them to cmd/fs_legacy.c and add a new CONFIG_CMD_FS_LEGACY (default y) to control their compilation. This frees up cmd/fs.c for new VFS-based implementations that use absolute paths through the virtual filesystem layer. CONFIG_CMD_FS_GENERIC selects CONFIG_CMD_FS_LEGACY for backward compatibility. Signed-off-by: Simon Glass --- cmd/Kconfig | 14 +++++++++++++- cmd/Makefile | 2 +- cmd/{fs.c => fs_legacy.c} | 0 3 files changed, 14 insertions(+), 2 deletions(-) rename cmd/{fs.c => fs_legacy.c} (100%) diff --git a/cmd/Kconfig b/cmd/Kconfig index d56293ea1be..95ccb9fca1a 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -2973,7 +2973,19 @@ config CMD_FS_GENERIC bool "filesystem commands" help Enables filesystem commands (e.g. load, ls) that work for multiple - fs types. + fs types. When VFS is available these use absolute paths through + the virtual filesystem. Otherwise the legacy interface/dev:part + commands are used. + +config CMD_FS_LEGACY + bool "Legacy filesystem commands (ls, load, save, ...)" + depends on CMD_FS_GENERIC && !VFS + default y + help + Enables the legacy filesystem commands that use the + interface/dev:part syntax to access filesystems directly. These + include load, save, ls, size, mkdir, rm, ln, mv, fstype and + fstypes. config CMD_FS_UUID bool "fsuuid command" diff --git a/cmd/Makefile b/cmd/Makefile index 82dcebf92c9..4d1b3eab90c 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -93,7 +93,7 @@ obj-$(CONFIG_CMD_FLASH) += flash.o obj-$(CONFIG_CMD_FPGA) += fpga.o obj-$(CONFIG_CMD_LUKS) += luks.o obj-$(CONFIG_CMD_FPGAD) += fpgad.o -obj-$(CONFIG_CMD_FS_GENERIC) += fs.o +obj-$(CONFIG_CMD_FS_LEGACY) += fs_legacy.o obj-$(CONFIG_CMD_FUSE) += fuse.o obj-$(CONFIG_CMD_FWU_METADATA) += fwu_mdata.o obj-$(CONFIG_CMD_GETTIME) += gettime.o diff --git a/cmd/fs.c b/cmd/fs_legacy.c similarity index 100% rename from cmd/fs.c rename to cmd/fs_legacy.c From patchwork Fri Apr 3 14:04:32 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2095 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=1775225169; bh=acXa9AATyJ/haf4WTu1IvEateTAlHmle6/pfEfVQB0s=; 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=XrNG64VmGYEENhLYD97RPCOxhdWh1ETZiAuiL9Ojwf94fkOj9ctHf4j6IZTOSkw6b eLVYkppj9p0oPIzcabUgwv1F4PHDwDeuGl2XvNDFryftw+j+QwfpxO5dyC5XF+f2Ma vHjuboGxQRClDvUtTnTFt+tTDIHHeF8bhTCmd6goCn7jXVbC7OyqFioQSuAEO3rv4P xIIvTJPMQ+r4I6IGwdtZ4N74kJYWE/SYA9a5ShGQxeqmHsHa/PnYbb936b2wV/XNKR 0gA1pS+/w/ZGiD+d4wt3lF6f39bLKdGKLDgyxgjz8ZNOuuL+ZRV2FnKhMNsms8LkF5 jOIgO5lj7/1eA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id A6D2F6A35E for ; Fri, 3 Apr 2026 08:06:09 -0600 (MDT) 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 GCBmN_YJneka for ; Fri, 3 Apr 2026 08:06:09 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225166; bh=acXa9AATyJ/haf4WTu1IvEateTAlHmle6/pfEfVQB0s=; 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=kxjRCmK6fyW+QDdAV3KjfvozBjF37LFFAd6GeQbK2QlaUDC4DLSoopDPXsRI7Ob4y UFgzc3m4Jbo53Nu7OUxxrSshD4yVMO0XU0l+i/LynvWlPgreLPJwss6WLpJB0Sjbt5 M5Nt9e5n37mLtDZ4M3PuhrISZx5YXhbF7OKuAnnEEYM2v8ieBo05pgieoOfvhF0Fxg qEa/E413ccpO9d4FOnbmBl18Ltf3FCuCyQJEDx4rfmDmc+AaXqxLSIZgBkebEtmPDB Ne71BoBADWpKyWuCkdDqjuKnVjjJhfqvQwJeJQ3LAS3o7FCWIgGUCTon/fIMzQzMjI FfMQgobV4QLLA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 8B2E76A35C for ; Fri, 3 Apr 2026 08:06:06 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225163; bh=tKnPiUjNCs7Pqnr6bM6hFneuBUuVf1EqFUS/S8FfieM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=r5Sxe7+iM8dTJN0b2JRATM4SfCEWN3WmnAF28DxcTCQQU0F8H14ImFJ8pjvWB7rie MGPfE/TrQu4z8y1SMPnXXUe9tPmgqAVLkNFhrROAsMY3JfyNhn2BUujel0TMZ2TcxX t6HVErzfYZ8lz0e/fNGOg65sdTZ0Qh6uozb7S/ewIg4bTOhfv89plkx3cB7bBNUi5B bVM4c0HcfLt02aMEjQzA5R8waBvBWCWJe6LL0m+UxYUsUFzjcNozlFTM+L7GHPgkuy H8CRylrUQgh51tf2zjgaoonqQK+19sjQJMCOm81NFCNbbDdoLH/1F+DHpxA1VN9DQ9 jlD98Wl1bXFCA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 4BBD46A34F; Fri, 3 Apr 2026 08:06:03 -0600 (MDT) 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 Jemaf5hLwpVz; Fri, 3 Apr 2026 08:06:03 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225158; bh=Ru3RatScBtHsIthUWmJhxd4aRb4X9f0jMyx0u9QyVYI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=JVacsj6varhUqWLS6n0VTmM5ZpAEZtXxZ2WHVFeXQdtN4uGN6R8ze/iiccLHjAJ+i KeV1Hii1hY0hnZxHcXJVwbaovnrjqHjFWsCD//KiuXdMF7xpg3ZG9r/+Y1Xx52avne ek9TVkFMff0rScy/DBFm76BWx+QJBWiwHOPDmdJljx61twaprBUyX/OSNnDM7Su7uH xpCV/fwsyHNK0PdvWONYoToGhxFt2Xev27MZ8wt3Yb8ytguTyzBL6iqCpPmMCeVVBt g6ekUx3QM6IQGgq8m8/UV8wfXjv5kWFnFQf+tBszeqoO9uLAzmM0Q5IfgsRtwunp75 63kf60RpMFFRg== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 778A26A347; Fri, 3 Apr 2026 08:05:58 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:32 -0600 Message-ID: <20260403140523.1998228-11-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 4WY6GOWOY6J7JVXX45BSGWHX7ASJBWMQ X-Message-ID-Hash: 4WY6GOWOY6J7JVXX45BSGWHX7ASJBWMQ 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 10/34] vfs: cmd: Add fs command with mount and umount subcommands 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 a new 'fs' command for VFS operations: - fs mount [ ] - list or create mounts - fs umount - unmount a filesystem Signed-off-by: Simon Glass --- cmd/Kconfig | 8 +++++ cmd/Makefile | 1 + cmd/vfs.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++ test/dm/fs.c | 27 +++++++++++++++ 4 files changed, 132 insertions(+) create mode 100644 cmd/vfs.c diff --git a/cmd/Kconfig b/cmd/Kconfig index 95ccb9fca1a..60ebb56de00 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -2987,6 +2987,14 @@ config CMD_FS_LEGACY include load, save, ls, size, mkdir, rm, ln, mv, fstype and fstypes. +config CMD_VFS + bool "fs - virtual filesystem commands" + depends on VFS + default y if SANDBOX + help + Provides the 'fs' command with mount, umount and ls subcommands + for the virtual filesystem layer. + config CMD_FS_UUID bool "fsuuid command" default y if SANDBOX diff --git a/cmd/Makefile b/cmd/Makefile index 4d1b3eab90c..412a3096d0e 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -94,6 +94,7 @@ obj-$(CONFIG_CMD_FPGA) += fpga.o obj-$(CONFIG_CMD_LUKS) += luks.o obj-$(CONFIG_CMD_FPGAD) += fpgad.o obj-$(CONFIG_CMD_FS_LEGACY) += fs_legacy.o +obj-$(CONFIG_CMD_VFS) += vfs.o obj-$(CONFIG_CMD_FUSE) += fuse.o obj-$(CONFIG_CMD_FWU_METADATA) += fwu_mdata.o obj-$(CONFIG_CMD_GETTIME) += gettime.o diff --git a/cmd/vfs.c b/cmd/vfs.c new file mode 100644 index 00000000000..23b8cabdcdb --- /dev/null +++ b/cmd/vfs.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * VFS commands - 'fs mount', 'fs umount' + * + * Provides a new 'fs' command with subcommands for the virtual filesystem + * layer, co-existing with the legacy filesystem commands in cmd/fs.c. + * + * Copyright 2026 Simon Glass + */ + +#include +#include +#include +#include +#include + +static int fs_mount_handler(int argc, char *const argv[]) +{ + struct udevice *vfs, *fsdev, *dir; + int ret; + + vfs = vfs_root(); + if (!vfs) + return -ENXIO; + + if (argc < 2) { + vfs_print_mounts(); + return 0; + } + + if (argc < 3) + return -EINVAL; + + ret = uclass_get_device_by_name(UCLASS_FS, argv[1], &fsdev); + if (ret) + return ret; + + ret = vfs_resolve(vfs, argv[2], &dir); + if (ret) + return ret; + + return vfs_mount(vfs, dir, fsdev); +} + +static int do_fs_mount(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret; + + if (argc == 2) + return CMD_RET_USAGE; + + ret = fs_mount_handler(argc, argv); + if (ret) { + printf("fs mount failed: %dE\n", ret); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int fs_umount_handler(const char *path) +{ + struct udevice *vfs; + + vfs = vfs_root(); + if (!vfs) + return -ENXIO; + + return vfs_umount_path(vfs, path); +} + +static int do_fs_umount(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + ret = fs_umount_handler(argv[1]); + if (ret) { + printf("fs umount failed: %dE\n", ret); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +U_BOOT_LONGHELP(fs, + "mount [ ] - list or create mounts\n" + "fs umount - unmount a filesystem"); + +U_BOOT_CMD_WITH_SUBCMDS(fs, "Filesystem operations", fs_help_text, + U_BOOT_SUBCMD_MKENT(mount, 3, 1, do_fs_mount), + U_BOOT_SUBCMD_MKENT(umount, 2, 1, do_fs_umount)); diff --git a/test/dm/fs.c b/test/dm/fs.c index f31a11e90cb..f71ad361bcd 100644 --- a/test/dm/fs.c +++ b/test/dm/fs.c @@ -247,4 +247,31 @@ static int dm_test_vfs_mount(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_vfs_mount, UTF_SCAN_FDT); + +/* Test the VFS layer using the 'fs' command */ +static int dm_test_vfs_cmd(struct unit_test_state *uts) +{ + ut_assertok(vfs_init()); + + /* Mount the sandbox FS at /host */ + ut_assertok(run_command("fs mount hostfs /host", 0)); + ut_assert_console_end(); + + /* Verify it appears in the mount list */ + ut_assertok(run_command("fs mount", 0)); + ut_assert_nextlinen("/host"); + ut_assert_console_end(); + + /* Unmount */ + ut_assertok(run_command("fs umount /host", 0)); + ut_assert_console_end(); + + /* Mount list should now be empty */ + ut_assertok(run_command("fs mount", 0)); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_vfs_cmd, UTF_SCAN_FDT); + #endif From patchwork Fri Apr 3 14:04:33 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2096 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=1775225174; bh=ZkJbr370FfVNxX6PqVNPprLDUPuazJsW8LNAxOIhM3o=; 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=D9DmQ153mNmajr63HT1PQ+YavSjaEp1j9/R7owjx5KX05E4TF1j6Op/1f3BB1DQSJ rdNwLvZbMKbUJcQMfKKGVQq8v2rz3BZAtmmuTkp+PjcD6aNPqgPhMdKuLkoD1CIXCA Pyq7gZp/jWh++foMgjQfq8H/5wuo1lNgUUjHYPuxvRs2Me7PHgQp0tlxs0xV2PzCuu pXdlQ7B4n59O+07sciJ/ZFM3N9pMxyLTjYPO7qEipiNSRy17SF9h6VMXqGW5Oqu6tD htaKvIyyTLc8XLKnGVTnIn85qTmJziOHBMOVDAe/I6dMA1kaXspX7vjUJHiRetaUbb 7J35P1bcWjZcA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 355D96A379 for ; Fri, 3 Apr 2026 08:06:14 -0600 (MDT) 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 veLlXXt8aWLF for ; Fri, 3 Apr 2026 08:06:14 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225169; bh=ZkJbr370FfVNxX6PqVNPprLDUPuazJsW8LNAxOIhM3o=; 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=HUzSR2gJ0RPbwR+rna85R0qPTwZ2KXlfWgNQqLgbDM4Kawf7jDxhbBzRj2q+aYAY6 melHO/wHCu2TXqn1w+TUQ0J3w00EtZfSR5ju2h6SmLPh38VRRNG45V7exDZmtErqd6 tCpQV3CLAtc51nzGyyCehTt+YV3PosIJSbr/eKuCDZut3scvFQDziKeNpcFfUlCmgt 4o9n9+KFWgc63UGSflq6tDFGluipKLWJK72Z0JucYLf6mZ55kb5YCS8hQZKAUBJjqG 6AJbykry9g7gG3JbO/5CuyLxi4ktXJcv8nNzoTWUoAOjoKarbbklF2uifl1SJa7Sfa 65osnqBhRQ9wQ== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 1B6106A366 for ; Fri, 3 Apr 2026 08:06:09 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225167; bh=mff6/bIO1uQv5fcjGu/BloLS7R6LJiehvwAs4kD5NbA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Sw7rJ3W2ZJNcdrCdhKSdCXDij/jxDltPpyOiGpZ1Kj0Axe3a4N1TDEWot3GZJGz+/ W0pKhNHhi7m1sLmIX/7SBovB4STOvKiCK0L2Jx7u89CndayVhYXrVjf3fJhAQ3KQbV 0zNk56VOBd3/ka5G16d1tkD3XDBLnxCGv7W/DbpvaEKoNt84krP/M53nmSqHpqQr5k DQZMDRmhaRcpe/dyJxDMlNP8i2KioHhDlgkyx6UunH95lmYx9NRTj7VRN99XJZILAs yryg8dH/EiphVITPASY/fU1NlhqIulxq4dviydjjOEpbHmylrhDXQ4fxImiZbr5yPq 1v+ZBwuxQhVZA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 0DD286A365; Fri, 3 Apr 2026 08:06:07 -0600 (MDT) 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 aGRxMu_qmrtn; Fri, 3 Apr 2026 08:06:06 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225159; bh=fxaORihxlHWPpc9ww1gZxcloicpYMYZANzVJ+IZT3aM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=XU9ZOhUNcOope2gglt0LBbj74+TPio/CU1+/AYrFXkRBbGHEgpHpiZPo4910syIO8 QRIIP84huqFStuOSetilnYE8guIKr7uu1blxrpT74j9YPHyT3YPrE2y70Pg/6/NCOY gliwuwD6MMRpcBcI6SxLq5UdZR9/chxXTKhO2RMYBaj1z9mHH1P9mLWSQJIGWBQZUS RJAtpvC5tZf7rw8JgaYFsSeOzoGP3WLIewbRWuIHku/bCaazVof0aD7Ur8CVeV50G2 ufl6NKIj8GATGrgTKbODfIV7Bt/QnfgUVcpQG7dUu8XGMQSwL9xtTBbZhKD01l0REI rktmgAWWDanXA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 5E37F6A368; Fri, 3 Apr 2026 08:05:59 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:33 -0600 Message-ID: <20260403140523.1998228-12-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: QFCGBGHMZCG2ID4PGQHO3DI6H3ZB53FT X-Message-ID-Hash: QFCGBGHMZCG2ID4PGQHO3DI6H3ZB53FT 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 11/34] vfs: Filter dir lookup to skip non-DIR children 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 fs_lookup_dir() iterates all children of an FS device looking for a cached directory but does not filter by uclass. With the addition of the MOUNT uclass, a non-directory child whose uclass-private data is misinterpreted as struct dir_uc_priv can cause a false path match. Add a UCLASS_DIR check to skip non-directory children. Signed-off-by: Simon Glass --- fs/fs-uclass.c | 2 ++ fs/vfs.c | 7 +++++++ fs/vfs_dir.c | 10 ++++++++++ include/vfs.h | 8 ++++++++ 4 files changed, 27 insertions(+) diff --git a/fs/fs-uclass.c b/fs/fs-uclass.c index 6bb50bb2543..2525067f166 100644 --- a/fs/fs-uclass.c +++ b/fs/fs-uclass.c @@ -61,6 +61,8 @@ int fs_lookup_dir(struct udevice *dev, const char *path, struct udevice **dirp) if (!device_active(dir)) continue; + if (device_get_uclass_id(dir) != UCLASS_DIR) + continue; priv = dev_get_uclass_priv(dir); log_debug("dir %s '%s' '%s'\n", dir->name, path, priv->path); diff --git a/fs/vfs.c b/fs/vfs.c index c42e60b4dc6..db1aa84bfd6 100644 --- a/fs/vfs.c +++ b/fs/vfs.c @@ -311,6 +311,13 @@ int vfs_umount_path(struct udevice *vfs, const char *path) return vfs_umount(mnt_dev); } +bool vfs_is_mount_point(struct udevice *dir) +{ + struct udevice *mnt; + + return !find_mount(dir, &mnt); +} + void vfs_print_mounts(void) { struct vfsmount *mnt; diff --git a/fs/vfs_dir.c b/fs/vfs_dir.c index 87a4193516e..5c3b5a2952b 100644 --- a/fs/vfs_dir.c +++ b/fs/vfs_dir.c @@ -13,6 +13,7 @@ #include #include #include +#include #include static int vfs_rootfs_dir_open(struct udevice *dev, @@ -81,8 +82,17 @@ static struct dir_ops vfs_rootfs_dir_ops = { .close = vfs_rootfs_dir_close, }; +static int vfs_rootfs_dir_remove(struct udevice *dev) +{ + if (vfs_is_mount_point(dev)) + return log_msg_ret("drm", -EBUSY); + + return 0; +} + U_BOOT_DRIVER(vfs_rootfs_dir) = { .name = "vfs_rootfs_dir", .id = UCLASS_DIR, .ops = &vfs_rootfs_dir_ops, + .remove = vfs_rootfs_dir_remove, }; diff --git a/include/vfs.h b/include/vfs.h index d7b6449b088..6673f9a5ef1 100644 --- a/include/vfs.h +++ b/include/vfs.h @@ -112,6 +112,14 @@ int vfs_umount_path(struct udevice *vfs, const char *path); int vfs_find_mount(struct udevice *vfs, const char *path, struct udevice **mntp, const char **subpathp); +/** + * vfs_is_mount_point() - Check whether a directory is a mount point + * + * @dir: UCLASS_DIR device to check + * Return: true if this directory has an active mount, false otherwise + */ +bool vfs_is_mount_point(struct udevice *dir); + /** * vfs_print_mounts() - Print all current mounts */ From patchwork Fri Apr 3 14:04:34 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2097 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=1775225174; bh=p61vFgsaKXpRfhNKuiUJDvwlXyBP5r/DkPlNhM+NbeA=; 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=reNdvUau1BOT/dDbMGy/eYAL+hdcP5l4+GssFFTr6UeOGzHWtjjKy+Fgj6ejDloXJ X3nR8j7X/NL/AVpyri+LnAIcPdJIaxsJlbu21oBFF8x4ZaRkYTGsNmkdv0vNcJ0Det agMJeq1uVSskWRoi/YHxMybhYxtWaHJ6ID/QePJlg4Do95JybwBRQ73Nq+fQmu4gbO Brge98NGYWUMwVYXxFe1PK8KgrbjMrI0p55cB6i6DUiGBHtFfnhrBoqWHZXxZQvbOK SpiPX9qHNp2kNB+gWj2AgcBzkID3mK7HGk8UtGU09zxXM7zfDbeFP+1AdFaLacUWux GNDi+eDwyfrZg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 88E8C6A37F for ; Fri, 3 Apr 2026 08:06:14 -0600 (MDT) 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 ExIcak6ghnaU for ; Fri, 3 Apr 2026 08:06:14 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225170; bh=p61vFgsaKXpRfhNKuiUJDvwlXyBP5r/DkPlNhM+NbeA=; 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=U+x3uUhBa7moVvYsEmseGcB8TUilBY9FsR3OckEEaNK5vTWGbo268nfLC9ahfTOY2 gLHZLNIVpBv4MpEaQaWphnoVXXigGSSowSuL8rOaQVCO3EAWmlnfXVPvrXIPWVJ25p zi0/ZLIW2xPTEOeHw/z3mZpJ69z+weClF4N1xrkmE7VSz8UZfzjmAF6z7evD+IrBJj plISAXllgpcFslQxUIPzYTBWZ1cUIxAlWnnvEFAXM+FklX7Z/z1MfQlXlo0V07DNaK FNbzgnxJjwbl8z3YsvlUaZXwguWuCFlWy0k/lPd62tq//KpZYVQFVtkaeb5D1KogUv V857iT0vD9C5A== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 87E6A6A35A for ; Fri, 3 Apr 2026 08:06:10 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225167; bh=b8FTcoUPLSHR9eyuA4eWV819yR/IN7VPL15UUtLOy8Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WX3s1il0nwBvMYm/vJBGypN336+kYXB2Juynq0f+2tfLpU9pnBu87tjYAVEBP5CUR 1UwEDlV8xnmYMKLcnEeF6C4CKa7hmbKzik5mWV0Stf8/75xBwQqngTtWjL1fufThHe EdPPmsLvyx7KnylF1TBrkQabVdImNOAF8lGsvLKd6DMtxZbtH9auzLqj7WL3+8m58W H8Xq5GLABCpK0blEGTzkN/ATUlHtmFhFU/uH996dZj/VjnhPGCVhraRVVunlDXLrWd jHVggsC0uL6jAPorNqhj1Be+mTeEAhOZtTM/hVQk0Rmd5OvpgR+9wlP+mwliuPkc7H QEiSNr5JH1YsA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 188D46A366; Fri, 3 Apr 2026 08:06:07 -0600 (MDT) 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 c_ilVg8Moxvt; Fri, 3 Apr 2026 08:06:07 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225160; bh=CVGiVLicUEeY+an0oq6F0Wbk+XZxtv6wFrCBujb7tQM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=N7OAj4s9CEpkMtiBbtFK87E/HYNsyuYLNLs8cOKTIMEVt+LLVRldHBjL7cMnu44hH uS8oERLvCgZieYOHHXBDm5XxiBMmMxMZ6V/dCaGsNOfRCwoJ09HsrZaiXoncB3QcE6 iJ0zGwPnJ14BjZ9PL1k/5LBjcUPOD52b5Bi3T8a+ePS6jkF1cUn1B4+0HCrx6GhGP5 R+Tj3I+Cdt9PqDc74PWxNMiJjoTuOFTZ3ZSNILu6A7OaqAW81TRo8cGpwq2L3qoEYT rJwhHsS5mwskOr9+lYp4O8cN6VS0oaCmvoD8ot/vPWKQjt/Q2FfGiCDdzmmhxBS+Ug f7sZEX7MpsRig== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 24D046A34C; Fri, 3 Apr 2026 08:06:00 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:34 -0600 Message-ID: <20260403140523.1998228-13-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: XXNCW6V2L5J6GHADJYCAWSHGQKO47NKS X-Message-ID-Hash: XXNCW6V2L5J6GHADJYCAWSHGQKO47NKS 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 12/34] vfs: Add ls support using dir open/read/close 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 vfs_ls() which resolves a path through the mount table and lists directory entries using the dir_open(), dir_read() and dir_close() interface. At the root level, mount-point directories are shown. Add an 'fs ls' subcommand and update the help text to include it along with the mount -t syntax for block-device mounts. Signed-off-by: Simon Glass --- cmd/vfs.c | 28 +++++++++++++++++++++++----- fs/vfs.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ include/vfs.h | 11 +++++++++++ test/dm/fs.c | 10 ++++++++++ 4 files changed, 90 insertions(+), 5 deletions(-) diff --git a/cmd/vfs.c b/cmd/vfs.c index 23b8cabdcdb..5779f3f098d 100644 --- a/cmd/vfs.c +++ b/cmd/vfs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * VFS commands - 'fs mount', 'fs umount' + * VFS commands - 'fs mount', 'fs umount', 'fs ls' * * Provides a new 'fs' command with subcommands for the virtual filesystem * layer, co-existing with the legacy filesystem commands in cmd/fs.c. @@ -87,10 +87,28 @@ static int do_fs_umount(struct cmd_tbl *cmdtp, int flag, int argc, return CMD_RET_SUCCESS; } +static int do_fs_ls(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *path = argc >= 2 ? argv[1] : "/"; + int ret; + + ret = vfs_ls(path); + if (ret) { + printf("fs ls failed: %dE\n", ret); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + U_BOOT_LONGHELP(fs, - "mount [ ] - list or create mounts\n" - "fs umount - unmount a filesystem"); + "mount [ ] - list or create mounts\n" + "fs mount -t - mount from block device\n" + "fs umount - unmount a filesystem\n" + "fs ls [] - list directory (default /)"); U_BOOT_CMD_WITH_SUBCMDS(fs, "Filesystem operations", fs_help_text, - U_BOOT_SUBCMD_MKENT(mount, 3, 1, do_fs_mount), - U_BOOT_SUBCMD_MKENT(umount, 2, 1, do_fs_umount)); + U_BOOT_SUBCMD_MKENT(mount, 6, 1, do_fs_mount), + U_BOOT_SUBCMD_MKENT(umount, 2, 1, do_fs_umount), + U_BOOT_SUBCMD_MKENT(ls, 2, 1, do_fs_ls)); diff --git a/fs/vfs.c b/fs/vfs.c index db1aa84bfd6..e05a02ad5b0 100644 --- a/fs/vfs.c +++ b/fs/vfs.c @@ -331,6 +331,52 @@ void vfs_print_mounts(void) } } +int vfs_ls(const char *path) +{ + struct udevice *vfs, *mnt, *dir = NULL; + struct fs_dir_stream *strm; + struct fs_dirent dent; + const char *subpath; + bool empty = true; + int ret; + + vfs = vfs_root(); + if (!vfs) + return -ENXIO; + + ret = vfs_find_mount(vfs, path, &mnt, &subpath); + if (!ret && mnt) { + struct vfsmount *m = dev_get_uclass_priv(mnt); + + ret = fs_lookup_dir(m->target, subpath, &dir); + if (ret) + return ret; + } else if (!ret || !path[1]) { + /* Root "/" - list the VFS root dir */ + ret = fs_lookup_dir(vfs, "", &dir); + if (ret) + return ret; + } else { + return ret; + } + + ret = dir_open(dir, &strm); + if (ret) + return ret; + + while (!dir_read(dir, strm, &dent)) { + if (dent.type == FS_DT_DIR) + printf("DIR %10u %s\n", 0, dent.name); + else + printf(" %10llu %s\n", dent.size, dent.name); + empty = false; + } + + dir_close(dir, strm); + + return 0; +} + struct udevice *vfs_root(void) { struct udevice *dev; diff --git a/include/vfs.h b/include/vfs.h index 6673f9a5ef1..f85f45da4dd 100644 --- a/include/vfs.h +++ b/include/vfs.h @@ -125,4 +125,15 @@ bool vfs_is_mount_point(struct udevice *dir); */ void vfs_print_mounts(void); +/** + * vfs_ls() - List directory contents + * + * Resolves the path through the mount table and lists directory entries + * using dir_open(), dir_read() and dir_close(). + * + * @path: Absolute path to list, or "/" for root + * Return: 0 if OK, -ve on error + */ +int vfs_ls(const char *path); + #endif diff --git a/test/dm/fs.c b/test/dm/fs.c index f71ad361bcd..b14083590c6 100644 --- a/test/dm/fs.c +++ b/test/dm/fs.c @@ -262,6 +262,16 @@ static int dm_test_vfs_cmd(struct unit_test_state *uts) ut_assert_nextlinen("/host"); ut_assert_console_end(); + /* Root should show the "host" mount point */ + ut_assertok(run_command("fs ls /", 0)); + ut_assert_nextline("DIR %10u host", 0); + ut_assert_console_end(); + + /* Listing /host should show sandbox directory contents */ + ut_assertok(run_command("fs ls /host", 0)); + ut_assert_skip_to_linen("DIR "); + console_record_reset_enable(); + /* Unmount */ ut_assertok(run_command("fs umount /host", 0)); ut_assert_console_end(); From patchwork Fri Apr 3 14:04:35 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2098 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=1775225174; bh=yho+J91aUwL4r/nT38eSi4bMHsL7knbiU9n7BeF/9Mg=; 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=E5mtbhEdzqrVbe8sYR9z8qVvoplFaAYZk7OekaNUmwbbd8NrAKw0E8e47q2XwL9g+ Mix6ZSK1WtbxrC7d6P2FCe/cVahrn9IzP2K5fzvoCrft9itsYPnhTkqoN5WpaEpqxN G8ify0oTvS4Vrd+6pZIaNRbpm64xGHTdugICHJDG//3k3WlZg+fsOVwpkj5TS25lSD s0ruNpOKxtadCpaDwviDFGuPxqscCc120pgZu827jksMGOxd7OBwnK/xSRrGyfw1mF r7BsP0lgYjB1XW5tkDrVta6ESDNFEawOi7satPGq84voUG2HqdlVSdaJ2pzRfK92bY 25y2AACcP2Hcg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id F29276A36B for ; Fri, 3 Apr 2026 08:06:14 -0600 (MDT) 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 1XfK_EB1wM5Z for ; Fri, 3 Apr 2026 08:06:14 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225171; bh=yho+J91aUwL4r/nT38eSi4bMHsL7knbiU9n7BeF/9Mg=; 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=uNQzXPCd6Ae5HwqHTaNb6gS92Mo9r8v455SQLO2ugiyjpmdZCbiz7o5mZtW6e2ZOx JglVAhLuU5MSkSFO0y6lV/HXiJcqM0GWuxoXDMFIL2SO9A8/Jl2AhdDs2Z2Z0hiq0Y qCaaUw2ISTMmolY1ccONq0S8a2yzUvspSG1MGyxyDfumN95IY/xqRhLrxLMj/PAYz6 tlBaZpCF29+cjhgDpiFDo9IgzYhg7m97dPV+5GkyYjObJ1MYYpIIq+kjkte/iOcZDY OPjmsB0jhFp26I2y/RgnvuWJnmXgGHF9sda2sIB2L5KzoaEwFJfRlo2I6LJOSdWTkk rb4pIQRGUQzSQ== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 0E78A6A36E for ; Fri, 3 Apr 2026 08:06:11 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225167; bh=XSJiIcEROdk9J+oLtuwnmd+ovcbU7+rPZ27fN+zpSoQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ddbZWB1e+hl9CKab/eYOWpKM5l93m+r8XZOSFFBGYz7/7inGmYeEYckcPUtFyrZH0 feJKWsQMD2HS6Zfcb3KRR5dtb6O5K1dsOXmHpyFzA5ieBtJmOUsIodVqx882XASTKL whB+n3dvoCG+LKhi56D/q9ewjU0EYyjR8MePKB1sMa30zDYFE9mGcgeePN9HSrzOkG 3v/GhgPOwLvv/7t02Bm2/PNnntE1MqL4nh6gQG05+Ng8MrcbM+xN5X+hzzH4+8y8lz bQGphKd8B37OmZND1+gA7zx9ldJ9ScUEeK7elx4KXjAutDoI+lVbxOLCvmKZ9DH05b vmGdSrbgWBYCg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 250D46A34C; Fri, 3 Apr 2026 08:06:07 -0600 (MDT) 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 C5d0-fDmS5EG; Fri, 3 Apr 2026 08:06:07 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225164; bh=CM8vn6uHa6ez9ecKbNbxypFhF3j3pvHqWN1P0LBjKv8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=gTxcIe9gXBgxJ9R0SbioqhvNu2C1HV6QV4STgjd6PO1CrbShyYv2R/LNy+Jk+O0R4 7V3hjPaTZK6EVZbY6QLxinqpT2vhcCQJgiSVZ2dP6WV3P1f/4pHAc42ZvLefymmwSl BirEeK/pFKIhBYQvRT9HzaoxCG1Bw2xI55GyXynCVSTmRnpEsk/D9WPjMfzBkD21Hw sDlF8qvbDZgiVmP8tbeb2yY541OpoloEkJ2K6gXpL32QAe+8YhlfbfFBBfOAR7+nLr w1P3vRQrLqFr2HQTk/Y+WOytM5XE4TfPJjnrDvZaT2N4PSwgTIgJq7yBugte6XVt66 jp4wYO7fP3UBA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id A7CBB6A35D; Fri, 3 Apr 2026 08:06:04 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:35 -0600 Message-ID: <20260403140523.1998228-14-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: C57R5C2Z2PCAX72SEZRUZTKLADUETIQH X-Message-ID-Hash: C57R5C2Z2PCAX72SEZRUZTKLADUETIQH 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 13/34] vfs: Add vfs_open_file() to open a file by path 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 vfs_open_file() which resolves an absolute VFS path to a UCLASS_FILE device in one call, replacing the repeated sequence of vfs_find_mount / fs_split_path / fs_lookup_dir / dir_open_file. Signed-off-by: Simon Glass --- fs/vfs.c | 35 +++++++++++++++++++++++++++++++++++ include/vfs.h | 16 ++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/fs/vfs.c b/fs/vfs.c index e05a02ad5b0..f06844f2a33 100644 --- a/fs/vfs.c +++ b/fs/vfs.c @@ -331,6 +331,41 @@ void vfs_print_mounts(void) } } +int vfs_open_file(const char *path, enum dir_open_flags_t oflags, + struct udevice **filp) +{ + struct udevice *vfs, *mnt, *dir; + const char *subpath, *leaf; + struct vfsmount *mnt_priv; + char *dirpath; + int ret; + + vfs = vfs_root(); + if (!vfs) + return log_msg_ret("voi", -ENXIO); + + ret = vfs_find_mount(vfs, path, &mnt, &subpath); + if (ret) + return log_msg_ret("vom", ret); + + mnt_priv = dev_get_uclass_priv(mnt); + + ret = fs_split_path(subpath, &dirpath, &leaf); + if (ret) + return log_msg_ret("vos", ret); + + ret = fs_lookup_dir(mnt_priv->target, dirpath, &dir); + free(dirpath); + if (ret) + return log_msg_ret("vod", ret); + + ret = dir_open_file(dir, leaf, oflags, filp); + if (ret) + return log_msg_ret("vof", ret); + + return 0; +} + int vfs_ls(const char *path) { struct udevice *vfs, *mnt, *dir = NULL; diff --git a/include/vfs.h b/include/vfs.h index f85f45da4dd..53d1932e861 100644 --- a/include/vfs.h +++ b/include/vfs.h @@ -11,6 +11,8 @@ #ifndef __VFS_H #define __VFS_H +#include + struct udevice; /** @@ -136,4 +138,18 @@ void vfs_print_mounts(void); */ int vfs_ls(const char *path); +/** + * vfs_open_file() - Open a file by absolute VFS path + * + * Resolves the path through the mount tree, splits into directory and leaf, + * then opens the file. + * + * @path: Absolute VFS path to the file + * @oflags: Open flags (DIR_O_RDONLY, DIR_O_WRONLY, DIR_O_RDWR) + * @filp: Returns the UCLASS_FILE device + * Return: 0 if OK, -ve on error + */ +int vfs_open_file(const char *path, enum dir_open_flags_t oflags, + struct udevice **filp); + #endif From patchwork Fri Apr 3 14:04:36 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2099 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=1775225175; bh=/f1bxRRo5XYJPD8SpCY9bp/jzCstuZsng9lFr8r8N1U=; 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=rVQ7zuVXa8iE3Bl9XtGuIqHNehzwRArUcHeNLedL842EdqG+u8jBmWwZO+VIaP+2i 3cq5vMtznfartqPPrlVy6QRpXuTgoRY8DsPtJKS4qk/TpCwStn8djq2W4yxD3l/HiJ yFOOu7HYaYIXR+7baV+ClMFjNGnvAfa86G8+igBxQPGIV/N9lnue+6F64mbkHJs/Bc UcJVHgKqmZIQdViSDGPgcXtQxyPMbTG+Dp+x5tH1tcKBz+sBewoot6Rcj+9E3Hq+zp 5rc0rONmtb6NiRtJAvAUMxWfOdGTMR6BchkroxaTSqx8uzgI0s66KMi0oomZA2xj1P diWVTkqiq2TiA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 4FEA86A37B for ; Fri, 3 Apr 2026 08:06:15 -0600 (MDT) 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 Rio-migHqfV6 for ; Fri, 3 Apr 2026 08:06:15 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225171; bh=/f1bxRRo5XYJPD8SpCY9bp/jzCstuZsng9lFr8r8N1U=; 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=JEHHM+O3/YKGlJZiBsUABW+VC2K25EWyqnXiiF8L+YJ4bB15Frhm7YHUIeQUdll+7 Gbbv3bwBJUaf2EA/w5btavutRw8SEXodmKUM/TPr0zNacvOGjk3y4NRTVuopt89tAm RbYR319F7k212RVw0UsMdw+KzNuO0R+56Y5zI0/uOnk1cKkKZk8E90FjFk6kutEXBm CVnl++rlXKMiESfJWVj7WAjoA80HH6x0acWZEe4G7holL7YgCncFz0mK65w+dWeaEG j1hoXIrjhT8TU42sGECgq6uZIyjf7760nQQ2GGGuu4tRsyEDHLRvvVJwYkuZXjPZ58 edlph8rHNIBuA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id D47AE6A370 for ; Fri, 3 Apr 2026 08:06:11 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225169; bh=6ZFxwFcxpFqeWuxlkNU+5BatKLr4ENotXE7po22Fz2Q=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rLvWxiaF7pEYWCVdMs+dvUWanFvWCNDsnEIELs7Jk1+z6WIVjqLUEM8BT/QKcLy+C CTxWFwayzX+b/anIQBY/rzhsbJjGaU3xqGb66w7A7ZGHDzEw+jSC/Yka4LjT/fYyGO cJ42hEwNprflArG2dv2eclmivmHEZ4Z0TsoRxsHtXPO78488xi6ZMASf+J/1SpDymq OIAuPiPy1x6lM2c6MEi7GeESF18TB0dhlZYd9KS5kzR4AOZH6gAoEcdabcGXn2J97d sSoquEyu9oQ76mw+b91EITCUKHYPUsCohffbTFGhDfs5/JQGWFAt/MtZ3kFCOCQFtU UhpjI6ACvxRlw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 51A516A351; Fri, 3 Apr 2026 08:06:09 -0600 (MDT) 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 mfqhSTN4gGpn; Fri, 3 Apr 2026 08:06:09 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225165; bh=mUcuZ8lHVN1u3G0BLCDLawoQG5NxEbUjbzyng/HzL9o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=JG5bQhZchUoO2BghtyyPu5RH4Kaz7bJludAA2vOh3EgQ12FVA1FaJI6vn0moskDob xTTQ9buvPe/RNFAT2TrJJaC5yN/NXBfWt9xaJoC/WM9AqXwXpGTzlGloFBl8uZ6Z0f 6Vov1MvVBswUfNUKMumndsaZz80zCmPfCBIT6e7Bec1MFOsChvG3VgFwb6Zk9U7Dtl N0sWMqP4730tGHZW6gfsADzhC9HGDjLS1eSULb12YTAuhDyy95JIeKcR/YnLZPpQ+6 dKhyCZjF3IP/B/2s0Na+QVWyb934DBKSg0YCYWG+16xNKkZWRDnKOBNazNKIDFUuLA 77lim/RlEzbyQ== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 6C7B26A34F; Fri, 3 Apr 2026 08:06:05 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:36 -0600 Message-ID: <20260403140523.1998228-15-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 4K6WFE35QG5RND2WS3KQBFFP7BVLC3CO X-Message-ID-Hash: 4K6WFE35QG5RND2WS3KQBFFP7BVLC3CO 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 14/34] vfs: Add block-device fields to struct fs_plat 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 block device descriptor and partition information to struct fs_plat so that block-backed filesystems (ext4, FAT) can access the underlying storage. Non-block filesystems like the sandbox hostfs leave the descriptor as NULL. Signed-off-by: Simon Glass --- include/fs.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/fs.h b/include/fs.h index 6696b9e26c7..c6b6323be3e 100644 --- a/include/fs.h +++ b/include/fs.h @@ -12,6 +12,7 @@ #define __FS_H #include +#include struct udevice; @@ -23,10 +24,18 @@ enum { /** * struct fs_plat - Filesystem information * + * For block-device-backed filesystems, @desc and @part identify the + * underlying storage. Non-block filesystems (hostfs, rootfs) leave + * @desc as NULL. + * * @name: Name of the filesystem, or empty if not available + * @desc: Block device descriptor, or NULL if not block-backed + * @part: Partition information (valid only when @desc is non-NULL) */ struct fs_plat { char name[FS_MAX_NAME_LEN]; + struct blk_desc *desc; + struct disk_partition part; }; /** From patchwork Fri Apr 3 14:04:37 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2100 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=1775225177; bh=9wBUvrliMsQAU1rXkNH6YEnPXkOqJixzfo8fl/NvPoQ=; 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=soSwx9Hs8akKAU85fbrk5fNvhkUVTr6elEYZuXAH3vXSKX6GhqVwL5PDXGW9uJYuX g6OobUW8jLwamNg4+eZKH8GmjCxGXQnO58yhX0DJNtBSguy3yEG1AFEsBM5iwBrjCi oCa+jVFrVleoSHIWqdnOM6PJZ3b/XBSlKCbet0B31m/6Z2jKMB6Kd7cRO51rTMc9ar yzGc4ft3QFpnFOzHr7M9f4jJbjvZFlVYtZr27hwv3WWDA4f0pdCPg9NDVFhSzXM62j kM1tSjGvdVuGwxjUd9xET5rd6bWSYMQKEZWRLFTL8BDhoYWdKG4cT44dGFUxFUDSBb h0vXtSg8zfUfw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id D45F06A34E for ; Fri, 3 Apr 2026 08:06:17 -0600 (MDT) 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 YPSLRAVO8pp4 for ; Fri, 3 Apr 2026 08:06:17 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225174; bh=9wBUvrliMsQAU1rXkNH6YEnPXkOqJixzfo8fl/NvPoQ=; 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=jrKf7J6l5lZshGbMp3SkOzT8nqtNU9S6GRJpGbHI9RBzHrKRsLOdXxD0VTaFkGjjJ /JkuuoTQR/cpXORdE7lQuVk6bXHAbzZbXCi9/ZaClqO5ztylzSlSWINqzJktaPmCBl Ln9s4EQE6bN0wPT/xWDrqHT+Xi6AXDZbn6VVyJUlA3DA3YxcR4dUKQ1IShmOoo9D4V NfEmsrw1i+YAGZEPwITzZf7HM1bGYXBvbIsiDZBbzvrUm9cf9fv/E5A7YykBpOmt/U zR0WmZDT56INUeYaRvcJA7/6Q4UfsLRjpI5yZpEse7JE5gYApB33eOs+38xMWTsc8S An7ViVBHTLq9w== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 622246A35F for ; Fri, 3 Apr 2026 08:06:14 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225172; bh=mg7dH1br2VKaMrECOGBVMfr3lyEgCqtrXYHOwb1nqB4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ClGFUgSoQUD9XO6RiuNx5fbjrp5/lqPFvan3xSk1zFCvisqnkF2PxliQ2+pBajSpD QLTIMB3z9fULgDZS18p2P9EiH9/h8uji0rL77o2MgW5eEwC/v94Pnf2TwSOq9NG6ze bYw6w6IwZtdkHfAfH/Xge0SLLoXsWG/5QCj5SnqkvxClEWUOBz2W1IYgyNXdkYmuKX 64rMDC8BO9c/OihFy7P3Jq0sTL/SQhKUn1hbaYepK11HggFkTLgsGCYCG43eREB86S ZMHUmLSDBDa0C6j4kLLQfeBrp4/eOzCMR+IylKagjep7NJzuEy5gvS5MFHq1ZotbN+ tbisgVJYFAC3Q== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 136936A376; Fri, 3 Apr 2026 08:06:12 -0600 (MDT) 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 qTp5g1q_Sy8h; Fri, 3 Apr 2026 08:06:12 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225166; bh=/EM2gdYx+NPFGnqV6+4ojO1rhUTUh2Ey/geVjghHN3Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=O4Qof9Oivf2b/E6hnCeWgzGHaeNO0ZX7ngNu5+AuzN4aAzrerQnIobpn0KL1grcN7 BkQJH5YhpOzEBWRcHmMOF4st9k7ZXAPf2nwIZ3V9XAi0hvvxcw3hCXNsku/EN7iuP+ lEAoh7WvHQzMyFQe9UNX5SM68VXCXEdgZ+Nr2PMZiY/yIdlnGGsBgSadSW32hn0az3 iBMmeI+OW1+H8Zz8pNZwlfoyE9vN6PCQAnJt3JMTZZvRDNJ9FJPjc8Djj+yHeg048B goTluUEqZpo2u1dTkC+27oldbiJ7GSgDBvcCJtToEjYFR7u+pmbc/ZnI8KWkKRPTeE xZBspYb1vL/nw== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 3248A6A350; Fri, 3 Apr 2026 08:06:06 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:37 -0600 Message-ID: <20260403140523.1998228-16-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: NY53CDQ5K742WKDOWH5MJADQVVOXJLIA X-Message-ID-Hash: NY53CDQ5K742WKDOWH5MJADQVVOXJLIA 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 15/34] vfs: Add current working directory and path helpers 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 a current working directory (cwd) to the VFS layer, stored in the vfs_rootfs driver-private data, with vfs_chdir() and vfs_getcwd() to change and query it. Add vfs_resolve_cwd() which resolves relative paths against the cwd, and use it in vfs_ls() so that relative paths work. Add vfs_resolve_mount() which combines vfs_root lookup, cwd resolution and mount lookup in one call. Signed-off-by: Simon Glass --- fs/vfs.c | 194 ++++++++++++++++++++++++++++++++++++++++++---- fs/vfs_internal.h | 4 + include/vfs.h | 30 +++++++ test/dm/fs.c | 42 ++++++++++ 4 files changed, 256 insertions(+), 14 deletions(-) diff --git a/fs/vfs.c b/fs/vfs.c index f06844f2a33..79b1ca0ef89 100644 --- a/fs/vfs.c +++ b/fs/vfs.c @@ -11,6 +11,7 @@ #define LOG_CATEGORY UCLASS_MOUNT +#include #include #include #include @@ -30,6 +31,139 @@ (pos) && ((mnt) = dev_get_uclass_priv(pos)); \ uclass_next_device(&(pos))) +/** + * canonicalise_path() - Remove . and .. components from an absolute path + * + * Modifies @buf in place. The path must start with '/'. + * + * @buf: Absolute path to canonicalise (modified in place) + */ +static int canonicalise_path(char *buf) +{ + char *p, *token; + char *stack[64]; + int depth = 0; + + /* Tokenise by '/' and resolve . and .. */ + p = buf + 1; /* skip leading '/' */ + while (*p) { + token = p; + while (*p && *p != '/') + p++; + if (*p) + *p++ = '\0'; + + if (!*token || !strcmp(token, ".")) { + continue; + } else if (!strcmp(token, "..")) { + if (depth > 0) + depth--; + } else { + if (depth >= ARRAY_SIZE(stack)) + return -ENAMETOOLONG; + stack[depth++] = token; + } + } + + /* + * Rebuild the path from the stack. This always fits in buf since + * removing . and .. components can only shorten the path. + */ + p = buf; + *p++ = '/'; + for (int i = 0; i < depth; i++) { + if (i > 0) + *p++ = '/'; + strcpy(p, stack[i]); + p += strlen(stack[i]); + } + *p = '\0'; + + /* Ensure root is "/" not "" */ + if (!buf[1]) + buf[0] = '/'; + + return 0; +} + +const char *vfs_path_resolve(const char *cwd, const char *path, char *buf, + int size) +{ + if (!path || !*path) { + strncpy(buf, cwd, size); + buf[size - 1] = '\0'; + return buf; + } + + if (*path == '/') { + strncpy(buf, path, size); + buf[size - 1] = '\0'; + } else { + int len = strlen(cwd); + + if (len == 1) + snprintf(buf, size, "/%s", path); + else + snprintf(buf, size, "%s/%s", cwd, path); + } + + if (canonicalise_path(buf)) + return NULL; + + return buf; +} + +const char *vfs_getcwd(void) +{ + struct udevice *vfs = vfs_root(); + struct vfs_priv *priv; + + if (!vfs) + return "/"; + priv = dev_get_priv(vfs); + + return priv->cwd; +} + +int vfs_chdir(const char *path) +{ + char resolved[FILE_MAX_PATH_LEN]; + struct udevice *vfs, *mnt; + const char *subpath, *abs; + struct vfs_priv *priv; + int len, ret; + + vfs = vfs_root(); + if (!vfs) + return log_msg_ret("vci", -ENXIO); + priv = dev_get_priv(vfs); + + abs = vfs_path_resolve(vfs_getcwd(), path, resolved, sizeof(resolved)); + if (!abs) + return log_msg_ret("vcp", -ENAMETOOLONG); + + /* Verify the path exists by trying to resolve it */ + if (strcmp(abs, "/")) { + ret = vfs_find_mount(vfs, abs, &mnt, &subpath); + if (ret) + return log_msg_ret("vcf", ret); + } + + len = strlen(abs); + + /* Strip trailing slash (but keep "/" for root) */ + if (len > 1 && abs[len - 1] == '/') + len--; + + if (len >= sizeof(priv->cwd)) + return log_msg_ret("vcl", -ENAMETOOLONG); + + memcpy(priv->cwd, abs, len); + priv->cwd[len] = '\0'; + + return 0; +} + /** * find_mount() - Check whether a directory is a mount point * @@ -174,10 +308,13 @@ static int vfs_mount_path(struct udevice *mnt_dev, char *buf, int size) static int vfs_rootfs_mount(struct udevice *dev) { struct fs_priv *uc_priv = dev_get_uclass_priv(dev); + struct vfs_priv *priv = dev_get_priv(dev); if (uc_priv->mounted) return log_msg_ret("rfm", -EISCONN); + strcpy(priv->cwd, "/"); + uc_priv->mounted = true; return 0; @@ -243,6 +380,34 @@ int vfs_find_mount(struct udevice *vfs, const char *path, struct udevice **mntp, return 0; } +/** + * vfs_resolve_mount() - Resolve a path to its mount and subpath + * + * Handles vfs_root lookup, cwd resolution and mount lookup in one call. + * + * @path: Absolute or relative VFS path + * @resolved: Buffer for cwd-resolved path + * @size: Size of @resolved + * @mntp: Returns the UCLASS_MOUNT device (NULL for root) + * @subpathp: Returns the remaining path within the mount + * Return: 0 if OK, -ve on error + */ +static int vfs_resolve_mount(const char *path, char *resolved, int size, + struct udevice **mntp, const char **subpathp) +{ + struct udevice *vfs; + + vfs = vfs_root(); + if (!vfs) + return log_msg_ret("vrv", -ENXIO); + + path = vfs_path_resolve(vfs_getcwd(), path, resolved, size); + if (!path) + return log_msg_ret("vrp", -ENAMETOOLONG); + + return vfs_find_mount(vfs, path, mntp, subpathp); +} + int vfs_resolve(struct udevice *vfs, const char *path, struct udevice **dirp) { @@ -296,11 +461,13 @@ int vfs_umount(struct udevice *mnt_dev) int vfs_umount_path(struct udevice *vfs, const char *path) { + char resolved[FILE_MAX_PATH_LEN]; struct udevice *mnt_dev; const char *subpath; int ret; - ret = vfs_find_mount(vfs, path, &mnt_dev, &subpath); + ret = vfs_resolve_mount(path, resolved, sizeof(resolved), + &mnt_dev, &subpath); if (ret) return log_msg_ret("vuf", ret); @@ -368,32 +535,31 @@ int vfs_open_file(const char *path, enum dir_open_flags_t oflags, int vfs_ls(const char *path) { - struct udevice *vfs, *mnt, *dir = NULL; + char resolved[FILE_MAX_PATH_LEN]; + struct udevice *mnt, *dir = NULL; struct fs_dir_stream *strm; struct fs_dirent dent; const char *subpath; bool empty = true; int ret; - vfs = vfs_root(); - if (!vfs) - return -ENXIO; + ret = vfs_resolve_mount(path, resolved, sizeof(resolved), + &mnt, &subpath); + if (ret) + return ret; - ret = vfs_find_mount(vfs, path, &mnt, &subpath); - if (!ret && mnt) { + if (mnt) { struct vfsmount *m = dev_get_uclass_priv(mnt); ret = fs_lookup_dir(m->target, subpath, &dir); - if (ret) - return ret; - } else if (!ret || !path[1]) { + } else { /* Root "/" - list the VFS root dir */ + struct udevice *vfs = vfs_root(); + ret = fs_lookup_dir(vfs, "", &dir); - if (ret) - return ret; - } else { - return ret; } + if (ret) + return ret; ret = dir_open(dir, &strm); if (ret) diff --git a/fs/vfs_internal.h b/fs/vfs_internal.h index ac7f756e24f..36539741402 100644 --- a/fs/vfs_internal.h +++ b/fs/vfs_internal.h @@ -8,15 +8,19 @@ #ifndef __VFS_INTERNAL_H #define __VFS_INTERNAL_H +#include + struct udevice; /** * struct vfs_priv - VFS root FS device driver-private data * * @mount_count: Counter for unique mount device names + * @cwd: Current working directory (absolute path) */ struct vfs_priv { int mount_count; + char cwd[FILE_MAX_PATH_LEN]; }; /** diff --git a/include/vfs.h b/include/vfs.h index 53d1932e861..521494e4199 100644 --- a/include/vfs.h +++ b/include/vfs.h @@ -41,6 +41,36 @@ struct vfsmount { */ int vfs_init(void); +/** + * vfs_chdir() - Change the current working directory + * + * @path: New directory (absolute or relative to current cwd) + * Return: 0 if OK, -ve on error + */ +int vfs_chdir(const char *path); + +/** + * vfs_getcwd() - Get the current working directory + * + * Return: pointer to the cwd string (static buffer, do not free) + */ +const char *vfs_getcwd(void); + +/** + * vfs_path_resolve() - Resolve a path against a given working directory + * + * If @path starts with '/', it is used as an absolute path. Otherwise it + * is joined with @cwd. In both cases, '.' and '..' components are resolved. + * + * @cwd: Current working directory (must be absolute) + * @path: Input path (absolute or relative), or NULL/empty for cwd + * @buf: Buffer to write the resolved absolute path + * @size: Size of @buf + * Return: pointer to @buf + */ +const char *vfs_path_resolve(const char *cwd, const char *path, + char *buf, int size); + /** * vfs_root() - Get the VFS root FS device * diff --git a/test/dm/fs.c b/test/dm/fs.c index b14083590c6..e45441e5241 100644 --- a/test/dm/fs.c +++ b/test/dm/fs.c @@ -284,4 +284,46 @@ static int dm_test_vfs_cmd(struct unit_test_state *uts) } DM_TEST(dm_test_vfs_cmd, UTF_SCAN_FDT); +/* Test current working directory */ +static int dm_test_vfs_cwd(struct unit_test_state *uts) +{ + struct udevice *vfs, *fsdev, *dir; + + ut_assertok(vfs_init()); + vfs = vfs_root(); + ut_assertnonnull(vfs); + + /* Default cwd is "/" */ + ut_asserteq_str("/", vfs_getcwd()); + + /* Mount sandbox FS at /host */ + ut_assertok(uclass_get_device_by_name(UCLASS_FS, "hostfs", &fsdev)); + ut_assertok(vfs_resolve(vfs, "/host", &dir)); + ut_assertok(vfs_mount(vfs, dir, fsdev)); + + /* cd to /host */ + ut_assertok(vfs_chdir("/host")); + ut_asserteq_str("/host", vfs_getcwd()); + + /* ls with NULL should list cwd (i.e. /host) */ + console_record_reset_enable(); + ut_assertok(vfs_ls(NULL)); + ut_assert_skip_to_linen("DIR "); + console_record_reset_enable(); + + /* cd back to root */ + ut_assertok(vfs_chdir("/")); + ut_asserteq_str("/", vfs_getcwd()); + + /* cd to non-existent path should fail */ + ut_asserteq(-ENOENT, vfs_chdir("/nowhere")); + /* cwd should be unchanged */ + ut_asserteq_str("/", vfs_getcwd()); + + ut_assertok(vfs_umount_path(vfs, "/host")); + + return 0; +} +DM_TEST(dm_test_vfs_cwd, UTF_SCAN_FDT); + #endif From patchwork Fri Apr 3 14:04:38 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2101 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=1775225178; bh=WUquslnPlqi7xjAA4yjMwIZmqnk5XcSp1j8Q1E/p/RQ=; 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=XsqrEwFmkRePsK/4OnRUsLxso74tsJjrpCpJMcXN8wKQjdY8ePbFIcNCOTNB0GCrA MNe3DUoY8x3WlGr1rHAFZ4O3AtWrXzZgD4xYlZWQMxFHdB6bfJCXltH9v7Wq933G3f n4cGccteUfIWGaqS4eF3M3Qt/zJxDc+gP7PXNOOitg1lIIO9Tgngg1N4+B76AEG2B5 lraVo/lMcRzfASJbHEDug1mPMr7vq63zsiBJy4dPguyrmapeRdrKLi3eaJjE+hZbx1 NiZXpa+oWFqdywxPTrRGJP6x2pq2zBqRnUqwPVbpbft8mg0BHV9qB+HadDApyewgjp PcsTtA7WSlZ/Q== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 308466A364 for ; Fri, 3 Apr 2026 08:06:18 -0600 (MDT) 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 oZ9hXtziMnpD for ; Fri, 3 Apr 2026 08:06:18 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225175; bh=WUquslnPlqi7xjAA4yjMwIZmqnk5XcSp1j8Q1E/p/RQ=; 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=K8RDwbQSSEa2JJRehvNoOf/jolwGF0M60gdLisEblARmfYGflVH2wq7m/HRqo6Qu+ 8Dxbnc3D/WJkE7IQhHVPX2ibWn0I8H0FEm3R8V+DiKxdx6x1yyFLFoPr/wTs1cZSSY gnKjX6SvXlP55G515QoK9/jIlsi/iejx+ARa5pNsONE3r4n8OKBH+RPqrPJMCKNwQu rBn6HEJ9iVQ4sTlbFaEBiosa6SDDZkABDB0mpJ37LkCOpvSi3I2+YM5dev981Mmz76 p3iVnvB/w6QKx7S8z0dMuekNyjGnZAQNVSUEdY43l3WpNgs09/J/GwVwuwrSuDz3n0 itNXoW4w8bQCQ== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 15E2367EE6 for ; Fri, 3 Apr 2026 08:06:15 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225172; bh=hfNPGy2v7gfAB9QMjd+oXYSGpsc8j2dcEHxkJgdObhM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NzAGlHAzj02S8EqG7dCCYz0syoxlsvUXkSW0tnMPkqsiR3mLFIxYP+0k1L8rVACby 73vbu1tpaVsQLyEdVcRPgqF775DJ3jCpxjkaYrKKj61217JM6rweaTX1URgonLsjGo 0EXdkqPd+WI8BIZEARq+4zt1fuc/XtnvkTmFV594SbLIPpiw8wIWHEq7M90p/4i7Ks LIhmQZ+1kYgQdU3re8yAGPqNygH7tzf0gNfh32Sa0FcVS5BZrENraXVo/ryqugtDdJ 5iLYmMdXiIa3WJtnLnlZp8tG1+lC/iRRcjgj7qVZUSdPcRzOGBKSdURwxBMWhSmOZP L1Owjv2KTJecg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 1E00B6A377; Fri, 3 Apr 2026 08:06:12 -0600 (MDT) 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 ktAtozG7t14W; Fri, 3 Apr 2026 08:06:12 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225167; bh=wfj9jwKRUj6096mp6mKQUj2GQIOeF/ls60Lti2f/dAU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ITBCxfxNyO26T8EvglnijAM98h1Eg264qZhHYKfCpKecnNxRBOTy2libSrhJvR8ci a2E853WV5daijoml5asHyFJbAP4vTXJlEQFOy4TJjqepb2ihKGD8lWcmdsfl/0cIEF pz3sUCK0NOaQjuTYJQiq6iC/o1CciP60/p2l8XcVaPhVDPRNXqA+JG++VqUo0rLNcM PuYo3/g3zcROqYcBoVE7oKQHTQ1wpig08M3Z7Mcva59JszPqYnybZYkLum4eYywuSE mbpLNbDlhkrjNMqd2akl+ee3wWPLg6k3Ae5cG9hb5RhH5spTyKHUcyK9ixzjT07iKa cRwOASQD01cdQ== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id A014F6A35D; Fri, 3 Apr 2026 08:06:07 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:38 -0600 Message-ID: <20260403140523.1998228-17-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: DGHOZ3IUEID6ASJYMONPLZ2J6Y2ZAB5J X-Message-ID-Hash: DGHOZ3IUEID6ASJYMONPLZ2J6Y2ZAB5J 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 16/34] vfs: cmd: Add commands for mount, umount, ls and load 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 standalone VFS commands for mount, umount, ls and load that replace the legacy filesystem commands with versions using absolute paths through the virtual filesystem layer. Mark the legacy ext and ext4l tests with buildconfigspec('cmd_fs_legacy') since the VFS commands replace the legacy syntax. Signed-off-by: Simon Glass --- cmd/Kconfig | 2 +- cmd/Makefile | 2 +- cmd/fs.c | 262 +++++++++++++++++++++++++++ fs/fs-uclass.c | 15 ++ fs/vfs.c | 81 ++++++--- include/fs.h | 14 ++ test/py/tests/test_fs/test_basic.py | 3 +- test/py/tests/test_fs/test_ext.py | 3 +- test/py/tests/test_fs/test_ext4l.py | 5 +- test/py/tests/test_fs/test_fs_cmd.py | 3 +- test/py/tests/test_fs/test_fs_fat.py | 3 +- 11 files changed, 362 insertions(+), 31 deletions(-) create mode 100644 cmd/fs.c diff --git a/cmd/Kconfig b/cmd/Kconfig index 60ebb56de00..58e3f473bf3 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -2990,7 +2990,7 @@ config CMD_FS_LEGACY config CMD_VFS bool "fs - virtual filesystem commands" depends on VFS - default y if SANDBOX + default VFS help Provides the 'fs' command with mount, umount and ls subcommands for the virtual filesystem layer. diff --git a/cmd/Makefile b/cmd/Makefile index 412a3096d0e..a4958843b40 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -94,7 +94,7 @@ obj-$(CONFIG_CMD_FPGA) += fpga.o obj-$(CONFIG_CMD_LUKS) += luks.o obj-$(CONFIG_CMD_FPGAD) += fpgad.o obj-$(CONFIG_CMD_FS_LEGACY) += fs_legacy.o -obj-$(CONFIG_CMD_VFS) += vfs.o +obj-$(CONFIG_CMD_VFS) += vfs.o fs.o obj-$(CONFIG_CMD_FUSE) += fuse.o obj-$(CONFIG_CMD_FWU_METADATA) += fwu_mdata.o obj-$(CONFIG_CMD_GETTIME) += gettime.o diff --git a/cmd/fs.c b/cmd/fs.c new file mode 100644 index 00000000000..21fe6ba3655 --- /dev/null +++ b/cmd/fs.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * VFS-based filesystem commands - mount, umount, ls, load + * + * These replace the legacy commands in cmd/fs_legacy.c with versions that + * use absolute paths through the virtual filesystem layer. + * + * Copyright 2026 Simon Glass + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +int do_load(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[], + int fstype); +int do_fs_types(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]); + +static int mount_handler(int argc, char *const argv[]) +{ + struct udevice *vfs, *fsdev, *dir, *mnt; + const char *subpath; + int ret; + + vfs = vfs_root(); + if (!vfs) + return -ENXIO; + + if (argc < 2) { + vfs_print_mounts(); + return 0; + } + + if (argc < 3) + return -EINVAL; + + /* Check if already mounted */ + ret = vfs_find_mount(vfs, argv[2], &mnt, &subpath); + if (!ret && mnt && !*subpath) + return -EBUSY; + + ret = uclass_get_device_by_name(UCLASS_FS, argv[1], &fsdev); + if (ret) + return ret; + + ret = vfs_resolve(vfs, argv[2], &dir); + if (ret) + return ret; + + return vfs_mount(vfs, dir, fsdev); +} + +static int do_mount(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret; + + ret = mount_handler(argc, argv); + if (ret) { + printf("mount failed: %dE\n", ret); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + mount, 3, 1, do_mount, + "mount a filesystem", + "[ ]\n" + " - With no args, list all mounts\n" + " - Mount device 'dev' at 'mountpoint'" +); + +static int umount_handler(const char *path) +{ + struct udevice *vfs; + + vfs = vfs_root(); + if (!vfs) + return -ENXIO; + + return vfs_umount_path(vfs, path); +} + +static int do_umount(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + ret = umount_handler(argv[1]); + if (ret) { + printf("Error: %dE\n", ret); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + umount, 2, 1, do_umount, + "unmount a filesystem", + "\n" + " - Unmount the filesystem at 'mountpoint'" +); + +static int do_cd(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *path = argc >= 2 ? argv[1] : "/"; + int ret; + + ret = vfs_chdir(path); + if (ret) { + printf("Error: %dE\n", ret); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + cd, 2, 1, do_cd, + "change working directory", + "[]\n" + " - Change to 'path' in the VFS (default /)" +); + +static int do_pwd(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + printf("%s\n", vfs_getcwd()); + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + pwd, 1, 1, do_pwd, + "print working directory", + "\n - Print the current VFS working directory" +); + +static int do_ls(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *path = argc >= 2 ? argv[1] : NULL; + int ret; + + ret = vfs_ls(path); + if (ret) { + printf("Error: %dE\n", ret); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + ls, 2, 1, do_ls, + "list files in a directory (default cwd)", + "[]\n" + " - List files at 'path' in the VFS (default cwd)" +); + + +static int do_vfs_load(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct file_uc_priv *uc_priv; + ulong addr, bytes = 0; + struct udevice *fil; + long len_read; + loff_t pos = 0; + void *buf; + int ret; + + if (argc < 3) + return CMD_RET_USAGE; + + addr = hextoul(argv[1], NULL); + if (argc >= 4) + bytes = hextoul(argv[3], NULL); + if (argc >= 5) + pos = hextoull(argv[4], NULL); + + ret = vfs_open_file(argv[2], DIR_O_RDONLY, &fil); + if (ret) { + printf("Error: %dE\n", ret); + return CMD_RET_FAILURE; + } + + uc_priv = dev_get_uclass_priv(fil); + if (!bytes) + bytes = uc_priv->size - pos; + + buf = map_sysmem(addr, bytes); + len_read = file_read_at(fil, buf, pos, bytes); + unmap_sysmem(buf); + + if (len_read < 0) { + printf("Read failed: %ldE\n", len_read); + return CMD_RET_FAILURE; + } + + env_set_hex("fileaddr", addr); + env_set_hex("filesize", len_read); + + printf("%ld bytes read\n", len_read); + + return CMD_RET_SUCCESS; +} + +static int do_load_vfs(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char *endp; + + /* + * Detect legacy syntax: load [ ...] + * If argv[1] is not a pure hex number, assume legacy syntax. + */ + if (argc >= 2) { + hextoul(argv[1], &endp); + if (*endp) + return do_load(cmdtp, flag, argc, argv, FS_TYPE_ANY); + } + + return do_vfs_load(cmdtp, flag, argc, argv); +} + +static int do_fstypes(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_fs_types(cmdtp, flag, argc, argv); +} + +U_BOOT_CMD( + fstypes, 1, 1, do_fstypes, + "List supported filesystem types", "" +); + +U_BOOT_CMD( + load, 7, 0, do_load_vfs, + "load binary file from a filesystem", + " [bytes [pos]]\n" + " - Load binary file from 'path' in the VFS to address 'addr'.\n" + " 'bytes' gives the size to load in bytes.\n" + " If 'bytes' is 0 or omitted, the file is read until the end.\n" + " 'pos' gives the file byte position to start reading from.\n" + " If 'pos' is 0 or omitted, the file is read from the start.\n" + "load [ [ [ [bytes [pos]]]]]\n" + " - Legacy: load from block device interface" +); diff --git a/fs/fs-uclass.c b/fs/fs-uclass.c index 2525067f166..4492ff60522 100644 --- a/fs/fs-uclass.c +++ b/fs/fs-uclass.c @@ -46,6 +46,21 @@ int fs_split_path(const char *fname, char **subdirp, const char **leafp) return 0; } +void fs_split_path_inplace(char *fname, const char **dirp, const char **leafp) +{ + char *last_slash; + + last_slash = strrchr(fname, '/'); + if (last_slash) { + *leafp = last_slash + 1; + *last_slash = '\0'; + *dirp = fname; + } else { + *leafp = fname; + *dirp = ""; + } +} + int fs_lookup_dir(struct udevice *dev, const char *path, struct udevice **dirp) { struct fs_ops *ops = fs_get_ops(dev); diff --git a/fs/vfs.c b/fs/vfs.c index 79b1ca0ef89..0cce5872735 100644 --- a/fs/vfs.c +++ b/fs/vfs.c @@ -408,8 +408,57 @@ static int vfs_resolve_mount(const char *path, char *resolved, int size, return vfs_find_mount(vfs, path, mntp, subpathp); } -int vfs_resolve(struct udevice *vfs, const char *path, - struct udevice **dirp) +/** + * vfs_resolve_dir() - Resolve a path to its parent directory and leaf name + * + * Resolves the mount, splits the subpath into directory and leaf, and + * looks up the directory device. + * + * @path: Absolute or relative VFS path + * @dirp: Returns the UCLASS_DIR device for the parent directory + * @leafp: Returns allocated copy of the leaf filename (caller must free) + * Return: 0 if OK, -ve on error + */ +static int vfs_resolve_dir(const char *path, struct udevice **dirp, + char **leafp) +{ + char resolved[FILE_MAX_PATH_LEN]; + struct udevice *mnt; + struct vfsmount *mnt_priv; + const char *subpath, *dirpart, *leaf; + char *sub; + int ret; + + ret = vfs_resolve_mount(path, resolved, sizeof(resolved), + &mnt, &subpath); + if (ret) + return log_msg_ret("vdm", ret); + + if (!mnt) + return log_msg_ret("vdr", -ENOENT); + + mnt_priv = dev_get_uclass_priv(mnt); + + /* + * Split in place - subpath points into resolved[], so we can + * modify it. After the split, dirpart is the directory portion + * and leaf points to the leaf filename. + */ + sub = (char *)subpath; + fs_split_path_inplace(sub, &dirpart, &leaf); + + ret = fs_lookup_dir(mnt_priv->target, dirpart, dirp); + if (ret) + return log_msg_ret("vdd", ret); + + *leafp = strdup(leaf); + if (!*leafp) + return log_msg_ret("vdl", -ENOMEM); + + return 0; +} + +int vfs_resolve(struct udevice *vfs, const char *path, struct udevice **dirp) { struct udevice *cur_fs, *best; const char *remain; @@ -501,34 +550,18 @@ void vfs_print_mounts(void) int vfs_open_file(const char *path, enum dir_open_flags_t oflags, struct udevice **filp) { - struct udevice *vfs, *mnt, *dir; - const char *subpath, *leaf; - struct vfsmount *mnt_priv; - char *dirpath; + struct udevice *dir; + char *leaf; int ret; - vfs = vfs_root(); - if (!vfs) - return log_msg_ret("voi", -ENXIO); - - ret = vfs_find_mount(vfs, path, &mnt, &subpath); - if (ret) - return log_msg_ret("vom", ret); - - mnt_priv = dev_get_uclass_priv(mnt); - - ret = fs_split_path(subpath, &dirpath, &leaf); - if (ret) - return log_msg_ret("vos", ret); - - ret = fs_lookup_dir(mnt_priv->target, dirpath, &dir); - free(dirpath); + ret = vfs_resolve_dir(path, &dir, &leaf); if (ret) - return log_msg_ret("vod", ret); + return log_msg_ret("vof", ret); ret = dir_open_file(dir, leaf, oflags, filp); + free(leaf); if (ret) - return log_msg_ret("vof", ret); + return log_msg_ret("voo", ret); return 0; } diff --git a/include/fs.h b/include/fs.h index c6b6323be3e..efca9b80611 100644 --- a/include/fs.h +++ b/include/fs.h @@ -122,4 +122,18 @@ int fs_lookup_dir(struct udevice *dev, const char *path, struct udevice **dirp); */ int fs_split_path(const char *fname, char **subdirp, const char **leafp); +/** + * fs_split_path_inplace() - Split a path into directory and leaf in place + * + * Modifies @fname by null-terminating at the last '/'. Sets @dirp to + * point to the directory part and @leafp to the leaf. If there is no + * '/', @dirp is set to "" and @leafp points to @fname unchanged. + * + * @fname: Path to split (modified in place when it contains '/') + * @dirp: Returns pointer to the directory part + * @leafp: Returns pointer to the leaf filename + */ +void fs_split_path_inplace(char *fname, const char **dirp, + const char **leafp); + #endif diff --git a/test/py/tests/test_fs/test_basic.py b/test/py/tests/test_fs/test_basic.py index 174e2e074f4..17e197df894 100644 --- a/test/py/tests/test_fs/test_basic.py +++ b/test/py/tests/test_fs/test_basic.py @@ -16,7 +16,8 @@ from fstest_defs import SMALL_FILE, BIG_FILE from fstest_helpers import assert_fs_integrity -@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('sandbox') +@pytest.mark.boardspec('!sandbox') @pytest.mark.slow class TestFsBasic: """Test basic filesystem operations via C unit tests.""" diff --git a/test/py/tests/test_fs/test_ext.py b/test/py/tests/test_fs/test_ext.py index 41f126e7876..a72e13a87bc 100644 --- a/test/py/tests/test_fs/test_ext.py +++ b/test/py/tests/test_fs/test_ext.py @@ -26,7 +26,8 @@ def str2fat(long_filename): name = '%s~1' % name[:6] return '%-8s %s' % (name, ext) -@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('sandbox') +@pytest.mark.boardspec('!sandbox') @pytest.mark.slow class TestFsExt(object): def test_fs_ext1(self, ubman, fs_obj_ext): diff --git a/test/py/tests/test_fs/test_ext4l.py b/test/py/tests/test_fs/test_ext4l.py index 0930ebd01ea..eb332d1b154 100644 --- a/test/py/tests/test_fs/test_ext4l.py +++ b/test/py/tests/test_fs/test_ext4l.py @@ -15,7 +15,8 @@ from tempfile import NamedTemporaryFile import pytest -@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('sandbox') +@pytest.mark.buildconfigspec('fs_ext4l') class TestExt4l: """Test ext4l filesystem operations.""" @@ -79,6 +80,7 @@ class TestExt4l: with ubman.log.section('Test ext4l msgs'): ubman.run_ut('fs', 'fs_test_ext4l_msgs', fs_image=ext4_image) + @pytest.mark.boardspec('!sandbox') def test_ls(self, ubman, ext4_image): """Test that ext4l can list directory contents.""" with ubman.log.section('Test ext4l ls'): @@ -114,6 +116,7 @@ class TestExt4l: with ubman.log.section('Test ext4l statfs'): ubman.run_ut('fs', 'fs_test_ext4l_statfs', fs_image=ext4_image) + @pytest.mark.boardspec('!sandbox') def test_fsinfo(self, ubman, ext4_image): """Test that fsinfo command displays filesystem statistics.""" with ubman.log.section('Test ext4l fsinfo'): diff --git a/test/py/tests/test_fs/test_fs_cmd.py b/test/py/tests/test_fs/test_fs_cmd.py index c925547c7bc..336300afe3d 100644 --- a/test/py/tests/test_fs/test_fs_cmd.py +++ b/test/py/tests/test_fs/test_fs_cmd.py @@ -4,7 +4,8 @@ import pytest -@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('sandbox') +@pytest.mark.boardspec('!sandbox') @pytest.mark.buildconfigspec('cmd_fs_generic') def test_fstypes(ubman): """Test that `fstypes` prints a result which includes `sandbox`.""" diff --git a/test/py/tests/test_fs/test_fs_fat.py b/test/py/tests/test_fs/test_fs_fat.py index b61d8ab9eac..8028213dae3 100644 --- a/test/py/tests/test_fs/test_fs_fat.py +++ b/test/py/tests/test_fs/test_fs_fat.py @@ -11,7 +11,8 @@ This test verifies fat specific file system behaviour. import pytest import re -@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('sandbox') +@pytest.mark.boardspec('!sandbox') @pytest.mark.slow class TestFsFat(object): def test_fs_fat1(self, ubman, fs_obj_fat): From patchwork Fri Apr 3 14:04:39 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2102 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=1775225180; bh=6I6DLYePF9HbRn2vgOoe2amAEyZcloS7Q1QfYa5+/cs=; 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=fXcmqWruutSjJGiicbnUmQ3kuXzbDIPT8iDvwEan2gAtjK8Nydp9oC4n4xu8W+bs+ sfePRQyfYDAGLcPz+UQtxj6ssogxuaQNXz8zFKwIO5LNCQnL07y23ApAoLd0Cryatk W63C2Xmg4uWgqgPKTjK8yvxJwZDNMO4yHwvV5t8gLkKHqPLhgj7QTzk/6l2yIHFyc8 Yk9BEwPbhK0Utp1ydobGFQDIlmwwFDF1z01dp/M7iA0kvDCujuwtkDkkgneyTta7c1 dZBbA8OsZzdnneVcO8v6n31cPBqG/nYYqmQWprMUnyS2qtB4rNAxbfd1VDWpbgoW8N GsUnfHkpMOYZw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id A03036A34C for ; Fri, 3 Apr 2026 08:06:20 -0600 (MDT) 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 E6TL71wrMFL0 for ; Fri, 3 Apr 2026 08:06:20 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225178; bh=6I6DLYePF9HbRn2vgOoe2amAEyZcloS7Q1QfYa5+/cs=; 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=wXvZqNwpwnFKdk8Agqiokd+zkOp0zeSspsOmQ0eTrAWNrjC7lhH1ClfhfLeroW292 aQsYNuI/wi945AWiLAZppMsTCpdHZya0oeF4aARGWryzw+CoEo0Fdu8QzurFuUIqLk IXDZQWtq3zuH9Djf6fkmohguy+FSwIVxP8gPgB/Ni3F2Wi0qd+5A6ahbfqJCMKKfAF vx58/liwsPuc1jT1flHqrOfvNF4+oPfJ0NtFbM6MoXU4CVN4RuQ47SXeiHDpNWf3ye SrFDBWA9+GEjOkeptHHgqXXF06tcBv1QWDDR8ntzXAjp2hoiy93by3LQ3QhUQh0PoX LuNG9uen1Tg8g== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id B37A36A34E for ; Fri, 3 Apr 2026 08:06:18 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225177; bh=YntwcLXn94XTuGLrOb7MHlMG+N0bOihuidTQL9GyhUY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lWVtBt2pefaGmMU/WuLo8MGTfuYgftyBB4MQeG5JnGb/8IXmwAX8o1cmp9UzuyT06 VhTdWui/G+fJY4w6PN4CWGjcQFe9pbWaHTEeN+0dni2r6rmi2olzOYtbAYn9kaX2qI Tw97YN3HjxfdDHO//4tISFHOdCIpr1Pp32Xn5yl8P5TRnurW/b0Bx+0rQ7TbLflk6n hopMn4ckc8uwn20F2BvLZ2NHpcHt+L8xqKfYLTCglv2XH2T0Cfm1Nmnx5y3usGXquG rpZ4voZ13k3rFRVSkbisdTrbTavy4uT9e+zSVnMqThT+/hX/wjEKj7WKTApdtSpE7W USNqIe4MoVzeA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id B77D66A34F; Fri, 3 Apr 2026 08:06:17 -0600 (MDT) 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 JzKwwcDAIYC5; Fri, 3 Apr 2026 08:06:17 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225168; bh=LHPjf8A0rQuQCzuYvMgU4NgChKqGnjY+fvxa+KCi2Ms=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=uYoawkVHEkW+km4LRCm0CxB2nquST33eKuGQDX7YdVUpcCw6n43ILVD3+fyyE7lLm nZmQq7aDfd+d0ohSsenCCYaKhOPus00jYt91Cop9IkvHV3TxIENtAo/7yC+UFGyPyw jz8WOjzkIrD80gem7hpG231KEwAoIiygFWB/f5P9sbONQk+zZsWI7DKYVQWjhuDf7m HQVBtajeIxZJ/j2aTXax4HCMexjzEVevs89so8I7QLg72NU05Gtqy4eZHFw6zblIgB fbt/EuXa7f8RswfYK0vv5MztPLqHmQY+KaBe/7o8fz6XNUpOdx/cHrlUpiiWB5iTbW SrvgDhTo6fVxg== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id B1C6B6A34C; Fri, 3 Apr 2026 08:06:08 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:39 -0600 Message-ID: <20260403140523.1998228-18-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: XWDZJGJIBFYKA6CJLORFMXYB3PAND6VG X-Message-ID-Hash: XWDZJGJIBFYKA6CJLORFMXYB3PAND6VG 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 17/34] vfs: Add support for writing files 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 The FILE uclass currently only supports reading. Add file_write_at() and write_iter() operations so that files can be written using driver model. Provide a sandbox implementation (sandbox_write_iter) and allow sandbox_dir_open_file() to create new files when opened for writing. Also add a simple test. Signed-off-by: Simon Glass --- fs/file-uclass.c | 21 +++++++++++++++++++++ fs/sandbox/sandboxfs.c | 30 +++++++++++++++++++++++++++--- include/file.h | 23 +++++++++++++++++++++++ test/dm/fs.c | 30 ++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 3 deletions(-) diff --git a/fs/file-uclass.c b/fs/file-uclass.c index 91ced31a94e..58d0a75f0c3 100644 --- a/fs/file-uclass.c +++ b/fs/file-uclass.c @@ -96,6 +96,27 @@ long file_read_at(struct udevice *dev, void *buf, loff_t offset, long len) return ret; } +long file_write_at(struct udevice *dev, const void *buf, loff_t offset, + long len) +{ + struct file_uc_priv *uc_priv = dev_get_uclass_priv(dev); + struct file_ops *ops = file_get_ops(dev); + struct iov_iter iter; + ssize_t ret; + + if (!ops->write_iter) + return log_msg_ret("fwn", -ENOSYS); + + iter_ubuf(&iter, false, (void *)buf, len); + + ret = ops->write_iter(dev, &iter, offset); + if (ret < 0) + return log_msg_ret("fww", ret); + uc_priv->pos = offset + ret; + + return ret; +} + UCLASS_DRIVER(file) = { .name = "file", .id = UCLASS_FILE, diff --git a/fs/sandbox/sandboxfs.c b/fs/sandbox/sandboxfs.c index 494af0d373a..70fb829ae67 100644 --- a/fs/sandbox/sandboxfs.c +++ b/fs/sandbox/sandboxfs.c @@ -277,8 +277,29 @@ static ssize_t sandbox_read_iter(struct udevice *dev, struct iov_iter *iter, return ret; } +static ssize_t sandbox_write_iter(struct udevice *dev, struct iov_iter *iter, + loff_t pos) +{ + struct file_priv *priv = dev_get_priv(dev); + ssize_t ret; + + log_debug("start dev '%s' len %lx\n", dev->name, iter->count); + ret = os_lseek(priv->fd, pos, OS_SEEK_SET); + if (ret < 0) + return log_msg_ret("vfs", ret); + + ret = os_write(priv->fd, iter_iov_ptr(iter), iter_iov_avail(iter)); + if (ret < 0) + return log_msg_ret("vfw", ret); + iter_advance(iter, ret); + log_debug("wrote %lx bytes\n", ret); + + return ret; +} + static struct file_ops sandbox_file_ops = { .read_iter = sandbox_read_iter, + .write_iter = sandbox_write_iter, }; static const struct udevice_id file_ids[] = { @@ -309,10 +330,13 @@ static int sandbox_dir_open_file(struct udevice *dir, const char *leaf, snprintf(pathname, sizeof(pathname), "%s/%s", *uc_priv->path ? uc_priv->path: ".", leaf); ftype = os_get_filetype(pathname); - if (ftype < 0) - return log_msg_ret("soF", ftype); - if (ftype != OS_FILET_REG) + if (ftype < 0) { + /* Allow creating new files for write modes */ + if (oflags == DIR_O_RDONLY) + return log_msg_ret("soF", ftype); + } else if (ftype != OS_FILET_REG) { return log_msg_ret("sOf", -EINVAL); + } if (oflags == DIR_O_RDONLY) mode = OS_O_RDONLY; diff --git a/include/file.h b/include/file.h index d1fdf3c13f1..051e5e37549 100644 --- a/include/file.h +++ b/include/file.h @@ -43,6 +43,17 @@ struct file_ops { */ ssize_t (*read_iter)(struct udevice *dev, struct iov_iter *iter, loff_t pos); + + /** + * write_iter() - Write data to a file + * + * @dev: File to write to + * @iter: Iterator providing the data to write + * @pos: File position to write at + * Return: number of bytes written, or -ve error code + */ + ssize_t (*write_iter)(struct udevice *dev, struct iov_iter *iter, + loff_t pos); }; /* Get access to a file's operations */ @@ -69,6 +80,18 @@ long file_read(struct udevice *dev, void *buf, long len); */ long file_read_at(struct udevice *dev, void *buf, loff_t offset, long len); +/** + * file_write_at() - Write data to a file at a particular position + * + * @dev: File to write to + * @buf: Buffer containing data to write + * @offset: Offset within the file to start writing + * @len: Number of bytes to write + * Return: number of bytes written, or -ve error code + */ +long file_write_at(struct udevice *dev, const void *buf, loff_t offset, + long len); + /** * file_add_probe() - Create a new file device for a file * diff --git a/test/dm/fs.c b/test/dm/fs.c index e45441e5241..f42604fb097 100644 --- a/test/dm/fs.c +++ b/test/dm/fs.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -107,6 +108,35 @@ static int dm_test_fs_file(struct unit_test_state *uts) } DM_TEST(dm_test_fs_file, UTF_SCAN_FDT); +/* Test writing a file */ +static int dm_test_fs_file_write(struct unit_test_state *uts) +{ + struct udevice *fsdev, *dir, *fil; + static const char data[] = "hello world\n"; + char buf[32]; + + ut_assertok(uclass_first_device_err(UCLASS_FS, &fsdev)); + ut_assertok(fs_mount(fsdev)); + ut_assertok(fs_lookup_dir(fsdev, "", &dir)); + + /* Write a new file */ + ut_assertok(dir_open_file(dir, "_test_write.tmp", DIR_O_WRONLY, &fil)); + ut_asserteq(sizeof(data) - 1, + file_write_at(fil, data, 0, sizeof(data) - 1)); + + /* Read it back */ + ut_assertok(dir_open_file(dir, "_test_write.tmp", DIR_O_RDONLY, &fil)); + memset(buf, '\0', sizeof(buf)); + ut_asserteq(sizeof(data) - 1, file_read(fil, buf, sizeof(buf))); + ut_asserteq_str("hello world\n", buf); + + /* Clean up */ + os_unlink("_test_write.tmp"); + + return 0; +} +DM_TEST(dm_test_fs_file_write, UTF_SCAN_FDT); + #if IS_ENABLED(CONFIG_VFS) /* Test VFS init and root directory operations */ static int dm_test_vfs_init(struct unit_test_state *uts) From patchwork Fri Apr 3 14:04:40 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2103 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=1775225183; bh=qgW+OlXfhAKjaFtO7zOcsQ/mJxgcnjWBYBFtAkqJUjk=; 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=UkY+idWPqtpmq6hukrpaQyC5m+kWevlot2tB+YE/oFjfXEWwLBHoWenO9xdLy4Hi3 TfW8qSdT1D+gmqim4IUrFznbGwMh3zwmJ0+zUC0WivBAmqfAXzVh9irax4eZ4Nroap mFiR9whqmdKv9Iy7slu/tYXwQCxQs1uDF7J5C4QInyZ/MhRekQj9lsBgEISvmOoyBf 8z2Je7sOv84yE9SPvA8g5pJFf56+5fOWT/WM85GBguBm6ccAasn03NhRoQ+EHCdtaf Y7eATzdSYmqQW+RPqp/qd7cHyUpjdF/Lx0OyqPRQSuScM++sbA4d93nxFLwZSgIYaN D6IJ75CYmIgiA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 012126A35C for ; Fri, 3 Apr 2026 08:06:23 -0600 (MDT) 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 RgDfUpjCyjcE for ; Fri, 3 Apr 2026 08:06:22 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225181; bh=qgW+OlXfhAKjaFtO7zOcsQ/mJxgcnjWBYBFtAkqJUjk=; 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=LTs69oU/ICurPbldFpaMp7e8whsRBWg6+8zHkRuW7c/BpGJc9PX7/1+0lXy+3xK2k amAjMX5ClfiXdrADIfmMk8XYbawwU1/LK78qs+NK70fxhJPwJBGYnt7TfkgT8jnvXi aYAoyT/sWliSMzdDFvyFiOEVx0RNer7CzEuH8S074eJfXntBLBJH+CWIJbRhww7G7W qlxG9mWgjp2nrdYdfUStiNjDd/mpQJsssnhuAEjD14wF6MOGAMw9T9SeZ+3m7+gqYb Cx1gEvnSOJEOsT6jme9oK+qkLRJZeF6REHgb0Lcpqmn1zAO2LyPMns/gpkzq5pnXjF 5AA35g67z7y5g== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 325686A35F for ; Fri, 3 Apr 2026 08:06:21 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225177; bh=JghRX2GQqggrY967tQcJKWn6wjEfS7iwaAuofjixfHM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=wF8mWN0luw3mvsm4Wp6kY2PIQ8OvQWHN1laNsRfFqFr8e58DgHnzgGYicRlTG2CTn pI41NesJRr/RMDj6jeqTORFvTpg58t0K2/4S5FPOwv6mKRtH8H/wyT1Qi66TCbOiLn zztxtzCIQqXs9k0Sh66vC4euW0D6uUX1kibSAYNwI09pNPTclZT9dDm7Bnx3EBun+M wQG2dKlZr48g9Y+B3cnm8n6ERZ4Idolc3btvGd57LWtAMBGgX+AD7X3mr8Vq96oqdU lNsfcNphhqF5/vm7Oj8vAURm7aF5YiAY8n2Rbp0JedCSMQjmsxnjBqrg0sz2B4dTEf NHZcXUCuuxsuA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id C15D16A34C; Fri, 3 Apr 2026 08:06:17 -0600 (MDT) 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 lTDID7SWfUtV; Fri, 3 Apr 2026 08:06:17 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225173; bh=E7bXGd6Gv5bV/LW10ndZenXXSdxxgk+NHdKeq2e7Gcs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ms2ARrqQDeqm1+KtauI5qnhCa3JBLdL258EqWKfwCrwGlEAFmNMnGXJRa0j0+bfq5 YLYCkvHY8dFf/iJ/Ceno7QO4zsy/BStyin0WibcP426t1iz0Qro0vmTQXhHoItnR0A 3MFArYgyMcnIbGgnIXOJf2WmaQ9a02osHGbUgpFi3u6LZxpWNjNubcMx4IPIKvYbCW ftEQGHYP9FMSwogf7zDvfwUM4VCJ0rUP5v4zETchvRlmXmwaCo6pzbZn0K7obwukzz fwfOtAtnOESV6nBn9l6aTsMRIu1m/3HyJ8WvlmjmMWOGSwtPsMNNmFM36eqcH7ty+k 3NaQptaFlGjYA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 3BE876A376; Fri, 3 Apr 2026 08:06:13 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:40 -0600 Message-ID: <20260403140523.1998228-19-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 25D7PDYUUW7455RYVN66WSX3V355G7EL X-Message-ID-Hash: 25D7PDYUUW7455RYVN66WSX3V355G7EL 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 18/34] vfs: Add stat and block-device mount 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 vfs_stat() to look up file or directory information by path. Add fs_mount_blkdev() and fs_mount_blkdev_auto() to create and mount a filesystem device from a block device, with VFS_FS_TYPE() registration for auto-detection of filesystem types. Signed-off-by: Simon Glass --- cmd/fs.c | 29 ++++++++++- fs/vfs.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/vfs.h | 109 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 277 insertions(+), 1 deletion(-) diff --git a/cmd/fs.c b/cmd/fs.c index 21fe6ba3655..694222e5d10 100644 --- a/cmd/fs.c +++ b/cmd/fs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * VFS-based filesystem commands - mount, umount, ls, load + * VFS-based filesystem commands - mount, umount, ls, load, size * * These replace the legacy commands in cmd/fs_legacy.c with versions that * use absolute paths through the virtual filesystem layer. @@ -171,6 +171,33 @@ U_BOOT_CMD( " - List files at 'path' in the VFS (default cwd)" ); +static int do_size(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct fs_dirent dent; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + ret = vfs_stat(argv[1], &dent); + if (ret) { + printf("Error: %dE\n", ret); + return CMD_RET_FAILURE; + } + + env_set_hex("filesize", dent.size); + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + size, 2, 0, do_size, + "determine a file's size", + "\n" + " - Find file at 'path' in the VFS, determine its size,\n" + " and store in the 'filesize' variable." +); static int do_vfs_load(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) diff --git a/fs/vfs.c b/fs/vfs.c index 0cce5872735..3812706af6c 100644 --- a/fs/vfs.c +++ b/fs/vfs.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "vfs_internal.h" #include @@ -566,6 +567,40 @@ int vfs_open_file(const char *path, enum dir_open_flags_t oflags, return 0; } +int vfs_stat(const char *path, struct fs_dirent *dent) +{ + struct fs_dir_stream *strm; + struct fs_dirent entry; + struct udevice *dir; + char *leaf; + int ret; + + ret = vfs_resolve_dir(path, &dir, &leaf); + if (ret) + return log_msg_ret("vsd", ret); + + /* Scan the directory for the matching entry */ + ret = dir_open(dir, &strm); + if (ret) { + free(leaf); + return log_msg_ret("vso", ret); + } + + while (!(ret = dir_read(dir, strm, &entry))) { + if (!strcmp(entry.name, leaf)) { + *dent = entry; + dir_close(dir, strm); + free(leaf); + return 0; + } + } + + dir_close(dir, strm); + free(leaf); + + return log_msg_ret("vsn", -ENOENT); +} + int vfs_ls(const char *path) { char resolved[FILE_MAX_PATH_LEN]; @@ -611,6 +646,111 @@ int vfs_ls(const char *path) return 0; } +int fs_mount_blkdev_auto(struct blk_desc *desc, int part_num, + struct disk_partition *part, const char *mountpoint) +{ + struct driver *drv = ll_entry_start(struct driver, driver); + const int n_ents = ll_entry_count(struct driver, driver); + int i; + + for (i = 0; i < n_ents; i++, drv++) { + const char *name = drv->name; + int len = strlen(name); + char type[30]; + int ret; + + if (drv->id != UCLASS_FS) + continue; + + /* Strip "_fs" suffix to get the type name */ + if (len < 4 || strcmp(name + len - 3, "_fs")) + continue; + + strlcpy(type, name, min((int)sizeof(type), len - 2)); + + ret = fs_mount_blkdev(type, desc, part_num, part, mountpoint); + if (!ret) + return 0; + if (ret == -EBUSY) + return log_msg_ret("fmb", ret); + } + + return -ENODEV; +} + +int fs_mount_blkdev(const char *type, struct blk_desc *desc, int part_num, + struct disk_partition *part, const char *mountpoint) +{ + char resolved[FILE_MAX_PATH_LEN]; + static int blkfs_count; + char drv_name[30], dev_name[30]; + struct udevice *vfs, *mnt, *dir, *dev; + const char *subpath; + struct fs_plat *plat; + char *str; + int ret; + + vfs = vfs_root(); + if (!vfs) + return log_msg_ret("fmi", -ENXIO); + + mountpoint = vfs_path_resolve(vfs_getcwd(), mountpoint, + resolved, sizeof(resolved)); + if (!mountpoint) + return log_msg_ret("fmp", -ENAMETOOLONG); + + snprintf(drv_name, sizeof(drv_name), "%s_fs", type); + snprintf(dev_name, sizeof(dev_name), "%s.%d", drv_name, blkfs_count); + + str = strdup(dev_name); + if (!str) + return -ENOMEM; + + ret = device_bind_driver(dm_root(), drv_name, str, &dev); + if (ret) { + free(str); + return log_msg_ret("fmb", ret); + } + device_set_name_alloced(dev); + + /* Set block device info in uclass platdata before probing */ + plat = dev_get_uclass_plat(dev); + plat->desc = desc; + plat->part = *part; + + ret = device_probe(dev); + if (ret) { + device_unbind(dev); + return log_msg_ret("fmp", ret); + } + + /* Check if the mountpoint is already in use */ + ret = vfs_find_mount(vfs, mountpoint, &mnt, &subpath); + if (!ret && mnt && !*subpath) { + device_remove(dev, DM_REMOVE_NORMAL); + device_unbind(dev); + return log_msg_ret("fme", -EBUSY); + } + + ret = vfs_resolve(vfs, mountpoint, &dir); + if (ret) { + device_remove(dev, DM_REMOVE_NORMAL); + device_unbind(dev); + return log_msg_ret("fmr", ret); + } + + ret = vfs_mount(vfs, dir, dev); + if (ret) { + device_remove(dev, DM_REMOVE_NORMAL); + device_unbind(dev); + return log_msg_ret("fmm", ret); + } + + blkfs_count++; + + return 0; +} + struct udevice *vfs_root(void) { struct udevice *dev; diff --git a/include/vfs.h b/include/vfs.h index 521494e4199..6e0c67ca031 100644 --- a/include/vfs.h +++ b/include/vfs.h @@ -13,6 +13,10 @@ #include +struct blk_desc; +struct disk_partition; +struct fs_dirent; +struct fs_statfs; struct udevice; /** @@ -182,4 +186,109 @@ int vfs_ls(const char *path); int vfs_open_file(const char *path, enum dir_open_flags_t oflags, struct udevice **filp); +/** + * vfs_stat() - Get information about a file or directory + * + * Looks up the named entry in its parent directory and fills in a + * struct fs_dirent with type, size and timestamps. Handles both files + * and directories. Calls vfs_init() if needed. + * + * @path: Absolute or relative VFS path + * @dent: Returns the directory entry information + * Return: 0 if OK, -ve on error + */ +int vfs_stat(const char *path, struct fs_dirent *dent); + +/** + * vfs_mkdir() - Create a directory by absolute or relative VFS path + * + * @path: Path of the directory to create + * Return: 0 if OK, -ve on error + */ +int vfs_mkdir(const char *path); + +/** + * vfs_unlink() - Delete a file by absolute or relative VFS path + * + * @path: Path of the file to delete + * Return: 0 if OK, -ve on error + */ +int vfs_unlink(const char *path); + +/** + * vfs_rename() - Rename or move a file or directory + * + * Both paths must be on the same mounted filesystem. + * + * @old_path: Current absolute or relative VFS path + * @new_path: New absolute or relative VFS path + * Return: 0 if OK, -EXDEV if paths are on different mounts, other -ve on error + */ +/** + * vfs_ln() - Create a symbolic link + * + * @path: Absolute or relative VFS path for the new symlink + * @target: Target path the symlink points to + * Return: 0 if OK, -ve on error + */ +int vfs_ln(const char *path, const char *target); + +int vfs_rename(const char *old_path, const char *new_path); + +/** + * vfs_readlink() - Read a symbolic link target + * + * @path: Absolute or relative VFS path to the symlink + * @buf: Buffer to receive the target path + * @size: Size of buffer + * Return: length of target, or -ve on error + */ +int vfs_readlink(const char *path, char *buf, int size); + +/** + * vfs_statfs() - Get filesystem statistics for a mount point + * + * @path: Absolute or relative VFS path (any path within the mount) + * @stats: Returns filesystem statistics + * Return: 0 if OK, -ve on error + */ +int vfs_statfs(const char *path, struct fs_statfs *stats); + +/** + * fs_mount_blkdev_auto() - Auto-detect and mount a filesystem from a block + * device + * + * Tries each UCLASS_FS driver with a "_fs" suffix in turn until one + * succeeds. + * + * @desc: Block device descriptor + * @part: Partition information + * @mountpoint: Absolute path to mount at + * Return: 0 if OK, -ENODEV if no FS type matched, other -ve on error + */ +int fs_mount_blkdev_auto(struct blk_desc *desc, int part_num, + struct disk_partition *part, const char *mountpoint); + +/** + * fs_mount_blkdev() - Create and mount a FS device from a block device + * + * Uses a naming convention to find the right driver: filesystem type "ext4" + * maps to driver "ext4_fs". The block device and partition info are stored + * in the generic struct fs_plat so that any block-backed FS driver can + * read them. + * + * @type: Filesystem type name (e.g. "ext4") + * @desc: Block device descriptor + * @part: Partition information + * @mountpoint: Absolute path to mount at + * Return: 0 if OK, -ve on error + */ +int fs_mount_blkdev(const char *type, struct blk_desc *desc, int part_num, + struct disk_partition *part, const char *mountpoint); + +/** + * vfs_print_mounts() - Print all current mounts + */ +void vfs_print_mounts(void); + #endif From patchwork Fri Apr 3 14:04:41 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2104 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=1775225183; bh=n37I4TGUOBXeNxtLanXFAoNRBclbKxN7ioNe+Uhcys8=; 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=Q7MLbZ+2YhPAlYkkXb9awBmCSR3mUuxw2NkOQrLCeWwSkANCl5g4fdzuHs8j4/B3E TJNGKFnMKW74vJ4KGzgrg6M9RpmwsNzNt5QPSZcvmSjMsZtTO9hxDnvGcl4elZt9Ye tIGbEsM6BIQF6PWrYYPmTH948r056dBetxrCFW6qtWiUlOYr8zG0POwq33YMU1WR+8 THflCoZArAZhsH5ATRwr+m1g0B6kdo8pdaxCJ7TieY/JmpHD3pZwv2QLQ85rkSi4tI W5cfRLIEC6zE/21JAzvrME4ixJiQhnLPLYg94v9Ay+0DN/4u8eUq6mzosYALfCN1ri nv5Maol8ejDqw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 5BD5A6A370 for ; Fri, 3 Apr 2026 08:06:23 -0600 (MDT) 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 P2EICasl_SJk for ; Fri, 3 Apr 2026 08:06:23 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225182; bh=n37I4TGUOBXeNxtLanXFAoNRBclbKxN7ioNe+Uhcys8=; 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=lJT91rePChUwkE8vUWaCrCpRJEbN+42toMZFLpeFmrU7KR4WyNsEwEY1fKqG4BiZc Et/ikJgC2FHiEwMbe5UFqvQTeBWpD2DaCfEGRXsqiyBgZy+eHrKZs5WQraVuAOTp7o PijzBz7ZWTAZeeGemicq++LjIHsUnS2NWO0nU3hUF1i0G3vKrBXh/zGoVodA2iXQIt Biz0bxEMwf0HZdoZaOHa48GzEWCrY0mgvyr5atH2VjEuFsRJb8y2J2mP+P+MFAi3Jd /DVxJu5119eGuohTpAUnR3lNw/UdmPTfGUr5+PvTtWHHiVoSdOO+W4ug3bFHCXpXlI 9zt3YFi6x6swQ== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id EB1866A35A for ; Fri, 3 Apr 2026 08:06:22 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225180; bh=1JaXGanODMFRmf1fDAJD7U77Pe1HdnM6bt0N+1Q37bA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Ac5E/9XXfLde3NaQVEns3KRkaHmLzMdfBO2UAeRL8/PAJJ3VIuJ2mYaO6fkB8S5ar +n7BzL7wXMM8GXfznwHikF76FAKMc487aLfvscIwTZw84yLD9D+Swu143A28N6l0fE axWxv3J4p8iPFFgmacmDa2Cb6KbqGoO7R/pNG7DIXVOgtdNr5PnqHeTqUUJJQv82ME 5P+C3QOXauyc9P0fIEJw8rj34WTbXhZ78En1zN2cXITR9PpJ1LeZlmZ0bVx6Jsn3Mz 2JOXKYEXq/5S8DfLe8DtzvyWXjyQafkM1xnMx67SdfTJjibcjNuCq6Gaymbth7pBU2 OYUag2GHo8+jQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 95C616A347; Fri, 3 Apr 2026 08:06:20 -0600 (MDT) 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 E_FLsWHVPItj; Fri, 3 Apr 2026 08:06:20 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225174; bh=CuL68HHdNIUxSQZK4Qn8fiK7BhYRfIyDHxwU7cY+Keg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=R5iLdwTPfZ7J0rTiy/QHaEzDOTP7viQlyb3G67ltdq2zuXySUfuv8Dd491VBThil2 MpPTzvAgBGjVLnC8DF1+M1d4lRG5EZ4EtfhZd/MHdcqRxQcLTYq57AWc3fKUUwzgS5 EyX+GIdB2C7Ez7GO3DafFir3fRx5mEnSEdR9Cs2gbZAOlHXjZUdwJOAZ9NsaKH276r ugzzawT4i4iS/gJbBdjM4l+e2jSyrplwgXivo+0b96vTZhVC21k4b7VTGspGrOw/QZ XJo6yST3WSewGNVSjdea6Y6gc8RSPjZXoKEFGh7SwFXo3MciXLVLR8YQwg6CJwxw17 ivfiADuTsn4KA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 5305D6A367; Fri, 3 Apr 2026 08:06:14 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:41 -0600 Message-ID: <20260403140523.1998228-20-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 2EGR2PR7MJAHV4BPIAWMJXCHC7GLN6U7 X-Message-ID-Hash: 2EGR2PR7MJAHV4BPIAWMJXCHC7GLN6U7 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 19/34] vfs: Add boot integration and block-device mount 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 bootflow support for block-backed VFS filesystems. When a filesystem with a block device is mounted, its bootdev can discover boot scripts and extlinux configs through the standard bootmeth interface. Non-block filesystems (e.g. hostfs) return -ENOENT so the bootflow scanner skips them gracefully. Add fs_mount_blkdev() and fs_mount_blkdev_auto() for mounting filesystems from block devices. Auto-detection iterates all UCLASS_FS drivers whose name ends in '_fs' until one succeeds. Reject mounts on already-occupied mount points with -EBUSY. Extend the mount command to support 'mount ' and 'mount -t ' syntax. Signed-off-by: Simon Glass --- cmd/fs.c | 45 ++++++++++++++++++++++++++++++++++++++++++--- fs/fs-uclass.c | 30 +++++++++++++++++++++++++++--- include/fs.h | 2 ++ 3 files changed, 71 insertions(+), 6 deletions(-) diff --git a/cmd/fs.c b/cmd/fs.c index 694222e5d10..7231e4efcd1 100644 --- a/cmd/fs.c +++ b/cmd/fs.c @@ -8,12 +8,15 @@ * Copyright 2026 Simon Glass */ +#include #include #include #include #include +#include #include #include +#include #include #include @@ -26,7 +29,7 @@ static int mount_handler(int argc, char *const argv[]) { struct udevice *vfs, *fsdev, *dir, *mnt; const char *subpath; - int ret; + int part_num, ret; vfs = vfs_root(); if (!vfs) @@ -37,6 +40,39 @@ static int mount_handler(int argc, char *const argv[]) return 0; } + /* mount -t */ + if (!strcmp(argv[1], "-t")) { + struct disk_partition info; + struct blk_desc *desc; + + if (argc < 6) + return -EINVAL; + + ret = blk_get_device_part_str(argv[3], argv[4], &desc, + &info, 1); + if (ret < 0) + return ret; + part_num = ret; + + return fs_mount_blkdev(argv[2], desc, part_num, &info, + argv[5]); + } + + /* mount - auto-detect type */ + if (argc == 4) { + struct disk_partition info; + struct blk_desc *desc; + + ret = blk_get_device_part_str(argv[1], argv[2], &desc, + &info, 1); + if (ret < 0) + return ret; + part_num = ret; + + return fs_mount_blkdev_auto(desc, part_num, &info, argv[3]); + } + + /* mount - mount an existing UCLASS_FS device */ if (argc < 3) return -EINVAL; @@ -71,11 +107,14 @@ static int do_mount(struct cmd_tbl *cmdtp, int flag, int argc, } U_BOOT_CMD( - mount, 3, 1, do_mount, + mount, 6, 1, do_mount, "mount a filesystem", "[ ]\n" " - With no args, list all mounts\n" - " - Mount device 'dev' at 'mountpoint'" + "mount \n" + " - Auto-detect and mount a filesystem\n" + "mount -t \n" + " - Mount a specific filesystem type" ); static int umount_handler(const char *path) diff --git a/fs/fs-uclass.c b/fs/fs-uclass.c index 4492ff60522..435fd32bc75 100644 --- a/fs/fs-uclass.c +++ b/fs/fs-uclass.c @@ -7,7 +7,9 @@ #define LOG_CATEGORY UCLASS_FS +#include #include +#include #include #include #include @@ -124,16 +126,38 @@ static int fs_get_bootflow(struct udevice *dev, struct bootflow_iter *iter, struct bootflow *bflow) { struct udevice *fsdev = dev_get_parent(dev); + struct fs_plat *plat = dev_get_uclass_plat(fsdev); + char name[60]; int ret; log_debug("get_bootflow fs '%s'\n", fsdev->name); - /* for now, always fail here as we don't have FS support in bootmeths */ - return -ENOENT; + /* + * Block-backed filesystems expose their blk device and partition so + * that existing bootmeths (script, extlinux) can read files using the + * legacy FS layer. + */ + if (!plat->desc || !plat->desc->bdev) + return log_msg_ret("blk", -ENOENT); + + bflow->blk = plat->desc->bdev; + bflow->part = plat->part_num; + + snprintf(name, sizeof(name), "%s.bootflow", fsdev->name); + bflow->name = strdup(name); + if (!bflow->name) + return log_msg_ret("nam", -ENOMEM); + + bflow->state = BOOTFLOWST_MEDIA; ret = bootmeth_check(bflow->method, iter); if (ret) - return log_msg_ret("check", ret); + return log_msg_ret("chk", ret); + + /* Let the bootmeth discover files (extlinux.conf, boot.scr, etc.) */ + ret = bootmeth_read_bootflow(bflow->method, bflow); + if (ret) + return log_msg_ret("rd", ret); return 0; } diff --git a/include/fs.h b/include/fs.h index efca9b80611..925f810902b 100644 --- a/include/fs.h +++ b/include/fs.h @@ -30,11 +30,13 @@ enum { * * @name: Name of the filesystem, or empty if not available * @desc: Block device descriptor, or NULL if not block-backed + * @part_num: Partition number (valid only when @desc is non-NULL) * @part: Partition information (valid only when @desc is non-NULL) */ struct fs_plat { char name[FS_MAX_NAME_LEN]; struct blk_desc *desc; + int part_num; struct disk_partition part; }; From patchwork Fri Apr 3 14:04:42 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2105 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=1775225188; bh=b00zC0ocb2CFekhdhOSGmj8Sd4fgVms8NyPvVooF5c4=; 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=MPsEC8Z4XRLcALz+FJXtUdn+8loGhd4Ud9uhRGM4fZvtDUdQlwEWuTh2ZMCNT9Zg3 362nR7pPMSzNZQBzr+UbGtawHtsNJ5Mq6vrFDN4amX3uT2Zl5+gTXGFCmP752PBCt+ pgzMR/84JcnfeOEfmSIqOcRLXBh5Mws9H/lOfphdaLL4t7IvtdKt2gOehReiIG146f +FTM+uA25YEsbIov8LYcM715VM6xm1wG9OVevD738I6Xz4ro56pO0RcCl9tzp/RB5O r/zocvizRMPDZ6rEnHav0iDf7SFlnRn6M9VhLpDPgV8RMdANXF3Vc5n6w4/1UXc8mq elTEplTIYXoSQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 03BDC6A34E for ; Fri, 3 Apr 2026 08:06:28 -0600 (MDT) 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 Qma8J4ldjmg9 for ; Fri, 3 Apr 2026 08:06:27 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225187; bh=b00zC0ocb2CFekhdhOSGmj8Sd4fgVms8NyPvVooF5c4=; 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=benqNtosc2+tTkcY8kTSVZXvRd/ko4nwW19Uxjq19GiJbwy5VdXHKPmINnZrDFs9L TwmJFVRaj3xUTxd7sWCTtyhaPn6ys1Io2riERkObjtEpjSPbOrmjWEkHECHAadB0EA rxXZe63RE1zA83Z8RU3e6B63KDFdce1xXK0TXuILb2p/DKgHUscWt/9i4N5rKhviJ5 ncqGjq/Aco+4dhHW0Ox7ehsuxbhw5fwa1K+SEf1QbZPtnjq48rSzOiPALduw+P/T3S UutqirXTx8uC2ltKsgFCQ9M6HSpgcBQ4grrslA8uyTgxT/fH0Ut3PvLwcAHN1pR2uA XdcfP3CmTnbtA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id E4DBE67EE6 for ; Fri, 3 Apr 2026 08:06:27 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225185; bh=GnPCY92n988F7ileZfLKUOGOiXddjuDxb0rW9CjtIyY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Dgh+GMADgyBh8wWfgZKNuNPdmTuu+NMJ3pbRppN75Css6IcY9Ls6m9pC5vIA5Q6jV 0aFLg9GlBbreHdBpCUSAUJlE0b1p1uYIkV+P5/Z0C4txr7U9YEru/ujSbLIHUECuzg W4nlnc9FyEeEiVtNoYO3JmedW1cP9jisdT647DTd4kcpRBgNckIQxGY9ZD5JujoUt8 rTG5Um5H4eQ1Cdme3Jgi0uG90J0NQKiVmFSdWy5VPfEoyBwmb6m25ihDp9oAgNxdkC 19fP0kOi81XM60YtWd4B7x/91kiGSlyCTc2un2i6UT++uWJTnkBGo+5OGdPe8R6C73 RXMurqImU8CdQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id BB7CB6A34C; Fri, 3 Apr 2026 08:06:25 -0600 (MDT) 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 14L08QEn8l8p; Fri, 3 Apr 2026 08:06:25 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225179; bh=ipZ4+QVv/dDSi06og0m1BhblOeK8MQCfGtDMiGmpJ8k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=m6RQeJXxdHeJ9KhZqQcOvRh3Dq2yYrOL7QJtID9vfilyauUEN2FfuZPOjYryojIFJ Wn7v6ypcVEo5ndP6eeszmgjQxlYr/qjtS+kl1MOjUXAQQlyLD1IpNCzlvFpgulhFw7 0eIcHvg+zx8Gc4++GZ0uR24hM8pX7yjoXukLmBrUJfBZvX9kIn4FNiAVS+j6zGoq+E KgqJqVYGOdu6AU1exg/74Hj1m5Wjc23ixiEyBalIvZWjT+XqwRxXqsnzGNbtr8F3Uz IMV3l6onRBQrr/k/lu8VXhOFnCnTg0zlhEiOuUwJhz7U2UUNpUEyIqj7vb0K3+hhVj y/TRE4PfhHIew== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id E9B3C67EE6; Fri, 3 Apr 2026 08:06:18 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:42 -0600 Message-ID: <20260403140523.1998228-21-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: VWOLYTAE6DCMVE5SEVR4YFWGYT7R4ULF X-Message-ID-Hash: VWOLYTAE6DCMVE5SEVR4YFWGYT7R4ULF 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 20/34] vfs: Add tab completion for VFS paths 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 vfs_complete() which matches partial paths against mounted filesystem entries, and vfs_cmd_complete() as a ready-made callback for U_BOOT_CMD_COMPLETE. Wire it up for the existing commands. Signed-off-by: Simon Glass --- cmd/fs.c | 20 ++++++---- fs/vfs.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/vfs.h | 26 ++++++++++++ test/dm/fs.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 249 insertions(+), 8 deletions(-) diff --git a/cmd/fs.c b/cmd/fs.c index 7231e4efcd1..944d15b4520 100644 --- a/cmd/fs.c +++ b/cmd/fs.c @@ -145,11 +145,12 @@ static int do_umount(struct cmd_tbl *cmdtp, int flag, int argc, return CMD_RET_SUCCESS; } -U_BOOT_CMD( +U_BOOT_CMD_COMPLETE( umount, 2, 1, do_umount, "unmount a filesystem", "\n" - " - Unmount the filesystem at 'mountpoint'" + " - Unmount the filesystem at 'mountpoint'", + vfs_cmd_complete ); static int do_cd(struct cmd_tbl *cmdtp, int flag, int argc, @@ -167,11 +168,12 @@ static int do_cd(struct cmd_tbl *cmdtp, int flag, int argc, return CMD_RET_SUCCESS; } -U_BOOT_CMD( +U_BOOT_CMD_COMPLETE( cd, 2, 1, do_cd, "change working directory", "[]\n" - " - Change to 'path' in the VFS (default /)" + " - Change to 'path' in the VFS (default /)", + vfs_cmd_complete ); static int do_pwd(struct cmd_tbl *cmdtp, int flag, int argc, @@ -203,11 +205,12 @@ static int do_ls(struct cmd_tbl *cmdtp, int flag, int argc, return CMD_RET_SUCCESS; } -U_BOOT_CMD( +U_BOOT_CMD_COMPLETE( ls, 2, 1, do_ls, "list files in a directory (default cwd)", "[]\n" - " - List files at 'path' in the VFS (default cwd)" + " - List files at 'path' in the VFS (default cwd)", + vfs_cmd_complete ); static int do_size(struct cmd_tbl *cmdtp, int flag, int argc, @@ -314,7 +317,7 @@ U_BOOT_CMD( "List supported filesystem types", "" ); -U_BOOT_CMD( +U_BOOT_CMD_COMPLETE( load, 7, 0, do_load_vfs, "load binary file from a filesystem", " [bytes [pos]]\n" @@ -324,5 +327,6 @@ U_BOOT_CMD( " 'pos' gives the file byte position to start reading from.\n" " If 'pos' is 0 or omitted, the file is read from the start.\n" "load [ [ [ [bytes [pos]]]]]\n" - " - Legacy: load from block device interface" + " - Legacy: load from block device interface", + vfs_cmd_complete ); diff --git a/fs/vfs.c b/fs/vfs.c index 3812706af6c..328b1076280 100644 --- a/fs/vfs.c +++ b/fs/vfs.c @@ -12,6 +12,7 @@ #define LOG_CATEGORY UCLASS_MOUNT #include +#include #include #include #include @@ -751,6 +752,112 @@ int fs_mount_blkdev(const char *type, struct blk_desc *desc, int part_num, return 0; } +#ifdef CONFIG_AUTO_COMPLETE +int vfs_complete(char *buf, const char *path, int maxv, char *cmdv[]) +{ + char resolved[FILE_MAX_PATH_LEN]; + struct udevice *vfs, *mnt_dev, *dir; + struct fs_dir_stream *strm; + const char *subpath, *prefix; + struct vfsmount *mnt; + struct fs_dirent dent; + int n = 0; + + vfs = vfs_root(); + if (!vfs) + return 0; + + path = vfs_path_resolve(vfs_getcwd(), path, resolved, sizeof(resolved)); + if (!path) + return 0; + + /* + * Split into directory part and prefix to match. + * "/host/ar" -> dir="/host", prefix="ar" + * "/host/" -> dir="/host", prefix="" + * "/" -> dir="/", prefix="" + */ + prefix = strrchr(path, '/'); + if (!prefix) + return 0; + prefix++; /* skip the '/' */ + + /* Complete from root mount points */ + if (prefix == path + 1) { + struct dir_uc_priv *uc_priv; + + vfs_foreach_mount(mnt, mnt_dev) { + uc_priv = dev_get_uclass_priv(mnt->dir); + if (!strncmp(uc_priv->path, prefix, strlen(prefix))) { + if (n >= maxv - 1) + break; + sprintf(buf, "/%s/", uc_priv->path); + cmdv[n++] = buf; + buf += strlen(buf) + 1; + } + } + cmdv[n] = NULL; + return n; + } + + /* Resolve the directory portion */ + if (vfs_find_mount(vfs, path, &mnt_dev, &subpath)) + return 0; + + mnt = dev_get_uclass_priv(mnt_dev); + + /* Get the directory part of subpath (everything before prefix) */ + { + char dirpath[FILE_MAX_PATH_LEN]; + int dir_len; + + dir_len = prefix - 1 - (path + strlen(path) - strlen(subpath)); + if (dir_len < 0) + dir_len = 0; + memcpy(dirpath, subpath, dir_len); + dirpath[dir_len] = '\0'; + + if (fs_lookup_dir(mnt->target, dirpath, &dir)) + return 0; + } + + if (dir_open(dir, &strm)) + return 0; + + while (!dir_read(dir, strm, &dent)) { + if (strncmp(dent.name, prefix, strlen(prefix))) + continue; + if (n >= maxv - 1) + break; + strcpy(buf, dent.name); + if (dent.type == FS_DT_DIR) + strcat(buf, "/"); + cmdv[n++] = buf; + buf += strlen(buf) + 1; + } + + dir_close(dir, strm); + cmdv[n] = NULL; + + return n; +} + +int vfs_cmd_complete(int argc, char *const argv[], char last_char, + int maxv, char *cmdv[]) +{ + static char complete_buf[2048]; + int space = last_char == '\0' || isblank(last_char); + + /* Complete the last argument as a path */ + if (space && argc >= 1) + return vfs_complete(complete_buf, "", maxv, cmdv); + if (!space && argc >= 2) + return vfs_complete(complete_buf, argv[argc - 1], maxv, cmdv); + + return 0; +} +#endif + struct udevice *vfs_root(void) { struct udevice *dev; diff --git a/include/vfs.h b/include/vfs.h index 6e0c67ca031..7c31ded7796 100644 --- a/include/vfs.h +++ b/include/vfs.h @@ -14,6 +14,7 @@ #include struct blk_desc; +struct cmd_tbl; struct disk_partition; struct fs_dirent; struct fs_statfs; @@ -291,4 +292,29 @@ int fs_mount_blkdev(const char *type, struct blk_desc *desc, int part_num, */ void vfs_print_mounts(void); +#ifdef CONFIG_AUTO_COMPLETE +/** + * vfs_complete() - Complete a partial VFS path + * + * Suitable for use as (or called from) a U_BOOT_CMD complete callback. + * Fills @cmdv with matching directory entries. + * + * @buf: Scratch buffer (must be at least FILE_MAX_PATH_LEN bytes) + * @path: Partial path to complete (absolute or relative) + * @maxv: Maximum number of entries in @cmdv + * @cmdv: Output array of matching names (NULL-terminated) + * Return: number of matches + */ +int vfs_complete(char *buf, const char *path, int maxv, char *cmdv[]); + +/** + * vfs_cmd_complete() - Complete callback for commands taking a VFS path + * + * Completes the last argument as a VFS path. Can be passed directly to + * U_BOOT_CMD_COMPLETE. + */ +int vfs_cmd_complete(int argc, char *const argv[], char last_char, + int maxv, char *cmdv[]); +#endif + #endif diff --git a/test/dm/fs.c b/test/dm/fs.c index f42604fb097..0ef52ca0ee4 100644 --- a/test/dm/fs.c +++ b/test/dm/fs.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -138,6 +139,109 @@ static int dm_test_fs_file_write(struct unit_test_state *uts) DM_TEST(dm_test_fs_file_write, UTF_SCAN_FDT); #if IS_ENABLED(CONFIG_VFS) +/* Helper: resolve and compare */ +static int check_resolve(struct unit_test_state *uts, const char *cwd, + const char *path, const char *expected) +{ + char buf[256]; + const char *ret; + + ret = vfs_path_resolve(cwd, path, buf, sizeof(buf)); + ut_assertnonnull(ret); + ut_asserteq_str(expected, ret); + + return 0; +} + +/* Test vfs_path_resolve() - no VFS device needed */ +static int dm_test_vfs_path_resolve(struct unit_test_state *uts) +{ + char buf[512], buf2[512]; + int i; + + /* NULL/empty path returns cwd */ + ut_assertok(check_resolve(uts, "/", NULL, "/")); + ut_assertok(check_resolve(uts, "/", "", "/")); + ut_assertok(check_resolve(uts, "/host", NULL, "/host")); + + /* Absolute path ignores cwd */ + ut_assertok(check_resolve(uts, "/host", "/mnt", "/mnt")); + ut_assertok(check_resolve(uts, "/host", "/a/b", "/a/b")); + + /* Relative path is joined with cwd */ + ut_assertok(check_resolve(uts, "/", "foo", "/foo")); + ut_assertok(check_resolve(uts, "/host", "file.txt", "/host/file.txt")); + ut_assertok(check_resolve(uts, "/host", "sub/file", "/host/sub/file")); + + /* . is resolved */ + ut_assertok(check_resolve(uts, "/host", ".", "/host")); + ut_assertok(check_resolve(uts, "/host", "./a", "/host/a")); + + /* .. is resolved */ + ut_assertok(check_resolve(uts, "/host", "..", "/")); + ut_assertok(check_resolve(uts, "/host/sub", "..", "/host")); + ut_assertok(check_resolve(uts, "/host/a", "../b", "/host/b")); + + /* .. at root stays at root */ + ut_assertok(check_resolve(uts, "/", "..", "/")); + ut_assertok(check_resolve(uts, "/", "../..", "/")); + + /* Absolute path with . and .. */ + ut_assertok(check_resolve(uts, "/", "/host/./sub/..", "/host")); + ut_assertok(check_resolve(uts, "/", "/a/b/../c", "/a/c")); + + /* Trailing slash */ + ut_assertok(check_resolve(uts, "/", "/host/", "/host")); + + /* Stack overflow returns NULL (path with >64 components) */ + strcpy(buf, "/"); + for (i = 0; i < 65; i++) { + if (i) + strcat(buf, "/"); + strcat(buf, "a"); + } + ut_assertnull(vfs_path_resolve("/", buf, buf2, sizeof(buf2))); + + return 0; +} +DM_TEST(dm_test_vfs_path_resolve, 0); + +/* Test vfs_complete() tab completion */ +static int dm_test_vfs_complete(struct unit_test_state *uts) +{ + struct udevice *vfs, *fsdev, *dir; + char buf[2048]; + char *cmdv[16]; + + ut_assertok(vfs_init()); + vfs = vfs_root(); + ut_assertnonnull(vfs); + + ut_assertok(uclass_get_device_by_name(UCLASS_FS, "hostfs", &fsdev)); + ut_assertok(vfs_resolve(vfs, "/host", &dir)); + ut_assertok(vfs_mount(vfs, dir, fsdev)); + + /* Complete from root - should find "host" mount point */ + ut_assert(vfs_complete(buf, "/", 16, cmdv) >= 1); + ut_asserteq_str("/host/", cmdv[0]); + + /* Partial mount name */ + ut_assert(vfs_complete(buf, "/h", 16, cmdv) >= 1); + ut_asserteq_str("/host/", cmdv[0]); + + /* Non-matching prefix should return 0 */ + ut_asserteq(0, vfs_complete(buf, "/z", 16, cmdv)); + + /* Complete with empty prefix shows entries */ + ut_assert(vfs_complete(buf, "/host/", 16, cmdv) >= 1); + + os_unlink(".comp_test"); + ut_assertok(vfs_umount_path(vfs, "/host")); + + return 0; +} +DM_TEST(dm_test_vfs_complete, UTF_SCAN_FDT); + /* Test VFS init and root directory operations */ static int dm_test_vfs_init(struct unit_test_state *uts) { From patchwork Fri Apr 3 14:04:43 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2106 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=1775225190; bh=774jexmY42H8raaHYc2ryRw52f74bHJN6xS8SFHQ1hE=; 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=hGbGHzqGpUmkBnYbWyrkrG1rkaIZnj6gjjtOxLp6GUbprv+j2tWo0tJDL/oOwr8ME RRnX+1UzGNXGhQRXG2OI6o6gPm+FaHNPam+SsjmV5/QVdEpnkMj1iZCDC1wRwzkl8B 4dkNvUHa5NYlNK8XOh6k+Znbbn2wX1Gdxbovgm4ywA/yNsZWP9wTJ8TEPK7Mxx+yt9 UXraqxJ8LwCAJMV5i1y/FlEi21J3aotwSqQtaAGlm5E9liCfF7RffCLqLU0dGLLg2U gljBXfkoaD99H7n/Uno5984QdFvIrA9bGdhV/3UsvL5ojRuiPZJ+aw+6aFlK13deYO /BnbsLviHUNEg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 9A9196A378 for ; Fri, 3 Apr 2026 08:06:30 -0600 (MDT) 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 XMaXqdlK9YoV for ; Fri, 3 Apr 2026 08:06:30 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225188; bh=774jexmY42H8raaHYc2ryRw52f74bHJN6xS8SFHQ1hE=; 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=kGzpjj3OLmGViMY9M5tk0KUY1n4jQU5o2JqSUIW+x7aHmt++hMDBxpV2Tu/P91BWn CPF8MUswzIIIVhn2M8bej4fXf2qFnYBc9Q536wlZxZOjuKQKtDT/7IufSxQddDPKDh lLZTWhXiL9Ftbi7JNkUuEhBHuHZuApbEKpLvisw4EWPsRMElxtaNUxgr5q1hJc3o5u cJn8HMLpNhrf4qAVkE6vNMLckZhbM6w/zBjj4mdpFuB9cRrat5jfSuOAYVbQ9ao7m/ 0L0z2AYiG0Ol2C5tBml29CVevCmSc0VQGLr9kQ4Mbe+2hbuYT6lhuZ5gxHRTynfd47 MAzPBLot7annw== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 9A8296A34F for ; Fri, 3 Apr 2026 08:06:28 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225185; bh=04mP5Arj7JcFpnTqAWGL9N8drvHUOsI6fKxLcRsc6W4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=eTsneRamS95aexeYrn+HzlpTOwmet/Yo3BZxRENtg8T5YpqucOnLEZIm4hX1DTb5D pMrABuoZXAYwEZxbY4XnqBCT/XUhI4Corm1JLdzxqCQBy+wutg5pn7p3yvnw7Vf7rF g2Jtm/V4Prb4xU1ux22FFK/Wya6F+2xaXa5yDnQi+8WrWDoYZp6gYJlwn9/Vr4Nhz4 FF+r/N5PUYRyKdMRbnVQIbEEC+H/0jXBaLW3RGqHRXupugPHqF2JKtzxKuf2ZcZj6p F6ik/+rj5NlBIMvIqQhEe7Nd+a25p9elm/WfM+OKfKMuWN4ksM3IR071oLadsmw/3r SZWObvuZBvPFg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id C4F0E67EE6; Fri, 3 Apr 2026 08:06:25 -0600 (MDT) 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 ZyR-lokDSndm; Fri, 3 Apr 2026 08:06:25 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225183; bh=doqX/nUOgV4P1s34Y3Esa9C7Z3J86FYAmnUKaA+wdTM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RYlzhFY41h9BKjQdKzVuDNQ58QaowH79TmJmhfzPh38lO9idxWaZUs2m2p8AqArVj 7Va/2hH4dEEnc08EcY22gRXe/iPA9WJ4Hu2W0zKaecw6wibQKVFoc6tVp2QhNYxx7/ 1UD2qkWs8BdNzmfdO/ZOLOWL5aZ5We5RoFcFJGMURdyY+VYUxY3LkgQb2jRZPFsCzG fAgGbAAIYGiavqyGuu7wW2VwzGAczWsWCUo8aLPmRHAr0vvUiYYoh1Gc/b1X04RrhH kcy75EGsACblfcFS5ieAwvrkaSwijzizT3qa7vxtfQtUJ4tyQaQT6xSplH4SNT1feH R7Gtyeb1s+arQ== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 8C9696A35F; Fri, 3 Apr 2026 08:06:23 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:43 -0600 Message-ID: <20260403140523.1998228-22-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: HUYFAV2GGITM4PWD4CSVVAUYMAMCFKZ2 X-Message-ID-Hash: HUYFAV2GGITM4PWD4CSVVAUYMAMCFKZ2 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 21/34] cmd: Add save, stat, cd, pwd and cp commands 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 VFS commands for save, stat, cd, pwd and cp (as an fs subcommand). These use the VFS layer for path resolution, current working directory and file access. Signed-off-by: Simon Glass --- cmd/fs.c | 175 ++++++++++++++++++++++++++++++++++++++++++++- cmd/vfs.c | 9 ++- include/vfs.h | 8 +++ test/dm/fs.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 378 insertions(+), 6 deletions(-) diff --git a/cmd/fs.c b/cmd/fs.c index 944d15b4520..e3fad83d68c 100644 --- a/cmd/fs.c +++ b/cmd/fs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * VFS-based filesystem commands - mount, umount, ls, load, size + * VFS-based filesystem commands - mount, umount, ls, load, save, size * * These replace the legacy commands in cmd/fs_legacy.c with versions that * use absolute paths through the virtual filesystem layer. @@ -213,6 +213,126 @@ U_BOOT_CMD_COMPLETE( vfs_cmd_complete ); +#define COPY_BUF_SIZE 0x1000 + +int do_cp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct file_uc_priv *uc_priv; + struct udevice *src, *dst; + char buf[COPY_BUF_SIZE]; + loff_t remaining, pos; + long total = 0; + int ret; + + if (argc < 3) + return CMD_RET_USAGE; + + ret = vfs_open_file(argv[1], DIR_O_RDONLY, &src); + if (ret) { + printf("Source '%s' not found: %dE\n", argv[1], ret); + return CMD_RET_FAILURE; + } + + ret = vfs_open_file(argv[2], DIR_O_WRONLY, &dst); + if (ret) { + printf("Dest '%s' failed: %dE\n", argv[2], ret); + return CMD_RET_FAILURE; + } + + uc_priv = dev_get_uclass_priv(src); + remaining = uc_priv->size; + pos = 0; + + while (remaining > 0) { + long chunk = min((loff_t)COPY_BUF_SIZE, remaining); + long nread, nwritten; + + nread = file_read_at(src, buf, pos, chunk); + if (nread < 0) { + printf("Read failed: %ldE\n", nread); + return CMD_RET_FAILURE; + } + if (!nread) + break; + + nwritten = file_write_at(dst, buf, pos, nread); + if (nwritten < 0) { + printf("Write failed: %ldE\n", nwritten); + return CMD_RET_FAILURE; + } + + pos += nread; + remaining -= nread; + total += nwritten; + } + + printf("%ld bytes copied\n", total); + + return CMD_RET_SUCCESS; +} + +static const char *fs_type_name(unsigned int type) +{ + switch (type) { + case FS_DT_DIR: + return "directory"; + case FS_DT_REG: + return "regular file"; + case FS_DT_LNK: + return "symbolic link"; + default: + return "unknown"; + } +} + +static int do_stat(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct fs_dirent dent; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + ret = vfs_stat(argv[1], &dent); + if (ret) { + printf("Error: %dE\n", ret); + return CMD_RET_FAILURE; + } + + printf(" File: %s\n", dent.name); + printf(" Size: %llu\n", dent.size); + printf(" Type: %s\n", fs_type_name(dent.type)); + if (dent.change_time.tm_year) { + printf("Modify: %04d-%02d-%02d %02d:%02d:%02d\n", + dent.change_time.tm_year, dent.change_time.tm_mon, + dent.change_time.tm_mday, dent.change_time.tm_hour, + dent.change_time.tm_min, dent.change_time.tm_sec); + } + if (dent.access_time.tm_year) { + printf("Access: %04d-%02d-%02d %02d:%02d:%02d\n", + dent.access_time.tm_year, dent.access_time.tm_mon, + dent.access_time.tm_mday, dent.access_time.tm_hour, + dent.access_time.tm_min, dent.access_time.tm_sec); + } + if (dent.create_time.tm_year) { + printf(" Birth: %04d-%02d-%02d %02d:%02d:%02d\n", + dent.create_time.tm_year, dent.create_time.tm_mon, + dent.create_time.tm_mday, dent.create_time.tm_hour, + dent.create_time.tm_min, dent.create_time.tm_sec); + } + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD_COMPLETE( + stat, 2, 1, do_stat, + "display file status", + "\n" + " - Show type, size and timestamps of a file or directory", + vfs_cmd_complete +); + static int do_size(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -233,12 +353,13 @@ static int do_size(struct cmd_tbl *cmdtp, int flag, int argc, return CMD_RET_SUCCESS; } -U_BOOT_CMD( +U_BOOT_CMD_COMPLETE( size, 2, 0, do_size, "determine a file's size", "\n" " - Find file at 'path' in the VFS, determine its size,\n" - " and store in the 'filesize' variable." + " and store in the 'filesize' variable.", + vfs_cmd_complete ); static int do_vfs_load(struct cmd_tbl *cmdtp, int flag, int argc, @@ -330,3 +451,51 @@ U_BOOT_CMD_COMPLETE( " - Legacy: load from block device interface", vfs_cmd_complete ); + +static int do_save(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *fil; + long bytes, written; + unsigned long addr; + loff_t pos = 0; + void *buf; + int ret; + + if (argc < 4) + return CMD_RET_USAGE; + + addr = hextoul(argv[1], NULL); + bytes = hextoul(argv[3], NULL); + if (argc >= 5) + pos = hextoul(argv[4], NULL); + + ret = vfs_open_file(argv[2], DIR_O_WRONLY, &fil); + if (ret) { + printf("Error: %dE\n", ret); + return CMD_RET_FAILURE; + } + + buf = map_sysmem(addr, bytes); + written = file_write_at(fil, buf, pos, bytes); + unmap_sysmem(buf); + + if (written < 0) { + printf("Write failed: %ldE\n", written); + return CMD_RET_FAILURE; + } + + printf("%ld bytes written\n", written); + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD_COMPLETE( + save, 5, 0, do_save, + "save memory to a file", + " [pos]\n" + " - Save 'bytes' from address 'addr' to 'path' in the VFS.\n" + " 'pos' gives the file byte position to start writing to.\n" + " If 'pos' is 0 or omitted, the file is written from the start.", + vfs_cmd_complete +); diff --git a/cmd/vfs.c b/cmd/vfs.c index 5779f3f098d..33407da4594 100644 --- a/cmd/vfs.c +++ b/cmd/vfs.c @@ -104,11 +104,14 @@ static int do_fs_ls(struct cmd_tbl *cmdtp, int flag, int argc, U_BOOT_LONGHELP(fs, "mount [ ] - list or create mounts\n" - "fs mount -t - mount from block device\n" + "fs mount - auto-detect and mount\n" + "fs mount -t - mount specific type\n" "fs umount - unmount a filesystem\n" - "fs ls [] - list directory (default /)"); + "fs ls [] - list directory (default /)\n" + "fs cp - copy a file"); U_BOOT_CMD_WITH_SUBCMDS(fs, "Filesystem operations", fs_help_text, U_BOOT_SUBCMD_MKENT(mount, 6, 1, do_fs_mount), U_BOOT_SUBCMD_MKENT(umount, 2, 1, do_fs_umount), - U_BOOT_SUBCMD_MKENT(ls, 2, 1, do_fs_ls)); + U_BOOT_SUBCMD_MKENT(ls, 2, 1, do_fs_ls), + U_BOOT_SUBCMD_MKENT(cp, 3, 0, do_cp)); diff --git a/include/vfs.h b/include/vfs.h index 7c31ded7796..04cbd5d0ca9 100644 --- a/include/vfs.h +++ b/include/vfs.h @@ -292,6 +292,14 @@ int fs_mount_blkdev(const char *type, struct blk_desc *desc, int part_num, */ void vfs_print_mounts(void); +/** + * do_cp() - Copy a file within the VFS + * + * Implements the 'fs cp' subcommand. Defined in cmd/fs.c, declared here + * so that cmd/vfs.c can reference it. + */ +int do_cp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); + #ifdef CONFIG_AUTO_COMPLETE /** * vfs_complete() - Complete a partial VFS path diff --git a/test/dm/fs.c b/test/dm/fs.c index 0ef52ca0ee4..8bba5263546 100644 --- a/test/dm/fs.c +++ b/test/dm/fs.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #define READ_SIZE 0x20 @@ -232,6 +233,15 @@ static int dm_test_vfs_complete(struct unit_test_state *uts) /* Non-matching prefix should return 0 */ ut_asserteq(0, vfs_complete(buf, "/z", 16, cmdv)); + /* Create a known file to complete against */ + memcpy(map_sysmem(0x10000, 5), "hello", 5); + ut_assertok(run_command("save 10000 /host/.comp_test 5", 0)); + console_record_reset_enable(); + + /* Complete inside mount */ + ut_assert(vfs_complete(buf, "/host/.comp_", 16, cmdv) >= 1); + ut_asserteq_str(".comp_test", cmdv[0]); + /* Complete with empty prefix shows entries */ ut_assert(vfs_complete(buf, "/host/", 16, cmdv) >= 1); @@ -460,4 +470,186 @@ static int dm_test_vfs_cwd(struct unit_test_state *uts) } DM_TEST(dm_test_vfs_cwd, UTF_SCAN_FDT); +/* Test cd and pwd commands */ +static int dm_test_vfs_cd(struct unit_test_state *uts) +{ + ut_assertok(vfs_init()); + + ut_assertok(run_command("mount hostfs /host", 0)); + ut_assert_console_end(); + + /* Default cwd is root */ + ut_assertok(run_command("pwd", 0)); + ut_assert_nextline("/"); + ut_assert_console_end(); + + /* cd to an absolute path */ + ut_assertok(run_command("cd /host", 0)); + ut_assert_console_end(); + ut_assertok(run_command("pwd", 0)); + ut_assert_nextline("/host"); + ut_assert_console_end(); + + /* ls without a path should list cwd */ + ut_assertok(run_command("ls", 0)); + ut_assert_skip_to_linen("DIR "); + console_record_reset_enable(); + + /* cd back to root */ + ut_assertok(run_command("cd /", 0)); + ut_assert_console_end(); + ut_assertok(run_command("pwd", 0)); + ut_assert_nextline("/"); + ut_assert_console_end(); + + ut_assertok(run_command("umount /host", 0)); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_vfs_cd, UTF_SCAN_FDT); + +/* Test multi-component path resolution (files in subdirectories) */ +static int dm_test_vfs_path(struct unit_test_state *uts) +{ + ut_assertok(vfs_init()); + + ut_assertok(run_command("mount hostfs /host", 0)); + ut_assert_console_end(); + + /* stat a file in a subdirectory */ + ut_assertok(run_command("stat /host/cmd/vfs.c", 0)); + ut_assert_nextline(" File: vfs.c"); + ut_assert_nextlinen(" Size: "); + ut_assert_nextline(" Type: regular file"); + console_record_reset_enable(); + + /* load from a subdirectory */ + ut_assertok(run_command("load 1000000 /host/cmd/vfs.c", 0)); + ut_assert_nextlinen("%s", ""); + ut_assert_console_end(); + + /* ls a nested subdirectory */ + ut_assertok(run_command("ls /host/arch", 0)); + ut_assert_skip_to_linen("DIR "); + console_record_reset_enable(); + + ut_assertok(run_command("umount /host", 0)); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_vfs_path, UTF_SCAN_FDT); + +/* Test the cp command via VFS */ +static int dm_test_vfs_cp(struct unit_test_state *uts) +{ + char buf[32]; + + ut_assertok(vfs_init()); + ut_assertok(run_command("mount hostfs /host", 0)); + ut_assert_console_end(); + + /* Write a source file */ + memcpy(map_sysmem(0x1000, 5), "hello", 5); + ut_assertok(run_command("save 1000 /host/.cp_src 5", 0)); + ut_assert_nextline("5 bytes written"); + ut_assert_console_end(); + + /* Copy it */ + ut_assertok(run_command("fs cp /host/.cp_src /host/.cp_dst", 0)); + ut_assert_nextline("5 bytes copied"); + ut_assert_console_end(); + + /* Read back the copy and verify */ + memset(map_sysmem(0x2000, 8), 0, 8); + ut_assertok(run_command("load 2000 /host/.cp_dst", 0)); + ut_assert_nextline("5 bytes read"); + ut_assert_console_end(); + + memcpy(buf, map_sysmem(0x2000, 5), 5); + buf[5] = '\0'; + ut_asserteq_str("hello", buf); + + os_unlink(".cp_src"); + os_unlink(".cp_dst"); + + ut_assertok(run_command("umount /host", 0)); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_vfs_cp, UTF_SCAN_FDT); + +/* Test the stat command via VFS */ +static int dm_test_vfs_stat(struct unit_test_state *uts) +{ + ut_assertok(vfs_init()); + + ut_assertok(run_command("mount hostfs /host", 0)); + ut_assert_console_end(); + + /* stat a regular file */ + ut_assertok(run_command("stat /host/README", 0)); + ut_assert_nextline(" File: README"); + ut_assert_nextlinen(" Size: "); + ut_assert_nextline(" Type: regular file"); + console_record_reset_enable(); + + /* stat a directory */ + ut_assertok(run_command("stat /host/cmd", 0)); + ut_assert_nextline(" File: cmd"); + ut_assert_nextlinen(" Size: "); + ut_assert_nextline(" Type: directory"); + console_record_reset_enable(); + + /* stat non-existent file */ + ut_asserteq(1, run_command("stat /host/does-not-exist", 0)); + console_record_reset_enable(); + + ut_assertok(run_command("umount /host", 0)); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_vfs_stat, UTF_SCAN_FDT); + +/* Test save and load round-trip via VFS */ +static int dm_test_vfs_save(struct unit_test_state *uts) +{ + char buf[32]; + + ut_assertok(vfs_init()); + + /* Mount hostfs */ + ut_assertok(run_command("mount hostfs /host", 0)); + ut_assert_console_end(); + + /* Write a known pattern to a temp file */ + memset(map_sysmem(0x1000, 16), 0, 16); + memcpy(map_sysmem(0x1000, 11), "hello world", 11); + ut_assertok(run_command("save 1000 /host/.vfs_test_tmp 0xb", 0)); + ut_assert_nextline("11 bytes written"); + ut_assert_console_end(); + + /* Read it back and verify */ + memset(map_sysmem(0x2000, 16), 0, 16); + ut_assertok(run_command("load 2000 /host/.vfs_test_tmp", 0)); + ut_assert_nextline("11 bytes read"); + ut_assert_console_end(); + + memcpy(buf, map_sysmem(0x2000, 11), 11); + buf[11] = '\0'; + ut_asserteq_str("hello world", buf); + + /* Clean up the temp file */ + os_unlink(".vfs_test_tmp"); + + ut_assertok(run_command("umount /host", 0)); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_vfs_save, UTF_SCAN_FDT); + #endif From patchwork Fri Apr 3 14:04:44 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2107 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=1775225191; bh=JYIC1DJKtQjXXWCprgUvRxHsuYIavImRqKMKMo+HA7w=; 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=RwJustc7RW090qpL0lCVCnEur8Vqn7mhc+klZUYYuTiofGJBHgN8PndC6exJKUT/0 kn3ewZBsDmBTonQEH7VW1aVra246rcSy9gGqFTwZlTEZU1T6f7ksdm2R1L+o33uO4g LgkeUAQU5iqWEqJkw+mMvlp+HMYH0jdFAx7rPKq3BNKaXNV+Tm7ABkmVQAkT/g1B89 aIUkKjrWIONZQPysa8Z1WQ1nNquFI254xQAgQDS0smjI14WTYbJg5XQ6LkY/UhHMqj C265SFsOW3Ij90V6GUekXZp9V9yTzCvvuvxUAFytX+12N5KZdho5HOqllVl6H9BJLX iMpVbHAP7YQRQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 0A6196A37D for ; Fri, 3 Apr 2026 08:06:31 -0600 (MDT) 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 Xl61GajF_LxU for ; Fri, 3 Apr 2026 08:06:30 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225189; bh=JYIC1DJKtQjXXWCprgUvRxHsuYIavImRqKMKMo+HA7w=; 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=Hp/qbtHaSbhiS3uIHLYqI02WzUPdh0pEH2fw3p3JYuI5qLKz94C5PBgFYfG7Wm3mv w3+o3qPKqabqiiVnb9kFS7deRiZD6hK7kYIfA5Wj0M4MMHlZ8vY4ChFYTd7mFn/XxC ZOgw3Kh6Mt02GTRgWXLiTJqfUekVsK2/EvGoQbVwHBbDVkNtQkzyxqW1OhINtItshK 4RWfiXDYNiXze+KiquR6id/RjtUVoszdXuHAxe4oCP1iQwQQmyyeOTPttYijUIoD5r L/PciVv2khKYU1aGJ/8W0UKdz/XUah8EAXuuWpTEip5IKDSjeRPqD0FaXv/zfSNEfF 4geQZWRZMXFFQ== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 92C756A364 for ; Fri, 3 Apr 2026 08:06:29 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225185; bh=GXndC6TfsnbAhau7qw3XHMSSi1AUC7EdIz21MksfFBQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hn1J06a44o2R2meQX7k7NaaCT4wB1agw4SFmHSzW2TYn1I3PAvqFyr2SQx7FDmp27 WsTCmGqm1J/sOtkOHusocio5zo3VvXM383jpQIqVeHhQ0INi8wdtp1TrV/oL2Tf3BH zGQPmOAhzAV6IIzriMr1sqjTOBOm7NVNOcjx6BUdIL+inwpzBRidBiJwQJmPMRXNyl OkZ5Y1VeHdXMFH9H4QpdinSiEWLozXPGHg6X8C/n4e2yDFflDahD7pbRB8WGd2DdBU qfds+d/zDnul6ekZWFY8TAYZ5//ugE3sqhqWjRh9xIsKMKNiIgJfmTFhsID8zduTAQ ut9QnE/6GyQFQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id CF9336A34E; Fri, 3 Apr 2026 08:06:25 -0600 (MDT) 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 PfMb6N_VD0Ur; Fri, 3 Apr 2026 08:06:25 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225184; bh=sA8imtHh4pys89EJBCbMST+9NitGplI1g8h1agLQPGg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=s0vVEz7INu/Lsc91erB7uNhQGiHZ+DXeNZCrPal3JKqBicAr3kkG/Nuuo8ZUNMOsl qFVObrwKxQdCFC3yEU5OIlqDAVJZMNpy2r4Zuj2YBTF3dyU1XAlg6rW13Z0UhajCZ0 0rt8ry4RUAZf83cw8AUmjk3uGS63UAOEl2A38LERz85+0tII7J5VAAiVRH4XEMzFja TWEmIoT0ER6akkhxNnbMBguzJE3nkk8paMdmNDdQSCaDM8aunXmjjEqX1SKJDjAiWi vIZhNwtHrDd5lFLSSTdcbu0i3zYkkEu2bo2asWFBm6X69T17P5VADmOHLoL9s8UaNu 02Gt+aJozP1cA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 98BFF6A347; Fri, 3 Apr 2026 08:06:24 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:44 -0600 Message-ID: <20260403140523.1998228-23-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: U7GJRLD6OEFDJVNZGIELOTGWHD3J7ZOB X-Message-ID-Hash: U7GJRLD6OEFDJVNZGIELOTGWHD3J7ZOB 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 22/34] cmd: Move VFS cat implementation to cmd/cat.c 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 Move the VFS cat command from cmd/fs.c into cmd/cat.c alongside the legacy implementation. The file now uses #if IS_ENABLED(CONFIG_VFS) to select between VFS and legacy versions, avoiding duplicate command registrations. Remove the ifndef CONFIG_CMD_VFS guard in cmd/Makefile since cmd/cat.c now handles both cases. Signed-off-by: Simon Glass --- cmd/cat.c | 78 ++++++++++++++++++++++++++++++++++++--- test/dm/fs.c | 27 ++++++++++++++ test/py/tests/test_cat.py | 3 +- 3 files changed, 101 insertions(+), 7 deletions(-) diff --git a/cmd/cat.c b/cmd/cat.c index 80627f12cfc..8e0a065473d 100644 --- a/cmd/cat.c +++ b/cmd/cat.c @@ -6,11 +6,72 @@ #include #include +#include +#include #include #include #include +#include #include +#if IS_ENABLED(CONFIG_VFS) + +#define CAT_BUF_SIZE 0x1000 + +static int do_cat(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct file_uc_priv *uc_priv; + struct udevice *fil; + char buf[CAT_BUF_SIZE]; + loff_t remaining; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + ret = vfs_open_file(argv[1], DIR_O_RDONLY, &fil); + if (ret) { + printf("Error: %dE\n", ret); + return CMD_RET_FAILURE; + } + + uc_priv = dev_get_uclass_priv(fil); + remaining = uc_priv->size; + + while (remaining > 0) { + long chunk = min((loff_t)CAT_BUF_SIZE - 1, remaining); + long nread; + + nread = file_read(fil, buf, chunk); + if (nread < 0) { + printf("Read failed: %ldE\n", nread); + return CMD_RET_FAILURE; + } + if (!nread) + break; + + buf[nread] = '\0'; + puts(buf); + remaining -= nread; + } + + return CMD_RET_SUCCESS; +} + +U_BOOT_LONGHELP(cat, + "\n" + " - Print the contents of a file in the VFS"); + +U_BOOT_CMD_COMPLETE( + cat, 2, 1, do_cat, + "print file to standard output", + cat_help_text, + vfs_cmd_complete +); + +#else /* !CONFIG_VFS */ + static int do_cat(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -29,28 +90,32 @@ static int do_cat(struct cmd_tbl *cmdtp, int flag, int argc, ret = fs_load_alloc(ifname, dev, file, 0, 0, &buf); - // check file exists + /* check file exists */ switch (ret) { case 0: break; case -ENOMEDIUM: return CMD_RET_FAILURE; case -ENOENT: - log_err("File does not exist: ifname=%s dev=%s file=%s\n", ifname, dev, file); + log_err("File does not exist: ifname=%s dev=%s file=%s\n", + ifname, dev, file); return CMD_RET_FAILURE; case -E2BIG: - log_err("File is too large: ifname=%s dev=%s file=%s\n", ifname, dev, file); + log_err("File is too large: ifname=%s dev=%s file=%s\n", + ifname, dev, file); return CMD_RET_FAILURE; case -ENOMEM: - log_err("Not enough memory: ifname=%s dev=%s file=%s\n", ifname, dev, file); + log_err("Not enough memory: ifname=%s dev=%s file=%s\n", + ifname, dev, file); return CMD_RET_FAILURE; default: case -EIO: - log_err("File-read failed: ifname=%s dev=%s file=%s\n", ifname, dev, file); + log_err("File-read failed: ifname=%s dev=%s file=%s\n", + ifname, dev, file); return CMD_RET_FAILURE; } - // print file content + /* print file content */ ((char *)buf.data)[buf.size] = '\0'; puts(buf.data); @@ -67,3 +132,4 @@ U_BOOT_CMD(cat, 4, 1, do_cat, "Print file to standard output", cat_help_text ); +#endif /* CONFIG_VFS */ diff --git a/test/dm/fs.c b/test/dm/fs.c index 8bba5263546..a8bd2a8e100 100644 --- a/test/dm/fs.c +++ b/test/dm/fs.c @@ -614,6 +614,33 @@ static int dm_test_vfs_stat(struct unit_test_state *uts) } DM_TEST(dm_test_vfs_stat, UTF_SCAN_FDT); +/* Test the cat command via VFS */ +static int dm_test_vfs_cat(struct unit_test_state *uts) +{ + ut_assertok(vfs_init()); + + ut_assertok(run_command("mount hostfs /host", 0)); + ut_assert_console_end(); + + /* Write a small file and cat it */ + memcpy(map_sysmem(0x1000, 11), "hello world", 11); + ut_assertok(run_command("save 1000 /host/.cat_test 0xb", 0)); + ut_assert_nextlinen("11 bytes"); + ut_assert_console_end(); + + ut_assertok(run_command("cat /host/.cat_test", 0)); + ut_assert_nextline("hello world"); + ut_assert_console_end(); + + os_unlink(".cat_test"); + + ut_assertok(run_command("umount /host", 0)); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_vfs_cat, UTF_SCAN_FDT); + /* Test save and load round-trip via VFS */ static int dm_test_vfs_save(struct unit_test_state *uts) { diff --git a/test/py/tests/test_cat.py b/test/py/tests/test_cat.py index f793b9fe0a1..ad5ef963e8d 100644 --- a/test/py/tests/test_cat.py +++ b/test/py/tests/test_cat.py @@ -6,7 +6,8 @@ import pytest from tests.fs_helper import FsHelper -@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('sandbox') +@pytest.mark.boardspec('!sandbox') @pytest.mark.buildconfigspec('cmd_cat') def test_cat(ubman): """ Unit test for cat From patchwork Fri Apr 3 14:04:45 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2108 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=1775225191; bh=kpT+YNTJZp6jjCgdavEC6xDiXuhfbAUCi/ME5CLecaY=; 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=AOEA5OtQPH4hn6VbIrYhlJStz/NvI0WzP54QOZjMU87K7Gkpnt53TH/UZ7/sk1ak4 Za2mOm4zxZqzPGMbYpq2EejVpRpruWzIshYh6Ll+gO72GjzS90HGfc7sUbwvXxeARr L1cH84wF6w3xvUvxVdLYrez08GxMvHqzjs1C5lDXf/8g7g9x6jbIOygQZrkG2/Hcyj Au8TiqVsaAQDlIkZM1+KLYAEdQ8PIoroKFEsI6UQvETgeLsdpOwvxY+md7dS5BDp9b WIviKT6yS3kuSL8QeFv2n3cpRf06Da0KisFC9awZxDomiM9GVcJoFvBWnh1Zybrf44 AV3LL56pgmpoA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 792CF6A368 for ; Fri, 3 Apr 2026 08:06:31 -0600 (MDT) 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 DPTv8-Ve7COW for ; Fri, 3 Apr 2026 08:06:31 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225190; bh=kpT+YNTJZp6jjCgdavEC6xDiXuhfbAUCi/ME5CLecaY=; 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=FCcm23W1bRb5P7kjgYgqRdZZLgItBEuthbQ40VTRbRg5WQL/6t/167GtawkEenTfK ZEIxjNor/fGu/b2VdSM4qpmes15VhZ+dUAWWr56velGj3HBaqL0CMABr3ijJEsm8tx Kk+coCvGJxcqTG3CISxeVR+qXI9mo9y1UzEPe61+kZQsb0Ml2mIfz+igvCGB/LpVDT ODOP50flOPyagSUfcAyGszYXPps5kT6f9z09iiKG/8RrzdhKpr1ZBUwtYGY/g+LgKI 0xRxc4fuCipMMltIkYmpt7RPqbwDqf6wLae905okpWWKyktcdVV6wWIRKqgGsp6VmF Q+nFh6vRKQmVw== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 389E86A36D for ; Fri, 3 Apr 2026 08:06:30 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225186; bh=6LSvMLJr8cuMFGUJjNHhAyF7UltxX0dTCI0+JebmNL4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pw1M0zDczqEKJJ9t6t5hoNiyx5Fw8LelRi5EgCtC7HxRKJ7IoToJCgkpz0Fdnquv4 wtfQlhEZjSxohThB3zYK0hoZgSK5yDcrTw2JqwfU+PW1QwUvq3bkVAvd3DW8YJ7CSJ TkVvi/tFJzUX0n1rkthUixqVOa4VvZV+qTjbAxJCt/qktCe7vzLb+NQ8js5ed9U2a6 5r7rrIbn1WPzWcMmTlNJ9opP2/HqXqd7CI4VJyX/4mAZYZkZ8SLHtbFDkzMFdXZnkD U5Vmg36kO6IcfUCfNOsAn+yZE+b+yOswbwuI6+EuDrtmR0rftJi46EJHgLYxvJJ1L1 9ZBuRa/53OoUg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 5CA136A347; Fri, 3 Apr 2026 08:06:26 -0600 (MDT) 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 TSvyWrA1mo-T; Fri, 3 Apr 2026 08:06:26 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225185; bh=4lR7lFti0pl0qvYS6LDI+XdmNOXS8vvGTGYP34C7ay8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ffYo5EJWS7Ej81JaZMvIxPEw0UC2PjBatpo+2R5FacS5CuqL4XcipuacOW1OdD6vi cPIAIbVK38iFcA5GBhumsxGfJk/8ezXYNNzxnD4URwIUpI4pt72BoC/8hgcJdX8FEv ZqFGihH4vRUCb4SW/5DuN9hv1ZoXPqNEbMvk3Fi/xazZarroV26bTBrJzbSPMwbVGw lqnyha7L0enVLAllv108OGf2caFtHmYvfwWvhtAWcsUPMohPNjGIoDMYr6E3SBTygs fd5i5yF4bnJyqkz2NygDz4P/RaXKbXCSsRAIP44ZcaVQYHjMKuKW9l+dK0dthlgV78 ktrsARQOBs+2w== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 7D46E6A340; Fri, 3 Apr 2026 08:06:25 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:45 -0600 Message-ID: <20260403140523.1998228-24-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: WNZ5OUOGZZOW7M2ONEIP3SZPKKUU63XL X-Message-ID-Hash: WNZ5OUOGZZOW7M2ONEIP3SZPKKUU63XL 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 23/34] vfs: Add mkdir, rm, mv, df, symlink, ln and umount-all 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 VFS operations for directory and file manipulation: - vfs_mkdir() for creating directories - vfs_unlink() for file deletion - vfs_rename() for rename/move (same-mount only) - vfs_statfs() and vfs_print_df() for filesystem usage - vfs_readlink() for reading symbolic links - vfs_ln() for creating symbolic links - vfs_umount_all() for unmounting all filesystems Add corresponding fs_ops (mkdir, unlink, rename, statfs, readlink, ln) and fs_do_*() wrappers in fs-uclass.c. Implement in ext4l where applicable. Signed-off-by: Simon Glass --- fs/fs-uclass.c | 61 ++++++++++++ fs/sandbox/sandboxfs.c | 33 +++++++ fs/vfs.c | 208 ++++++++++++++++++++++++++++++++++++++++- include/fs.h | 122 ++++++++++++++++++++++++ include/vfs.h | 17 ++++ 5 files changed, 437 insertions(+), 4 deletions(-) diff --git a/fs/fs-uclass.c b/fs/fs-uclass.c index 435fd32bc75..7338a266da9 100644 --- a/fs/fs-uclass.c +++ b/fs/fs-uclass.c @@ -115,6 +115,67 @@ int fs_mount(struct udevice *dev) return 0; } +int fs_do_ln(struct udevice *dev, const char *path, const char *target) +{ + struct fs_ops *ops = fs_get_ops(dev); + + if (!ops->ln) + return log_msg_ret("fln", -ENOSYS); + + return ops->ln(dev, path, target); +} + +int fs_do_rename(struct udevice *dev, const char *old_path, + const char *new_path) +{ + struct fs_ops *ops = fs_get_ops(dev); + + if (!ops->rename) + return log_msg_ret("frn", -ENOSYS); + + return ops->rename(dev, old_path, new_path); +} + +int fs_readlink(struct udevice *dev, const char *path, char *buf, int size) +{ + struct fs_ops *ops = fs_get_ops(dev); + + if (!ops->readlink) + return log_msg_ret("frl", -ENOSYS); + + return ops->readlink(dev, path, buf, size); +} + +int fs_do_statfs(struct udevice *dev, struct fs_statfs *stats) +{ + struct fs_ops *ops = fs_get_ops(dev); + + if (!ops->statfs) + return log_msg_ret("fss", -ENOSYS); + + return ops->statfs(dev, stats); +} + +int fs_do_unlink(struct udevice *dev, const char *path) +{ + struct fs_ops *ops = fs_get_ops(dev); + + if (!ops->unlink) + return log_msg_ret("fsu", -ENOSYS); + + return ops->unlink(dev, path); +} + +int fs_do_mkdir(struct udevice *dev, const char *path) +{ + struct fs_ops *ops = fs_get_ops(dev); + + if (!ops->mkdir) + return log_msg_ret("fsm", -ENOSYS); + + return ops->mkdir(dev, path); +} + int fs_unmount(struct udevice *dev) { struct fs_ops *ops = fs_get_ops(dev); diff --git a/fs/sandbox/sandboxfs.c b/fs/sandbox/sandboxfs.c index 70fb829ae67..87eec0b2acd 100644 --- a/fs/sandbox/sandboxfs.c +++ b/fs/sandbox/sandboxfs.c @@ -417,6 +417,34 @@ static int sandbox_fs_lookup_dir(struct udevice *dev, const char *path, return 0; } +static int sandbox_fs_ln(struct udevice *dev, const char *path, + const char *target) +{ + return os_symlink(target, path); +} + +static int sandbox_fs_rename(struct udevice *dev, const char *old_path, + const char *new_path) +{ + return os_rename(old_path, new_path); +} + +static int sandbox_fs_readlink(struct udevice *dev, const char *path, + char *buf, int size) +{ + return os_readlink(path, buf, size); +} + +static int sandbox_fs_unlink(struct udevice *dev, const char *path) +{ + return os_unlink(path); +} + +static int sandbox_fs_mkdir(struct udevice *dev, const char *path) +{ + return os_mkdir(path, 0755); +} + static int sandbox_fs_remove(struct udevice *dev) { return 0; @@ -426,6 +454,11 @@ static const struct fs_ops sandbox_fs_ops = { .mount = sandbox_fs_mount, .unmount = sandbox_fs_unmount, .lookup_dir = sandbox_fs_lookup_dir, + .ln = sandbox_fs_ln, + .rename = sandbox_fs_rename, + .readlink = sandbox_fs_readlink, + .unlink = sandbox_fs_unlink, + .mkdir = sandbox_fs_mkdir, }; static const struct udevice_id sandbox_fs_ids[] = { diff --git a/fs/vfs.c b/fs/vfs.c index 328b1076280..2e781e00d7e 100644 --- a/fs/vfs.c +++ b/fs/vfs.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -431,8 +432,8 @@ static int vfs_resolve_dir(const char *path, struct udevice **dirp, char *sub; int ret; - ret = vfs_resolve_mount(path, resolved, sizeof(resolved), - &mnt, &subpath); + ret = vfs_resolve_mount(path, resolved, + sizeof(resolved), &mnt, &subpath); if (ret) return log_msg_ret("vdm", ret); @@ -536,6 +537,36 @@ bool vfs_is_mount_point(struct udevice *dir) return !find_mount(dir, &mnt); } +int vfs_umount_all(void) +{ + struct udevice *dev, *next; + struct uclass *uc; + int ret, err = 0; + + ret = uclass_get(UCLASS_MOUNT, &uc); + if (ret) + return ret; + + uclass_foreach_dev_safe(dev, next, uc) { + struct vfsmount *mnt = dev_get_uclass_priv(dev); + + if (!device_active(dev)) + continue; + + ret = fs_unmount(mnt->target); + if (ret && ret != -ENOTCONN) { + err = ret; + continue; + } + + ret = fs_mount_uninit(dev); + if (ret) + err = ret; + } + + return err; +} + void vfs_print_mounts(void) { struct vfsmount *mnt; @@ -568,6 +599,175 @@ int vfs_open_file(const char *path, enum dir_open_flags_t oflags, return 0; } +void vfs_print_df(void) +{ + struct vfsmount *mnt; + struct udevice *dev; + + vfs_foreach_mount(mnt, dev) { + char path[FILE_MAX_PATH_LEN]; + struct fs_statfs stats; + int ret; + + if (vfs_mount_path(dev, path, sizeof(path))) + continue; + + ret = fs_do_statfs(mnt->target, &stats); + if (!ret) { + u64 used = stats.blocks - stats.bfree; + + printf("%-16s %6lu %10llu %10llu %10llu\n", path, + stats.bsize, stats.blocks * stats.bsize, + used * stats.bsize, + stats.bfree * stats.bsize); + } else { + printf("%-16s %6s %10s %10s %10s\n", path, + "-", "-", "-", "-"); + } + } +} + +int vfs_ln(const char *path, const char *target) +{ + char resolved[FILE_MAX_PATH_LEN]; + struct udevice *vfs, *mnt; + const char *subpath; + struct vfsmount *mnt_priv; + int ret; + + vfs = vfs_root(); + if (!vfs) + return log_msg_ret("vli", -ENXIO); + + path = vfs_path_resolve(vfs_getcwd(), path, resolved, sizeof(resolved)); + + if (!path) + return log_msg_ret("vrp", -ENAMETOOLONG); + ret = vfs_find_mount(vfs, path, &mnt, &subpath); + if (ret) + return log_msg_ret("vlm", ret); + + mnt_priv = dev_get_uclass_priv(mnt); + + return fs_do_ln(mnt_priv->target, subpath, target); +} + +int vfs_rename(const char *old_path, const char *new_path) +{ + char resolved_old[FILE_MAX_PATH_LEN]; + char resolved_new[FILE_MAX_PATH_LEN]; + struct udevice *mnt_old, *mnt_new; + const char *sub_old, *sub_new; + struct vfsmount *mnt_priv; + int ret; + + ret = vfs_resolve_mount(old_path, resolved_old, + sizeof(resolved_old), + &mnt_old, &sub_old); + if (ret) + return log_msg_ret("vro", ret); + + ret = vfs_resolve_mount(new_path, resolved_new, + sizeof(resolved_new), + &mnt_new, &sub_new); + if (ret) + return log_msg_ret("vrn", ret); + + /* Both paths must be on the same mount */ + if (mnt_old != mnt_new) + return log_msg_ret("vrx", -EXDEV); + + mnt_priv = dev_get_uclass_priv(mnt_old); + + return fs_do_rename(mnt_priv->target, sub_old, sub_new); +} + +int vfs_readlink(const char *path, char *buf, int size) +{ + char resolved[FILE_MAX_PATH_LEN]; + struct udevice *mnt; + struct vfsmount *mnt_priv; + const char *subpath; + int ret; + + ret = vfs_resolve_mount(path, resolved, + sizeof(resolved), &mnt, &subpath); + if (ret) + return log_msg_ret("rlm", ret); + + mnt_priv = dev_get_uclass_priv(mnt); + + return fs_readlink(mnt_priv->target, subpath, buf, size); +} + +int vfs_statfs(const char *path, struct fs_statfs *stats) +{ + char resolved[FILE_MAX_PATH_LEN]; + struct udevice *mnt; + struct vfsmount *mnt_priv; + const char *subpath; + int ret; + + ret = vfs_resolve_mount(path, resolved, + sizeof(resolved), &mnt, &subpath); + if (ret) + return log_msg_ret("dfm", ret); + + mnt_priv = dev_get_uclass_priv(mnt); + + return fs_do_statfs(mnt_priv->target, stats); +} + +int vfs_unlink(const char *path) +{ + char resolved[FILE_MAX_PATH_LEN]; + struct udevice *vfs, *mnt; + struct vfsmount *mnt_priv; + const char *subpath; + int ret; + + vfs = vfs_root(); + if (!vfs) + return log_msg_ret("vui", -ENXIO); + + path = vfs_path_resolve(vfs_getcwd(), path, resolved, sizeof(resolved)); + if (!path) + return log_msg_ret("vup", -ENAMETOOLONG); + + ret = vfs_find_mount(vfs, path, &mnt, &subpath); + if (ret) + return log_msg_ret("vum", ret); + + mnt_priv = dev_get_uclass_priv(mnt); + + return fs_do_unlink(mnt_priv->target, subpath); +} + +int vfs_mkdir(const char *path) +{ + char resolved[FILE_MAX_PATH_LEN]; + struct udevice *vfs, *mnt; + struct vfsmount *mnt_priv; + const char *subpath; + int ret; + + vfs = vfs_root(); + if (!vfs) + return log_msg_ret("vmi", -ENXIO); + + path = vfs_path_resolve(vfs_getcwd(), path, resolved, sizeof(resolved)); + if (!path) + return log_msg_ret("vmp", -ENAMETOOLONG); + + ret = vfs_find_mount(vfs, path, &mnt, &subpath); + if (ret) + return log_msg_ret("vmm", ret); + + mnt_priv = dev_get_uclass_priv(mnt); + + return fs_do_mkdir(mnt_priv->target, subpath); +} + int vfs_stat(const char *path, struct fs_dirent *dent) { struct fs_dir_stream *strm; @@ -612,8 +812,8 @@ int vfs_ls(const char *path) bool empty = true; int ret; - ret = vfs_resolve_mount(path, resolved, sizeof(resolved), - &mnt, &subpath); + ret = vfs_resolve_mount(path, resolved, + sizeof(resolved), &mnt, &subpath); if (ret) return ret; diff --git a/include/fs.h b/include/fs.h index 925f810902b..74cfd6af831 100644 --- a/include/fs.h +++ b/include/fs.h @@ -14,6 +14,7 @@ #include #include +struct fs_statfs; struct udevice; enum { @@ -79,6 +80,66 @@ struct fs_ops { */ int (*lookup_dir)(struct udevice *dev, const char *path, struct udevice **dirp); + + /** + * rename() - Rename or move a file or directory + * + * @dev: Filesystem device + * @old_path: Current path + * @new_path: New path + * Return 0 if OK, -ve on error + */ + int (*rename)(struct udevice *dev, const char *old_path, + const char *new_path); + + /** + * ln() - Create a symbolic link + * + * @dev: Filesystem device + * @path: Path of symlink to create + * @target: Target the symlink points to + * Return 0 if OK, -ve on error + */ + int (*ln)(struct udevice *dev, const char *path, const char *target); + + /** + * readlink() - Read the target of a symbolic link + * + * @dev: Filesystem device + * @path: Path to the symbolic link + * @buf: Buffer to receive the target path + * @size: Size of buffer + * Return: length of target string, or -ve on error + */ + int (*readlink)(struct udevice *dev, const char *path, char *buf, + int size); + + /** + * statfs() - Get filesystem statistics + * + * @dev: Filesystem device + * @stats: Returns filesystem statistics + * Return 0 if OK, -ve on error + */ + int (*statfs)(struct udevice *dev, struct fs_statfs *stats); + + /** + * unlink() - Delete a file + * + * @dev: Filesystem device + * @path: Path of the file to delete + * Return 0 if OK, -ve on error + */ + int (*unlink)(struct udevice *dev, const char *path); + + /** + * mkdir() - Create a directory + * + * @dev: Filesystem device + * @path: Path of the directory to create + * Return 0 if OK, -ve on error + */ + int (*mkdir)(struct udevice *dev, const char *path); }; /* Get access to a filesystem's operations */ @@ -112,6 +173,67 @@ int fs_unmount(struct udevice *dev); */ int fs_lookup_dir(struct udevice *dev, const char *path, struct udevice **dirp); +/** + * fs_do_ln() - Create a symbolic link on a filesystem + * + * @dev: Filesystem device + * @path: Path of symlink to create (within the filesystem) + * @target: Target the symlink points to + * Return: 0 if OK, -ENOSYS if not supported, other -ve on error + */ +int fs_do_ln(struct udevice *dev, const char *path, const char *target); + +/** + * fs_do_rename() - Rename or move a file on a filesystem + * + * Both paths must be within the same filesystem. + * + * @dev: Filesystem device + * @old_path: Current path (within the filesystem) + * @new_path: New path (within the filesystem) + * Return: 0 if OK, -ENOSYS if not supported, other -ve on error + */ +int fs_do_rename(struct udevice *dev, const char *old_path, + const char *new_path); + +/** + * fs_readlink() - Read a symbolic link target on a filesystem + * + * @dev: Filesystem device + * @path: Path to the symbolic link (within the filesystem) + * @buf: Buffer to receive the target path + * @size: Size of buffer + * Return: length of target, -ENOSYS if not supported, other -ve on error + */ +int fs_readlink(struct udevice *dev, const char *path, char *buf, int size); + +/** + * fs_do_statfs() - Get filesystem statistics + * + * @dev: Filesystem device + * @stats: Returns filesystem statistics + * Return: 0 if OK, -ENOSYS if not supported, other -ve on error + */ +int fs_do_statfs(struct udevice *dev, struct fs_statfs *stats); + +/** + * fs_do_unlink() - Delete a file on a filesystem + * + * @dev: Filesystem device + * @path: Path of the file to delete (within the filesystem) + * Return: 0 if OK, -ENOSYS if not supported, other -ve on error + */ +int fs_do_unlink(struct udevice *dev, const char *path); + +/** + * fs_do_mkdir() - Create a directory on a filesystem + * + * @dev: Filesystem device + * @path: Path of the directory to create (within the filesystem) + * Return: 0 if OK, -ENOSYS if not supported, other -ve on error + */ +int fs_do_mkdir(struct udevice *dev, const char *path); + /** * fs_split_path() - Get a list of subdirs in a filename * diff --git a/include/vfs.h b/include/vfs.h index 04cbd5d0ca9..9f82bc52bf9 100644 --- a/include/vfs.h +++ b/include/vfs.h @@ -131,6 +131,15 @@ int vfs_umount(struct udevice *mnt_dev); */ int vfs_umount_path(struct udevice *vfs, const char *path); +/** + * vfs_umount_all() - Unmount all filesystems + * + * Unmounts all mounted filesystems. Returns an error if any unmount fails. + * + * Return: 0 if OK, -ve on error + */ +int vfs_umount_all(void); + /** * vfs_find_mount() - Find the mount covering a path * @@ -292,6 +301,14 @@ int fs_mount_blkdev(const char *type, struct blk_desc *desc, int part_num, */ void vfs_print_mounts(void); +/** + * vfs_print_df() - Print filesystem usage for all mounts + * + * Iterates all mounts and prints statfs info for those that support it. + * Mounts that do not support statfs are listed with dashes. + */ +void vfs_print_df(void); + /** * do_cp() - Copy a file within the VFS * From patchwork Fri Apr 3 14:04:47 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2109 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=1775225195; bh=s1edS834cRKpw4uzuZk2uGH7WTfeAOD2UkgkcVQ4b68=; 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=GlDg9h/hRqh2CzqMvpyiQsXcvJUzSGWlBUSoOK0DRIXO5xZ9LkqzMhW66NS+UAFpF TrD7dDl/tBZUUY8ovRwBQlv2qRlwZ3JX1O34gnNFtojvpW+pma+XwdlHRaaUMBelhk 0lDhKhm+WxCrQ4c12osbw+l2hXEyyjlRrwdjp8vTIzUv5T+Yp4fFTG1kAAEumoChSW tirEehlblBlslSCVl5KLICWFT+EIZKYDbVHsALiKdfdhz4eBfhTqX63yBdOb6nkR+9 26aK5OT5f8uxpPbx3DbA4ZTG+omEPYb6WDurwAWfoZR8b1ROJV8KoFWAiGGbF1zdqt p+59f9fv3zqSg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id EEFDD6A34F for ; Fri, 3 Apr 2026 08:06:35 -0600 (MDT) 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 Ykc-IOoA9rjp for ; Fri, 3 Apr 2026 08:06:35 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225195; bh=s1edS834cRKpw4uzuZk2uGH7WTfeAOD2UkgkcVQ4b68=; 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=GlDg9h/hRqh2CzqMvpyiQsXcvJUzSGWlBUSoOK0DRIXO5xZ9LkqzMhW66NS+UAFpF TrD7dDl/tBZUUY8ovRwBQlv2qRlwZ3JX1O34gnNFtojvpW+pma+XwdlHRaaUMBelhk 0lDhKhm+WxCrQ4c12osbw+l2hXEyyjlRrwdjp8vTIzUv5T+Yp4fFTG1kAAEumoChSW tirEehlblBlslSCVl5KLICWFT+EIZKYDbVHsALiKdfdhz4eBfhTqX63yBdOb6nkR+9 26aK5OT5f8uxpPbx3DbA4ZTG+omEPYb6WDurwAWfoZR8b1ROJV8KoFWAiGGbF1zdqt p+59f9fv3zqSg== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id D85B66A340 for ; Fri, 3 Apr 2026 08:06:35 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225194; bh=npTcauRzhsl8ez9PFFwUm00pCXIoxxgPr4xRrKIcyiY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=e6sBMZuItoxGVM2wMurZUnwqqVQ8bBHln1fr70NajFVkoMb5pMJxp3m1uSao+JR75 cEPNBVfFSacssqEg/DZJqYEzt90EBLyYSO0IPjCtHAkBdRQhxfpeqAJ3dwn6aCiG1F GMlMbvxv4wXEFJLxfl7kU8QuKmnM4JRhDOo8TED/WbNdY38cimngps/7Q0Ky5dP6v4 Fby9+JdA2MZlNTIkGeiVc6pFwGJbxjfkkCL2n1PLYIygocBChE7MeMVeOYHAApxEF0 DizeFPAv4ELN1VsQjtLYGm4P9fGOQ5jOyYOneackJ/BtVXMTlk2RVX/qTaHRzoZQX6 ox0gHk/HfKpRQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 38F1F6A340; Fri, 3 Apr 2026 08:06:34 -0600 (MDT) 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 2Pkj06Dyqz5U; Fri, 3 Apr 2026 08:06:34 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225188; bh=7/SLoFb7Kn1Ll2Z2ACiTRtNIw0BZVXJBglgIPeicXmc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CAlZJRS1UBoE3nesBA3JO+Vgct1p5dn9ArWeMYy+eQFtZOp41aaK0ihI00dR6FuGh ebRo/afODCzP8Id0iqIrYWviA1V5HYiq2cfsZ/ezRPkdyw6kybCPUsfWK5nTFdpwB4 jRm+Xsx+IF6W9Zs5Emc5DucN4/eOchdpdjBP7lRcZMWaT353LF9uIgLPlpSyQagmFl cZAwLmGOOufLKWMiB4Bo9Ar9BEM+893FaZzOwHcYIXxllDxGUB0cGsYpJqJ6lMJY3M y3ecm7NYTJcbN0KTZPXEp/DW7Wkb3tWwReWnJQrSUwRDZfe5IJpo4fWuomxRy3cSD7 JPOFfZsmqSFbQ== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 1677867EE6; Fri, 3 Apr 2026 08:06:28 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:47 -0600 Message-ID: <20260403140523.1998228-26-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 7RZTTW7LAH4ZSSLPZDBNSRH6WJAGSYG3 X-Message-ID-Hash: 7RZTTW7LAH4ZSSLPZDBNSRH6WJAGSYG3 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 25/34] vfs: Support VFS paths in the source command 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 Extend the source command to accept VFS paths: source /boot/boot.scr source /host/setup.cmd When the argument contains a '/', it is treated as a VFS path. The file is loaded into memory and checked for a mkimage header. If present, cmd_source_script() handles it as a FIT or legacy image script. Otherwise, the file is executed as a plain text command list via run_command_list(). This is the key integration point for using VFS in boot scripts. Signed-off-by: Simon Glass --- cmd/source.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++ test/dm/fs.c | 33 ++++++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/cmd/source.c b/cmd/source.c index c9b5f8e400a..c5a3e41d8ef 100644 --- a/cmd/source.c +++ b/cmd/source.c @@ -16,13 +16,78 @@ #include #include +#include +#include +#include #include #include #include #include +#include #include #include +#if IS_ENABLED(CONFIG_VFS) +/** + * source_vfs_file() - Load and execute a script from a VFS path + * + * Loads the file into memory. If it has a valid image header, runs it + * via cmd_source_script(). Otherwise treats it as a plain text command + * list. + * + * @path: VFS path to the script file + * Return: 0 on success, non-zero on error + */ +static int source_vfs_file(const char *path) +{ + struct file_uc_priv *uc_priv; + struct udevice *fil; + long actual; + char *buf; + ulong addr; + int ret; + + ret = vfs_open_file(path, DIR_O_RDONLY, &fil); + if (ret) { + printf("## Cannot find script '%s'\n", path); + return CMD_RET_FAILURE; + } + + uc_priv = dev_get_uclass_priv(fil); + buf = malloc(uc_priv->size + 1); + if (!buf) + return CMD_RET_FAILURE; + + actual = file_read_at(fil, buf, 0, uc_priv->size); + if (actual < 0) { + free(buf); + printf("## Cannot read script '%s'\n", path); + return CMD_RET_FAILURE; + } + + buf[actual] = '\0'; + + /* Check for a mkimage header (legacy or FIT) */ + if (actual >= sizeof(struct legacy_img_hdr) && + image_get_magic((struct legacy_img_hdr *)buf) == IH_MAGIC) { + addr = map_to_sysmem(buf); + ret = cmd_source_script(addr, NULL, NULL); + } else { + /* Plain text command list */ + ret = run_command_list(buf, actual, 0); + } + + free(buf); + + return ret; +} + +static bool is_vfs_path(const char *arg) +{ + return arg && (arg[0] == '/' || strchr(arg, '/')); +} +#endif + static int do_source(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -30,6 +95,13 @@ static int do_source(struct cmd_tbl *cmdtp, int flag, int argc, int rcode; const char *fit_uname = NULL, *confname = NULL; +#if IS_ENABLED(CONFIG_VFS) + if (argc >= 2 && is_vfs_path(argv[1])) { + printf("## Executing script from '%s'\n", argv[1]); + return source_vfs_file(argv[1]); + } +#endif + /* Find script image */ if (argc < 2) { addr = CONFIG_SYS_LOAD_ADDR; diff --git a/test/dm/fs.c b/test/dm/fs.c index 4158d2ff3dc..2e046a4bd0c 100644 --- a/test/dm/fs.c +++ b/test/dm/fs.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -793,6 +794,38 @@ static int dm_test_vfs_symlink(struct unit_test_state *uts) } DM_TEST(dm_test_vfs_symlink, UTF_SCAN_FDT); +/* Test source command with VFS path */ +static int dm_test_vfs_source(struct unit_test_state *uts) +{ + ut_assertok(vfs_init()); + + ut_assertok(run_command("mount hostfs /host", 0)); + ut_assert_console_end(); + + /* Create a simple script that sets an env var */ + memcpy(map_sysmem(0x1000, 18), "setenv vfs_test ok", 18); + ut_assertok(run_command("save 1000 /host/.vfs_script 12", 0)); + ut_assert_nextline("18 bytes written"); + ut_assert_console_end(); + + /* Run it via source */ + ut_assertok(run_command("source /host/.vfs_script", 0)); + ut_assert_nextlinen("## Executing script"); + ut_assert_console_end(); + + /* Verify the env var was set */ + ut_asserteq_str("ok", env_get("vfs_test")); + + os_unlink(".vfs_script"); + env_set("vfs_test", NULL); + + ut_assertok(run_command("umount /host", 0)); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_vfs_source, UTF_SCAN_FDT); + /* Test the ln command via VFS */ static int dm_test_vfs_ln(struct unit_test_state *uts) { From patchwork Fri Apr 3 14:04:49 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2110 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=1775225204; bh=sZjOQAorUueDvWOr53c34XYGtJIVSmxM5T3rGB7710g=; 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=GI5UvTHoajxlCH7CJyYKIxpsJw+FXPZiHgENBMT4l06EJdkFDRzgC3W1mksh2FOA2 7D2kbhtxvHHBLiIrxuubO0L6yJxrJhTdxG0+lzcf5kXBnZK8Vk4HU+5oZp1wtuLcWd shPlMk39eIOH64U6o//u0Fk74MP15itzAZfjLXJ1HqaVD16V75dqSg6b2sa8+vaW1A lelTK6v/yUf3frocLrRASKIEDu9V6BdZncpiTKw90KtjIudVafv4KUe2e7vKZxZs7R FcGlnCbGj7iSdaZTKG9S24f7IP/ooKSVhWn7b0XKRkhxyiu43jdzxTrw2mFwXYBeID bu6osZY04vACw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id C4B546A376 for ; Fri, 3 Apr 2026 08:06:44 -0600 (MDT) 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 RGftsP1MM7bg for ; Fri, 3 Apr 2026 08:06:44 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225202; bh=sZjOQAorUueDvWOr53c34XYGtJIVSmxM5T3rGB7710g=; 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=V6N/8oiHfJRcVKyb1Yy3Wp74kZjUWi49CVwJxFoEmob3Z887yDnAVODrX4X8h4jhx FXxT6NPxyfUw/QhVtw/gtjRZl+VlhZquEhsQOAlE1ncdX0qP1U9VF273+E0cbTuGR6 ozQ44MJjaO0xqK8RjgQC/s+nj5eqCSB8BDPPI1fHpGmJLofUcXvcx98/9Ecs1ge4oO thFi3poSs90MKVbz3KhpiqTEoVBFI/Ys0AvCFKr3HjfxJUeP0XjbUUAh+v+84V+PCW WmVWIso/xz+07rKAR3jMx/vw4mBe67aToCknTSF0XUWkgY00ql7NEIHeJLD0NFdaAo ziskymtIQnqFw== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id BE65E6A362 for ; Fri, 3 Apr 2026 08:06:42 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225200; bh=aYpMmAgIr0xMwOD/cJerMUfR+lFWcOY2/uEm0JB4fHs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ELQyCGQvZRVYHJRJsuhE/if9s3F07wNLJkTngbHJfz5lW6l3wGjbofQbV4fZ8q3b1 x4lVW8HMBg2Rgc9WqulNXXXgfqSgaEO8Zeux4fkLHEFuP2BbPcvcwSJsmxpnkvPoPi wOQQiZJ5YOKXHcOTc+TzNlFanqK7Xxmrs13UCMjmNscgKvpq42XdDp4hLyR1F5Ng4q A20TP1o5sLXbDe4VY38IBFfaijTpaTiAv+xzbu9d3u+sVwKqYq2Ev97+5qBU5+DEYW 0yC0Y44yOiD5eBd5a+u3A+CKwxBy9seGVQY/s9/KDzzi5g/LuwPZKwesgckq2DPgG7 RmPqHTPviZsEQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 89A476A34C; Fri, 3 Apr 2026 08:06:40 -0600 (MDT) 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 gIIC6SEGSvkI; Fri, 3 Apr 2026 08:06:40 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225197; bh=luhfssFOhjd2d9Gs4kotcDoeygkrzBSLeca9jrTdDvA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HRHO3jZd+boGA8f0/6ohQIvDzqm/eMDIftD1ixIMho2z/weeu2bjTOpTBlO47069+ 5c3uHf1Gk2lpHXnlt3U0093gyLutDtaTC+XLiUgkfhp2GBF52IRzNcnuWBNGVm9GNY WiQ3JC/MDe3tP+MvIW6AU0RXCTw3Er6PkrYMMFx6IYe9sVWf0UIDNx/wK04XviYlnT sn8VDusxPyVY4GHurlhMpXriz79OkvUprjq/QZ60BLG0thJA87dm8WBNZNXc02f2cO H84gURQn4Y0g0F1PJ/dBPXb8J6YmY963+8Up3fsnKdUSpszyU/QA2CVJtxacogq0ck PRKzxNe7J1rqg== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 2908467EE6; Fri, 3 Apr 2026 08:06:37 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:49 -0600 Message-ID: <20260403140523.1998228-28-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 77G6G3GF336S2YU2MGCLDCEZM4NBNWAG X-Message-ID-Hash: 77G6G3GF336S2YU2MGCLDCEZM4NBNWAG 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 27/34] cmd: Add VFS file and directory tests to the test command 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 -f and -d operators to the test command when VFS is enabled. These use vfs_stat() to check whether a path is a regular file (-f) or a directory (-d), complementing the existing -e operator which uses the legacy filesystem interface. Signed-off-by: Simon Glass --- cmd/test.c | 24 ++++++++++++++++++++++++ test/dm/fs.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/cmd/test.c b/cmd/test.c index 6ff7adf2074..93e654a8597 100644 --- a/cmd/test.c +++ b/cmd/test.c @@ -6,6 +6,8 @@ #include #include +#include +#include #include #include #include @@ -29,6 +31,8 @@ #define OP_INT_GE 15 #define OP_FILE_EXISTS 16 #define OP_REGEX 17 +#define OP_VFS_FILE 18 +#define OP_VFS_DIR 19 const struct { int arg; @@ -52,6 +56,10 @@ const struct { {0, "-z", OP_STR_EMPTY, 2}, {0, "-n", OP_STR_NEMPTY, 2}, {0, "-e", OP_FILE_EXISTS, 4}, +#if IS_ENABLED(CONFIG_VFS) + {0, "-f", OP_VFS_FILE, 2}, + {0, "-d", OP_VFS_DIR, 2}, +#endif #ifdef CONFIG_REGEX {1, "=~", OP_REGEX, 3}, #endif @@ -147,6 +155,22 @@ static int do_test(struct cmd_tbl *cmdtp, int flag, int argc, case OP_FILE_EXISTS: expr = file_exists(ap[1], ap[2], ap[3], FS_TYPE_ANY); break; +#if IS_ENABLED(CONFIG_VFS) + case OP_VFS_FILE: { + struct fs_dirent dent; + + expr = !vfs_stat(ap[1], &dent) && + dent.type == FS_DT_REG; + break; + } + case OP_VFS_DIR: { + struct fs_dirent dent; + + expr = !vfs_stat(ap[1], &dent) && + dent.type == FS_DT_DIR; + break; + } +#endif #ifdef CONFIG_REGEX case OP_REGEX: { struct slre slre; diff --git a/test/dm/fs.c b/test/dm/fs.c index 60a0408dd99..e0c7f37e155 100644 --- a/test/dm/fs.c +++ b/test/dm/fs.c @@ -1020,6 +1020,37 @@ static int dm_test_vfs_stat(struct unit_test_state *uts) } DM_TEST(dm_test_vfs_stat, UTF_SCAN_FDT); +/* Test the -f and -d operators in the test command */ +static int dm_test_vfs_test_fd(struct unit_test_state *uts) +{ + ut_assertok(vfs_init()); + + ut_assertok(run_command("mount hostfs /host", 0)); + ut_assert_console_end(); + + /* -f should succeed for a regular file */ + ut_assertok(run_command("test -f /host/README", 0)); + + /* -f should fail for a directory */ + ut_asserteq(1, run_command("test -f /host/cmd", 0)); + + /* -d should succeed for a directory */ + ut_assertok(run_command("test -d /host/cmd", 0)); + + /* -d should fail for a regular file */ + ut_asserteq(1, run_command("test -d /host/README", 0)); + + /* Both should fail for non-existent paths */ + ut_asserteq(1, run_command("test -f /host/no-such-file", 0)); + ut_asserteq(1, run_command("test -d /host/no-such-dir", 0)); + + ut_assertok(run_command("umount /host", 0)); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_vfs_test_fd, UTF_SCAN_FDT); + /* Test the cat command via VFS */ static int dm_test_vfs_cat(struct unit_test_state *uts) { From patchwork Fri Apr 3 14:04:50 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2111 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=1775225205; bh=JdUXrncmNlbxjMaDPaaWU39FmWj/oll7KDQF6DLYkJY=; 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=hpHgjkvhwKyykparm/nLTinsGDS6NJXtnH3AFlwBE++YSoy+WTleYZdzpdbIYswey QhVTcSKd9poBvpdgjENzoEvBXfBWtvOoBv568Jr3dO9fbvW/ri5LS1joWso4SzZfpZ vQBJR4OrcWxjchYf9ya2aaUbD1aQ7RQCo+kL2/uwwflD/WhCObb1hgI/kHnMa0cQc6 R2QTmy+cCFl2rRFN0QsTc++g0UPYiFBiwvYeGGeJC0GOq16blvBc9EgQNetw5aCSXQ k0J05xe44p4Y7N4cknORutX68c0olnAhFS38xYIMdtypfT+FARJnhQuaPKWIwbIKx/ +xHVHWys+zCzw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 479D36A375 for ; Fri, 3 Apr 2026 08:06:45 -0600 (MDT) 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 l83OWjzLB_y6 for ; Fri, 3 Apr 2026 08:06:45 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225204; bh=JdUXrncmNlbxjMaDPaaWU39FmWj/oll7KDQF6DLYkJY=; 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=R8cQMZ2aP9urEzAEm19RedzlFZB8Xb2ZV38ySyd4NjF1P9XV5nw9un+7sGp4DkAWQ lAVc5qD/5ljsD/aRy7aznDAl/4K7cK8s2pdDhcjBSwKFISc0AglwEQVJlFBcX53NsJ 7YMAi7IsdR25NyJhK1UZaZVRmcr8QiGb3HoYow/SrTeNKRbMCpdJcagm11K1NQjFhr nfpijRrXwpWBMD+8hWjWTwcBDJslLQgEXq+I2LQj4QYLIJyaGQrJRNjY03oSMvUQvu mjzzmhYZAScdNFRA9Yyqp6U2wFgc0bm/KI8Iinzoaw+byntnQcfma7h5iz+AzeQ+RZ dL3R93nD8NKow== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 93D5F6A366 for ; Fri, 3 Apr 2026 08:06:44 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225200; bh=O+S3hFfNmfIZ2bUlrfbxl35NvsX+rdXTVc1Dy7aFpF4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=U1+iQ2dhx84lkLIa0IEWj/B7Ivhh7bbOU+819mdnp4ZXh0a1wvfgNRAqw+Nbfd4w1 KpRuZLC/cI6o5dnIW/Y0YVBOxAAVHtpO91Du7pB8qPdnIy9fv0NZCjs26gC+AZshRz DjkrWNviv3fcK3H/GVu8HKeInUtQgxxFRqWqYXkKC80hIxfyVYUJ0sfgJXKWEtoU5j NkTRjSWWzg4y/idf2vBsvxffCYfWUsLYOYBR9jvXfTX/BvOwB7B9iBn2Dh/tFTVQok uto9I2LFEiC6PNGIyB7sBIByC5K7QNSApGCQVCFId2DwbxEmP35326UdhqzQluXz79 D9aT9G63LOv1A== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 9C86267EE6; Fri, 3 Apr 2026 08:06:40 -0600 (MDT) 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 xccWoIYRAIff; Fri, 3 Apr 2026 08:06:40 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225198; bh=M/4BJS5JnXZasyzfAF9Gj5WHyVcK3bpnszGjA6bnM/k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=QZY15SmNhL0CZXkjl/xJXhirNvQObLFEsiV4Yr6rwTUXN5b4aOyftTRdFlmmbMewE t+D3PIuFwiN+l3t33tYfhlVcg/EPHd8aBDRG81A3oIsxm49C59u3Z3G4ASa0Xjs0Bv 81sH67ZkhICFe8YhD80RvDaHrEOrIy48/x2pD3xGHonOb9mt168TopNU3RKoV13MYj CPp1wqfyHx01UJhFp612E2I0qCDl1g0zCNh54yZCW+EcK63re68wddra0BJmeOsTCY x7VwNuZagzerxqCzw5Xd0yMHXLVQav0h6A5dZY/FsHa0Ak8MFuAjP2+vopZhf+NF92 JlUtg13gsPMKA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id DE8E56A34E; Fri, 3 Apr 2026 08:06:37 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:50 -0600 Message-ID: <20260403140523.1998228-29-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: VYDC42ZOKK2WEMCAXCKHYR5PEAOL7TR6 X-Message-ID-Hash: VYDC42ZOKK2WEMCAXCKHYR5PEAOL7TR6 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 28/34] vfs: Sort directory listing output alphabetically 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 Collect directory entries into an array and sort them by name before printing, so that vfs_ls() produces deterministic output regardless of the on-disk directory-entry order. Signed-off-by: Simon Glass --- fs/vfs.c | 49 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/fs/vfs.c b/fs/vfs.c index 2e781e00d7e..c22b8fa37dc 100644 --- a/fs/vfs.c +++ b/fs/vfs.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "vfs_internal.h" #include @@ -802,15 +803,25 @@ int vfs_stat(const char *path, struct fs_dirent *dent) return log_msg_ret("vsn", -ENOENT); } +static int dirent_cmp(const void *a, const void *b) +{ + const struct fs_dirent *da = a; + const struct fs_dirent *db = b; + + return strcmp(da->name, db->name); +} + int vfs_ls(const char *path) { char resolved[FILE_MAX_PATH_LEN]; struct udevice *mnt, *dir = NULL; struct fs_dir_stream *strm; + struct fs_dirent *entries; struct fs_dirent dent; const char *subpath; - bool empty = true; - int ret; + int count = 0; + int max = 64; + int ret, i; ret = vfs_resolve_mount(path, resolved, sizeof(resolved), &mnt, &subpath); @@ -830,17 +841,41 @@ int vfs_ls(const char *path) if (ret) return ret; + entries = malloc(max * sizeof(*entries)); + if (!entries) + return -ENOMEM; + ret = dir_open(dir, &strm); - if (ret) + if (ret) { + free(entries); return ret; + } while (!dir_read(dir, strm, &dent)) { - if (dent.type == FS_DT_DIR) - printf("DIR %10u %s\n", 0, dent.name); + if (count == max) { + struct fs_dirent *tmp; + + max *= 2; + tmp = realloc(entries, max * sizeof(*entries)); + if (!tmp) { + free(entries); + return -ENOMEM; + } + entries = tmp; + } + entries[count++] = dent; + } + + qsort(entries, count, sizeof(*entries), dirent_cmp); + + for (i = 0; i < count; i++) { + if (entries[i].type == FS_DT_DIR) + printf("DIR %10u %s\n", 0, entries[i].name); else - printf(" %10llu %s\n", dent.size, dent.name); - empty = false; + printf(" %10llu %s\n", entries[i].size, + entries[i].name); } + free(entries); dir_close(dir, strm); From patchwork Fri Apr 3 14:04:52 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2112 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=1775225205; bh=X3aSiOJiN+kHBTfXb5/P0MEKDFOMKMlcywD1VBvKKzE=; 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=s2uN6d2KQvNr2OrwW1BosCCfDfNhZqpcByKu8RyAOyi01fZsaSCVfYlkJhJ1yTJjZ oWTJarsUk5zNh6q8SFibMEso+coCVoYWpKEwxcUSNVE1hH+Dm3EJYvbxZfIFOPdTVC vEyulgXtH5Rmk0KKy/Sj1FbnSwh4LqHpwTkQhF6ESufVoyXkc7pol/pC9SkIqxPlvL 1gc3K8fbvkTHP8mcmF6rq21qQHHN/sbgUi7YLMQDhCX5EJx7d94RV+5qIAu81NGCDL 9sNNUxCExaisU2p7FsI5FwH8TfCl9R3rEb1WhVxfaz0cuzoplJ+n7rBZ1o5Nw/VHac izxXM7586detg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id A8D5A6A376 for ; Fri, 3 Apr 2026 08:06:45 -0600 (MDT) 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 Ip0ewIZHyTQg for ; Fri, 3 Apr 2026 08:06:45 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225205; bh=X3aSiOJiN+kHBTfXb5/P0MEKDFOMKMlcywD1VBvKKzE=; 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=s2uN6d2KQvNr2OrwW1BosCCfDfNhZqpcByKu8RyAOyi01fZsaSCVfYlkJhJ1yTJjZ oWTJarsUk5zNh6q8SFibMEso+coCVoYWpKEwxcUSNVE1hH+Dm3EJYvbxZfIFOPdTVC vEyulgXtH5Rmk0KKy/Sj1FbnSwh4LqHpwTkQhF6ESufVoyXkc7pol/pC9SkIqxPlvL 1gc3K8fbvkTHP8mcmF6rq21qQHHN/sbgUi7YLMQDhCX5EJx7d94RV+5qIAu81NGCDL 9sNNUxCExaisU2p7FsI5FwH8TfCl9R3rEb1WhVxfaz0cuzoplJ+n7rBZ1o5Nw/VHac izxXM7586detg== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 2758A6A34F for ; Fri, 3 Apr 2026 08:06:45 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225200; bh=CGmUioSTsZSlVdJB0VcGtXtUu6wk/IWXi8v+mwsmkIQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ACnSx+SOFH90h7D1HNVmefBYYpVuOUDUfzYoP34kjhX1nPuPPZFlpuWjW6liA85gy IughaawuV3BrbfZSUq+ddCNQomTtQA5yx8hkdgqbviZI3jPUAePf7jlm3SgtZ0DNAp /GSKL++2kOuYqAZ3TWE2MNXJIHdwZTfxOAmN7q42WK/1QxIspUxNm0c4DlS/ha1Ytb XkTCIbEvx8RzKm2X6svu5DjT2bXhtPM7DJQQPDLa62bsBvtfBWPcsjbOPbuC1v9z7W c8k97mtfADUkBPpQc4tWZVshJXyvypSbg01WRlMANBhhNF2zep4WmjHuuxhX/K6HYh HPzdp1cGPI3KA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id B05596A34E; Fri, 3 Apr 2026 08:06:40 -0600 (MDT) 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 N8mPsxxL-iWk; Fri, 3 Apr 2026 08:06:40 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225199; bh=+J/WIHFfy1N2iWJVnhG8R2Y1n/bvSsGv1c85qGp9Hks=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=eve0oQNFXBHYeVxfhTvcasOU6Jw0ftav90IY2hXGZMhwYiyVhMxF6hjj6Gnw9mZhp h/c/bDNJIG9EAPG7yjBHHC3Kewbip8g6qxgyxIcC5z7VQTp/8J+DymO72YCXT2gQIQ 9J3gSqefVCnkdU/ifunV5pze932lKuytpvJuJcPC9KeB1859HYobIwZ9IWdLLD74pq WWaCpkwu9VKUcdAWjgsI1VFePx4K8v07b1/02dfaPQF0oWT9fe9FhfV5Z1s8agsy7+ ig7wpwLpwKgXIHnRkjmR4cS49irerY1EfndQQqCFENIb/NuS6HEC54cB0gpw29LYU9 uyoxw8Yv5MfpA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 8B85A6A350; Fri, 3 Apr 2026 08:06:39 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:52 -0600 Message-ID: <20260403140523.1998228-31-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: TEPZ6R4EBPRKNKJQ6W5BYK6K5JFVYFU4 X-Message-ID-Hash: TEPZ6R4EBPRKNKJQ6W5BYK6K5JFVYFU4 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 30/34] fat: Add fat_statfs() to report filesystem statistics 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 fat_statfs() which reads the FAT metadata and scans the FAT table to count free clusters, filling in block size, total blocks and free blocks. This is used by the FAT VFS driver's statfs operation. Signed-off-by: Simon Glass --- fs/fat/fat.c | 27 +++++++++++++++++++++++++++ include/fat.h | 8 ++++++++ 2 files changed, 35 insertions(+) diff --git a/fs/fat/fat.c b/fs/fat/fat.c index 42bc7bffb0f..53f610ff2f3 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -697,6 +697,33 @@ static int get_fs_info(struct fsdata *mydata) return 0; } +int fat_statfs(struct fs_statfs *stats) +{ + struct fsdata fsdata; + u32 total_clust, free_clust, entry; + int ret; + + ret = get_fs_info(&fsdata); + if (ret) + return ret; + + total_clust = (fsdata.total_sect - fsdata.data_begin) / + fsdata.clust_size; + free_clust = 0; + for (entry = 2; entry < total_clust + 2; entry++) { + if (!get_fatent(&fsdata, entry)) + free_clust++; + } + + stats->bsize = fsdata.clust_size * fsdata.sect_size; + stats->blocks = total_clust; + stats->bfree = free_clust; + + free(fsdata.fatbuf); + + return 0; +} + int fat_itr_root(struct fat_itr *itr, struct fsdata *fsdata) { if (get_fs_info(fsdata)) diff --git a/include/fat.h b/include/fat.h index d08cd5d1c47..e6370c3afb0 100644 --- a/include/fat.h +++ b/include/fat.h @@ -413,6 +413,14 @@ int fat_rename(const char *old_path, const char *new_path); */ int fat_mkdir(const char *dirname); +/** + * fat_statfs() - get filesystem statistics + * + * @stats: pointer to struct fs_statfs to fill + * Return: 0 on success, -ve on error + */ +int fat_statfs(struct fs_statfs *stats); + /** * fat_close() - close FAT filesystem and release resources */ From patchwork Fri Apr 3 14:04:53 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2113 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=1775225206; bh=zpQ/uR1Stmpop5L40B308MqyVL6Q6HhzQRLeQ7DoNYw=; 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=FrxAhU4K4BxBDbkecnOliRzlhAhfX1DlBaTHLx65rw0iyiWSK5+YYWz/h84q8F1pT zQgbOUg3mfEJOLwASWruknyTlNR6IQYCYvAwQ2dpl6uEeS/xwPAC9fOmZ5E7W+4ljl edmppTpzAATmqSY4aeOV5ze+ivJawgYa5RQQY475J+SXrcm611mvsUE0LIvL/0ab1A TQamk5chHo2R2h8VbwKFCLP9ZnaoVQLUOzUsF8hD/yP0aSwxfQ/g9P99m8B+uGDIyp S9+6InOsXOi4uE9iGhjk8ClGW8lMSTd2YKAur51g5Q+UsBZ9xR+69GN0tkgtZOpSu7 cZ6DztmD/i4uw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 04A716A36B for ; Fri, 3 Apr 2026 08:06:46 -0600 (MDT) 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 Ve5azIVarAio for ; Fri, 3 Apr 2026 08:06:45 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225205; bh=zpQ/uR1Stmpop5L40B308MqyVL6Q6HhzQRLeQ7DoNYw=; 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=o3ynFR8o24PxrZqO+QoEPL9hQ/S6aesTvswh8NmJDZcKbZIIQiziH1iBt4QW9bF/r tbb0fLqi/HRzauIJ46M8edksN4Gs2DnaINcz+RvLZJlXZpUTPjQVRniQX6X7y7hFkJ h/NClAvZQzH2FOpcdk8a+mL5i1RrqrCcTNQ+O1X7m0NloyfbodPnFjX7msPDx/ikr5 G0JxTJh9eQpgwBPib5NCLeMOdcGYBZLzmiMNpvC2diJo+LV2mXtWovu0IzZ759uOrQ o2tWMNu245jaFbNMo1YXqNWbOGZR8WoCSw94G7IF6rHcU6r6efRoRFH2c5Gb2fYb7I PxpnPxQ7lKPwA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id C48F66A35C for ; Fri, 3 Apr 2026 08:06:45 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225200; bh=w6NlIpDpGVzMO9jKVpQ+oJLJM2VW6YHMlmeeaql8TI8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=AK0MziNwDOjAaDkw9CsIvdFIArL9+oTxn4SfU/uudJ+cAPlHa2LMVtzGUaAA2V8EU dJtgRBIZRK7JNr9k6d0ytkdZDdremP42QHSR+fKZFhcKGYaKJWCJRD725ZrAKsXe2a lHhC8NG2Iv4NFqHkYOPoPf/RweauwldFcEfyg7DeGXZw8gQKJKO/y1vrsVvC52ZUxW jn9WlMWr/K7gDOX6aLFmwcbf9k246lt8dGBVPXrNpCvlWb1ClVNmGyL0+Si0zXx3Ag ToSmwgoWQ3C0uZCYIUYGjDJ9kz91CN0gqpHHLWd4J0chh/rI9kCw2Wbl46o948/15D o1NnnSlVgFG2w== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id D30366A350; Fri, 3 Apr 2026 08:06:40 -0600 (MDT) 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 BSd8aPHOyTP8; Fri, 3 Apr 2026 08:06:40 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225200; bh=nGOyb99sQjmKYMkzyjfOh0wTP6Jta1zlwtJ6IyAZSTc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dXJE/xCZuV/LUomeQ/tbQOTrD+5zpAx3kgZrJJUI0iBaWST8gOUbKnPvr5dtIgYxg i70S6Zzk5Js8lUTWDLpWqKleCpfib2V4QfphUhVo7T/PfqckDDD+GP5SZtq8zfrho2 9SIssgMrXjNE/FMkH1Qk2r3spXVT8MUYj0zE/XCifMH46wNp9ajK4ceP48RFtvCS76 JR1V2xam66zvANiL8EKTB7wrzrpvKDTMtJiH5e/czCLAy5M9fy9XzE3AKBEL1k1ZVd Xzyw/ScYxXdc1pUhGcE7MZwDX1iU4cKEXjqlywiBviCNg6HuyQGuxOfa7tGLQlLYT0 TghmH3vzsoTsw== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 40A9E6A34F; Fri, 3 Apr 2026 08:06:40 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:53 -0600 Message-ID: <20260403140523.1998228-32-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: L2RX4QJVS352RK77TZND7MQ2QZMMDPBX X-Message-ID-Hash: L2RX4QJVS352RK77TZND7MQ2QZMMDPBX 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 31/34] fs: Add FAT VFS driver 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 a UCLASS_FS driver for the FAT filesystem, allowing it to be mounted through the VFS layer. The driver wraps the existing FAT functions for mount, unmount, directory listing, file read/write, mkdir, unlink and rename. Signed-off-by: Simon Glass --- fs/fat/Makefile | 1 + fs/fat/fs.c | 266 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 267 insertions(+) create mode 100644 fs/fat/fs.c diff --git a/fs/fat/Makefile b/fs/fat/Makefile index f3982fca4c6..36d75b884d2 100644 --- a/fs/fat/Makefile +++ b/fs/fat/Makefile @@ -2,4 +2,5 @@ # obj-$(CONFIG_$(PHASE_)FS_FAT) = fat.o +obj-$(CONFIG_$(PHASE_)VFS) += fs.o obj-$(CONFIG_$(PHASE_)FAT_WRITE) += fat_write.o diff --git a/fs/fat/fs.c b/fs/fat/fs.c new file mode 100644 index 00000000000..43e03337142 --- /dev/null +++ b/fs/fat/fs.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * FAT filesystem driver for the VFS layer + * + * Wraps the existing FAT implementation to provide UCLASS_FS and + * UCLASS_DIR devices, following the same pattern as sandboxfs.c. + * + * Copyright 2026 Simon Glass + */ + +#define LOG_CATEGORY UCLASS_FS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct fat_dir_priv - Private info for FAT directory devices + * + * @strm: Directory stream from fat_opendir(), or NULL + */ +struct fat_dir_priv { + struct fs_dir_stream *strm; +}; + +static int fat_vfs_mount(struct udevice *dev) +{ + struct fs_priv *uc_priv = dev_get_uclass_priv(dev); + struct fs_plat *plat = dev_get_uclass_plat(dev); + + if (uc_priv->mounted) + return log_msg_ret("fmm", -EISCONN); + + if (!plat->desc) + return log_msg_ret("fmd", -ENODEV); + + if (fat_set_blk_dev(plat->desc, &plat->part)) + return log_msg_ret("fmf", -EINVAL); + + uc_priv->mounted = true; + + return 0; +} + +static int fat_vfs_unmount(struct udevice *dev) +{ + struct fs_priv *uc_priv = dev_get_uclass_priv(dev); + + if (!uc_priv->mounted) + return log_msg_ret("fuu", -ENOTCONN); + + fat_close(); + uc_priv->mounted = false; + + return 0; +} + +static int fat_vfs_lookup_dir(struct udevice *dev, const char *path, + struct udevice **dirp) +{ + struct udevice *dir; + int ret; + + ret = dir_add_probe(dev, DM_DRIVER_GET(fat_vfs_dir), path, &dir); + if (ret) + return log_msg_ret("fld", ret); + + *dirp = dir; + + return 0; +} + +static int fat_vfs_mkdir(struct udevice *dev, const char *path) +{ + return fat_mkdir(path); +} + +static int fat_vfs_unlink(struct udevice *dev, const char *path) +{ + return fat_unlink(path); +} + +static int fat_vfs_rename(struct udevice *dev, const char *old_path, + const char *new_path) +{ + return fat_rename(old_path, new_path); +} + +static int fat_vfs_statfs(struct udevice *dev, struct fs_statfs *stats) +{ + return fat_statfs(stats); +} + +static const struct fs_ops fat_vfs_ops = { + .mount = fat_vfs_mount, + .unmount = fat_vfs_unmount, + .lookup_dir = fat_vfs_lookup_dir, + .mkdir = fat_vfs_mkdir, + .unlink = fat_vfs_unlink, + .rename = fat_vfs_rename, + .statfs = fat_vfs_statfs, +}; + +U_BOOT_DRIVER(fat_fs) = { + .name = "fat_fs", + .id = UCLASS_FS, + .ops = &fat_vfs_ops, +}; + +/* FAT directory driver */ + +static int fat_dir_open(struct udevice *dev, struct fs_dir_stream *strm) +{ + struct fat_dir_priv *priv = dev_get_priv(dev); + struct dir_uc_priv *uc_priv = dev_get_uclass_priv(dev); + struct fs_dir_stream *fat_strm; + const char *path; + int ret; + + path = *uc_priv->path ? uc_priv->path : "/"; + ret = fat_opendir(path, &fat_strm); + if (ret) + return log_msg_ret("fdo", ret); + + priv->strm = fat_strm; + + return 0; +} + +static int fat_dir_read(struct udevice *dev, struct fs_dir_stream *strm, + struct fs_dirent *dent) +{ + struct fat_dir_priv *priv = dev_get_priv(dev); + struct fs_dirent *fat_dent; + int ret; + + ret = fat_readdir(priv->strm, &fat_dent); + if (ret) + return ret; + + memcpy(dent, fat_dent, sizeof(*dent)); + + return 0; +} + +static int fat_dir_close(struct udevice *dev, struct fs_dir_stream *strm) +{ + struct fat_dir_priv *priv = dev_get_priv(dev); + + fat_closedir(priv->strm); + priv->strm = NULL; + + return 0; +} + +/* FAT file driver - stores the full path for path-based I/O */ + +/** + * struct fat_file_priv - Private info for FAT file devices + * + * @path: Full path within the FAT filesystem + */ +struct fat_file_priv { + char path[FILE_MAX_PATH_LEN]; +}; + +static ssize_t fat_read_iter(struct udevice *dev, struct iov_iter *iter, + loff_t pos) +{ + struct fat_file_priv *priv = dev_get_priv(dev); + loff_t actual; + int ret; + + ret = fat_read_file(priv->path, iter_iov_ptr(iter), pos, + iter_iov_avail(iter), &actual); + if (ret) + return log_msg_ret("ffr", ret); + iter_advance(iter, actual); + + return actual; +} + +static ssize_t fat_write_iter(struct udevice *dev, struct iov_iter *iter, + loff_t pos) +{ + struct fat_file_priv *priv = dev_get_priv(dev); + loff_t actual; + int ret; + + ret = file_fat_write(priv->path, (void *)iter_iov_ptr(iter), pos, + iter_iov_avail(iter), &actual); + if (ret) + return log_msg_ret("ffw", ret); + iter_advance(iter, actual); + + return actual; +} + +static struct file_ops fat_file_ops = { + .read_iter = fat_read_iter, + .write_iter = fat_write_iter, +}; + +U_BOOT_DRIVER(fat_vfs_file) = { + .name = "fat_vfs_file", + .id = UCLASS_FILE, + .ops = &fat_file_ops, + .priv_auto = sizeof(struct fat_file_priv), +}; + +static int fat_dir_open_file(struct udevice *dir, const char *leaf, + enum dir_open_flags_t oflags, + struct udevice **filp) +{ + struct dir_uc_priv *uc_priv = dev_get_uclass_priv(dir); + struct fat_file_priv *priv; + struct udevice *dev; + char path[FILE_MAX_PATH_LEN]; + loff_t size = 0; + int ret; + + if (*uc_priv->path) + snprintf(path, sizeof(path), "%s/%s", uc_priv->path, leaf); + else + snprintf(path, sizeof(path), "/%s", leaf); + + if (oflags == DIR_O_RDONLY) { + if (!fat_exists(path)) + return log_msg_ret("foe", -ENOENT); + ret = fat_size(path, &size); + if (ret) + return log_msg_ret("fos", ret); + } + + ret = file_add_probe(dir, DM_DRIVER_REF(fat_vfs_file), leaf, + size, oflags, &dev); + if (ret) + return log_msg_ret("fop", ret); + + priv = dev_get_priv(dev); + strlcpy(priv->path, path, sizeof(priv->path)); + *filp = dev; + + return 0; +} + +static struct dir_ops fat_dir_ops = { + .open = fat_dir_open, + .read = fat_dir_read, + .close = fat_dir_close, + .open_file = fat_dir_open_file, +}; + +U_BOOT_DRIVER(fat_vfs_dir) = { + .name = "fat_vfs_dir", + .id = UCLASS_DIR, + .ops = &fat_dir_ops, + .priv_auto = sizeof(struct fat_dir_priv), +}; From patchwork Fri Apr 3 14:04:54 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2114 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=1775225208; bh=laXwvaAsOe/upSvm8kAM2FnUNx05LwnNI6AWeh47NjY=; 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=b1NaUCg28LPQfsTQ7QOtWb9N83iZbCIWw58KeIHuxWV2Cwv24Gpl9pESDXhDFQEGB JUWx2FIEDvTxnCQ4apkuGPKXEo6dk5oADetR8kB+rnEskRhoczzK66SN3IgC4iVW/+ OpzdpaGJwZhoq2xifteIBOhOoKcxJSYb7KOwGMY3o+1Rs7p+H4EYB1Tzc5tuZ7cEUH yhj/BZ44kZxqqBXgKLoHUjQFLW0WkrH+2neL4r8PEMetf0MZYlsa6v+S+PkzSHkqLZ cz+7Gl44Oz2Gh9IF/l9Ye7isMaiPPc4oXFSwMmPyiL9G8Hlq1VmwJFTWxsOq2xlmVj ua4VV/MZAPhww== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 910DE67EE6 for ; Fri, 3 Apr 2026 08:06:48 -0600 (MDT) 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 yYW2PSqfaOIw for ; Fri, 3 Apr 2026 08:06:48 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225206; bh=laXwvaAsOe/upSvm8kAM2FnUNx05LwnNI6AWeh47NjY=; 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=VpexVgl1FZBnxUFuZFLjDJgW+Pj78t1GElXwPXsAdv/6oQQfivczHJrGVUbQxDdMY TR+ugT/IrQWqUGmf9uEcbcu47ETK332k+osZgW/Lst0Ua1ti0Pfmue5dgr88aIkVPY ckNK/DRXDJ0aQvUyf+b4uF0JN19f+aXOPb7UlSuMPoDVXaMqIjfEjr38QmgHb2eLrt CPWCF8nYjZit9oVSkfUzWEwTkvx0MNNSu7yJXrj0AIcHZhjgfeyDfih9MCaQalpkQW HHfhPifZ+YGfwgPoUTLcJwXWanosXE6RL8vjKY7m9kEog86tC6q0ph5+F3DqrMv9Zl CBVpM85Ix3U5g== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 5E19E6A364 for ; Fri, 3 Apr 2026 08:06:46 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225201; bh=LJ1Bk1z+aWcAvpoYL4QuD7LwpeR/0l2nWsiJnDTJCQg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Enad5e6l24w1i7pCLl10QXT1z7bCzhMoiWwyxWVaC9rqvC5DMnmInTex+HC4fd+l0 lmON/hGSdZdxKKzYn+dYcMqFSOm3FJ4bz7ySNJFGGqdeRe3BAKI5c56YcPCAPJZuL0 h/JkHcNWEnlZjWBa0c54Dkw2q7Sj0Xpzn8jekDAqBcxm3J275gIRRgpIDSWqgCjygm F3+gOsQtQmses2ZihYoDRwJq8jeCwWwCuDYhnpQjYm0iL6N3pjOhuKyK45WOvbGPhN TovhOXmeCAIBU1qc4nNvedezfsME0ozVWAK8LK/B8qYiyb4t+6XI6Bru8azxp4FV6V 3/xbRNjf/TBJg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id AAFAD67EE6; Fri, 3 Apr 2026 08:06:41 -0600 (MDT) 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 Kp7FxqVAHIAn; Fri, 3 Apr 2026 08:06:41 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225201; bh=eBr/QNvapV4Qap9Lfhg0k5UBBLuT2hkx9GFjIto4C7I=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FcFzJ8LFj78elzBUfUJ1JzMWwxmBQM1hUAvPybAOlp3Bh0PDdTT8HFpwXjfQbLyrz 8AMeJ5Ykh5t3msRSUR2lNKMyyXxaqc/Qt9Gt0MhFZLNdASdVeMctTDdeuAXRPDRE2/ kb18WeI/U6dzIPEA33qZI3oonp7u9NdXsm9N9Anv8NdX7RH2cNe/M/n8hJv8/CkGGM kbWa1nGagLSx7EQiJqw3L2Q6020tBmNlTw8cAekHUwPgrWNwmBsgZI5hOiSrKpbq1t nOmS/nZLCRbcQQkqmf118Fwi4DarSlrysJUkgDLYi/xgnzvgjpFp+tTDFUEKlB5Pp8 KUg1z5F7zjoRw== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 2027C6A34F; Fri, 3 Apr 2026 08:06:41 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:54 -0600 Message-ID: <20260403140523.1998228-33-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: BLD5NTARKFLCXGMNBBP3LBSNNVHUKGTM X-Message-ID-Hash: BLD5NTARKFLCXGMNBBP3LBSNNVHUKGTM 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 32/34] fs: Add ext4 VFS driver 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 a UCLASS_FS driver for the old ext4 filesystem implementation, allowing it to be mounted through the VFS layer. The driver wraps the existing ext4fs functions for mount, unmount, directory listing, file read/write, unlink and symlink creation. Write operations are guarded by CONFIG_EXT4_WRITE. This complements the ext4l VFS driver, enabling VFS on boards that use the original ext4 implementation. Signed-off-by: Simon Glass --- fs/ext4/Makefile | 1 + fs/ext4/fs.c | 264 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 265 insertions(+) create mode 100644 fs/ext4/fs.c diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile index 6ae44a2d0a3..f79baccc6a7 100644 --- a/fs/ext4/Makefile +++ b/fs/ext4/Makefile @@ -9,3 +9,4 @@ obj-y := ext4fs.o ext4_common.o dev.o obj-$(CONFIG_EXT4_WRITE) += ext4_write.o ext4_journal.o +obj-$(CONFIG_$(PHASE_)VFS) += fs.o diff --git a/fs/ext4/fs.c b/fs/ext4/fs.c new file mode 100644 index 00000000000..09cbddee50f --- /dev/null +++ b/fs/ext4/fs.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ext4 filesystem driver for the VFS layer + * + * Wraps the existing ext4 implementation to provide UCLASS_FS and + * UCLASS_DIR devices, following the same pattern as the FAT VFS driver. + * + * Copyright 2026 Simon Glass + */ + +#define LOG_CATEGORY UCLASS_FS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct ext4_dir_priv - Private info for ext4 directory devices + * + * @strm: Directory stream from ext4fs_opendir(), or NULL + */ +struct ext4_dir_priv { + struct fs_dir_stream *strm; +}; + +static int ext4_vfs_mount(struct udevice *dev) +{ + struct fs_priv *uc_priv = dev_get_uclass_priv(dev); + struct fs_plat *plat = dev_get_uclass_plat(dev); + int ret; + + if (uc_priv->mounted) + return log_msg_ret("emm", -EISCONN); + + if (!plat->desc) + return log_msg_ret("emd", -ENODEV); + + ret = ext4fs_probe(plat->desc, &plat->part); + if (ret) + return log_msg_ret("emp", ret); + + uc_priv->mounted = true; + + return 0; +} + +static int ext4_vfs_unmount(struct udevice *dev) +{ + struct fs_priv *uc_priv = dev_get_uclass_priv(dev); + + if (!uc_priv->mounted) + return log_msg_ret("euu", -ENOTCONN); + + ext4fs_close(); + uc_priv->mounted = false; + + return 0; +} + +static int ext4_vfs_lookup_dir(struct udevice *dev, const char *path, + struct udevice **dirp) +{ + struct udevice *dir; + int ret; + + ret = dir_add_probe(dev, DM_DRIVER_GET(ext4_vfs_dir), path, &dir); + if (ret) + return log_msg_ret("eld", ret); + + *dirp = dir; + + return 0; +} + +#if IS_ENABLED(CONFIG_EXT4_WRITE) +static int ext4_vfs_unlink(struct udevice *dev, const char *path) +{ + return ext4fs_filename_unlink((char *)path); +} + +static int ext4_vfs_ln(struct udevice *dev, const char *path, + const char *target) +{ + return ext4fs_create_link(target, path); +} +#endif + +static const struct fs_ops ext4_vfs_ops = { + .mount = ext4_vfs_mount, + .unmount = ext4_vfs_unmount, + .lookup_dir = ext4_vfs_lookup_dir, +#if IS_ENABLED(CONFIG_EXT4_WRITE) + .unlink = ext4_vfs_unlink, + .ln = ext4_vfs_ln, +#endif +}; + +U_BOOT_DRIVER(ext4_old_fs) = { + .name = "ext4_old_fs", + .id = UCLASS_FS, + .ops = &ext4_vfs_ops, +}; + +/* ext4 directory driver */ + +static int ext4_dir_open(struct udevice *dev, struct fs_dir_stream *strm) +{ + struct ext4_dir_priv *priv = dev_get_priv(dev); + struct dir_uc_priv *uc_priv = dev_get_uclass_priv(dev); + struct fs_dir_stream *ext4_strm; + const char *path; + int ret; + + path = *uc_priv->path ? uc_priv->path : "/"; + ret = ext4fs_opendir(path, &ext4_strm); + if (ret) + return log_msg_ret("edo", ret); + + priv->strm = ext4_strm; + + return 0; +} + +static int ext4_dir_read(struct udevice *dev, struct fs_dir_stream *strm, + struct fs_dirent *dent) +{ + struct ext4_dir_priv *priv = dev_get_priv(dev); + struct fs_dirent *ext4_dent; + int ret; + + ret = ext4fs_readdir(priv->strm, &ext4_dent); + if (ret) + return ret; + + *dent = *ext4_dent; + + return 0; +} + +static int ext4_dir_close(struct udevice *dev, struct fs_dir_stream *strm) +{ + struct ext4_dir_priv *priv = dev_get_priv(dev); + + ext4fs_closedir(priv->strm); + priv->strm = NULL; + + return 0; +} + +/* ext4 file driver */ + +/** + * struct ext4_file_priv - Private info for ext4 file devices + * + * @path: Full path within the ext4 filesystem + */ +struct ext4_file_priv { + char path[FILE_MAX_PATH_LEN]; +}; + +static ssize_t ext4_read_iter(struct udevice *dev, struct iov_iter *iter, + loff_t pos) +{ + struct ext4_file_priv *priv = dev_get_priv(dev); + loff_t actual; + int ret; + + ret = ext4_read_file(priv->path, iter_iov_ptr(iter), pos, + iter_iov_avail(iter), &actual); + if (ret) + return log_msg_ret("efr", ret); + iter_advance(iter, actual); + + return actual; +} + +#if IS_ENABLED(CONFIG_EXT4_WRITE) +static ssize_t ext4_write_iter(struct udevice *dev, struct iov_iter *iter, + loff_t pos) +{ + struct ext4_file_priv *priv = dev_get_priv(dev); + loff_t actual; + int ret; + + ret = ext4_write_file(priv->path, (void *)iter_iov_ptr(iter), pos, + iter_iov_avail(iter), &actual); + if (ret) + return log_msg_ret("efw", ret); + iter_advance(iter, actual); + + return actual; +} +#endif + +static struct file_ops ext4_file_ops = { + .read_iter = ext4_read_iter, +#if IS_ENABLED(CONFIG_EXT4_WRITE) + .write_iter = ext4_write_iter, +#endif +}; + +U_BOOT_DRIVER(ext4_vfs_file) = { + .name = "ext4_vfs_file", + .id = UCLASS_FILE, + .ops = &ext4_file_ops, + .priv_auto = sizeof(struct ext4_file_priv), +}; + +static int ext4_dir_open_file(struct udevice *dir, const char *leaf, + enum dir_open_flags_t oflags, + struct udevice **filp) +{ + struct dir_uc_priv *uc_priv = dev_get_uclass_priv(dir); + struct ext4_file_priv *priv; + struct udevice *dev; + char path[FILE_MAX_PATH_LEN]; + loff_t size = 0; + int ret; + + if (*uc_priv->path) + snprintf(path, sizeof(path), "%s/%s", uc_priv->path, leaf); + else + snprintf(path, sizeof(path), "/%s", leaf); + + if (oflags == DIR_O_RDONLY) { + if (!ext4fs_exists(path)) + return log_msg_ret("eoe", -ENOENT); + ret = ext4fs_size(path, &size); + if (ret) + return log_msg_ret("eos", ret); + } + + ret = file_add_probe(dir, DM_DRIVER_REF(ext4_vfs_file), leaf, + size, oflags, &dev); + if (ret) + return log_msg_ret("eop", ret); + + priv = dev_get_priv(dev); + strlcpy(priv->path, path, sizeof(priv->path)); + *filp = dev; + + return 0; +} + +static struct dir_ops ext4_dir_ops = { + .open = ext4_dir_open, + .read = ext4_dir_read, + .close = ext4_dir_close, + .open_file = ext4_dir_open_file, +}; + +U_BOOT_DRIVER(ext4_vfs_dir) = { + .name = "ext4_vfs_dir", + .id = UCLASS_DIR, + .ops = &ext4_dir_ops, + .priv_auto = sizeof(struct ext4_dir_priv), +}; From patchwork Fri Apr 3 14:04:55 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2115 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=1775225213; bh=wbHpgiMgtCkdvr1JU4hYPJYGyRDBfNTyN01aB4yUwAE=; 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=lbS8Tba9orbm9DQJ/84oDvHqwym/WUY3dntj3YdSAstELmh+xxJz6oF/HA2R0QVJl mzCIcD4Kx/UFfqfV7AgF7Af1wKomWCh96GJ9tnI7gr1seccKqs8pzd8RWNu+T1A+9s K6yKiRAeHIUSJXIBBm9nVZbyLC7uydLGGUPiw2N8q0Pfdb3rQZ4NC+VbqtROTwz75d jJJBNaRjVb0AjuRF+5dIOHCBkh5fWlp49t1o+pBHfORxJLq1d9wBvmjIoh9EZcJa4F x9iOzuRhVfFU30lJjpy3f0mtLJSu/qmIe4TelgTUIe1gy0cQRRqNRAhqMtm6RXzodW WSuYtfdvCQSEg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id DA6AA6A362 for ; Fri, 3 Apr 2026 08:06:53 -0600 (MDT) 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 LNVq-21lZOEo for ; Fri, 3 Apr 2026 08:06:53 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225210; bh=wbHpgiMgtCkdvr1JU4hYPJYGyRDBfNTyN01aB4yUwAE=; 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=pr1loUUrAXP9nsm9kAOXM/HIvmoRgA2XM9ts53UzLiBRZyAQZhD7D9qVBQJO2f06X IZ8xdjYPkrhwrXhg+1MFMuFH7GWfv164U80Hu1eciwr2U6N0yOWZ/zbMhIHJ9aAD3Y FKrtGvzkiaFgpa83xuWEVhkNHwcg4wB/gMoaMYoToKzumFzKucoOz+WBcrECx3POuy h/UpRMDIgzo2iFgdQOs0ypgMcQBAI8eUsIWAc7z3cDSC1WymUwfoG1NhtEk13fYMAQ FrEz2lWU5yUwYH0/G7BWXwnNSV4o+hXlRKCicxLBlKv/Yn7KjQDpKq4GbHeLykWkHn LCncZeskLUn5g== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id A3D976A340 for ; Fri, 3 Apr 2026 08:06:50 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225208; bh=utfHz19HEs/adIeFkf+9YN3U2XIjYQ8LeA55/TaikIk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=elCODkSRmUNdlMzxHAtLoudKGSpqskZFfg3FHT79ATp2VqJJ/tWFWMVhmHh0AwJND 7rH9LP09QaFdkhUh3vkHQmvRo6F+U55WvdnSn3CsEIJgpyUhD/A2UOuDbXYjDYN14s F7xszc0nMiiTmkaa0M4BiH8N4u2m0FgRl9JwhEVRDU2N5X82ERsU1q0Gdc0AJE0ROg IWnjGU4dCm8/sdHSHjiIyvkSa7bEs9LUOyVlS9UaFC/zoZIlsAk58LarhnMGH0oeMJ 2siYTqcSv4Ub/ZUJv8GElUzdzRC8983s6Fp3qMzRCmER5vbil+hTYQ/IKXxY6OTHui yVFl5ppvMH92Q== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 7FEC36A340; Fri, 3 Apr 2026 08:06:48 -0600 (MDT) 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 RpC-qm9KK_0O; Fri, 3 Apr 2026 08:06:48 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225202; bh=uC/mcvuhX+8ikg+V5UmT+8fGxiVYatM2m+zirPpmP2o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ZOOvdMzADNtnyLOSRJY1TMpYPvvNCbqQdxrkX2JzIDvJ+Msac5XoctCppi+VIyTWd gzHR4a/IkUvSrj/0OtRjACuQoTkiVCogkIRBwurMGbGY3ZiGaz40Pc9/mkmDVlJ7lL yOHvcELGFusKKM/zgryvWTvTqVk4hNBBMvXeCGTjH9Buav00rqmYONBDTCs4MJhve7 3VPfoFs486kn6DSmOa8a4FNe0LkKfEKmfcrRiQSUi73YiZfaF5lkkb4a4ESJ8NUD4A a62oF0RX2FnEPKX6hxEHNpZhWDkYJP2UtnlfVVxLmB3MQv4O4kJY1BMyFPwYpTDmOW qoFymu6aEs0Jg== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id EB41D6A34C; Fri, 3 Apr 2026 08:06:41 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:55 -0600 Message-ID: <20260403140523.1998228-34-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: QHHBJH32ML6PKWYYK7X7I6BPMYDG5PO7 X-Message-ID-Hash: QHHBJH32ML6PKWYYK7X7I6BPMYDG5PO7 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: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 33/34] test: Add VFS tests for ext4 and FAT filesystems 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 C unit tests and a Python test harness that exercise VFS operations on real ext4 and FAT filesystem images. The Python harness creates the images with test files, then the C tests mount them via VFS and verify ls, stat, load, save, cat, size, df, mkdir, rm, mv, nested paths and cd/pwd. Most tests are shared between ext4 and FAT. The stat helper detects the filesystem type to handle FAT's timestamp lines. Also add mkdir, unlink, rename and statfs operations to the FAT VFS driver, wrapping the existing FAT functions. Signed-off-by: Simon Glass --- test/fs/Makefile | 1 + test/fs/vfs.c | 503 ++++++++++++++++++++++++++++++ test/py/tests/test_fs/test_vfs.py | 98 ++++++ 3 files changed, 602 insertions(+) create mode 100644 test/fs/vfs.c create mode 100644 test/py/tests/test_fs/test_vfs.py diff --git a/test/fs/Makefile b/test/fs/Makefile index a8fd1227a1d..0e7d167a345 100644 --- a/test/fs/Makefile +++ b/test/fs/Makefile @@ -2,3 +2,4 @@ obj-y += fs_basic.o obj-$(CONFIG_FS_EXT4L) += ext4l.o +obj-$(CONFIG_$(PHASE_)VFS) += vfs.o diff --git a/test/fs/vfs.c b/test/fs/vfs.c new file mode 100644 index 00000000000..aaf024fcf36 --- /dev/null +++ b/test/fs/vfs.c @@ -0,0 +1,503 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * VFS tests on real filesystem images + * + * These tests are marked UTF_MANUAL and are called from test_vfs.py + * which creates the filesystem image. + * + * Copyright 2026 Simon Glass + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VFS_ARG_IMAGE 0 /* fs_image: path to filesystem image */ +#define BUF_ADDR 0x10000 + +/* Regex for a stat timestamp line, e.g. "Modify: 2026-04-02 08:21:38" */ +#define TIMESTAMP_RE(label) \ + label ": [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9:][0-9:]+" + +/** + * vfs_is_fat() - Check whether /mnt is a FAT filesystem + * + * Return: true if the mount at /mnt uses the FAT driver + */ +static bool vfs_is_fat(void) +{ + struct udevice *vfs, *mnt; + struct vfsmount *m; + const char *subpath; + + vfs = vfs_root(); + if (!vfs) + return false; + if (vfs_find_mount(vfs, "/mnt", &mnt, &subpath) || !mnt) + return false; + m = dev_get_uclass_priv(mnt); + + return !strcmp(m->target->driver->name, "fat_fs"); +} + +/** + * vfs_check_stat_end() - Check stat timestamps based on filesystem type + * + * FAT populates timestamps so we expect Modify/Access/Birth lines. + * ext4 (via the test images created with mkfs -d) does not. + */ +static int vfs_check_stat_end(struct unit_test_state *uts) +{ + if (vfs_is_fat()) { + ut_assert_nextline_regex(TIMESTAMP_RE("Modify")); + ut_assert_nextline_regex(TIMESTAMP_RE("Access")); + ut_assert_nextline_regex(TIMESTAMP_RE(" Birth")); + } + ut_assert_console_end(); + + return 0; +} + +/** + * vfs_mount_image() - Bind a host image and mount it via VFS + * + * @uts: Unit test state + * @image: Path to filesystem image + * @devp: Returns the host device + * Return: 0 on success + */ +static int vfs_mount_image(struct unit_test_state *uts, const char *image, + struct udevice **devp) +{ + struct udevice *blk; + struct blk_desc *desc; + + ut_assertok(vfs_init()); + ut_assertok(host_create_device("vfstest", true, DEFAULT_BLKSZ, devp)); + ut_assertok(host_attach_file(*devp, image)); + ut_assertok(blk_get_from_parent(*devp, &blk)); + ut_assertok(device_probe(blk)); + desc = dev_get_uclass_plat(blk); + + ut_assertok(run_commandf("mount host %x:0 /mnt", desc->devnum)); + ut_assert_console_end(); + + return 0; +} + +/** + * vfs_umount_image() - Unmount and clean up a host image + * + * @uts: Unit test state + * @dev: The host device to detach + * Return: 0 on success + */ +static int vfs_umount_image(struct unit_test_state *uts, struct udevice *dev) +{ + ut_assertok(run_command("umount /mnt", 0)); + ut_assert_console_end(); + ut_assertok(host_detach_file(dev)); + ut_assertok(device_unbind(dev)); + + return 0; +} + +/* Test ls on VFS-mounted filesystem */ +static int fs_test_vfs_ls_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(VFS_ARG_IMAGE); + struct udevice *dev; + + ut_assertok(vfs_mount_image(uts, fs_image, &dev)); + + ut_assertok(run_command("ls /mnt", 0)); + if (!vfs_is_fat()) { + ut_assert_nextline("DIR 0 ."); + ut_assert_nextline("DIR 0 .."); + ut_assert_nextline("DIR 0 lost+found"); + ut_assert_nextline("DIR 0 subdir"); + ut_assert_nextline(" 12 testfile.txt"); + } else { + ut_assert_nextline("DIR 0 subdir"); + ut_assert_nextline(" 12 testfile.txt"); + } + ut_assert_console_end(); + + ut_assertok(vfs_umount_image(uts, dev)); + + return 0; +} +FS_TEST_ARGS(fs_test_vfs_ls_norun, + UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); + +/* Test stat on VFS-mounted filesystem */ +static int fs_test_vfs_stat_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(VFS_ARG_IMAGE); + struct udevice *dev; + + ut_assertok(vfs_mount_image(uts, fs_image, &dev)); + + ut_assertok(run_command("stat /mnt/testfile.txt", 0)); + ut_assert_nextline(" File: testfile.txt"); + ut_assert_nextline(" Size: 12"); + ut_assert_nextline(" Type: regular file"); + ut_assertok(vfs_check_stat_end(uts)); + + /* Stat a directory */ + ut_assertok(run_command("stat /mnt/subdir", 0)); + ut_assert_nextline(" File: subdir"); + ut_assert_nextline(" Size: %d", vfs_is_fat() ? 0 : 4096); + ut_assert_nextline(" Type: directory"); + ut_assertok(vfs_check_stat_end(uts)); + + ut_assertok(vfs_umount_image(uts, dev)); + + return 0; +} +FS_TEST_ARGS(fs_test_vfs_stat_norun, + UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); + +/* Test load from VFS-mounted filesystem */ +static int fs_test_vfs_load_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(VFS_ARG_IMAGE); + struct udevice *dev; + char *buf; + + ut_assertok(vfs_mount_image(uts, fs_image, &dev)); + + buf = map_sysmem(BUF_ADDR, 0x100); + memset(buf, '\0', 0x100); + ut_assertok(run_commandf("load %x /mnt/testfile.txt", BUF_ADDR)); + ut_assert_nextline("12 bytes read"); + ut_assert_console_end(); + ut_asserteq_str("hello world\n", buf); + unmap_sysmem(buf); + + ut_assertok(vfs_umount_image(uts, dev)); + + return 0; +} +FS_TEST_ARGS(fs_test_vfs_load_norun, + UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); + +/* Test save to VFS-mounted filesystem */ +static int fs_test_vfs_save_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(VFS_ARG_IMAGE); + struct udevice *dev; + char *buf; + + ut_assertok(vfs_mount_image(uts, fs_image, &dev)); + + /* Write a file */ + buf = map_sysmem(BUF_ADDR, 0x10); + memcpy(buf, "test data\n", 10); + ut_assertok(run_commandf("save %x /mnt/newfile.txt a", BUF_ADDR)); + ut_assert_nextline("10 bytes written"); + ut_assert_console_end(); + + /* Read it back */ + memset(buf, '\0', 0x10); + ut_assertok(run_commandf("load %x /mnt/newfile.txt", BUF_ADDR)); + ut_assert_nextline("10 bytes read"); + ut_assert_console_end(); + ut_asserteq_mem("test data\n", buf, 10); + unmap_sysmem(buf); + + ut_assertok(vfs_umount_image(uts, dev)); + + return 0; +} +FS_TEST_ARGS(fs_test_vfs_save_norun, + UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); + +/* Test mkdir on VFS-mounted filesystem */ +static int fs_test_vfs_mkdir_norun(struct unit_test_state *uts) +{ + static int mkdir_seq; + const char *fs_image = ut_str(VFS_ARG_IMAGE); + struct udevice *dev; + char name[32]; + + /* + * Use a unique name each run because the test image is shared + * between live-tree and flat-tree passes, and VFS has no rmdir + * to clean up afterwards. + */ + snprintf(name, sizeof(name), ".vfs_mkdir_%d", mkdir_seq++); + + ut_assertok(vfs_mount_image(uts, fs_image, &dev)); + + /* Verify it does not exist yet */ + ut_asserteq(1, run_commandf("stat /mnt/%s", name)); + console_record_reset_enable(); + + ut_assertok(run_commandf("mkdir /mnt/%s", name)); + ut_assert_console_end(); + + ut_assertok(run_commandf("stat /mnt/%s", name)); + ut_assert_nextline(" File: %s", name); + ut_assert_nextline(" Size: %d", vfs_is_fat() ? 0 : 4096); + ut_assert_nextline(" Type: directory"); + ut_assertok(vfs_check_stat_end(uts)); + + ut_assertok(vfs_umount_image(uts, dev)); + + return 0; +} +FS_TEST_ARGS(fs_test_vfs_mkdir_norun, + UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); + +/* Test rm on VFS-mounted filesystem */ +static int fs_test_vfs_rm_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(VFS_ARG_IMAGE); + struct udevice *dev; + char *buf; + + ut_assertok(vfs_mount_image(uts, fs_image, &dev)); + + /* Write a file then delete it */ + buf = map_sysmem(BUF_ADDR, 0x10); + memcpy(buf, "delete me\n", 10); + ut_assertok(run_commandf("save %x /mnt/.rm_test a", BUF_ADDR)); + ut_assert_nextline("10 bytes written"); + ut_assert_console_end(); + + ut_assertok(run_command("rm /mnt/.rm_test", 0)); + ut_assert_console_end(); + + /* Verify it is gone */ + ut_asserteq(1, run_command("stat /mnt/.rm_test", 0)); + ut_assert_nextline("Error: -2: No such file or directory"); + ut_assert_console_end(); + + unmap_sysmem(buf); + ut_assertok(vfs_umount_image(uts, dev)); + + return 0; +} +FS_TEST_ARGS(fs_test_vfs_rm_norun, + UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); + +/* Test mv on VFS-mounted filesystem */ +static int fs_test_vfs_mv_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(VFS_ARG_IMAGE); + struct udevice *dev; + char *buf; + + ut_assertok(vfs_mount_image(uts, fs_image, &dev)); + + /* Write a file */ + buf = map_sysmem(BUF_ADDR, 0x10); + memcpy(buf, "move me\n", 8); + ut_assertok(run_commandf("save %x /mnt/.mv_src 8", BUF_ADDR)); + ut_assert_nextline("8 bytes written"); + ut_assert_console_end(); + + /* Rename it */ + ut_assertok(run_command("mv /mnt/.mv_src /mnt/.mv_dst", 0)); + ut_assert_console_end(); + + /* Old name should be gone */ + ut_asserteq(1, run_command("stat /mnt/.mv_src", 0)); + ut_assert_nextline("Error: -2: No such file or directory"); + ut_assert_console_end(); + + /* New name should have the data */ + memset(buf, '\0', 0x10); + ut_assertok(run_commandf("load %x /mnt/.mv_dst", BUF_ADDR)); + ut_assert_nextline("8 bytes read"); + ut_assert_console_end(); + ut_asserteq_mem("move me\n", buf, 8); + + unmap_sysmem(buf); + ut_assertok(vfs_umount_image(uts, dev)); + + return 0; +} +FS_TEST_ARGS(fs_test_vfs_mv_norun, + UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); + +/* Test cat on VFS-mounted filesystem */ +static int fs_test_vfs_cat_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(VFS_ARG_IMAGE); + struct udevice *dev; + + ut_assertok(vfs_mount_image(uts, fs_image, &dev)); + + ut_assertok(run_command("cat /mnt/testfile.txt", 0)); + ut_assert_nextline("hello world"); + ut_assert_console_end(); + + ut_assertok(vfs_umount_image(uts, dev)); + + return 0; +} +FS_TEST_ARGS(fs_test_vfs_cat_norun, + UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); + +/* Test size on VFS-mounted filesystem */ +static int fs_test_vfs_size_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(VFS_ARG_IMAGE); + struct udevice *dev; + + ut_assertok(vfs_mount_image(uts, fs_image, &dev)); + + env_set("filesize", NULL); + ut_assertok(run_command("size /mnt/testfile.txt", 0)); + ut_assert_console_end(); + ut_asserteq(12, env_get_hex("filesize", 0)); + + ut_assertok(vfs_umount_image(uts, dev)); + + return 0; +} +FS_TEST_ARGS(fs_test_vfs_size_norun, + UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); + +/* Test df on VFS-mounted filesystem */ +static int fs_test_vfs_df_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(VFS_ARG_IMAGE); + struct udevice *dev; + + ut_assertok(vfs_mount_image(uts, fs_image, &dev)); + + ut_assertok(run_command("df /mnt", 0)); + ut_assert_nextline( + "Filesystem Blksz Total Used Free Size"); + ut_assert_nextlinen("/mnt"); + ut_assert_console_end(); + + ut_assertok(vfs_umount_image(uts, dev)); + + return 0; +} +FS_TEST_ARGS(fs_test_vfs_df_norun, + UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); + +/* Test nested path access on VFS-mounted filesystem */ +static int fs_test_vfs_nested_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(VFS_ARG_IMAGE); + struct udevice *dev; + char *buf; + + ut_assertok(vfs_mount_image(uts, fs_image, &dev)); + + /* Load file from subdirectory */ + buf = map_sysmem(BUF_ADDR, 0x100); + memset(buf, '\0', 0x100); + ut_assertok(run_commandf("load %x /mnt/subdir/nested.txt", BUF_ADDR)); + ut_assert_nextline("12 bytes read"); + ut_assert_console_end(); + ut_asserteq_str("hello world\n", buf); + + /* Stat the subdirectory file */ + ut_assertok(run_command("stat /mnt/subdir/nested.txt", 0)); + ut_assert_nextline(" File: nested.txt"); + ut_assert_nextline(" Size: 12"); + ut_assert_nextline(" Type: regular file"); + ut_assertok(vfs_check_stat_end(uts)); + + unmap_sysmem(buf); + ut_assertok(vfs_umount_image(uts, dev)); + + return 0; +} +FS_TEST_ARGS(fs_test_vfs_nested_norun, + UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); + +/* Test cd and pwd on VFS-mounted filesystem */ +static int fs_test_vfs_cd_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(VFS_ARG_IMAGE); + struct udevice *dev; + + ut_assertok(vfs_mount_image(uts, fs_image, &dev)); + + ut_assertok(run_command("cd /mnt", 0)); + ut_assert_console_end(); + + ut_assertok(run_command("pwd", 0)); + ut_assert_nextline("/mnt"); + ut_assert_console_end(); + + /* ls without path should list cwd */ + ut_assertok(run_command("ls", 0)); + if (!vfs_is_fat()) { + ut_assert_nextline("DIR 0 ."); + ut_assert_nextline("DIR 0 .."); + ut_assert_nextline("DIR 0 lost+found"); + ut_assert_nextline("DIR 0 subdir"); + ut_assert_nextline(" 12 testfile.txt"); + } else { + ut_assert_nextline("DIR 0 subdir"); + ut_assert_nextline(" 12 testfile.txt"); + } + ut_assert_console_end(); + + /* cd back to root */ + ut_assertok(run_command("cd /", 0)); + ut_assert_console_end(); + + ut_assertok(vfs_umount_image(uts, dev)); + + return 0; +} +FS_TEST_ARGS(fs_test_vfs_cd_norun, + UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); + +/* Test ln (symlink) on VFS-mounted ext4 */ +static int fs_test_vfs_ext4_ln_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(VFS_ARG_IMAGE); + struct udevice *dev; + + ut_assertok(vfs_mount_image(uts, fs_image, &dev)); + + /* Create a symlink to testfile.txt */ + ut_assertok(run_command("ln testfile.txt /mnt/link.txt", 0)); + ut_assert_console_end(); + + /* Stat should show it as a symlink */ + ut_assertok(run_command("stat /mnt/link.txt", 0)); + ut_assert_nextline(" File: link.txt"); + ut_assert_nextline(" Size: 12"); + ut_assert_nextline(" Type: symbolic link"); + ut_assertok(vfs_check_stat_end(uts)); + + ut_assertok(vfs_umount_image(uts, dev)); + + return 0; +} +FS_TEST_ARGS(fs_test_vfs_ext4_ln_norun, + UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); diff --git a/test/py/tests/test_fs/test_vfs.py b/test/py/tests/test_fs/test_vfs.py new file mode 100644 index 00000000000..7f0fbdc802e --- /dev/null +++ b/test/py/tests/test_fs/test_vfs.py @@ -0,0 +1,98 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2026 Simon Glass +# +# Test VFS operations on real filesystem images + +""" +Test VFS mount, ls, stat, load, save, cat, mkdir, rm, mv on ext4 and +FAT images. Python creates the filesystem image; C tests mount it via +VFS and exercise the operations. +""" + +import os + +import pytest + +from tests.fs_helper import FsHelper + + +# Tests shared by all filesystems. Each entry 'foo' runs the C test +# fs_test_vfs_foo. Filesystem-specific tests (e.g. ext4-only 'ln') are +# listed separately and use the fs_test_vfs__ naming. +VFS_TESTS = [ + 'cat', 'cd', 'df', 'load', 'ls', 'mkdir', 'mv', 'nested', 'rm', 'save', + 'size', 'stat', +] + +EXT4_TESTS = ['ln'] + + +def _define_test(name, prefix='vfs'): + """Create a test method that runs fs_test__.""" + + def test(self, ubman, fs_image): + ubman.run_ut('fs', f'fs_test_{prefix}_{name}', + fs_image=fs_image) + + test.__name__ = f'test_{name}' + test.__doc__ = f'Test {name} on a VFS-mounted filesystem.' + return test + + +def _populate_srcdir(srcdir): + """Create the standard test files in a source directory.""" + with open(os.path.join(srcdir, 'testfile.txt'), 'w') as f: + f.write('hello world\n') + subdir = os.path.join(srcdir, 'subdir') + os.mkdir(subdir) + with open(os.path.join(subdir, 'nested.txt'), 'w') as f: + f.write('hello world\n') + + +@pytest.mark.buildconfigspec('cmd_vfs') +@pytest.mark.buildconfigspec('fs_ext4', 'fs_ext4l') +class TestVfsExt4: + """Test VFS operations on an ext4 filesystem.""" + + @pytest.fixture(scope='class') + def fs_image(self, u_boot_config): + """Create an ext4 filesystem image with test files.""" + fsh = FsHelper(u_boot_config, 'ext4', 64, 'vfs') + fsh.setup() + _populate_srcdir(fsh.srcdir) + fsh.mk_fs() + + yield fsh.fs_img + + fsh.cleanup() + + +# Attach shared and ext4-specific test methods to TestVfsExt4 +for _name in VFS_TESTS: + setattr(TestVfsExt4, f'test_{_name}', _define_test(_name)) +for _name in EXT4_TESTS: + setattr(TestVfsExt4, f'test_{_name}', + _define_test(_name, 'vfs_ext4')) + + +@pytest.mark.buildconfigspec('cmd_vfs') +@pytest.mark.buildconfigspec('fs_fat') +class TestVfsFat: + """Test VFS operations on a FAT filesystem.""" + + @pytest.fixture(scope='class') + def fs_image(self, u_boot_config): + """Create a FAT filesystem image with test files.""" + fsh = FsHelper(u_boot_config, 'fat32', 64, 'vfs') + fsh.setup() + _populate_srcdir(fsh.srcdir) + fsh.mk_fs() + + yield fsh.fs_img + + fsh.cleanup() + + +# Attach shared test methods to TestVfsFat +for _name in VFS_TESTS: + setattr(TestVfsFat, f'test_{_name}', _define_test(_name))