[Concept,17/34] vfs: Add support for writing files

Message ID 20260403140523.1998228-18-sjg@u-boot.org
State New
Headers
Series Add a virtual filesystem (VFS) layer to U-Boot |

Commit Message

Simon Glass April 3, 2026, 2:04 p.m. UTC
  From: Simon Glass <sjg@chromium.org>

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 <sjg@chromium.org>
---

 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(-)
  

Patch

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 <dm.h>
 #include <file.h>
 #include <fs.h>
+#include <os.h>
 #include <vfs.h>
 #include <dm/test.h>
 #include <test/ut.h>
@@ -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)