--- options.c.orig2 2011-09-22 11:27:01.000000000 -0600 +++ options.c 2011-09-22 16:34:52.000000000 -0600 @@ -110,6 +110,7 @@ size_t bwlimit_writemax = 0; int ignore_existing = 0; int rename_existing = 0; +int skip_open_files = 0; int ignore_non_existing = 0; int need_messages_from_generator = 0; int max_delete = INT_MIN; @@ -367,6 +368,7 @@ rprintf(F," --existing skip creating new files on receiver\n"); rprintf(F," --ignore-existing skip updating files that already exist on receiver\n"); rprintf(F," --rename-existing rename files on receiver that already exist on receiver\n"); + rprintf(F," --skip-open-files skip files that are open on the sender\n"); rprintf(F," --remove-source-files sender removes synchronized files (non-dirs)\n"); rprintf(F," --del an alias for --delete-during\n"); rprintf(F," --delete delete extraneous files from destination dirs\n"); @@ -539,6 +541,7 @@ {"ignore-non-existing",0,POPT_ARG_NONE, &ignore_non_existing, 0, 0, 0 }, {"ignore-existing", 0, POPT_ARG_NONE, &ignore_existing, 0, 0, 0 }, {"rename-existing", 0, POPT_ARG_NONE, &rename_existing, 0, 0, 0 }, + {"skip-open-files", 0, POPT_ARG_NONE, &skip_open_files, 0, 0, 0 }, {"max-size", 0, POPT_ARG_STRING, &max_size_arg, OPT_MAX_SIZE, 0, 0 }, {"min-size", 0, POPT_ARG_STRING, &min_size_arg, OPT_MIN_SIZE, 0, 0 }, {"sparse", 'S', POPT_ARG_VAL, &sparse_files, 1, 0, 0 }, @@ -1975,6 +1978,8 @@ args[ac++] = "--super"; if (size_only) args[ac++] = "--size-only"; + if (skip_open_files) + args[ac++] = "--skip-open-files"; } else { if (skip_compress) { if (asprintf(&arg, "--skip-compress=%s", skip_compress) < 0) --- sender.c.orig 2011-09-22 11:48:26.000000000 -0600 +++ sender.c 2011-09-26 13:30:49.000000000 -0600 @@ -42,6 +42,7 @@ extern int inplace; extern int batch_fd; extern int write_batch; +extern int skip_open_files; extern struct stats stats; extern struct file_list *cur_flist, *first_flist, *dir_flist; @@ -279,6 +280,74 @@ exit_cleanup(RERR_PROTOCOL); } + if (skip_open_files) { + int len, i; + const char testdir[] = ".rsync-tmp"; + char *basename, *testfile; + STRUCT_STAT st; + + if (mkdir(testdir, 511) == -1) { + if (errno != EEXIST) { + rsyserr(FERROR, errno, "skip_open_files: failed to mkdir %s", testdir); + io_error |= IOERR_GENERAL; + free_sums(s); + if (protocol_version >= 30) + send_msg_int(MSG_NO_SEND, ndx); + continue; + } + } + basename = strrchr(fname, '/'); + if (basename == NULL) { + basename = fname; + } else { + basename++; + } + len = strlen(testdir) + 1 + strlen(basename) + 1; + testfile = malloc(len); + snprintf(testfile, len, "%s/%s", testdir, basename); + + /* Unlink in case it was there from a prior run */ + do_unlink(testfile); + + /* Ensure that the file we're about to link doesn't exist */ + i = link_stat(testfile, &st, 0); + if (i == 0 || (i == -1 && errno != ENOENT)) { + /* file exists, or stat had an error OTHER than file does not exist */ + rsyserr(FERROR, errno, "skip_open_files: failed to stat %s", testfile); + io_error |= IOERR_GENERAL; + free_sums(s); + free(testfile); + if (protocol_version >= 30) + send_msg_int(MSG_NO_SEND, ndx); + continue; + } + + /* link current file to our test file */ + if (do_link(fname, testfile) != 0) { + rsyserr(FERROR, errno, "skip_open_files: failed to link %s to %s", full_fname(fname), testfile); + io_error |= IOERR_GENERAL; + free_sums(s); + free(testfile); + if (protocol_version >= 30) + send_msg_int(MSG_NO_SEND, ndx); + continue; + } + + /* test the unlink, if it fails, file is locked - cygwin/win behavior only */ + if (do_unlink(testfile) != 0) { + io_error |= IOERR_GENERAL; + rprintf(FERROR, "skip_open_files: file cannot be unlinked, not sending: %s\n", full_fname(fname)); + free_sums(s); + free(testfile); + if (protocol_version >= 30) + send_msg_int(MSG_NO_SEND, ndx); + continue; + } + if (verbose > 1) + rprintf(FINFO, "skip_open_files: open file not detected\n"); + free(testfile); + } + fd = do_open(fname, O_RDONLY, 0); if (fd == -1) { if (errno == ENOENT) { @@ -300,6 +369,23 @@ continue; } + /* ignore locked files */ + if (skip_open_files) { + if (flock(fd, LOCK_EX|LOCK_NB) == 0) { + flock(fd, LOCK_UN); + } else { + io_error |= IOERR_GENERAL; + rprintf(FERROR, "skip_open_files: file is locked, not sending: %s\n", full_fname(fname)); + free_sums(s); + close(fd); + if (protocol_version >= 30) + send_msg_int(MSG_NO_SEND, ndx); + continue; + } + if (verbose > 1) + rprintf(FINFO, "skip_open_files: locked file not detected\n"); + } + /* map the local file */ if (do_fstat(fd, &st) != 0) { io_error |= IOERR_GENERAL;