btrfs: add BTRFS_IOC_ENCODED_WRITE
[platform/kernel/linux-starfive.git] / fs / btrfs / ioctl.c
index a6994d2..238cee5 100644 (file)
@@ -105,6 +105,8 @@ struct btrfs_ioctl_encoded_io_args_32 {
 
 #define BTRFS_IOC_ENCODED_READ_32 _IOR(BTRFS_IOCTL_MAGIC, 64, \
                                       struct btrfs_ioctl_encoded_io_args_32)
+#define BTRFS_IOC_ENCODED_WRITE_32 _IOW(BTRFS_IOCTL_MAGIC, 64, \
+                                       struct btrfs_ioctl_encoded_io_args_32)
 #endif
 
 /* Mask out flags that are inappropriate for the given type of inode. */
@@ -5295,6 +5297,106 @@ out_acct:
        return ret;
 }
 
+static int btrfs_ioctl_encoded_write(struct file *file, void __user *argp, bool compat)
+{
+       struct btrfs_ioctl_encoded_io_args args;
+       struct iovec iovstack[UIO_FASTIOV];
+       struct iovec *iov = iovstack;
+       struct iov_iter iter;
+       loff_t pos;
+       struct kiocb kiocb;
+       ssize_t ret;
+
+       if (!capable(CAP_SYS_ADMIN)) {
+               ret = -EPERM;
+               goto out_acct;
+       }
+
+       if (!(file->f_mode & FMODE_WRITE)) {
+               ret = -EBADF;
+               goto out_acct;
+       }
+
+       if (compat) {
+#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT)
+               struct btrfs_ioctl_encoded_io_args_32 args32;
+
+               if (copy_from_user(&args32, argp, sizeof(args32))) {
+                       ret = -EFAULT;
+                       goto out_acct;
+               }
+               args.iov = compat_ptr(args32.iov);
+               args.iovcnt = args32.iovcnt;
+               args.offset = args32.offset;
+               args.flags = args32.flags;
+               args.len = args32.len;
+               args.unencoded_len = args32.unencoded_len;
+               args.unencoded_offset = args32.unencoded_offset;
+               args.compression = args32.compression;
+               args.encryption = args32.encryption;
+               memcpy(args.reserved, args32.reserved, sizeof(args.reserved));
+#else
+               return -ENOTTY;
+#endif
+       } else {
+               if (copy_from_user(&args, argp, sizeof(args))) {
+                       ret = -EFAULT;
+                       goto out_acct;
+               }
+       }
+
+       ret = -EINVAL;
+       if (args.flags != 0)
+               goto out_acct;
+       if (memchr_inv(args.reserved, 0, sizeof(args.reserved)))
+               goto out_acct;
+       if (args.compression == BTRFS_ENCODED_IO_COMPRESSION_NONE &&
+           args.encryption == BTRFS_ENCODED_IO_ENCRYPTION_NONE)
+               goto out_acct;
+       if (args.compression >= BTRFS_ENCODED_IO_COMPRESSION_TYPES ||
+           args.encryption >= BTRFS_ENCODED_IO_ENCRYPTION_TYPES)
+               goto out_acct;
+       if (args.unencoded_offset > args.unencoded_len)
+               goto out_acct;
+       if (args.len > args.unencoded_len - args.unencoded_offset)
+               goto out_acct;
+
+       ret = import_iovec(WRITE, args.iov, args.iovcnt, ARRAY_SIZE(iovstack),
+                          &iov, &iter);
+       if (ret < 0)
+               goto out_acct;
+
+       file_start_write(file);
+
+       if (iov_iter_count(&iter) == 0) {
+               ret = 0;
+               goto out_end_write;
+       }
+       pos = args.offset;
+       ret = rw_verify_area(WRITE, file, &pos, args.len);
+       if (ret < 0)
+               goto out_end_write;
+
+       init_sync_kiocb(&kiocb, file);
+       ret = kiocb_set_rw_flags(&kiocb, 0);
+       if (ret)
+               goto out_end_write;
+       kiocb.ki_pos = pos;
+
+       ret = btrfs_do_write_iter(&kiocb, &iter, &args);
+       if (ret > 0)
+               fsnotify_modify(file);
+
+out_end_write:
+       file_end_write(file);
+       kfree(iov);
+out_acct:
+       if (ret > 0)
+               add_wchar(current, ret);
+       inc_syscw(current);
+       return ret;
+}
+
 long btrfs_ioctl(struct file *file, unsigned int
                cmd, unsigned long arg)
 {
@@ -5441,9 +5543,13 @@ long btrfs_ioctl(struct file *file, unsigned int
                return fsverity_ioctl_measure(file, argp);
        case BTRFS_IOC_ENCODED_READ:
                return btrfs_ioctl_encoded_read(file, argp, false);
+       case BTRFS_IOC_ENCODED_WRITE:
+               return btrfs_ioctl_encoded_write(file, argp, false);
 #if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT)
        case BTRFS_IOC_ENCODED_READ_32:
                return btrfs_ioctl_encoded_read(file, argp, true);
+       case BTRFS_IOC_ENCODED_WRITE_32:
+               return btrfs_ioctl_encoded_write(file, argp, true);
 #endif
        }