1 /*
2  *  csync2 - cluster synchronization tool, 2nd generation
3  *  Copyright (C) 2004 - 2013 LINBIT Information Technologies GmbH
4  *  http://www.linbit.com; see also AUTHORS
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include "csync2.h"
22 #include <librsync.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <stdio.h>
27 
28 /* for tmpfile replacement: */
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 
33 /* for MAXPATHLEN */
34 #include <sys/param.h>
35 
36 
37 #ifdef __CYGWIN__
38 #include <w32api/windows.h>
39 #endif
40 
41 
42 /* This has been taken from rsync:lib/compat.c */
43 
44 #if 0
45 /**
46  * Like strncpy but does not 0 fill the buffer and always null
47  * terminates.
48  *
49  * @param bufsize is the size of the destination buffer.
50  *
51  * @return index of the terminating byte.
52  **/
53 static size_t strlcpy(char *d, const char *s, size_t bufsize)
54 {
55         size_t len = strlen(s);
56         size_t ret = len;
57         if (bufsize > 0) {
58                 if (len >= bufsize)
59                         len = bufsize-1;
60                 memcpy(d, s, len);
61                 d[len] = 0;
62         }
63         return ret;
64 }
65 #endif
66 
67 /* splits filepath at the last '/', if any, like so:
68  *	dirname		basename	filepath
69  *	"/"		""		"/"
70  *	"/"		"foo"		"/foo"
71  * no trailing slash in dirname, unless it is "/".
72  *	"/some/path"	""		"/some/path/"
73  *	"/some/path"	"foo"		"/some/path/foo"
74  *	""		"foo"		"foo"
75  *
76  * caller needs to supply enough room in dirname and basename
77  * to hold the result, or NULL, if not interested.
78  */
split_dirname_basename(char * dirname,char * basename,const char * filepath)79 void split_dirname_basename(char *dirname, char* basename, const char *filepath)
80 {
81 	const char *base = strrchr(filepath, '/');
82 	size_t pathlen, dirlen, baselen;
83 
84 	/* skip over last slash, if any. */
85 	base = base ? base + 1 : filepath;
86 
87 	pathlen = strlen(filepath);
88 	baselen = strlen(base);
89 	dirlen = pathlen - baselen;
90 
91 	/* backtrack trailing slash(es) */
92 	while (dirlen > 1 && filepath[dirlen-1] == '/')
93 		--dirlen;
94 
95 	if (dirname)
96 		strlcpy(dirname, filepath, dirlen + 1);
97 	if (basename)
98 		strlcpy(basename, base, baselen + 1);
99 }
100 
101 /*
102  * Recursively creates the given path, with the given mode
103  * Note that path argument is not directory name here but rather
104  * a path to a file that you are going to create after calling mkpath().
105  * Works with relative paths as well.
106  * Shamelessly copied from
107  * Stackoverlow.com#http://stackoverflow.com/questions/2336242/recursive-mkdir-system-call-on-unix
108  * Returns: 0 on success and -1 on error
109  */
mkpath(const char * path,mode_t mode)110 int mkpath(const char *path, mode_t mode) {
111 	char temp[MAXPATHLEN];
112 	char *remaining;
113 
114 	if(!mode) {
115 		mode=S_IRWXU;
116 	}
117 	if(!path){
118 		csync_debug(2,"invalid path");
119 		return -1;
120 	}
121 
122 	if(strlcpy(temp,path,sizeof(temp))>=sizeof(temp)) {
123 		csync_debug(1,"path too long: %s",path);
124 		return -1;
125 	}
126 	csync_debug(1,"mkpath full path: %s",temp);
127 	for( remaining=strchr(temp+1, '/'); remaining!=NULL; remaining=strchr(remaining+1, '/') ){
128 		*remaining='\0';
129 		if(mkdir(temp, mode)==-1) { //strchr keeps the parent in temp and child[ren] in remaining
130 			if(errno != EEXIST) {
131 				*remaining='/';
132 				csync_debug(1,"error occured while creating path %s; cause : %s",temp,strerror(errno));
133 				return -1;
134 			}
135 		}
136 		csync_debug(1,"mkdir parent dir: %s",temp);
137 		*remaining='/';
138 	}
139 	return 0;
140 }
141 
142 /* This has been taken from rsync sources: syscall.c */
143 
144 #ifndef O_BINARY
145 #define O_BINARY 0
146 #endif
147 
148 /* like mkstemp but forces permissions */
do_mkstemp(char * template,mode_t perms)149 int do_mkstemp(char *template, mode_t perms)
150 {
151 	perms |= S_IWUSR;
152 
153 #if defined HAVE_SECURE_MKSTEMP && defined HAVE_FCHMOD && (!defined HAVE_OPEN64 || defined HAVE_MKSTEMP64)
154 	{
155 		int fd = mkstemp(template);
156 		if (fd == -1)
157 			return -1;
158 		if (fchmod(fd, perms) != 0) {
159 			int errno_save = errno;
160 			close(fd);
161 			unlink(template);
162 			errno = errno_save;
163 			return -1;
164 		}
165 #if defined HAVE_SETMODE && O_BINARY
166 		setmode(fd, O_BINARY);
167 #endif
168 		return fd;
169 	}
170 #else
171 	if (!mktemp(template))
172 		return -1;
173 	return open(template, O_RDWR|O_EXCL|O_CREAT | O_BINARY, perms);
174 #endif
175 }
176 
177 
178 /* define the order in which directories are tried when creating temp files */
next_tempdir(char ** dir,unsigned int stage)179 static int next_tempdir(char **dir, unsigned int stage)
180 {
181 	static char *dirs_to_try[] = {
182 		NULL /* csync_tempdir */,
183 		NULL /* stays NULL, same dir as input name */,
184 		NULL /* getenv("TMPDIR") */,
185 		P_tmpdir,
186 		"/tmp",
187 	};
188 	static int n_dirs;
189 	int i;
190 
191 	if (!n_dirs) {
192 		n_dirs = sizeof(dirs_to_try)/sizeof(dirs_to_try[0]);
193 		dirs_to_try[0] = csync_tempdir;
194 		dirs_to_try[2] = getenv("TMPDIR");
195 		for (i = 0; i < n_dirs; i++) {
196 			struct stat sbuf;
197 			int ret;
198 			if (!dirs_to_try[i])
199 				continue;
200 			if (!dirs_to_try[i][0]) {
201 				/* drop "" */
202 				dirs_to_try[i] = NULL;
203 				continue;
204 			}
205 			ret = stat(dirs_to_try[i], &sbuf);
206 			if (ret || !S_ISDIR(sbuf.st_mode)) {
207 				csync_debug(1, "dropping tempdir candidate '%s': not a directory\n",
208 					dirs_to_try[i]);
209 				dirs_to_try[i] = NULL;
210 			}
211 		}
212 	}
213 
214 	/* skip this stage, if previous stages have been equal. */
215 	for (; stage < n_dirs; stage++) {
216 		if (dirs_to_try[stage] && !dirs_to_try[stage][0])
217 			continue;
218 
219 		for (i = 0; i < stage; i++) {
220 			if (dirs_to_try[i] == dirs_to_try[stage])
221 				break;
222 			if (!dirs_to_try[i] || !dirs_to_try[stage])
223 				continue;
224 			if (!strcmp(dirs_to_try[i], dirs_to_try[stage]))
225 				break;
226 		}
227 		if (i == stage) {
228 			*dir = dirs_to_try[stage];
229 			return stage+1;
230 		}
231 	}
232 	return -1;
233 }
234 
235 /* This has been taken from rsync sources: receiver.c,
236  * and adapted: dropped the "make_unique" parameter,
237  * as in our use case it is always false.
238  *
239  * Added tempdir parameter,
240  * so we can try several dirs before giving up.
241  */
242 
243 #define TMPNAME_SUFFIX ".XXXXXX"
244 #define TMPNAME_SUFFIX_LEN ((int)sizeof TMPNAME_SUFFIX - 1)
245 
246 /* get_tmpname() - create a tmp filename for a given filename
247  *
248  * If a tmpdir is defined, use that as the directory to put it in.  Otherwise,
249  * the tmp filename is in the same directory as the given name.  Note that
250  * there may be no directory at all in the given name!
251  *
252  * The tmp filename is basically the given filename with a dot prepended, and
253  * .XXXXXX appended (for mkstemp() to put its unique gunk in).  We take care
254  * to not exceed either the MAXPATHLEN or NAME_MAX, especially the last, as
255  * the basename basically becomes 8 characters longer.  In such a case, the
256  * original name is shortened sufficiently to make it all fit.
257  *
258  * Of course, the only reason the file is based on the original name is to
259  * make it easier to figure out what purpose a temp file is serving when a
260  * transfer is in progress. */
get_tmpname(char * fnametmp,const char * tempdir,const char * fname)261 static int get_tmpname(char *fnametmp, const char *tempdir, const char *fname)
262 {
263 	int maxname, length = 0;
264 	const char *f;
265 	char *suf;
266 
267 	if (tempdir) {
268 		/* Note: this can't overflow, so the return value is safe */
269 		length = strlcpy(fnametmp, tempdir, MAXPATHLEN - 2);
270 		fnametmp[length++] = '/';
271 	}
272 
273 	if ((f = strrchr(fname, '/')) != NULL) {
274 		++f;
275 		if (!tempdir) {
276 			length = f - fname;
277 			/* copy up to and including the slash */
278 			strlcpy(fnametmp, fname, length + 1);
279 		}
280 	} else
281 		f = fname;
282 	if (*f == '.') /* avoid an extra leading dot for OS X's sake */
283 		f++;
284 	fnametmp[length++] = '.';
285 
286 	/* The maxname value is bufsize, and includes space for the '\0'.
287 	 * NAME_MAX needs an extra -1 for the name's leading dot. */
288 	maxname = MIN(MAXPATHLEN - length - TMPNAME_SUFFIX_LEN,
289 		      NAME_MAX - 1 - TMPNAME_SUFFIX_LEN);
290 
291 	if (maxname < 0) {
292 		csync_debug(1, "temporary filename too long: %s\n", fname);
293 		fnametmp[0] = '\0';
294 		return 0;
295 	}
296 
297 	if (maxname) {
298 		int added = strlcpy(fnametmp + length, f, maxname);
299 		if (added >= maxname)
300 			added = maxname - 1;
301 		suf = fnametmp + length + added;
302 
303 		/* Trim any dangling high-bit chars if the first-trimmed char (if any) is
304 		 * also a high-bit char, just in case we cut into a multi-byte sequence.
305 		 * We are guaranteed to stop because of the leading '.' we added. */
306 		if ((int)f[added] & 0x80) {
307 			while ((int)suf[-1] & 0x80)
308 				suf--;
309 		}
310 		/* trim one trailing dot before our suffix's dot */
311 		if (suf[-1] == '.')
312 			suf--;
313 	} else
314 		suf = fnametmp + length - 1; /* overwrite the leading dot with suffix's dot */
315 
316 	memcpy(suf, TMPNAME_SUFFIX, TMPNAME_SUFFIX_LEN+1);
317 
318 	return 1;
319 }
320 
321 /* Returns open file handle for a temp file that resides in the
322  * csync_tempdir (if specified), or
323  * the same directory as fname.
324  * If tempfile creation was not possible, before giving up,
325  * TMPDIR, P_tmpdir, or /tmp are also tried, in that order.
326  * The file must be removed after usage, or renamed into place.
327  */
328 
open_temp_file(char * fnametmp,const char * fname)329 static FILE* open_temp_file(char *fnametmp, const char *fname)
330 {
331 	FILE *f = NULL;
332 	char *dir = NULL;
333 	int fd;
334 	int i = 0;
335 
336 	do {
337 		if (i > 0)
338 			csync_debug(3, "mkstemp %s failed: %s\n", fnametmp, strerror(errno));
339 
340 		i = next_tempdir(&dir, i);
341 		if (i < 0)
342 			return NULL;
343 
344 		if (!get_tmpname(fnametmp, dir, fname))
345 			return NULL;
346 
347 		fd = do_mkstemp(fnametmp, S_IRUSR|S_IWUSR);
348 	} while (fd < 0 && errno == ENOENT);
349 
350 	if (fd >= 0) {
351 		f = fdopen(fd, "wb+");
352 			/* not unlinking since rename wouldn't work then */
353 	}
354 
355 	if (fd < 0 || !f) {
356 		csync_debug(1, "mkstemp %s failed: %s\n", fnametmp, strerror(errno));
357 		return NULL;
358 	}
359 
360 	return f;
361 }
362 
363 /* FIXME ftell? long? seriously?
364  *
365  * Then again, it is only a sigfile, so the base file size would need to be
366  * positively huge to have the size of the signature overflow a 32 bit LONG_MAX.
367  * In which case csync2 would be the wrong tool anyways.
368  */
369 
csync_send_file(FILE * in)370 void csync_send_file(FILE *in)
371 {
372 	char buffer[512];
373 	int rc, chunk;
374 	long size;
375 
376 	fflush(in);
377 	size = ftell(in);
378 	rewind(in);
379 
380 	conn_printf("octet-stream %ld\n", size);
381 
382 	while ( size > 0 ) {
383 		chunk = size > 512 ? 512 : size;
384 		rc = fread(buffer, 1, chunk, in);
385 
386 		if ( rc <= 0 )
387 			csync_fatal("Read-error while sending data.\n");
388 		chunk = rc;
389 
390 		rc = conn_write(buffer, chunk);
391 		if ( rc != chunk )
392 			csync_fatal("Write-error while sending data.\n");
393 
394 		size -= chunk;
395 	}
396 }
397 
csync_recv_file(FILE * out)398 int csync_recv_file(FILE *out)
399 {
400 	char buffer[512];
401 	int rc, chunk;
402 	long size;
403 
404 	if ( !conn_gets(buffer, 100) || sscanf(buffer, "octet-stream %ld\n", &size) != 1 ) {
405 		if (!strcmp(buffer, "ERROR\n")) { errno=EIO; return -1; }
406 		csync_fatal("Format-error while receiving data.\n");
407 	}
408 	if (size < 0) { errno=EIO; return -1; }
409 
410 	csync_debug(3, "Receiving %ld bytes ..\n", size);
411 
412 	while ( size > 0 ) {
413 		chunk = size > 512 ? 512 : size;
414 		rc = conn_read(buffer, chunk);
415 
416 		if ( rc <= 0 )
417 			csync_fatal("Read-error while receiving data.\n");
418 		chunk = rc;
419 
420 		rc = fwrite(buffer, chunk, 1, out);
421 		if ( rc != 1 )
422 			csync_fatal("Write-error while receiving data.\n");
423 
424 		size -= chunk;
425 		csync_debug(3, "Got %d bytes, %ld bytes left ..\n",
426 				chunk, size);
427 	}
428 
429 	fflush(out);
430 	rewind(out);
431 	return 0;
432 }
433 
434 /*
435  * Return:
436  * 	0, *sig_file == NULL: base file does not exist, empty sig.
437  * 	0, *sig_file != NULL: sig_file contains the sig
438  *     -1, *sig_file == NULL: "IO Error"
439  *     -1, *sig_file != NULL: librsync error
440  */
csync_rs_sigfile(const char * filename,FILE ** sig_file_out)441 int csync_rs_sigfile(const char *filename, FILE **sig_file_out)
442 {
443 	char tmpfname[MAXPATHLEN];
444 	struct stat st;
445 	FILE *basis_file;
446 	FILE *sig_file = NULL;
447 	int r = -1;
448 	rs_result result;
449 	rs_stats_t stats;
450 
451 	csync_debug(3, "Opening basis_file and sig_file for %s\n", filename);
452 	*sig_file_out = NULL;
453 
454 	basis_file = fopen(prefixsubst(filename), "rb");
455 	if (!basis_file && errno == ENOENT) {
456 		csync_debug(3, "Basis file does not exist.\n");
457 		return 0;
458 	}
459 	if (!basis_file || fstat(fileno(basis_file), &st) || !S_ISREG(st.st_mode))
460 		goto out;
461 
462 	sig_file = open_temp_file(tmpfname, prefixsubst(filename));
463 	if (!sig_file)
464 		goto out;
465 	if (unlink(tmpfname) < 0)
466 		goto out;
467 
468 	csync_debug(3, "Running rs_sig_file() from librsync....\n");
469 /* see upstream
470  * https://github.com/librsync/librsync/commit/152323729ac831727032daf50a10c1448b48f252
471  * as reaction to SECURITY: CVE-2014-8242
472  */
473 #ifdef RS_DEFAULT_STRONG_LEN
474 	result = rs_sig_file(basis_file, sig_file, RS_DEFAULT_BLOCK_LEN, RS_DEFAULT_STRONG_LEN, &stats);
475 #else
476 	/* For backward compatibility, for now hardcode RS_MD4_SIG_MAGIC.
477 	 * TODO: allow changing to RS_BLAKE2_SIG_MAGIC. */
478 	result = rs_sig_file(basis_file, sig_file, RS_DEFAULT_BLOCK_LEN, 0, RS_MD4_SIG_MAGIC, &stats);
479 #endif
480 	*sig_file_out = sig_file;
481 	sig_file = NULL;
482 	if (result != RS_DONE)
483 		csync_debug(0, "Internal error from rsync library!\n");
484 	else
485 		r = 0;
486 out:
487 	if (basis_file)
488 		fclose(basis_file);
489 	if (sig_file)
490 		fclose(sig_file);
491 	return r;
492 }
493 
csync_rs_check(const char * filename,int isreg)494 int csync_rs_check(const char *filename, int isreg)
495 {
496 	FILE *sig_file = 0;
497 	char buffer1[512], buffer2[512];
498 	int rc, chunk, found_diff = 0;
499 	int backup_errno;
500 	long size;
501 	long my_size = 0;
502 
503 	csync_debug(3, "Csync2 / Librsync: csync_rs_check('%s', %d [%s])\n",
504 		    filename, isreg, isreg ? "regular file" : "non-regular file");
505 
506 	csync_debug(3, "Reading signature size from peer....\n");
507 	if (!conn_gets(buffer1, 100) || sscanf(buffer1, "octet-stream %ld\n", &size) != 1)
508 		csync_fatal("Format-error while receiving data.\n");
509 
510 	if (size < 0) {
511 		errno = EIO;
512 		goto io_error;
513 	}
514 
515 	csync_debug(3, "Receiving %ld bytes ..\n", size);
516 
517 	if (isreg) {
518 		if (csync_rs_sigfile(filename, &sig_file)) {
519 			if (!sig_file)
520 				goto io_error;
521 			goto error;
522 		}
523 		if (sig_file) {
524 			fflush(sig_file);
525 			my_size = ftell(sig_file);
526 			rewind(sig_file);
527 		}
528 	}
529 
530 	if (size != my_size) {
531 		csync_debug(2, "Signature size differs: local=%d, peer=%d\n", my_size, size);
532 		found_diff = 1;
533 	}
534 
535 	while (size > 0) {
536 		chunk = size > 512 ? 512 : size;
537 		rc = conn_read(buffer1, chunk);
538 
539 		if (rc <= 0)
540 			csync_fatal("Read-error while receiving data.\n");
541 		chunk = rc;
542 
543 		if (sig_file) {
544 			if (fread(buffer2, chunk, 1, sig_file) != 1) {
545 				csync_debug(2, "Found EOF in local sig file.\n");
546 				found_diff = 1;
547 			}
548 			if (memcmp(buffer1, buffer2, chunk)) {
549 				csync_debug(2, "Found diff in sig at -%d:-%d\n", size, size - chunk);
550 				found_diff = 1;
551 			}
552 		} /* else just drain */
553 
554 		size -= chunk;
555 		csync_debug(3, "Got %d bytes, %ld bytes left ..\n", chunk, size);
556 	}
557 
558 	csync_debug(3, "File has been checked successfully (%s).\n", found_diff ? "difference found" : "files are equal");
559 	if (sig_file)
560 		fclose(sig_file);
561 	return found_diff;
562 
563 io_error:
564 	csync_debug(0, "I/O Error '%s' in rsync-check: %s\n", strerror(errno), prefixsubst(filename));
565 error:
566 	backup_errno = errno;
567 
568 	/* drain response */
569 	while (size > 0) {
570 		chunk = size > 512 ? 512 : size;
571 		rc = conn_read(buffer1, chunk);
572 		if (rc <= 0)
573 			csync_fatal("Read-error while receiving data.\n");
574 		size -= rc;
575 	}
576 
577 	if (sig_file)
578 		fclose(sig_file);
579 	errno = backup_errno;
580 	return -1;
581 }
582 
csync_rs_sig(const char * filename)583 void csync_rs_sig(const char *filename)
584 {
585 	FILE *sig_file;
586 
587 	csync_debug(3, "Csync2 / Librsync: csync_rs_sig('%s')\n", filename);
588 
589 	if (csync_rs_sigfile(filename, &sig_file)) {
590 		/* error */
591 		if (sig_file)
592 			csync_fatal("Got an error from librsync, too bad!\n");
593 		csync_debug(0, "I/O Error '%s' in rsync-sig: %s\n",
594 				strerror(errno), prefixsubst(filename));
595 
596 		/* FIXME.
597 		 * Peer expected some sort of sig,
598 		 * we need to communicate an error instead. */
599 		conn_printf("octet-stream -1\n");
600 		return;
601 	}
602 
603 	/* no error */
604 	csync_debug(3, "Sending sig_file to peer..\n");
605 	if (sig_file) {
606 		csync_send_file(sig_file);
607 		fclose(sig_file);
608 	} else {
609 		/* This is the signature for an "empty" file
610 		 * as returned by rs_sig_file(/dev/null).
611 		 * No point in re-calculating it over and over again. */
612 		conn_printf("octet-stream 12\n");
613 		conn_write("rs\0016\000\000\010\000\000\000\000\010", 12);
614 	}
615 	csync_debug(3, "Signature has been successfully sent.\n");
616 }
617 
618 
619 
csync_rs_delta(const char * filename)620 int csync_rs_delta(const char *filename)
621 {
622 	FILE *sig_file = 0, *new_file = 0, *delta_file = 0;
623 	rs_result result;
624 	rs_signature_t *sumset;
625 	rs_stats_t stats;
626 	char tmpfname[MAXPATHLEN];
627 
628 	csync_debug(3, "Csync2 / Librsync: csync_rs_delta('%s')\n", filename);
629 
630 	csync_debug(3, "Receiving sig_file from peer..\n");
631 	sig_file = open_temp_file(tmpfname, prefixsubst(filename));
632 	if ( !sig_file ) goto io_error;
633 	if (unlink(tmpfname) < 0) goto io_error;
634 
635 	if ( csync_recv_file(sig_file) ) {
636 		fclose(sig_file);
637 		return -1;
638 	}
639 	result = rs_loadsig_file(sig_file, &sumset, &stats);
640 	if (result != RS_DONE)
641 		csync_fatal("Got an error from librsync, too bad!\n");
642 	fclose(sig_file);
643 
644 	csync_debug(3, "Opening new_file and delta_file..\n");
645 	new_file = fopen(prefixsubst(filename), "rb");
646 	if ( !new_file ) {
647 		int backup_errno = errno;
648 		const char *errstr = strerror(errno);
649 		csync_debug(0, "I/O Error '%s' while %s in rsync-delta: %s\n",
650 				errstr, "opening data file for reading", filename);
651 		conn_printf("%s\n", errstr);
652 		fclose(new_file);
653 		errno = backup_errno;
654 		return -1;
655 	}
656 
657 	delta_file = open_temp_file(tmpfname, prefixsubst(filename));
658 	if ( !delta_file ) goto io_error;
659 	if (unlink(tmpfname) < 0) goto io_error;
660 
661 	csync_debug(3, "Running rs_build_hash_table() from librsync..\n");
662 	result = rs_build_hash_table(sumset);
663 	if (result != RS_DONE)
664 		csync_fatal("Got an error from librsync, too bad!\n");
665 
666 	csync_debug(3, "Running rs_delta_file() from librsync..\n");
667 	result = rs_delta_file(sumset, new_file, delta_file, &stats);
668 	if (result != RS_DONE)
669 		csync_fatal("Got an error from librsync, too bad!\n");
670 
671 	csync_debug(3, "Sending delta_file to peer..\n");
672 	csync_send_file(delta_file);
673 
674 	csync_debug(3, "Delta has been created successfully.\n");
675 	rs_free_sumset(sumset);
676 	fclose(delta_file);
677 	fclose(new_file);
678 
679 	return 0;
680 
681 io_error:
682 	csync_debug(0, "I/O Error '%s' in rsync-delta: %s\n",
683 			strerror(errno), prefixsubst(filename));
684 
685 	if (new_file) fclose(new_file);
686 	if (delta_file) fclose(delta_file);
687 	if (sig_file) fclose(sig_file);
688 
689 	return -1;
690 }
691 
csync_rs_patch(const char * filename)692 int csync_rs_patch(const char *filename)
693 {
694 	FILE *basis_file = 0, *delta_file = 0, *new_file = 0;
695 	int backup_errno;
696 	rs_stats_t stats;
697 	rs_result result;
698 	char *errstr = "?";
699 	char tmpfname[MAXPATHLEN], newfname[MAXPATHLEN];
700 
701 	csync_debug(3, "Csync2 / Librsync: csync_rs_patch('%s')\n", filename);
702 
703 	csync_debug(3, "Receiving delta_file from peer..\n");
704 	delta_file = open_temp_file(tmpfname, prefixsubst(filename));
705 	if ( !delta_file ) { errstr="creating delta temp file"; goto io_error; }
706 	if (unlink(tmpfname) < 0) { errstr="removing delta temp file"; goto io_error; }
707 	if ( csync_recv_file(delta_file) ) goto error;
708 
709 	csync_debug(3, "Opening to be patched file on local host..\n");
710 	basis_file = fopen(prefixsubst(filename), "rb");
711 	if ( !basis_file ) {
712 		basis_file = open_temp_file(tmpfname, prefixsubst(filename));
713 		if ( !basis_file ) { errstr="opening data file for reading"; goto io_error; }
714 		if (unlink(tmpfname) < 0) { errstr="removing data temp file"; goto io_error; }
715 	}
716 
717 	csync_debug(3, "Opening temp file for new data on local host..\n");
718 	new_file = open_temp_file(newfname, prefixsubst(filename));
719 	if ( !new_file ) { errstr="creating new data temp file"; goto io_error; }
720 
721 	csync_debug(3, "Running rs_patch_file() from librsync..\n");
722 	result = rs_patch_file(basis_file, delta_file, new_file, &stats);
723 	if (result != RS_DONE) {
724 		csync_debug(0, "Internal error from rsync library!\n");
725 		goto error;
726 	}
727 
728 	csync_debug(3, "Renaming tmp file to data file..\n");
729 	fclose(basis_file);
730 	basis_file = NULL;
731 
732 #ifdef __CYGWIN__
733 
734 /* TODO: needed? */
735 	// This creates the file using the native windows API, bypassing
736 	// the cygwin wrappers and so making sure that we do not mess up the
737 	// permissions..
738 	{
739 		char winfilename[MAX_PATH];
740 		HANDLE winfh;
741 
742 		cygwin_conv_to_win32_path(prefixsubst(filename), winfilename);
743 
744 		winfh = CreateFile(TEXT(winfilename),
745 				GENERIC_WRITE,          // open for writing
746 				0,                      // do not share
747 				NULL,                   // default security
748 				CREATE_ALWAYS,          // overwrite existing
749 				FILE_ATTRIBUTE_NORMAL | // normal file
750 				FILE_FLAG_OVERLAPPED,   // asynchronous I/O
751 				NULL);                  // no attr. template
752 
753 		if (winfh == INVALID_HANDLE_VALUE) {
754 			csync_debug(0, "Win32 I/O Error %d in rsync-patch: %s\n",
755 					(int)GetLastError(), winfilename);
756 			errno = EACCES;
757 			goto error;
758 		}
759 		CloseHandle(winfh);
760 		goto copy;
761 	}
762 #endif
763 
764 	if (rename(newfname, prefixsubst(filename))) {
765 		char buffer[512];
766 		int rc;
767 
768 		if (errno != EXDEV) {
769 			errstr="renaming tmp file to to be patched file";
770 			goto io_error;
771 		}
772 #ifdef __CYGWIN__
773 copy:
774 #endif
775 		csync_debug(1, "rename not possible! Will truncate and copy instead.\n");
776 		basis_file = fopen(prefixsubst(filename), "wb");
777 		if ( !basis_file ) {
778 			errstr="opening data file for writing";
779 			goto io_error;
780 		}
781 
782 		/* FIXME
783 		 * Can easily lead to partially transfered files on the receiving side!
784 		 * Think truncate, then connection loss.
785 		 * Or any other failure scenario.
786 		 * Need better error checks!
787 		 */
788 		rewind(new_file);
789 		while ( (rc = fread(buffer, 1, 512, new_file)) > 0
790 			&& fwrite(buffer, rc, 1, basis_file) == rc )
791 			;
792 		/* at least retain the temp file, if something went wrong. */
793 		if (ferror(new_file) || ferror(basis_file)) {
794 			csync_debug(0, "ERROR while copying temp file '%s' to basis file '%s'; "
795 					"basis file may be corrupted; temp file has been retained.\n",
796 					newfname, prefixsubst(filename));
797 			goto error;
798 		}
799 		unlink(newfname);
800 		fclose(basis_file);
801 		basis_file = NULL;
802 	}
803 
804 	csync_debug(3, "File has been patched successfully.\n");
805 	fclose(delta_file);
806 	fclose(new_file);
807 
808 	return 0;
809 
810 io_error:
811 	backup_errno = errno;
812 	csync_debug(0, "I/O Error '%s' while %s in rsync-patch: %s\n",
813 			strerror(errno), errstr, prefixsubst(filename));
814 	errno = backup_errno;
815 error:;
816 	backup_errno = errno;
817 	if ( delta_file ) fclose(delta_file);
818 	if ( basis_file ) fclose(basis_file);
819 	if ( new_file )   fclose(new_file);
820 	errno = backup_errno;
821 	return -1;
822 }
823 
824