blob: 7e46d9fed7925f89d1e8eaa0c3fe5d374734495a [file]
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
#include <linux/btrfs.h>
#include <liburing.h>
#include "config.h"
/* IORING_OP_URING_CMD defined from liburing 2.2 onwards */
#if LIBURING_MAJOR_VERSION < 2 || (LIBURING_MAJOR_VERSION == 2 && LIBURING_MINOR_VERSION < 2)
#define IORING_OP_URING_CMD 46
#endif
#ifndef BTRFS_IOC_ENCODED_WRITE
struct btrfs_ioctl_encoded_io_args {
const struct iovec *iov;
unsigned long iovcnt;
__s64 offset;
__u64 flags;
__u64 len;
__u64 unencoded_len;
__u64 unencoded_offset;
__u32 compression;
__u32 encryption;
__u8 reserved[64];
};
#define BTRFS_IOC_ENCODED_WRITE _IOW(BTRFS_IOCTL_MAGIC, 64, struct btrfs_ioctl_encoded_io_args)
#endif
#define BTRFS_MAX_COMPRESSED 131072
#define QUEUE_DEPTH 1
static int encoded_write_ioctl(const char *filename, long long offset,
long long len, long long unencoded_len,
long long unencoded_offset, int compression,
char *buf, size_t size)
{
int ret, fd;
struct iovec iov;
struct btrfs_ioctl_encoded_io_args enc;
fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0644);
if (fd < 0) {
fprintf(stderr, "open failed for %s\n", filename);
return 1;
}
iov.iov_base = buf;
iov.iov_len = size;
memset(&enc, 0, sizeof(enc));
enc.iov = &iov;
enc.iovcnt = 1;
enc.offset = offset;
enc.len = len;
enc.unencoded_len = unencoded_len;
enc.unencoded_offset = unencoded_offset;
enc.compression = compression;
ret = ioctl(fd, BTRFS_IOC_ENCODED_WRITE, &enc);
if (ret < 0) {
printf("%i\n", -errno);
close(fd);
return 0;
}
printf("%i\n", ret);
close(fd);
return 0;
}
static int encoded_write_io_uring(const char *filename, long long offset,
long long len, long long unencoded_len,
long long unencoded_offset, int compression,
char *buf, size_t size)
{
int ret, fd;
struct iovec iov;
struct btrfs_ioctl_encoded_io_args enc;
struct io_uring ring;
struct io_uring_sqe *sqe;
struct io_uring_cqe *cqe;
io_uring_queue_init(QUEUE_DEPTH, &ring, 0);
fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0644);
if (fd < 0) {
fprintf(stderr, "open failed for %s\n", filename);
ret = 1;
goto out_uring;
}
iov.iov_base = buf;
iov.iov_len = size;
memset(&enc, 0, sizeof(enc));
enc.iov = &iov;
enc.iovcnt = 1;
enc.offset = offset;
enc.len = len;
enc.unencoded_len = unencoded_len;
enc.unencoded_offset = unencoded_offset;
enc.compression = compression;
sqe = io_uring_get_sqe(&ring);
if (!sqe) {
fprintf(stderr, "io_uring_get_sqe failed\n");
ret = 1;
goto out_close;
}
io_uring_prep_rw(IORING_OP_URING_CMD, sqe, fd, &enc, sizeof(enc), 0);
/* sqe->cmd_op union'd to sqe->off from liburing 2.3 onwards */
#if LIBURING_MAJOR_VERSION < 2 || (LIBURING_MAJOR_VERSION == 2 && LIBURING_MINOR_VERSION < 3)
sqe->off = BTRFS_IOC_ENCODED_WRITE;
#else
sqe->cmd_op = BTRFS_IOC_ENCODED_WRITE;
#endif
io_uring_submit(&ring);
ret = io_uring_wait_cqe(&ring, &cqe);
if (ret < 0) {
fprintf(stderr, "io_uring_wait_cqe returned %i\n", ret);
ret = 1;
goto out_close;
}
io_uring_cqe_seen(&ring, cqe);
if (cqe->res < 0) {
printf("%i\n", cqe->res);
ret = 0;
goto out_close;
}
printf("%i\n", cqe->res);
ret = 0;
out_close:
close(fd);
out_uring:
io_uring_queue_exit(&ring);
return ret;
}
static void usage()
{
fprintf(stderr, "Usage: btrfs_encoded_write ioctl|io_uring filename offset len unencoded_len unencoded_offset compression\n");
}
int main(int argc, char *argv[])
{
const char *filename;
long long offset, len, unencoded_len, unencoded_offset;
int compression;
char buf[BTRFS_MAX_COMPRESSED];
size_t size;
if (argc != 8) {
usage();
return 1;
}
filename = argv[2];
offset = atoll(argv[3]);
if (offset == 0 && errno != 0) {
usage();
return 1;
}
len = atoll(argv[4]);
if (len == 0 && errno != 0) {
usage();
return 1;
}
unencoded_len = atoll(argv[5]);
if (unencoded_len == 0 && errno != 0) {
usage();
return 1;
}
unencoded_offset = atoll(argv[6]);
if (unencoded_offset == 0 && errno != 0) {
usage();
return 1;
}
compression = atoi(argv[7]);
if (compression == 0 && errno != 0) {
usage();
return 1;
}
size = fread(buf, 1, BTRFS_MAX_COMPRESSED, stdin);
if (!strcmp(argv[1], "ioctl")) {
return encoded_write_ioctl(filename, offset, len, unencoded_len,
unencoded_offset, compression, buf,
size);
} else if (!strcmp(argv[1], "io_uring")) {
return encoded_write_io_uring(filename, offset, len,
unencoded_len, unencoded_offset,
compression, buf, size);
} else {
usage();
return 1;
}
}