generic: new case to test getcwd(2)
[xfstests-dev.git] / src / cloner.c
1 /*
2  *  Tiny program to perform file (range) clones using raw Btrfs ioctls.
3  *  It should only be needed until btrfs-progs has an xfs_io equivalent.
4  *
5  *  Copyright (C) 2014 SUSE Linux Products GmbH. All Rights Reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  */
21
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <sys/ioctl.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 #ifdef HAVE_BTRFS_IOCTL_H
34 #include <btrfs/ioctl.h>
35 #else
36
37 struct btrfs_ioctl_clone_range_args {
38         int64_t src_fd;
39         uint64_t src_offset;
40         uint64_t src_length;
41         uint64_t dest_offset;
42 };
43
44 #define BTRFS_IOCTL_MAGIC 0x94
45 #define BTRFS_IOC_CLONE       _IOW(BTRFS_IOCTL_MAGIC, 9, int)
46 #define BTRFS_IOC_CLONE_RANGE _IOW(BTRFS_IOCTL_MAGIC, 13, \
47                                    struct btrfs_ioctl_clone_range_args)
48 #endif
49
50 static void
51 usage(char *name, const char *msg)
52 {
53         printf("Fatal: %s\n"
54                "Usage:\n"
55                "%s [options] <src_file> <dest_file>\n"
56                "\tA full file clone (reflink) is performed by default, "
57                "unless any of the following are specified:\n"
58                "\t-s <offset>:  source file offset (default = 0)\n"
59                "\t-d <offset>:  destination file offset (default = 0)\n"
60                "\t-l <length>:  length of clone (default = 0)\n",
61                msg, name);
62         _exit(1);
63 }
64
65 static int
66 clone_file(int src_fd, int dst_fd)
67 {
68         int ret = ioctl(dst_fd, BTRFS_IOC_CLONE, src_fd);
69         if (ret != 0)
70                 ret = errno;
71         return ret;
72 }
73
74 static int
75 clone_file_range(int src_fd, int dst_fd, uint64_t src_off, uint64_t dst_off,
76                  uint64_t len)
77 {
78         struct btrfs_ioctl_clone_range_args cr_args;
79         int ret;
80
81         memset(&cr_args, 0, sizeof(cr_args));
82         cr_args.src_fd = src_fd;
83         cr_args.src_offset = src_off;
84         cr_args.src_length = len;
85         cr_args.dest_offset = dst_off;
86         ret = ioctl(dst_fd, BTRFS_IOC_CLONE_RANGE, &cr_args);
87         if (ret != 0)
88                 ret = errno;
89         return ret;
90 }
91
92 int
93 main(int argc, char **argv)
94 {
95         bool full_file = true;
96         uint64_t src_off = 0;
97         uint64_t dst_off = 0;
98         uint64_t len = 0;
99         char *src_file;
100         int src_fd;
101         char *dst_file;
102         int dst_fd;
103         int ret;
104         int opt;
105
106         while ((opt = getopt(argc, argv, "s:d:l:")) != -1) {
107                 char *sval_end;
108                 switch (opt) {
109                 case 's':
110                         errno = 0;
111                         src_off = strtoull(optarg, &sval_end, 10);
112                         if ((errno) || (*sval_end != '\0'))
113                                 usage(argv[0], "invalid source offset");
114                         full_file = false;
115                         break;
116                 case 'd':
117                         errno = 0;
118                         dst_off = strtoull(optarg, &sval_end, 10);
119                         if ((errno) || (*sval_end != '\0'))
120                                 usage(argv[0], "invalid destination offset");
121                         full_file = false;
122                         break;
123                 case 'l':
124                         errno = 0;
125                         len = strtoull(optarg, &sval_end, 10);
126                         if ((errno) || (*sval_end != '\0'))
127                                 usage(argv[0], "invalid length");
128                         full_file = false;
129                         break;
130                 default:
131                         usage(argv[0], "invalid argument");
132                 }
133         }
134
135         /* should be exactly two args left */
136         if (optind != argc - 2)
137                 usage(argv[0], "src_file and dst_file arguments are madatory");
138
139         src_file = (char *)strdup(argv[optind++]);
140         if (src_file == NULL) {
141                 ret = ENOMEM;
142                 printf("no memory\n");
143                 goto err_out;
144         }
145         dst_file = (char *)strdup(argv[optind++]);
146         if (dst_file == NULL) {
147                 ret = ENOMEM;
148                 printf("no memory\n");
149                 goto err_src_free;
150         }
151
152         src_fd = open(src_file, O_RDONLY);
153         if (src_fd == -1) {
154                 ret = errno;
155                 printf("failed to open %s: %s\n", src_file, strerror(errno));
156                 goto err_dst_free;
157         }
158         dst_fd = open(dst_file, O_CREAT | O_WRONLY, 0644);
159         if (dst_fd == -1) {
160                 ret = errno;
161                 printf("failed to open %s: %s\n", dst_file, strerror(errno));
162                 goto err_src_close;
163         }
164
165         if (full_file) {
166                 ret = clone_file(src_fd, dst_fd);
167         } else {
168                 ret = clone_file_range(src_fd, dst_fd, src_off, dst_off, len);
169         }
170         if (ret != 0) {
171                 printf("clone failed: %s\n", strerror(ret));
172                 goto err_dst_close;
173         }
174
175         ret = 0;
176 err_dst_close:
177         if (close(dst_fd)) {
178                 ret |= errno;
179                 printf("failed to close dst file: %s\n", strerror(errno));
180         }
181 err_src_close:
182         if (close(src_fd)) {
183                 ret |= errno;
184                 printf("failed to close src file: %s\n", strerror(errno));
185         }
186 err_dst_free:
187         free(dst_file);
188 err_src_free:
189         free(src_file);
190 err_out:
191         return ret;
192 }