@@ -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,
@@ -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;
@@ -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
*
@@ -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)