btrfs: add BTRFS_IOC_ENCODED_WRITE
[platform/kernel/linux-starfive.git] / fs / btrfs / file.c
index 7b4a545..c7a5690 100644 (file)
@@ -2037,12 +2037,43 @@ out:
        return err < 0 ? err : written;
 }
 
-static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
-                                   struct iov_iter *from)
+static ssize_t btrfs_encoded_write(struct kiocb *iocb, struct iov_iter *from,
+                       const struct btrfs_ioctl_encoded_io_args *encoded)
+{
+       struct file *file = iocb->ki_filp;
+       struct inode *inode = file_inode(file);
+       loff_t count;
+       ssize_t ret;
+
+       btrfs_inode_lock(inode, 0);
+       count = encoded->len;
+       ret = generic_write_checks_count(iocb, &count);
+       if (ret == 0 && count != encoded->len) {
+               /*
+                * The write got truncated by generic_write_checks_count(). We
+                * can't do a partial encoded write.
+                */
+               ret = -EFBIG;
+       }
+       if (ret || encoded->len == 0)
+               goto out;
+
+       ret = btrfs_write_check(iocb, from, encoded->len);
+       if (ret < 0)
+               goto out;
+
+       ret = btrfs_do_encoded_write(iocb, from, encoded);
+out:
+       btrfs_inode_unlock(inode, 0);
+       return ret;
+}
+
+ssize_t btrfs_do_write_iter(struct kiocb *iocb, struct iov_iter *from,
+                           const struct btrfs_ioctl_encoded_io_args *encoded)
 {
        struct file *file = iocb->ki_filp;
        struct btrfs_inode *inode = BTRFS_I(file_inode(file));
-       ssize_t num_written = 0;
+       ssize_t num_written, num_sync;
        const bool sync = iocb->ki_flags & IOCB_DSYNC;
 
        /*
@@ -2053,22 +2084,28 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
        if (BTRFS_FS_ERROR(inode->root->fs_info))
                return -EROFS;
 
-       if (!(iocb->ki_flags & IOCB_DIRECT) &&
-           (iocb->ki_flags & IOCB_NOWAIT))
+       if ((iocb->ki_flags & IOCB_NOWAIT) && !(iocb->ki_flags & IOCB_DIRECT))
                return -EOPNOTSUPP;
 
        if (sync)
                atomic_inc(&inode->sync_writers);
 
-       if (iocb->ki_flags & IOCB_DIRECT)
-               num_written = btrfs_direct_write(iocb, from);
-       else
-               num_written = btrfs_buffered_write(iocb, from);
+       if (encoded) {
+               num_written = btrfs_encoded_write(iocb, from, encoded);
+               num_sync = encoded->len;
+       } else if (iocb->ki_flags & IOCB_DIRECT) {
+               num_written = num_sync = btrfs_direct_write(iocb, from);
+       } else {
+               num_written = num_sync = btrfs_buffered_write(iocb, from);
+       }
 
        btrfs_set_inode_last_sub_trans(inode);
 
-       if (num_written > 0)
-               num_written = generic_write_sync(iocb, num_written);
+       if (num_sync > 0) {
+               num_sync = generic_write_sync(iocb, num_sync);
+               if (num_sync < 0)
+                       num_written = num_sync;
+       }
 
        if (sync)
                atomic_dec(&inode->sync_writers);
@@ -2077,6 +2114,11 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
        return num_written;
 }
 
+static ssize_t btrfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+       return btrfs_do_write_iter(iocb, from, NULL);
+}
+
 int btrfs_release_file(struct inode *inode, struct file *filp)
 {
        struct btrfs_file_private *private = filp->private_data;