open_by_handle: make -h (help) a valid option
[xfstests-dev.git] / src / cloner.c
1 /*
2  *  Tiny program to perform file (range) clones using raw Btrfs and CIFS ioctls.
3  *
4  *  Copyright (C) 2014 SUSE Linux Products GmbH. All Rights Reserved.
5  *
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.
10  *
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.
15  *
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.
19  */
20
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <sys/ioctl.h>
24 #include <sys/vfs.h>
25 #include <stdint.h>
26 #include <stdbool.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <linux/magic.h>
34 #ifdef HAVE_BTRFS_IOCTL_H
35 #include <btrfs/ioctl.h>
36 #else
37
38 struct btrfs_ioctl_clone_range_args {
39         int64_t src_fd;
40         uint64_t src_offset;
41         uint64_t src_length;
42         uint64_t dest_offset;
43 };
44
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)
49 #endif
50
51 #ifdef HAVE_CIFS_IOCTL_H
52 #include <cifs/ioctl.h>
53 #else
54
55 #define CIFS_IOCTL_MAGIC 0xCF
56 #define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int)
57
58 #endif
59
60 #ifndef BTRFS_SUPER_MAGIC
61 #define BTRFS_SUPER_MAGIC    0x9123683E
62 #endif
63 #ifndef CIFS_MAGIC_NUMBER
64 #define CIFS_MAGIC_NUMBER    0xFE534D42
65 #endif
66
67 static void
68 usage(char *name, const char *msg)
69 {
70         printf("Fatal: %s\n"
71                "Usage:\n"
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",
80                msg, name);
81         _exit(1);
82 }
83
84 static int
85 clone_file_btrfs(int src_fd, int dst_fd)
86 {
87         int ret = ioctl(dst_fd, BTRFS_IOC_CLONE, src_fd);
88         if (ret != 0)
89                 ret = errno;
90         return ret;
91 }
92
93 static int
94 clone_file_cifs(int src_fd, int dst_fd)
95 {
96         int ret = ioctl(dst_fd, CIFS_IOC_COPYCHUNK_FILE, src_fd);
97         if (ret != 0)
98                 ret = errno;
99         return ret;
100 }
101
102 static int
103 clone_file(unsigned int fs_type, int src_fd, int dst_fd)
104 {
105         switch (fs_type) {
106         case BTRFS_SUPER_MAGIC:
107                 return clone_file_btrfs(src_fd, dst_fd);
108                 break;
109         case CIFS_MAGIC_NUMBER:
110                 return clone_file_cifs(src_fd, dst_fd);
111                 break;
112         default:
113                 return ENOTSUP;
114                 break;
115         }
116 }
117
118 static int
119 clone_file_range_btrfs(int src_fd, int dst_fd, uint64_t src_off,
120                        uint64_t dst_off, uint64_t len)
121 {
122         struct btrfs_ioctl_clone_range_args cr_args;
123         int ret;
124
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);
131         if (ret != 0)
132                 ret = errno;
133         return ret;
134 }
135
136 static int
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)
139 {
140         switch (fs_type) {
141         case BTRFS_SUPER_MAGIC:
142                 return clone_file_range_btrfs(src_fd, dst_fd, src_off, dst_off,
143                                               len);
144                 break;
145         case CIFS_MAGIC_NUMBER: /* only supports full file server-side copies */
146         default:
147                 return ENOTSUP;
148                 break;
149         }
150 }
151
152 static int
153 cloner_check_fs_support(int src_fd, int dest_fd, unsigned int *fs_type)
154 {
155         int ret;
156         struct statfs sfs;
157
158         ret = fstatfs(src_fd, &sfs);
159         if (ret != 0) {
160                 printf("failed to stat source FS\n");
161                 return errno;
162         }
163
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);
168                 return ENOTSUP;
169         }
170
171         *fs_type = (unsigned int)sfs.f_type;
172
173         ret = fstatfs(dest_fd, &sfs);
174         if (ret != 0) {
175                 printf("failed to stat destination FS\n");
176                 return errno;
177         }
178
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);
182                 return ENOTSUP;
183         }
184
185         return 0;
186 }
187
188 int
189 main(int argc, char **argv)
190 {
191         bool full_file = true;
192         uint64_t src_off = 0;
193         uint64_t dst_off = 0;
194         uint64_t len = 0;
195         char *src_file;
196         int src_fd;
197         char *dst_file;
198         int dst_fd;
199         int ret;
200         int opt;
201         unsigned int fs_type = 0;
202
203         while ((opt = getopt(argc, argv, "s:d:l:")) != -1) {
204                 char *sval_end;
205                 switch (opt) {
206                 case 's':
207                         errno = 0;
208                         src_off = strtoull(optarg, &sval_end, 10);
209                         if ((errno) || (*sval_end != '\0'))
210                                 usage(argv[0], "invalid source offset");
211                         full_file = false;
212                         break;
213                 case 'd':
214                         errno = 0;
215                         dst_off = strtoull(optarg, &sval_end, 10);
216                         if ((errno) || (*sval_end != '\0'))
217                                 usage(argv[0], "invalid destination offset");
218                         full_file = false;
219                         break;
220                 case 'l':
221                         errno = 0;
222                         len = strtoull(optarg, &sval_end, 10);
223                         if ((errno) || (*sval_end != '\0'))
224                                 usage(argv[0], "invalid length");
225                         full_file = false;
226                         break;
227                 default:
228                         usage(argv[0], "invalid argument");
229                 }
230         }
231
232         /* should be exactly two args left */
233         if (optind != argc - 2)
234                 usage(argv[0], "src_file and dst_file arguments are madatory");
235
236         src_file = (char *)strdup(argv[optind++]);
237         if (src_file == NULL) {
238                 ret = ENOMEM;
239                 printf("no memory\n");
240                 goto err_out;
241         }
242         dst_file = (char *)strdup(argv[optind++]);
243         if (dst_file == NULL) {
244                 ret = ENOMEM;
245                 printf("no memory\n");
246                 goto err_src_free;
247         }
248
249         src_fd = open(src_file, O_RDONLY);
250         if (src_fd == -1) {
251                 ret = errno;
252                 printf("failed to open %s: %s\n", src_file, strerror(errno));
253                 goto err_dst_free;
254         }
255         dst_fd = open(dst_file, O_CREAT | O_WRONLY, 0644);
256         if (dst_fd == -1) {
257                 ret = errno;
258                 printf("failed to open %s: %s\n", dst_file, strerror(errno));
259                 goto err_src_close;
260         }
261
262         ret = cloner_check_fs_support(src_fd, dst_fd, &fs_type);
263         if (ret != 0) {
264                 goto err_dst_close;
265         }
266
267         if (full_file) {
268                 ret = clone_file(fs_type, src_fd, dst_fd);
269         } else {
270                 ret = clone_file_range(fs_type, src_fd, dst_fd, src_off,
271                                        dst_off, len);
272         }
273         if (ret != 0) {
274                 printf("clone failed: %s\n", strerror(ret));
275                 goto err_dst_close;
276         }
277
278         ret = 0;
279 err_dst_close:
280         if (close(dst_fd)) {
281                 ret |= errno;
282                 printf("failed to close dst file: %s\n", strerror(errno));
283         }
284 err_src_close:
285         if (close(src_fd)) {
286                 ret |= errno;
287                 printf("failed to close src file: %s\n", strerror(errno));
288         }
289 err_dst_free:
290         free(dst_file);
291 err_src_free:
292         free(src_file);
293 err_out:
294         return ret;
295 }