Fixed failing hunks.
[rsync-patches.git] / fname-convert.diff
1 Eran Tromer writes:
2
3 One feature missing from rsync, and requested on this list before, is
4 on-the-fly conversion of filename character encoding. For example, I
5 often need to sync files having Hebrew filenames from a UTF-8 system
6 (Linux) to an ISO8859-8 system (Cygwin on Windows 2000 using the
7 non-Unicode Win32 interface). Other circumstances surely abound.
8
9 Attached is a patch against rsync 2.6.2 that adds an "--fname-convert"
10 option. When the argument "--fname-convert CONV" is given, rsync pipes
11 every filename through the program CONV, and filename presented to the
12 server will be CONV's output instead of the raw filename.
13
14 Artificial example:
15 $ touch /tmp/xyz
16 $ rsync -fname-convert 'tr y Y' /tmp/xyz /tmp/
17 $ ls /tmp/x?z
18 /tmp/xyz  /tmp/xYz
19
20 Perhaps the most useful case is using iconv:
21 $ rsync --fname-convert 'iconv -f utf8 -t iso8859-8' ...
22
23 I chose to allow invocation of arbitrary programs instead of using
24 libiconv (or equivalent) in order to avoid external dependencies, and to
25 offer more flexibility. The price is that some heuristics were needed to
26 avoid the deadlock problems that tend to occur when filtering data
27 through a program that uses buffered I/O -- see the comments at the top
28 of the new file fnameconv.c. The delay you may have noticed in the above
29 artificial example using "tr" is due to these heuristics; it occurs just
30 once per rsync invocation, not for every file.
31
32 I believe there are no server-side security implications, since all
33 conversion is done at the client and the server is oblivious to it. On
34 the client, conversion is done before sanitize_path() and besides,
35 providing a sane converter program is the client's responsibility anyway.
36
37 In verbose mode the updating of non-regular files is reported via
38 rprintf() by the server, so the client will see the converted filename
39 instead the raw filename -- see my comment in recv_generator(). Fixing
40 this requires some delicate changes so I left it as is, but it seems
41 like a minor concern.
42
43 Most of the new code is in the new file fnameconv.c. The patch lightly
44 touches some other files, mostly flist.c and the addition/extension of
45 some utility functions.
46
47 Note that you'll need to run 'make proto' after applying this patch.
48
49
50 --- orig/Makefile.in    2004-11-03 11:56:03
51 +++ Makefile.in 2004-07-03 20:18:02
52 @@ -35,7 +35,7 @@ OBJS1=rsync.o generator.o receiver.o cle
53         main.o checksum.o match.o syscall.o log.o backup.o
54  OBJS2=options.o flist.o io.o compat.o hlink.o token.o uidlist.o socket.o \
55         fileio.o batch.o clientname.o
56 -OBJS3=progress.o pipe.o
57 +OBJS3=progress.o pipe.o fnameconv.o
58  DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
59  popt_OBJS=popt/findme.o  popt/popt.o  popt/poptconfig.o \
60         popt/popthelp.o popt/poptparse.o
61 --- orig/cleanup.c      2005-02-07 20:41:56
62 +++ cleanup.c   2005-01-10 10:40:51
63 @@ -25,6 +25,7 @@ extern int io_error;
64  extern int keep_partial;
65  extern int log_got_error;
66  extern char *partial_dir;
67 +extern char *fname_convert_cmd;
68  
69  /**
70   * Close all open sockets and files, allowing a (somewhat) graceful
71 @@ -125,6 +126,8 @@ void _exit_cleanup(int code, const char 
72                                 !partial_dir);
73         }
74         io_flush(FULL_FLUSH);
75 +       if (fname_convert_cmd)
76 +               cleanup_fname_convert();
77         if (cleanup_fname)
78                 do_unlink(cleanup_fname);
79         if (code)
80 --- orig/errcode.h      2003-12-15 08:04:14
81 +++ errcode.h   2004-07-02 21:38:59
82 @@ -34,6 +34,7 @@
83  #define RERR_STREAMIO   12      /* error in rsync protocol data stream */
84  #define RERR_MESSAGEIO  13      /* errors with program diagnostics */
85  #define RERR_IPC        14      /* error in IPC code */
86 +#define RERR_FNAMECONV  15      /* error in filename conversion */
87  
88  #define RERR_SIGNAL     20      /* status returned when sent SIGUSR1, SIGINT */
89  #define RERR_WAITCHILD  21      /* some error returned by waitpid() */
90 --- orig/flist.c        2005-02-26 05:22:50
91 +++ flist.c     2005-02-26 05:23:26
92 @@ -63,6 +63,7 @@ extern int force_delete;
93  extern int orig_umask;
94  extern int make_backups;
95  extern unsigned int curr_dir_len;
96 +extern char *fname_convert_cmd;
97  extern char *backup_dir;
98  extern char *backup_suffix;
99  extern int backup_suffix_len;
100 @@ -345,7 +346,10 @@ void send_file_entry(struct file_struct 
101  
102         io_write_phase = "send_file_entry";
103  
104 -       f_name_to(file, fname);
105 +       if (fname_convert_cmd && !am_server) /* fname conversion always done on client */
106 +               convert_fname(fname, f_name(file), MAXPATHLEN);
107 +       else
108 +               f_name_to(file, fname);
109  
110         flags = base_flags;
111  
112 @@ -561,6 +565,9 @@ static struct file_struct *receive_file_
113  
114         strlcpy(lastname, thisname, MAXPATHLEN);
115  
116 +       if (fname_convert_cmd && !am_server) /* fname conversion always done on client */
117 +               convert_fname(thisname, lastname, MAXPATHLEN);
118 +
119         clean_fname(thisname, 0);
120  
121         if (sanitize_paths)
122 @@ -1074,6 +1081,9 @@ struct file_list *send_file_list(int f, 
123         start_write = stats.total_written;
124         gettimeofday(&start_tv, NULL);
125  
126 +       if (!am_server)
127 +               init_fname_convert();
128 +
129         flist = flist_new(WITH_HLINK, "send_file_list");
130  
131         io_start_buffering_out();
132 @@ -1256,6 +1266,9 @@ struct file_list *send_file_list(int f, 
133         stats.flist_size = stats.total_written - start_write;
134         stats.num_files = flist->count;
135  
136 +       if (fname_convert_cmd && !am_server)
137 +               cleanup_fname_convert();
138 +
139         if (verbose > 3)
140                 output_flist(flist, who_am_i());
141  
142 @@ -1277,6 +1290,9 @@ struct file_list *recv_file_list(int f)
143  
144         start_read = stats.total_read;
145  
146 +       if (fname_convert_cmd && !am_server)
147 +               init_fname_convert();
148 +
149         flist = flist_new(WITH_HLINK, "recv_file_list");
150         received_flist = flist;
151  
152 @@ -1330,6 +1346,9 @@ struct file_list *recv_file_list(int f)
153                         io_error |= read_int(f);
154         }
155  
156 +       if (fname_convert_cmd && !am_server)
157 +               cleanup_fname_convert();
158 +
159         if (verbose > 3)
160                 output_flist(flist, who_am_i());
161  
162 --- orig/fnameconv.c    2004-07-02 21:38:59
163 +++ fnameconv.c 2004-07-02 21:38:59
164 @@ -0,0 +1,220 @@
165 +/*  -*- c-file-style: "linux" -*-
166 + *
167 + * Copyright (C) 2004 by Eran Tromer
168 + *
169 + * This program is free software; you can redistribute it and/or modify
170 + * it under the terms of the GNU General Public License as published by
171 + * the Free Software Foundation; either version 2 of the License, or
172 + * (at your option) any later version.
173 + *
174 + * This program is distributed in the hope that it will be useful,
175 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
176 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
177 + * GNU General Public License for more details.
178 + *
179 + * You should have received a copy of the GNU General Public License
180 + * along with this program; if not, write to the Free Software
181 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
182 + */
183 +
184 +/* Handles filename conversion through an external process. Implements
185 + * two modes of operation:
186 + * In persistent mode, a single filename converter is kept running;
187 + * for each query we feed it a single line and read back a single
188 + * line. This will fail for programs that used buffered I/O, and will
189 + * get into a deadlock.
190 + * In non-persistent mode, a converter is invoked and killed for each
191 + * query. This has a very high overhead, but will work for any
192 + * program.
193 + * We start in persistence mode, and if we suspect a deadlock (i.e.,
194 + * nothing happens for FNAME_CONV_PERSISTENCE_TIMEOUT milliseconds)
195 + * then we smoothly fall back to non-persistent mode.
196 + *
197 + * Filename conversion errors are always considered fatal, since an
198 + * incorrectly named file could cause unpredictable damage.
199 + */
200 +
201 +#include <rsync.h>
202 +
203 +#define FNAME_CONV_PERSISTENCE_TIMEOUT 3000 /* milliseconds */
204 +
205 +static int conv_persistent = 1;
206 +static pid_t conv_pid = -1;
207 +static int conv_write_fd = -1, conv_read_fd;
208 +extern char *fname_convert_cmd;
209 +extern int blocking_io;
210 +
211 +/**
212 + * Splits cmd on spaces.
213 + */
214 +static void split_on_spaces(char *cmd, char **parts) {
215 +       int nparts = 0;
216 +       char *tok;
217 +       char *cmd2 = strdup(cmd);
218 +       if (!cmd2) {
219 +               rprintf(FERROR, "Out of memory while parsing filename filter %s\n", cmd);
220 +               exit_cleanup(RERR_MALLOC);
221 +       }
222 +
223 +       for (tok = strtok(cmd2, " "); tok; tok = strtok(NULL, " ")) {
224 +               if (nparts >= MAX_ARGS) {
225 +                       rprintf(FERROR, "Filename conversion command is too long: %s\n", cmd);
226 +                       exit_cleanup(RERR_SYNTAX);
227 +               }
228 +               parts[nparts++] = tok;
229 +       }
230 +       parts[nparts] = NULL;
231 +}
232 +
233 +
234 +/**
235 + * Runs the filename converter process. Should be called before filename
236 + * conversion begins (actually it's not necessarh, but it keeps the proress report
237 + * nice and clean.
238 + **/
239 +void init_fname_convert()
240 +{
241 +       if (fname_convert_cmd && conv_pid < 0) {
242 +               char *args[MAX_ARGS];
243 +
244 +               if (verbose > 2)
245 +                       rprintf(FINFO, "Running filename converter: %s\n", fname_convert_cmd);
246 +               split_on_spaces(fname_convert_cmd, args);
247 +               /* Invoke child pipe with non-blocking IO and without registering it for
248 +                * autocleanup (the latter may blow up the all_pids table, and is not needed
249 +                * since we have our own cleanup handler. */
250 +               conv_pid = piped_child(args, &conv_read_fd, &conv_write_fd, 0, 0);
251 +               set_nonblocking(conv_write_fd);
252 +               set_nonblocking(conv_read_fd);
253 +       }
254 +}
255 +
256 +/**
257 + * Kills the filename converter process. Should be called when the file
258 + * list creation is done. We assume that the converter will terminate
259 + * soon after its standard input is closed.
260 + **/
261 +void cleanup_fname_convert()
262 +{
263 +       if (conv_pid >= 0) {
264 +               int status;
265 +               if (conv_write_fd >= 0) {
266 +                       close(conv_write_fd);
267 +                       conv_write_fd = -1;
268 +               }
269 +               close(conv_read_fd);
270 +               waitpid(conv_pid, &status, 0);
271 +               conv_pid = -1;
272 +       }
273 +}
274 +
275 +/**
276 + * Converts the filename from src into dest, using at most maxlen
277 + * characters of dest.
278 + **/
279 +void convert_fname(char *dest, const char *src, unsigned int maxlen)
280 +{
281 +       int res;
282 +       const char *srcp;
283 +       char *destp;
284 +       unsigned int srcrem, dstrem;
285 +
286 +       init_fname_convert();
287 +
288 +       /* Send and receive strings simultaneously to avoid deadlock: */
289 +       srcrem = strlen(src)+1; /* chars left to send (incl. terminating LF) */
290 +       dstrem = maxlen-1;      /* free chars left in dest                   */
291 +       srcp = src;
292 +       destp = dest;
293 +       while(1) {
294 +               /* Write as much as possible: */
295 +               if (srcrem > 1) {
296 +                       res = write(conv_write_fd, srcp, srcrem-1);
297 +                       if (res < 0 && errno != EAGAIN) {
298 +                               rprintf(FERROR, "Error writing to fname converter (filename: %s): %s\n", strerror(errno), src);
299 +                               exit_cleanup(RERR_FNAMECONV);
300 +                       }
301 +                       if (res > 0) { /* wrote something */
302 +                               srcp += res;
303 +                               srcrem -= res;
304 +                       }
305 +               }
306 +               if (srcrem == 1) { /* final LF */
307 +                       res = write(conv_write_fd, "\n", 1);
308 +                       if (res < 0 && errno != EAGAIN) {
309 +                               rprintf(FERROR, "Error writing to fname converter (filename: %s): %s\n", strerror(errno), src);
310 +                               exit_cleanup(RERR_FNAMECONV);
311 +                       }
312 +                       if (res > 0) { /* wrote final LF */
313 +                               srcrem = 0;
314 +                               if (!conv_persistent) {
315 +                                       close(conv_write_fd);
316 +                                       conv_write_fd = -1;
317 +                               }
318 +                       }
319 +               }
320 +
321 +               /* Read as much as possible: */
322 +               res = read(conv_read_fd, destp, dstrem);
323 +               if (res < 0 && errno != EAGAIN) {
324 +                       rprintf(FERROR, "Error reading from filename converter (filename: %s):%s \n", strerror(errno), src);
325 +                       exit_cleanup(RERR_FNAMECONV);
326 +               }
327 +               if (res == 0) { /* EOF */
328 +                       rprintf(FERROR, "EOF from filename converter (filename: %s)\n", src);
329 +                       exit_cleanup(RERR_FNAMECONV);
330 +               }
331 +               if (res > 0) {
332 +                       destp += res;
333 +                       dstrem -= res;
334 +                       if (destp[-1] == '\n' || destp[-1] == '\r')
335 +                               break; /* End of line. Yippy! */
336 +                       if (dstrem == 0) {
337 +                               rprintf(FINFO, "Name converter output too long (filename: %s)\n", src);
338 +                               exit_cleanup(RERR_FNAMECONV);
339 +                       }
340 +               }
341 +
342 +               /* Await activity */
343 +               if (!await_fds(conv_read_fd, !srcrem ? -1 : conv_write_fd, FNAME_CONV_PERSISTENCE_TIMEOUT)) {
344 +                       if (srcrem == 0 && conv_persistent) {
345 +                               /* We finished writing but nothing happens. It looks like the converter program
346 +                                * is using buffered I/O and thus wait to read more input, but we can't give it
347 +                                * the next filename yet. Fall back to non-persistent mode.                      */
348 +                               if (verbose > 0)
349 +                                       rprintf(FINFO, "Filename converter blocked, disabling persistence to recover.\n");
350 +
351 +                               conv_persistent = 0;
352 +                               close(conv_write_fd);
353 +                               conv_write_fd = -1;
354 +                       }
355 +               }
356 +       }
357 +
358 +       /* Cleanup and sanity check */
359 +       if (!conv_persistent)
360 +               cleanup_fname_convert();
361 +       if (srcrem > 0) {
362 +               close(conv_write_fd);
363 +               rprintf(FERROR, "Name converter produced output before reading all its input for file: %s\n", src);
364 +               exit_cleanup(RERR_FNAMECONV);
365 +       }
366 +
367 +       /* Chop newline chars */
368 +       destp--;
369 +       if (destp > dest && *destp == '\n')
370 +               --destp;
371 +       if (destp > dest && *destp == '\r')
372 +               --destp;
373 +       if (++destp == dest) {
374 +               rprintf(FERROR, "Name converter output is empty (filename: %s)\n", src);
375 +               exit_cleanup(RERR_FNAMECONV);
376 +       }
377 +       *destp = 0;
378 +       /* Also, we may have a leading CR left over from a CRLF of the previous line */
379 +       if (*dest == '\n')
380 +               memmove(dest, dest+1, destp-dest-1);
381 +
382 +       if (verbose > 2)
383 +               rprintf(FINFO, "Converted filename: %s -> %s\n", src, dest);
384 +}
385 --- orig/generator.c    2005-02-26 03:22:59
386 +++ generator.c 2005-02-03 02:07:33
387 @@ -343,7 +343,13 @@ static int find_fuzzy(struct file_struct
388   * start sending checksums.
389   *
390   * Note that f_out is set to -1 when doing final directory-permission and
391 - * modification-time repair. */
392 + * modification-time repair.
393 + *
394 + * TODO: The filename seen in recv_generator is after filename
395 + * conversion.  In verbose mode, directories, symlinks and device
396 + * files are printf()ed here but regular files are rprintf()ed on the
397 + * sender (unconverted). To solve the above, move all progress
398 + * reporting to the sender. */
399  static void recv_generator(char *fname, struct file_list *flist,
400                            struct file_struct *file, int ndx,
401                            int f_out, int f_out_name)
402 --- orig/log.c  2005-02-26 05:22:50
403 +++ log.c       2004-07-03 20:18:02
404 @@ -63,6 +63,7 @@ struct {
405         { RERR_STREAMIO   , "error in rsync protocol data stream" },
406         { RERR_MESSAGEIO  , "errors with program diagnostics" },
407         { RERR_IPC        , "error in IPC code" },
408 +       { RERR_FNAMECONV  , "error in filename conversion" },
409         { RERR_SIGNAL     , "received SIGUSR1 or SIGINT" },
410         { RERR_WAITCHILD  , "some error returned by waitpid()" },
411         { RERR_MALLOC     , "error allocating core memory buffers" },
412 --- orig/main.c 2005-02-23 02:57:26
413 +++ main.c      2004-07-22 00:31:47
414 @@ -363,7 +363,7 @@ static pid_t do_cmd(char *cmd, char *mac
415                         whole_file = 1;
416                 ret = local_child(argc, args, f_in, f_out, child_main);
417         } else
418 -               ret = piped_child(args,f_in,f_out);
419 +               ret = piped_child(args, f_in, f_out, blocking_io, 1);
420  
421         if (dir)
422                 free(dir);
423 --- orig/options.c      2005-02-25 18:44:31
424 +++ options.c   2005-02-14 02:50:32
425 @@ -137,6 +137,7 @@ char *basis_dir[MAX_BASIS_DIRS+1];
426  char *config_file = NULL;
427  char *shell_cmd = NULL;
428  char *log_format = NULL;
429 +char *fname_convert_cmd = NULL;
430  char *password_file = NULL;
431  char *rsync_path = RSYNC_PATH;
432  char *backup_dir = NULL;
433 @@ -318,6 +319,7 @@ void usage(enum logcode F)
434    rprintf(F," -y, --fuzzy                 find similar file for basis if no dest file\n");
435    rprintf(F,"     --compare-dest=DIR      also compare destination files relative to DIR\n");
436    rprintf(F,"     --link-dest=DIR         hardlink to files in DIR when unchanged\n");
437 +  rprintf(F,"     --fname-convert=CMD     invoke CMD for filename conversion\n");
438    rprintf(F," -z, --compress              compress file data during the transfer\n");
439    rprintf(F," -C, --cvs-exclude           auto-ignore files the same way CVS does\n");
440    rprintf(F," -f, --filter=RULE           add a file-filtering RULE\n");
441 @@ -426,6 +428,7 @@ static struct poptOption long_options[] 
442    {"compare-dest",     0,  POPT_ARG_STRING, 0, OPT_COMPARE_DEST, 0, 0 },
443    {"link-dest",        0,  POPT_ARG_STRING, 0, OPT_LINK_DEST, 0, 0 },
444    {"fuzzy",           'y', POPT_ARG_NONE,   &fuzzy_basis, 0, 0, 0 },
445 +  {"fname-convert",    0,  POPT_ARG_STRING, &fname_convert_cmd, 0, 0, 0 },
446    /* TODO: Should this take an optional int giving the compression level? */
447    {"compress",        'z', POPT_ARG_NONE,   &do_compression, 0, 0, 0 },
448    {"stats",            0,  POPT_ARG_NONE,   &do_stats, 0, 0, 0 },
449 --- orig/pipe.c 2005-02-07 20:41:56
450 +++ pipe.c      2004-07-03 20:18:02
451 @@ -23,7 +23,6 @@
452  
453  extern int am_sender;
454  extern int am_server;
455 -extern int blocking_io;
456  extern int orig_umask;
457  extern int write_batch;
458  extern int filesfrom_fd;
459 @@ -40,8 +39,10 @@ extern int filesfrom_fd;
460   * If blocking_io is set then use blocking io on both fds. That can be
461   * used to cope with badly broken rsh implementations like the one on
462   * Solaris.
463 + *
464 + * If register_child is nonzero then the child is registered for autocleanup.
465   **/
466 -pid_t piped_child(char **command, int *f_in, int *f_out)
467 +pid_t piped_child(char **command, int *f_in, int *f_out, int blocking_io, int register_child)
468  {
469         pid_t pid;
470         int to_child_pipe[2];
471 @@ -56,7 +57,7 @@ pid_t piped_child(char **command, int *f
472                 exit_cleanup(RERR_IPC);
473         }
474  
475 -       pid = do_fork();
476 +       pid = register_child ? do_fork() : fork();
477         if (pid == -1) {
478                 rsyserr(FERROR, errno, "fork");
479                 exit_cleanup(RERR_IPC);
480 --- orig/syscall.c      2005-02-14 02:45:11
481 +++ syscall.c   2004-07-02 21:39:00
482 @@ -259,3 +259,34 @@ char *d_name(struct dirent *di)
483         return di->d_name;
484  #endif
485  }
486 +
487 +/**
488 + * A wrapper around select(2) that guarantees Linux-like updating of
489 + * the timeout argument to contain the time left, so we can simply
490 + * re-invoke in case of EINTR or EAGAIN.  On BSD, select(2) doesn't
491 + * change the timeout argument by itself.
492 + **/
493 +int do_select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
494 +{
495 +       struct timeval intended, before, after;
496 +       int result;
497 +
498 +       if (timeout == NULL)
499 +               return select(n, readfds, writefds, exceptfds, timeout);
500 +
501 +       intended = *timeout;
502 +       gettimeofday(&before, NULL);
503 +       result = select(n, readfds, writefds, exceptfds, timeout);
504 +       gettimeofday(&after, NULL);
505 +       timeout->tv_sec = intended.tv_sec - (after.tv_sec - before.tv_sec);
506 +       timeout->tv_usec = intended.tv_usec - (after.tv_usec - before.tv_usec);
507 +       if (timeout->tv_usec >= 1000000) {
508 +               ++timeout->tv_sec;
509 +               timeout->tv_usec -= 1000000;
510 +       } else if (timeout->tv_usec < 0) {
511 +               --(timeout)->tv_sec;
512 +               timeout->tv_usec += 1000000;
513 +       }
514 +
515 +       return result;
516 +}
517 --- orig/util.c 2005-02-23 02:57:27
518 +++ util.c      2004-07-03 20:18:02
519 @@ -1327,3 +1327,55 @@ uint32 fuzzy_distance(const char *s1, in
520  
521         return a[len2-1];
522  }
523 +
524 +/**
525 + * Blocks until one of the following happens:
526 + * - read_fd is nonnegative and has data to read
527 + * - write_fd is nonnegative and can be written to
528 + * - something terrible happened to either
529 + * - the timeout (in milliseconds) has elapsed
530 + * Return value is zero iff the timeout occured.
531 + */
532 +char await_fds(int read_fd, int write_fd, int timeout_ms)
533 +{
534 +       fd_set read_fds, write_fds, except_fds;
535 +       struct timeval tv;
536 +       int res;
537 +
538 +       tv.tv_sec = timeout_ms / 1000;
539 +       tv.tv_usec = (timeout_ms % 1000) * 1000;
540 +
541 +       while (1) {
542 +               int maxfd = 0;
543 +               FD_ZERO(&read_fds);
544 +               FD_ZERO(&write_fds);
545 +               FD_ZERO(&except_fds);
546 +               if (write_fd >= 0) {
547 +                       FD_SET(write_fd, &write_fds);
548 +                       FD_SET(write_fd, &except_fds);
549 +                       if (write_fd > maxfd)
550 +                               maxfd = write_fd;
551 +               }
552 +               if (read_fd >= 0) {
553 +                       FD_SET(read_fd, &read_fds);
554 +                       FD_SET(read_fd, &except_fds);
555 +                       if (read_fd > maxfd)
556 +                               maxfd = read_fd;
557 +               }
558 +
559 +               res = do_select(maxfd+1, &read_fds, &write_fds, &except_fds, &tv);
560 +               if (res > 0)
561 +                       return 1;
562 +               if (res < 0) {
563 +                       if (errno == EINTR || errno == EAGAIN)
564 +                               continue; /* Retry */
565 +                       rprintf(FERROR, "Error awaiting fname converter: %s\n", strerror(errno));
566 +                       exit_cleanup(RERR_FNAMECONV);
567 +               }
568 +               if (read_fd >= 0 && (FD_ISSET(read_fd, &read_fds) || FD_ISSET(read_fd, &except_fds)))
569 +                       return 1;
570 +               if (write_fd >= 0 && (FD_ISSET(write_fd, &write_fds) || FD_ISSET(write_fd, &except_fds)))
571 +                       return 1;
572 +               return 0; /* res == 0 and no FDs set, hence a timeout. */
573 +       }
574 +}