-
Notifications
You must be signed in to change notification settings - Fork 2
/
patchdd.c
107 lines (82 loc) · 2.58 KB
/
patchdd.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <inttypes.h>
#include <time.h>
#include "utils.h"
#include "ui.h"
#define TRANSFER_SIZE (1024 * 1024)
static size_t memcmp_offset(uint8_t *a, uint8_t *b, size_t len)
{
size_t offset;
for (offset = 0; offset < len; offset++)
if (a[offset] != b[offset])
break;
return offset;
}
int main(int argc, char **argv)
{
u_log_init();
if (argc < 3 ||
(argc >= 2 && (streq(argv[1], "-h") || streq(argv[1], "--help")))) {
fprintf(stderr, "usage: %s src dst\n", argv[0]);
return EXIT_FAILURE;
}
int src, dst;
src = open(argv[1], O_RDONLY);
if (src < 0) {
u_log_errno("opening source '%s' failed", argv[1]);
return EXIT_FAILURE;
}
dst = open(argv[2], O_RDWR);
if (dst < 0) {
u_log_errno("opening destination '%s' failed", argv[2]);
return EXIT_FAILURE;
}
off_t src_size, dst_size;
checked(fd_size(src, &src_size), return EXIT_FAILURE);
checked(fd_size(dst, &dst_size), return EXIT_FAILURE);
if (dst_size < src_size) {
u_log(ERR, "target size (%llu) is smaller than source size (%llu)",
(unsigned long long)dst_size, (unsigned long long)src_size);
return EXIT_FAILURE;
}
if (src_size < dst_size)
u_log(INFO, "target size (%llu) is larger than source size (%llu)",
(unsigned long long)dst_size, (unsigned long long)src_size);
time_t now = time_monotonic();
time_t start = now;
uint8_t src_buf[TRANSFER_SIZE];
uint8_t dst_buf[TRANSFER_SIZE];
bool print_progress = isatty(STDOUT_FILENO);
progess_status_t progess_status = PROGRESS_STATUS_INIT;
off_t offset = 0;
off_t total_skipped = 0;
while (offset < src_size) {
size_t to_read = MIN(src_size - offset, TRANSFER_SIZE);
if (preadall(src, src_buf, to_read, offset) < 0)
return EXIT_FAILURE;
if (preadall(dst, dst_buf, to_read, offset) < 0)
return EXIT_FAILURE;
size_t first_diff = memcmp_offset(src_buf, dst_buf, to_read);
total_skipped += first_diff;
if (pwriteall(dst, &src_buf[first_diff], to_read - first_diff, offset + first_diff) < 0)
return EXIT_FAILURE;
offset += to_read;
if (print_progress)
show_progress((100ull * offset) / src_size, &progess_status);
}
if (fsync(dst) < 0) {
u_log_errno("fsync on target failed");
return EXIT_FAILURE;
}
now = time_monotonic();
printf("synchronization finished after %u seconds\n", (unsigned int)(now - start));
printf("skipped a total of %llu bytes (%.2f%% of the input)\n",
(unsigned long long)total_skipped,
(double)((100.0 * total_skipped)/src_size));
return EXIT_SUCCESS;
}