1 // SPDX-License-Identifier: GPL-2.0+
3 * Tiny program to perform file (range) clones using raw Btrfs and CIFS ioctls.
4 * Copyright (C) 2014 SUSE Linux Products GmbH. All Rights Reserved.
19 #include <linux/magic.h>
20 #ifdef HAVE_BTRFS_IOCTL_H
21 #include <btrfs/ioctl.h>
24 struct btrfs_ioctl_clone_range_args {
31 #define BTRFS_IOCTL_MAGIC 0x94
32 #define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int)
33 #define BTRFS_IOC_CLONE_RANGE _IOW(BTRFS_IOCTL_MAGIC, 13, \
34 struct btrfs_ioctl_clone_range_args)
37 #ifdef HAVE_CIFS_IOCTL_H
38 #include <cifs/ioctl.h>
41 #define CIFS_IOCTL_MAGIC 0xCF
42 #define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int)
46 #ifndef BTRFS_SUPER_MAGIC
47 #define BTRFS_SUPER_MAGIC 0x9123683E
49 #ifndef CIFS_MAGIC_NUMBER
50 #define CIFS_MAGIC_NUMBER 0xFE534D42
54 usage(char *name, const char *msg)
58 "%s [options] <src_file> <dest_file>\n"
59 "\tA full file clone is performed by default, "
60 "unless any of the following are specified (Btrfs only):\n"
61 "\t-s <offset>: source file offset (default = 0)\n"
62 "\t-d <offset>: destination file offset (default = 0)\n"
63 "\t-l <length>: length of clone (default = 0)\n\n"
64 "\tBoth Btrfs and CIFS are supported. On Btrfs, a COW clone "
65 "is attempted. On CIFS, a server-side copy is requested.\n",
71 clone_file_btrfs(int src_fd, int dst_fd)
73 int ret = ioctl(dst_fd, BTRFS_IOC_CLONE, src_fd);
80 clone_file_cifs(int src_fd, int dst_fd)
82 int ret = ioctl(dst_fd, CIFS_IOC_COPYCHUNK_FILE, src_fd);
89 clone_file(unsigned int fs_type, int src_fd, int dst_fd)
92 case BTRFS_SUPER_MAGIC:
93 return clone_file_btrfs(src_fd, dst_fd);
95 case CIFS_MAGIC_NUMBER:
96 return clone_file_cifs(src_fd, dst_fd);
105 clone_file_range_btrfs(int src_fd, int dst_fd, uint64_t src_off,
106 uint64_t dst_off, uint64_t len)
108 struct btrfs_ioctl_clone_range_args cr_args;
111 memset(&cr_args, 0, sizeof(cr_args));
112 cr_args.src_fd = src_fd;
113 cr_args.src_offset = src_off;
114 cr_args.src_length = len;
115 cr_args.dest_offset = dst_off;
116 ret = ioctl(dst_fd, BTRFS_IOC_CLONE_RANGE, &cr_args);
123 clone_file_range(unsigned int fs_type, int src_fd, int dst_fd, uint64_t src_off,
124 uint64_t dst_off, uint64_t len)
127 case BTRFS_SUPER_MAGIC:
128 return clone_file_range_btrfs(src_fd, dst_fd, src_off, dst_off,
131 case CIFS_MAGIC_NUMBER: /* only supports full file server-side copies */
139 cloner_check_fs_support(int src_fd, int dest_fd, unsigned int *fs_type)
144 ret = fstatfs(src_fd, &sfs);
146 printf("failed to stat source FS\n");
150 if ((sfs.f_type != BTRFS_SUPER_MAGIC)
151 && (sfs.f_type != CIFS_MAGIC_NUMBER)) {
152 printf("unsupported source FS 0x%x\n",
153 (unsigned int)sfs.f_type);
157 *fs_type = (unsigned int)sfs.f_type;
159 ret = fstatfs(dest_fd, &sfs);
161 printf("failed to stat destination FS\n");
165 if (sfs.f_type != *fs_type) {
166 printf("dest FS type 0x%x does not match source 0x%x\n",
167 (unsigned int)sfs.f_type, *fs_type);
175 main(int argc, char **argv)
177 bool full_file = true;
178 uint64_t src_off = 0;
179 uint64_t dst_off = 0;
187 unsigned int fs_type = 0;
189 while ((opt = getopt(argc, argv, "s:d:l:")) != -1) {
194 src_off = strtoull(optarg, &sval_end, 10);
195 if ((errno) || (*sval_end != '\0'))
196 usage(argv[0], "invalid source offset");
201 dst_off = strtoull(optarg, &sval_end, 10);
202 if ((errno) || (*sval_end != '\0'))
203 usage(argv[0], "invalid destination offset");
208 len = strtoull(optarg, &sval_end, 10);
209 if ((errno) || (*sval_end != '\0'))
210 usage(argv[0], "invalid length");
214 usage(argv[0], "invalid argument");
218 /* should be exactly two args left */
219 if (optind != argc - 2)
220 usage(argv[0], "src_file and dst_file arguments are madatory");
222 src_file = (char *)strdup(argv[optind++]);
223 if (src_file == NULL) {
225 printf("no memory\n");
228 dst_file = (char *)strdup(argv[optind++]);
229 if (dst_file == NULL) {
231 printf("no memory\n");
235 src_fd = open(src_file, O_RDONLY);
238 printf("failed to open %s: %s\n", src_file, strerror(errno));
241 dst_fd = open(dst_file, O_CREAT | O_WRONLY, 0644);
244 printf("failed to open %s: %s\n", dst_file, strerror(errno));
248 ret = cloner_check_fs_support(src_fd, dst_fd, &fs_type);
254 ret = clone_file(fs_type, src_fd, dst_fd);
256 ret = clone_file_range(fs_type, src_fd, dst_fd, src_off,
260 printf("clone failed: %s\n", strerror(ret));
268 printf("failed to close dst file: %s\n", strerror(errno));
273 printf("failed to close src file: %s\n", strerror(errno));