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