-
Notifications
You must be signed in to change notification settings - Fork 8
/
clone-dest.diff
253 lines (236 loc) · 8.67 KB
/
clone-dest.diff
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
This patch adds the --clone-dest option that works link --link-dest
but without requiring the metadata of the files to match in order
to be able to share the file's data.
NOTE: this patch is mostly untested because I don't currently have
a btrfs mount to test it out on. I still need to make sure that a
cloned file gets its destination attributes set correctly after the
clone, for instance.
To use this patch, run these commands for a successful build:
patch -p1 <patches/clone-dest.diff
./configure (optional if already run)
make
based-on: 6c8ca91c731b7bf2b081694bda85b7dadc2b7aff
diff --git a/Makefile.in b/Makefile.in
--- a/Makefile.in
+++ b/Makefile.in
@@ -53,7 +53,7 @@ popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
popt/popthelp.o popt/poptparse.o
OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) @BUILD_ZLIB@ @BUILD_POPT@
-TLS_OBJ = tls.o syscall.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
+TLS_OBJ = tls.o syscall.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o lib/wildmatch.o @BUILD_POPT@
# Programs we must have to run the test cases
CHECK_PROGS = rsync$(EXEEXT) tls$(EXEEXT) getgroups$(EXEEXT) getfsdev$(EXEEXT) \
@@ -170,7 +170,7 @@ getgroups$(EXEEXT): getgroups.o
getfsdev$(EXEEXT): getfsdev.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ getfsdev.o $(LIBS)
-TRIMSLASH_OBJ = trimslash.o syscall.o util2.o t_stub.o lib/compat.o lib/snprintf.o
+TRIMSLASH_OBJ = trimslash.o syscall.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o
trimslash$(EXEEXT): $(TRIMSLASH_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TRIMSLASH_OBJ) $(LIBS)
diff --git a/generator.c b/generator.c
--- a/generator.c
+++ b/generator.c
@@ -962,7 +962,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
best_match = j;
match_level = 2;
}
- if (unchanged_attrs(cmpbuf, file, sxp)) {
+ if (alt_dest_type == CLONE_DEST || unchanged_attrs(cmpbuf, file, sxp)) {
best_match = j;
match_level = 3;
break;
@@ -988,9 +988,16 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
goto got_nothing_for_ya;
}
#ifdef SUPPORT_HARD_LINKS
- if (alt_dest_type == LINK_DEST) {
- if (!hard_link_one(file, fname, cmpbuf, 1))
- goto try_a_copy;
+ if (alt_dest_type == LINK_DEST || alt_dest_type == CLONE_DEST) {
+ if (alt_dest_type == LINK_DEST) {
+ if (!hard_link_one(file, fname, cmpbuf, 1))
+ goto try_a_copy;
+ } else if (do_clone(cmpbuf, fname, file->mode) == 0) {
+ finish_transfer(fname, fname, cmpbuf, NULL, file, 1, 0);
+ } else {
+ rsyserr(FERROR_XFER, errno, "failed to clone %s to %s", cmpbuf, fname);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
if (atimes_ndx)
set_file_attrs(fname, file, sxp, NULL, 0);
if (preserve_hard_links && F_IS_HLINKED(file))
@@ -1104,7 +1111,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
if (match_level == 3) {
#ifdef SUPPORT_HARD_LINKS
- if (alt_dest_type == LINK_DEST
+ if ((alt_dest_type == LINK_DEST || alt_dest_type == CLONE_DEST)
#ifndef CAN_HARDLINK_SYMLINK
&& !S_ISLNK(file->mode)
#endif
diff --git a/options.c b/options.c
--- a/options.c
+++ b/options.c
@@ -582,7 +582,7 @@ enum {OPT_SERVER = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_MIN_SIZE, OPT_CHMOD,
OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE,
OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG, OPT_BLOCK_SIZE,
- OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_STDERR,
+ OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_STDERR, OPT_CLONE_DEST,
OPT_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS, OPT_OLD_ARGS,
OPT_STOP_AFTER, OPT_STOP_AT,
OPT_REFUSED_BASE = 9000};
@@ -743,6 +743,7 @@ static struct poptOption long_options[] = {
{"compare-dest", 0, POPT_ARG_STRING, 0, OPT_COMPARE_DEST, 0, 0 },
{"copy-dest", 0, POPT_ARG_STRING, 0, OPT_COPY_DEST, 0, 0 },
{"link-dest", 0, POPT_ARG_STRING, 0, OPT_LINK_DEST, 0, 0 },
+ {"clone-dest", 0, POPT_ARG_STRING, 0, OPT_CLONE_DEST, 0, 0 },
{"fuzzy", 'y', POPT_ARG_NONE, 0, 'y', 0, 0 },
{"no-fuzzy", 0, POPT_ARG_VAL, &fuzzy_basis, 0, 0, 0 },
{"no-y", 0, POPT_ARG_VAL, &fuzzy_basis, 0, 0, 0 },
@@ -1004,6 +1005,9 @@ static void set_refuse_options(void)
#ifndef SUPPORT_HARD_LINKS
parse_one_refuse_match(0, "link-dest", list_end);
#endif
+#ifndef FICLONE
+ parse_one_refuse_match(0, "clone-dest", list_end);
+#endif
#ifndef HAVE_MKTIME
parse_one_refuse_match(0, "stop-at", list_end);
#endif
@@ -1333,6 +1337,8 @@ char *alt_dest_opt(int type)
return "--copy-dest";
case LINK_DEST:
return "--link-dest";
+ case CLONE_DEST:
+ return "--clone-dest";
default:
NOISY_DEATH("Unknown alt_dest_opt type");
}
@@ -1714,6 +1720,10 @@ int parse_arguments(int *argc_p, const char ***argv_p)
want_dest_type = LINK_DEST;
goto set_dest_dir;
+ case OPT_CLONE_DEST:
+ want_dest_type = CLONE_DEST;
+ goto set_dest_dir;
+
case OPT_COPY_DEST:
want_dest_type = COPY_DEST;
goto set_dest_dir;
diff --git a/rsync.1.md b/rsync.1.md
--- a/rsync.1.md
+++ b/rsync.1.md
@@ -510,6 +510,7 @@ has its own detailed description later in this manpage.
--compare-dest=DIR also compare destination files relative to DIR
--copy-dest=DIR ... and include copies of unchanged files
--link-dest=DIR hardlink to files in DIR when unchanged
+--clone-dest=DIR clone (reflink) files from DIR when unchanged
--compress, -z compress file data during the transfer
--compress-choice=STR choose the compression algorithm (aka --zc)
--compress-level=NUM explicitly set compression level (aka --zl)
@@ -2720,6 +2721,18 @@ expand it.
this bug by avoiding the `-o` option (or using `--no-o`) when sending to an
old rsync.
+0. `--clone-dest=DIR`
+
+ This option behaves like [`--link-dest`](#opt), but unchanged files are
+ reflinked from _DIR_ to the destination directory. The files do not need
+ to match in attributes, as the data is cloned separately from the
+ attributes.
+
+ If _DIR_ is a relative path, it is relative to the destination directory.
+ See also [`--compare-dest`](#opt) and [`--copy-dest`](#opt).
+
+ All non-regular files are hard-linked (when possible).
+
0. `--compress`, `-z`
With this option, rsync compresses the file data as it is sent to the
diff --git a/rsync.h b/rsync.h
--- a/rsync.h
+++ b/rsync.h
@@ -175,6 +175,11 @@
#define COMPARE_DEST 1
#define COPY_DEST 2
#define LINK_DEST 3
+#define CLONE_DEST 4
+
+#if !defined FICLONE && defined __linux__
+#define FICLONE _IOW(0x94, 9, int)
+#endif
#define MPLEX_BASE 7
diff --git a/syscall.c b/syscall.c
--- a/syscall.c
+++ b/syscall.c
@@ -146,6 +146,54 @@ int do_link(const char *old_path, const char *new_path)
}
#endif
+int do_clone(const char *old_path, const char *new_path, mode_t mode)
+{
+#ifdef FICLONE
+ int ifd, ofd, ret, save_errno;
+
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+
+ if ((ifd = do_open(old_path, O_RDONLY, 0)) < 0) {
+ save_errno = errno;
+ rsyserr(FERROR_XFER, errno, "open %s", full_fname(old_path));
+ errno = save_errno;
+ return -1;
+ }
+
+ if (robust_unlink(new_path) && errno != ENOENT) {
+ save_errno = errno;
+ rsyserr(FERROR_XFER, errno, "unlink %s", full_fname(new_path));
+ close(ifd);
+ errno = save_errno;
+ return -1;
+ }
+
+ mode &= INITACCESSPERMS;
+ if ((ofd = do_open(new_path, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0) {
+ save_errno = errno;
+ rsyserr(FERROR_XFER, save_errno, "open %s", full_fname(new_path));
+ close(ifd);
+ errno = save_errno;
+ return -1;
+ }
+
+ ret = ioctl(ofd, FICLONE, ifd);
+ save_errno = errno;
+ close(ifd);
+ close(ofd);
+ if (ret < 0)
+ unlink(new_path);
+ errno = save_errno;
+ return ret;
+#else
+ (void)old_path;
+ (void)new_path;
+ errno = ENOTSUP;
+ return -1;
+#endif
+}
+
int do_lchown(const char *path, uid_t owner, gid_t group)
{
if (dry_run) return 0;
diff --git a/t_stub.c b/t_stub.c
--- a/t_stub.c
+++ b/t_stub.c
@@ -38,6 +38,7 @@ size_t max_alloc = 0; /* max_alloc is needed when combined with util2.o */
char *partial_dir;
char *module_dir;
filter_rule_list daemon_filter_list;
+short info_levels[COUNT_INFO], debug_levels[COUNT_DEBUG];
void rprintf(UNUSED(enum logcode code), const char *format, ...)
{
diff --git a/t_unsafe.c b/t_unsafe.c
--- a/t_unsafe.c
+++ b/t_unsafe.c
@@ -28,7 +28,6 @@ int am_root = 0;
int am_sender = 1;
int read_only = 0;
int list_only = 0;
-short info_levels[COUNT_INFO], debug_levels[COUNT_DEBUG];
int
main(int argc, char **argv)