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]);