]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blame - util/copy_sparse.c
android_config: remove HAVE_TERMIO_H
[thirdparty/e2fsprogs.git] / util / copy_sparse.c
CommitLineData
0d2993db
TT
1/*
2 * copy_sparse.c -- copy a very large sparse files efficiently
3 * (requires root privileges)
efc6f628 4 *
0d2993db
TT
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
17int 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
34extern char *optarg;
35extern 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
43int 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
48static 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
65static 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) {
efc6f628 73 if ((errno == EINTR) || (errno == EAGAIN))
0d2993db
TT
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
90static 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);
efc6f628 100
0d2993db
TT
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)
efc6f628 128 printf("File size of %s is %lld (%ld blocks)\n", src,
0d2993db
TT
129 (long long) fileinfo.st_size, numblocks);
130 } else {
131 fd = 0;
132 bs = 1024;
133 }
efc6f628 134
0d2993db
TT
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 }
efc6f628 146
0d2993db
TT
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) {
efc6f628 173 for (i=0; i < bs; i++)
0d2993db
TT
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
203static void usage(const char *progname)
204{
205 fprintf(stderr, "Usage: %s [-v] source_file destination_file\n", progname);
206 exit(1);
207}
208
209int 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