-
Notifications
You must be signed in to change notification settings - Fork 8
/
transliterate.diff
178 lines (162 loc) · 6.04 KB
/
transliterate.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
This patch adds an option --tr=BAD/GOOD to transliterate filenames. It
can be used to remove characters illegal on the destination filesystem.
Jeff Weber expressed interest in this:
http://lists.samba.org/archive/rsync/2007-October/018996.html
To use this patch, run these commands for a successful build:
patch -p1 <patches/transliterate.diff
./configure (optional if already run)
make
based-on: 6c8ca91c731b7bf2b081694bda85b7dadc2b7aff
diff --git a/flist.c b/flist.c
--- a/flist.c
+++ b/flist.c
@@ -78,6 +78,7 @@ extern uid_t our_uid;
extern struct stats stats;
extern char *filesfrom_host;
extern char *usermap, *groupmap;
+extern char *tr_opt;
extern struct name_num_item *file_sum_nni;
@@ -106,6 +107,8 @@ int file_old_total = 0; /* total of active items that will soon be gone */
int flist_eof = 0; /* all the file-lists are now known */
int xfer_flags_as_varint = 0;
+char tr_substitutions[256];
+
#define NORMAL_NAME 0
#define SLASH_ENDING_NAME 1
#define DOTDIR_NAME 2
@@ -679,6 +682,23 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
stats.total_size += F_LENGTH(file);
}
+static void transliterate(char *path, int len)
+{
+ while (1) {
+ /* Find position of any char in tr_opt in path, or the end of the path. */
+ int span = strcspn(path, tr_opt);
+ if ((len -= span) == 0)
+ return;
+ path += span;
+ if ((*path = tr_substitutions[*(uchar*)path]) == '\0')
+ memmove(path, path+1, len--); /* copies the trailing '\0' too. */
+ else {
+ path++;
+ len--;
+ }
+ }
+}
+
static struct file_struct *recv_file_entry(int f, struct file_list *flist, int xflags)
{
static int64 modtime, atime;
@@ -750,9 +770,13 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
outbuf.len = 0;
}
thisname[outbuf.len] = '\0';
+ basename_len = outbuf.len;
}
#endif
+ if (tr_opt)
+ transliterate(thisname, basename_len);
+
if (*thisname
&& (clean_fname(thisname, CFN_REFUSE_DOT_DOT_DIRS) < 0 || (!relative_paths && *thisname == '/'))) {
rprintf(FERROR, "ABORTING due to unsafe pathname from sender: %s\n", thisname);
@@ -2575,6 +2599,15 @@ struct file_list *recv_file_list(int f, int dir_ndx)
parse_name_map(usermap, True);
if (groupmap)
parse_name_map(groupmap, False);
+ if (tr_opt) { /* Parse FROM/TO string and populate tr_substitutions[] */
+ char *f, *t;
+ if ((t = strchr(tr_opt, '/')) != NULL)
+ *t++ = '\0';
+ else
+ t = "";
+ for (f = tr_opt; *f; f++)
+ tr_substitutions[*(uchar*)f] = *t ? *t++ : '\0';
+ }
}
start_read = stats.total_read;
diff --git a/options.c b/options.c
--- a/options.c
+++ b/options.c
@@ -211,6 +211,7 @@ int logfile_format_has_i = 0;
int logfile_format_has_o_or_i = 0;
int always_checksum = 0;
int list_only = 0;
+char *tr_opt = NULL;
#define MAX_BATCH_NAME_LEN 256 /* Must be less than MAXPATHLEN-13 */
char *batch_name = NULL;
@@ -813,6 +814,7 @@ static struct poptOption long_options[] = {
{"temp-dir", 'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 },
{"iconv", 0, POPT_ARG_STRING, &iconv_opt, 0, 0, 0 },
{"no-iconv", 0, POPT_ARG_NONE, 0, OPT_NO_ICONV, 0, 0 },
+ {"tr", 0, POPT_ARG_STRING, &tr_opt, 0, 0, 0 },
{"ipv4", '4', POPT_ARG_VAL, &default_af_hint, AF_INET, 0, 0 },
{"ipv6", '6', POPT_ARG_VAL, &default_af_hint, AF_INET6, 0, 0 },
{"8-bit-output", '8', POPT_ARG_VAL, &allow_8bit_chars, 1, 0, 0 },
@@ -2490,6 +2492,24 @@ int parse_arguments(int *argc_p, const char ***argv_p)
}
}
+ if (tr_opt) {
+ if (*tr_opt == '/' && tr_opt[1]) {
+ snprintf(err_buf, sizeof err_buf,
+ "Do not start the --tr arg with a slash\n");
+ return 0;
+ }
+ if (*tr_opt && *tr_opt != '/') {
+ need_unsorted_flist = 1;
+ arg = strchr(tr_opt, '/');
+ if (arg && strchr(arg+1, '/')) {
+ snprintf(err_buf, sizeof err_buf,
+ "--tr cannot transliterate slashes\n");
+ return 0;
+ }
+ } else
+ tr_opt = NULL;
+ }
+
if (trust_sender || am_server || read_batch)
trust_sender_args = trust_sender_filter = 1;
else if (old_style_args || filesfrom_host != NULL)
@@ -2958,6 +2978,12 @@ void server_options(char **args, int *argc_p)
if (relative_paths && !implied_dirs && (!am_sender || protocol_version >= 30))
args[ac++] = "--no-implied-dirs";
+ if (tr_opt) {
+ if (asprintf(&arg, "--tr=%s", tr_opt) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+
if (write_devices && am_sender)
args[ac++] = "--write-devices";
diff --git a/rsync.1.md b/rsync.1.md
--- a/rsync.1.md
+++ b/rsync.1.md
@@ -555,6 +555,7 @@ has its own detailed description later in this manpage.
--read-batch=FILE read a batched update from FILE
--protocol=NUM force an older protocol version to be used
--iconv=CONVERT_SPEC request charset conversion of filenames
+--tr=BAD/GOOD transliterate filenames
--checksum-seed=NUM set block/file checksum seed (advanced)
--ipv4, -4 prefer IPv4
--ipv6, -6 prefer IPv6
@@ -3755,6 +3756,22 @@ expand it.
free to specify just the local charset for a daemon transfer (e.g.
`--iconv=utf8`).
+0. `--tr=BAD/GOOD`
+
+ Transliterates filenames on the receiver, after the iconv conversion (if
+ any). This can be used to remove characters illegal on the destination
+ filesystem. If you use this option, consider saving a "find . -ls" listing
+ of the source in the destination to help you determine the original
+ filenames in case of need.
+
+ The argument consists of a string of characters to remove, optionally
+ followed by a slash and a string of corresponding characters with which to
+ replace them. The second string may be shorter, in which case any leftover
+ characters in the first string are simply deleted. For example,
+ `--tr=':\/!'` replaces colons with exclamation marks and deletes
+ backslashes. Slashes cannot be transliterated because it would cause
+ havoc.
+
0. `--ipv4`, `-4` or `--ipv6`, `-6`
Tells rsync to prefer IPv4/IPv6 when creating sockets or running ssh. This