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), +};