Whamcloud - gitweb
Update release notes, etc., for the 1.44.3-rc1 release
[tools/e2fsprogs.git] / util / copy_sparse.c
1 /*
2  * copy_sparse.c -- copy a very large sparse files efficiently
3  *      (requires root privileges)
4  *
5  * Copyright 2003, 2004 by Theodore Ts'o.
6  *
7  * %Begin-Header%
8  * This file may be redistributed under the terms of the GNU Public
9  * License.
10  * %End-Header%
11  */
12
13 #ifndef __linux__
14 #include <stdio.h>
15 #include <stdlib.h>
16
17 int main(void) {
18     fputs("This program is only supported on Linux!\n", stderr);
19     exit(EXIT_FAILURE);
20 }
21 #else
22 #define _LARGEFILE64_SOURCE
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <time.h>
29 #include <fcntl.h>
30 #include <errno.h>
31 #ifdef HAVE_GETOPT_H
32 #include <getopt.h>
33 #else
34 extern char *optarg;
35 extern int optind;
36 #endif
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/vfs.h>
40 #include <sys/ioctl.h>
41 #include <linux/fd.h>
42
43 int verbose = 0;
44
45 #define FIBMAP     _IO(0x00,1)  /* bmap access */
46 #define FIGETBSZ   _IO(0x00,2)  /* get the block size used for bmap */
47
48 static unsigned long get_bmap(int fd, unsigned long block)
49 {
50         int     ret;
51         unsigned long b;
52
53         b = block;
54         ret = ioctl(fd, FIBMAP, &b);
55         if (ret < 0) {
56                 if (errno == EPERM) {
57                         fprintf(stderr, "No permission to use FIBMAP ioctl; must have root privileges\n");
58                         exit(1);
59                 }
60                 perror("FIBMAP");
61         }
62         return b;
63 }
64
65 static int full_read(int fd, char *buf, size_t count)
66 {
67         int got, total = 0;
68         int pass = 0;
69
70         while (count > 0) {
71                 got = read(fd, buf, count);
72                 if (got == -1) {
73                         if ((errno == EINTR) || (errno == EAGAIN))
74                                 continue;
75                         return total ? total : -1;
76                 }
77                 if (got == 0) {
78                         if (pass++ >= 3)
79                                 return total;
80                         continue;
81                 }
82                 pass = 0;
83                 buf += got;
84                 total += got;
85                 count -= got;
86         }
87         return total;
88 }
89
90 static void copy_sparse_file(const char *src, const char *dest)
91 {
92         struct stat64   fileinfo;
93         long            lb, i, fd, ofd, bs, block, numblocks;
94         ssize_t         got, got2;
95         off64_t         offset = 0, should_be;
96         char            *buf;
97
98         if (verbose)
99                 printf("Copying sparse file from %s to %s\n", src, dest);
100
101         if (strcmp(src, "-")) {
102                 if (stat64(src, &fileinfo) < 0) {
103                         perror("stat");
104                         exit(1);
105                 }
106                 if (!S_ISREG(fileinfo.st_mode)) {
107                         printf("%s: Not a regular file\n", src);
108                         exit(1);
109                 }
110                 fd = open(src, O_RDONLY | O_LARGEFILE);
111                 if (fd < 0) {
112                         perror("open");
113                         exit(1);
114                 }
115                 if (ioctl(fd, FIGETBSZ, &bs) < 0) {
116                         perror("FIGETBSZ");
117                         close(fd);
118                         exit(1);
119                 }
120                 if (bs < 0) {
121                         printf("%s: Invalid block size: %ld\n", src, bs);
122                         exit(1);
123                 }
124                 if (verbose)
125                         printf("Blocksize of file %s is %ld\n", src, bs);
126                 numblocks = (fileinfo.st_size + (bs-1)) / bs;
127                 if (verbose)
128                         printf("File size of %s is %lld (%ld blocks)\n", src,
129                                (long long) fileinfo.st_size, numblocks);
130         } else {
131                 fd = 0;
132                 bs = 1024;
133         }
134
135         ofd = open(dest, O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0777);
136         if (ofd < 0) {
137                 perror(dest);
138                 exit(1);
139         }
140
141         buf = malloc(bs);
142         if (!buf) {
143                 fprintf(stderr, "Couldn't allocate buffer");
144                 exit(1);
145         }
146
147         for (lb = 0; !fd || lb < numblocks; lb++) {
148                 if (fd) {
149                         block = get_bmap(fd, lb);
150                         if (!block)
151                                 continue;
152                         should_be = ((off64_t) lb) * bs;
153                         if (offset != should_be) {
154                                 if (verbose)
155                                         printf("Seeking to %lld\n", should_be);
156                                 if (lseek64(fd, should_be, SEEK_SET) == (off_t) -1) {
157                                         perror("lseek src");
158                                         exit(1);
159                                 }
160                                 if (lseek64(ofd, should_be, SEEK_SET) == (off_t) -1) {
161                                         perror("lseek dest");
162                                         exit(1);
163                                 }
164                                 offset = should_be;
165                         }
166                 }
167                 got = full_read(fd, buf, bs);
168
169                 if (fd == 0 && got == 0)
170                         break;
171
172                 if (got == bs) {
173                         for (i=0; i < bs; i++)
174                                 if (buf[i])
175                                         break;
176                         if (i == bs) {
177                                 lseek(ofd, bs, SEEK_CUR);
178                                 offset += bs;
179                                 continue;
180                         }
181                 }
182                 got2 = write(ofd, buf, got);
183                 if (got != got2) {
184                         printf("short write\n");
185                         exit(1);
186                 }
187                 offset += got;
188         }
189         offset = fileinfo.st_size;
190         if (fstat64(ofd, &fileinfo) < 0) {
191                 perror("fstat");
192                 exit(1);
193         }
194         if (fileinfo.st_size != offset) {
195                 lseek64(ofd, offset-1, SEEK_CUR);
196                 buf[0] = 0;
197                 write(ofd, buf, 1);
198         }
199         close(fd);
200         close(ofd);
201 }
202
203 static void usage(const char *progname)
204 {
205         fprintf(stderr, "Usage: %s [-v] source_file destination_file\n", progname);
206         exit(1);
207 }
208
209 int main(int argc, char**argv)
210 {
211         int c;
212
213         while ((c = getopt(argc, argv, "v")) != EOF)
214                 switch (c) {
215                 case 'v':
216                         verbose++;
217                         break;
218                 default:
219                         usage(argv[0]);
220                         break;
221                 }
222         if (optind+2 != argc)
223                 usage(argv[0]);
224         copy_sparse_file(argv[optind], argv[optind+1]);
225
226         return 0;
227 }
228 #endif