2 * Tiny program to perform file (range) clones using raw Btrfs and CIFS ioctls.
4 * Copyright (C) 2014 SUSE Linux Products GmbH. All Rights Reserved.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 #include <sys/types.h>
23 #include <sys/ioctl.h>
33 #include <linux/magic.h>
34 #ifdef HAVE_BTRFS_IOCTL_H
35 #include <btrfs/ioctl.h>
38 struct btrfs_ioctl_clone_range_args {
45 #define BTRFS_IOCTL_MAGIC 0x94
46 #define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int)
47 #define BTRFS_IOC_CLONE_RANGE _IOW(BTRFS_IOCTL_MAGIC, 13, \
48 struct btrfs_ioctl_clone_range_args)
51 #ifdef HAVE_CIFS_IOCTL_H
52 #include <cifs/ioctl.h>
55 #define CIFS_IOCTL_MAGIC 0xCF
56 #define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int)
60 #ifndef BTRFS_SUPER_MAGIC
61 #define BTRFS_SUPER_MAGIC 0x9123683E
63 #ifndef CIFS_MAGIC_NUMBER
64 #define CIFS_MAGIC_NUMBER 0xFE534D42
68 usage(char *name, const char *msg)
72 "%s [options] <src_file> <dest_file>\n"
73 "\tA full file clone is performed by default, "
74 "unless any of the following are specified (Btrfs only):\n"
75 "\t-s <offset>: source file offset (default = 0)\n"
76 "\t-d <offset>: destination file offset (default = 0)\n"
77 "\t-l <length>: length of clone (default = 0)\n\n"
78 "\tBoth Btrfs and CIFS are supported. On Btrfs, a COW clone "
79 "is attempted. On CIFS, a server-side copy is requested.\n",
85 clone_file_btrfs(int src_fd, int dst_fd)
87 int ret = ioctl(dst_fd, BTRFS_IOC_CLONE, src_fd);
94 clone_file_cifs(int src_fd, int dst_fd)
96 int ret = ioctl(dst_fd, CIFS_IOC_COPYCHUNK_FILE, src_fd);
103 clone_file(unsigned int fs_type, int src_fd, int dst_fd)
106 case BTRFS_SUPER_MAGIC:
107 return clone_file_btrfs(src_fd, dst_fd);
109 case CIFS_MAGIC_NUMBER:
110 return clone_file_cifs(src_fd, dst_fd);
119 clone_file_range_btrfs(int src_fd, int dst_fd, uint64_t src_off,
120 uint64_t dst_off, uint64_t len)
122 struct btrfs_ioctl_clone_range_args cr_args;
125 memset(&cr_args, 0, sizeof(cr_args));
126 cr_args.src_fd = src_fd;
127 cr_args.src_offset = src_off;
128 cr_args.src_length = len;
129 cr_args.dest_offset = dst_off;
130 ret = ioctl(dst_fd, BTRFS_IOC_CLONE_RANGE, &cr_args);
137 clone_file_range(unsigned int fs_type, int src_fd, int dst_fd, uint64_t src_off,
138 uint64_t dst_off, uint64_t len)
141 case BTRFS_SUPER_MAGIC:
142 return clone_file_range_btrfs(src_fd, dst_fd, src_off, dst_off,
145 case CIFS_MAGIC_NUMBER: /* only supports full file server-side copies */
153 cloner_check_fs_support(int src_fd, int dest_fd, unsigned int *fs_type)
158 ret = fstatfs(src_fd, &sfs);
160 printf("failed to stat source FS\n");
164 if ((sfs.f_type != BTRFS_SUPER_MAGIC)
165 && (sfs.f_type != CIFS_MAGIC_NUMBER)) {
166 printf("unsupported source FS 0x%x\n",
167 (unsigned int)sfs.f_type);
171 *fs_type = (unsigned int)sfs.f_type;
173 ret = fstatfs(dest_fd, &sfs);
175 printf("failed to stat destination FS\n");
179 if (sfs.f_type != *fs_type) {
180 printf("dest FS type 0x%x does not match source 0x%x\n",
181 (unsigned int)sfs.f_type, *fs_type);
189 main(int argc, char **argv)
191 bool full_file = true;
192 uint64_t src_off = 0;
193 uint64_t dst_off = 0;
201 unsigned int fs_type = 0;
203 while ((opt = getopt(argc, argv, "s:d:l:")) != -1) {
208 src_off = strtoull(optarg, &sval_end, 10);
209 if ((errno) || (*sval_end != '\0'))
210 usage(argv[0], "invalid source offset");
215 dst_off = strtoull(optarg, &sval_end, 10);
216 if ((errno) || (*sval_end != '\0'))
217 usage(argv[0], "invalid destination offset");
222 len = strtoull(optarg, &sval_end, 10);
223 if ((errno) || (*sval_end != '\0'))
224 usage(argv[0], "invalid length");
228 usage(argv[0], "invalid argument");
232 /* should be exactly two args left */
233 if (optind != argc - 2)
234 usage(argv[0], "src_file and dst_file arguments are madatory");
236 src_file = (char *)strdup(argv[optind++]);
237 if (src_file == NULL) {
239 printf("no memory\n");
242 dst_file = (char *)strdup(argv[optind++]);
243 if (dst_file == NULL) {
245 printf("no memory\n");
249 src_fd = open(src_file, O_RDONLY);
252 printf("failed to open %s: %s\n", src_file, strerror(errno));
255 dst_fd = open(dst_file, O_CREAT | O_WRONLY, 0644);
258 printf("failed to open %s: %s\n", dst_file, strerror(errno));
262 ret = cloner_check_fs_support(src_fd, dst_fd, &fs_type);
268 ret = clone_file(fs_type, src_fd, dst_fd);
270 ret = clone_file_range(fs_type, src_fd, dst_fd, src_off,
274 printf("clone failed: %s\n", strerror(ret));
282 printf("failed to close dst file: %s\n", strerror(errno));
287 printf("failed to close src file: %s\n", strerror(errno));