2 * copy_sparse.c -- copy a very large sparse files efficiently
3 * (requires root privileges)
5 * Copyright 2003, 2004 by Theodore Ts'o.
8 * This file may be redistributed under the terms of the GNU Public
18 fputs("This program is only supported on Linux!\n", stderr);
22 #define _LARGEFILE64_SOURCE
37 #include <sys/types.h>
40 #include <sys/ioctl.h>
45 #define FIBMAP _IO(0x00,1) /* bmap access */
46 #define FIGETBSZ _IO(0x00,2) /* get the block size used for bmap */
48 static unsigned long get_bmap(int fd, unsigned long block)
54 ret = ioctl(fd, FIBMAP, &b);
57 fprintf(stderr, "No permission to use FIBMAP ioctl; must have root privileges\n");
65 static int full_read(int fd, char *buf, size_t count)
71 got = read(fd, buf, count);
73 if ((errno == EINTR) || (errno == EAGAIN))
75 return total ? total : -1;
90 static void copy_sparse_file(const char *src, const char *dest)
92 struct stat64 fileinfo;
93 long lb, i, fd, ofd, bs, block, numblocks;
95 off64_t offset = 0, should_be;
99 printf("Copying sparse file from %s to %s\n", src, dest);
101 if (strcmp(src, "-")) {
102 if (stat64(src, &fileinfo) < 0) {
106 if (!S_ISREG(fileinfo.st_mode)) {
107 printf("%s: Not a regular file\n", src);
110 fd = open(src, O_RDONLY | O_LARGEFILE);
115 if (ioctl(fd, FIGETBSZ, &bs) < 0) {
121 printf("%s: Invalid block size: %ld\n", src, bs);
125 printf("Blocksize of file %s is %ld\n", src, bs);
126 numblocks = (fileinfo.st_size + (bs-1)) / bs;
128 printf("File size of %s is %lld (%ld blocks)\n", src,
129 (long long) fileinfo.st_size, numblocks);
135 ofd = open(dest, O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0777);
143 fprintf(stderr, "Couldn't allocate buffer");
147 for (lb = 0; !fd || lb < numblocks; lb++) {
149 block = get_bmap(fd, lb);
152 should_be = ((off64_t) lb) * bs;
153 if (offset != should_be) {
155 printf("Seeking to %lld\n", should_be);
156 if (lseek64(fd, should_be, SEEK_SET) == (off_t) -1) {
160 if (lseek64(ofd, should_be, SEEK_SET) == (off_t) -1) {
161 perror("lseek dest");
167 got = full_read(fd, buf, bs);
169 if (fd == 0 && got == 0)
173 for (i=0; i < bs; i++)
177 lseek(ofd, bs, SEEK_CUR);
182 got2 = write(ofd, buf, got);
184 printf("short write\n");
189 offset = fileinfo.st_size;
190 if (fstat64(ofd, &fileinfo) < 0) {
194 if (fileinfo.st_size != offset) {
195 lseek64(ofd, offset-1, SEEK_CUR);
203 static void usage(const char *progname)
205 fprintf(stderr, "Usage: %s [-v] source_file destination_file\n", progname);
209 int main(int argc, char**argv)
213 while ((c = getopt(argc, argv, "v")) != EOF)
222 if (optind+2 != argc)
224 copy_sparse_file(argv[optind], argv[optind+1]);