@@ -17,7 +17,10 @@
*/
#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <unistd.h>
+#include <fcntl.h>
#include "ctree.h"
#include "ioctl.h"
@@ -25,6 +28,91 @@
#include "commands.h"
#include "utils.h"
+static const char * const cmd_dedup_files_usage[] = {
+ "btrfs dedup files <len> <path1> <off1> <path2> <off2> ...",
+ "Try to deduplicate the file extents given in arguments.",
+ "Automatic deduplication support does not have to be turned on for this to work.",
+ "Extents whose data differs will be skipped for deduplication.",
+ NULL
+};
+
+static int cmd_dedup_files(int argc, char **argv)
+{
+ int ret, src_fd, i, idx, numfiles;
+ char *srcf, *destf;
+ struct btrfs_ioctl_same_args *same;
+ struct btrfs_ioctl_same_extent_info *info;
+
+ if (check_argc_min(argc, 6) || check_argc_exact(argc % 2, 0)) {
+ usage(cmd_dedup_files_usage);
+ return -1;
+ }
+
+ numfiles = (argc - 2) / 2; /* - 2 for cmd, and len args */
+ same = calloc(1,
+ sizeof(struct btrfs_ioctl_same_args) +
+ sizeof(struct btrfs_ioctl_same_extent_info) * numfiles);
+ if (!same)
+ return -ENOMEM;
+
+ srcf = argv[2];
+
+ src_fd = open(srcf, O_RDWR);
+ if (src_fd < 0) {
+ ret = -errno;
+ fprintf(stderr, "Could not open file %s: (%d) %s\n",
+ srcf, ret, strerror(-ret));
+ return ret;
+ }
+
+ same->length = parse_size(argv[1]);
+ same->logical_offset = parse_size(argv[3]);
+
+ for (i = 0; i < (numfiles - 1); i++) {
+ idx = 4 + (i * 2);
+ destf = argv[idx];
+
+ ret = open(destf, O_RDONLY);
+ if (ret < 0) {
+ ret = -errno;
+ fprintf(stderr, "Could not open file %s: (%d) %s\n",
+ destf, ret, strerror(-ret));
+ goto close_files;
+ }
+
+ same->info[i].fd = ret;
+ same->info[i].logical_offset = parse_size(argv[idx + 1]);
+ same->dest_count++;
+ }
+
+ printf("Deduplicate extents identical to (%llu, %llu) from %s\n",
+ (unsigned long long)same->logical_offset,
+ (unsigned long long)same->length, srcf);
+
+ ret = ioctl(src_fd, BTRFS_IOC_FILE_EXTENT_SAME, same);
+ if (ret) {
+ ret = -errno;
+ fprintf(stderr, "ioctl failed (%d): %s\n", ret, strerror(-ret));
+ goto close_files;
+ }
+
+ printf("%-40s %-7s %-s\n", "File", "Status", "Bytes Deduped");
+ for (i = 0; i < same->dest_count; i++) {
+ info = &same->info[i];
+ idx = 4 + (i * 2);
+
+ printf("%-40s %-6d %-llu\n", argv[idx],
+ info->status, (unsigned long long)info->bytes_deduped);
+ }
+
+close_files:
+ close(src_fd);
+ for (i = 0; i < same->dest_count; i++)
+ close(same->info[i].fd);
+
+ return ret;
+}
+
static const char * const dedup_cmd_group_usage[] = {
"btrfs dedup <command> [options] <path>",
NULL
@@ -62,7 +150,8 @@ int dedup_ctl(int cmd, int argc, char **argv)
static const char * const cmd_dedup_reg_usage[] = {
"btrfs dedup register <path>",
- "Enable data deduplication support for a filesystem.",
+ "Enable automatic data deduplication support for a filesystem.",
+ "If this is turned on, btrfs will try to deduplicate every file write.",
NULL
};
@@ -76,7 +165,7 @@ static int cmd_dedup_reg(int argc, char **argv)
static const char * const cmd_dedup_unreg_usage[] = {
"btrfs dedup unregister <path>",
- "Disable data deduplication support for a filesystem.",
+ "Disable automatic data deduplication support for a filesystem.",
NULL
};
@@ -90,6 +179,7 @@ static int cmd_dedup_unreg(int argc, char **argv)
const struct cmd_group dedup_cmd_group = {
dedup_cmd_group_usage, NULL, {
+ { "files", cmd_dedup_files, cmd_dedup_files_usage, 0, 0},
{ "register", cmd_dedup_reg, cmd_dedup_reg_usage, NULL, 0 },
{ "unregister", cmd_dedup_unreg, cmd_dedup_unreg_usage, 0, 0 },
{ 0, 0, 0, 0, 0 }
@@ -317,6 +317,31 @@ struct btrfs_ioctl_defrag_range_args {
__u32 unused[4];
};
+#define BTRFS_SAME_DATA_DIFFERS 1
+/* For extent-same ioctl */
+struct btrfs_ioctl_same_extent_info {
+ __s64 fd; /* in - destination file */
+ __u64 logical_offset; /* in - start of extent in destination */
+ __u64 bytes_deduped; /* out - total # of bytes we were able
+ * to dedupe from this file */
+ /* status of this dedupe operation:
+ * 0 if dedup succeeds
+ * < 0 for error
+ * == BTRFS_SAME_DATA_DIFFERS if data differs
+ */
+ __s32 status; /* out - see above description */
+ __u32 reserved;
+};
+
+struct btrfs_ioctl_same_args {
+ __u64 logical_offset; /* in - start of extent in source */
+ __u64 length; /* in - length of extent */
+ __u16 dest_count; /* in - total elements in info array */
+ __u16 reserved1;
+ __u32 reserved2;
+ struct btrfs_ioctl_same_extent_info info[0];
+};
+
struct btrfs_ioctl_space_info {
__u64 flags;
__u64 total_bytes;
@@ -597,6 +622,8 @@ struct btrfs_ioctl_clone_range_args {
struct btrfs_ioctl_get_dev_stats)
#define BTRFS_IOC_DEV_REPLACE _IOWR(BTRFS_IOCTL_MAGIC, 53, \
struct btrfs_ioctl_dev_replace_args)
+#define BTRFS_IOC_FILE_EXTENT_SAME _IOWR(BTRFS_IOCTL_MAGIC, 54, \
+ struct btrfs_ioctl_same_args)
#define BTRFS_IOC_DEDUP_CTL _IOW(BTRFS_IOCTL_MAGIC, 55, int)
#ifdef __cplusplus