/* Has same return codes as make_backup(). */ static inline int link_or_rename(const char *from, const char *to, BOOL prefer_rename, STRUCT_STAT *stp) { #ifdef SUPPORT_HARD_LINKS if (!prefer_rename) { #ifndef CAN_HARDLINK_SYMLINK if (S_ISLNK(stp->st_mode)) return 0; /* Use copy code. */ #endif #ifndef CAN_HARDLINK_SPECIAL if (IS_SPECIAL(stp->st_mode) || IS_DEVICE(stp->st_mode)) return 0; /* Use copy code. */ #endif if (do_link(from, to) == 0) { if (DEBUG_GTE(BACKUP, 1)) rprintf(FINFO, "make_backup: HLINK %s successful.\n", from); return 2; } /* We prefer to rename a regular file rather than copy it. */ if (!S_ISREG(stp->st_mode) || errno == EEXIST || errno == EISDIR) return 0; } #endif if (do_rename(from, to) == 0) { if (stp->st_nlink > 1 && !S_ISDIR(stp->st_mode)) { /* If someone has hard-linked the file into the backup * dir, rename() might return success but do nothing! */ robust_unlink(from); /* Just in case... */ } if (DEBUG_GTE(BACKUP, 1)) rprintf(FINFO, "make_backup: RENAME %s successful.\n", from); return 1; } return 0; }
/* Transmit a literal and/or match token. * * This delightfully-named function is called either when we find a * match and need to transmit all the unmatched data leading up to it, * or when we get bored of accumulating literal data and just need to * transmit it. As a result of this second case, it is called even if * we have not matched at all! * * If i >= 0, the number of a matched token. If < 0, indicates we have * only literal data. A -1 will send a 0-token-int too, and a -2 sends * only literal data, w/o any token-int. */ static void matched(int f, struct sum_struct *s, struct map_struct *buf, OFF_T offset, int32 i) { int32 n = (int32)(offset - last_match); /* max value: block_size (int32) */ int32 j; if (DEBUG_GTE(DELTASUM, 2) && i >= 0) { rprintf(FINFO, "match at %s last_match=%s j=%d len=%ld n=%ld\n", big_num(offset), big_num(last_match), i, (long)s->sums[i].len, (long)n); } send_token(f, i, buf, last_match, n, i < 0 ? 0 : s->sums[i].len); data_transfer += n; if (i >= 0) { stats.matched_data += s->sums[i].len; n += s->sums[i].len; } for (j = 0; j < n; j += CHUNK_SIZE) { int32 n1 = MIN(CHUNK_SIZE, n - j); sum_update(map_ptr(buf, last_match + j, n1), n1); } if (i >= 0) last_match = offset + s->sums[i].len; else last_match = offset; if (buf && INFO_GTE(PROGRESS, 1)) show_progress(last_match, buf->file_size); }
/** * Create a child connected to us via its stdin/stdout. * * This is derived from CVS code * * Note that in the child STDIN is set to blocking and STDOUT * is set to non-blocking. This is necessary as rsh relies on stdin being blocking * and ssh relies on stdout being non-blocking * * If blocking_io is set then use blocking io on both fds. That can be * used to cope with badly broken rsh implementations like the one on * Solaris. **/ pid_t piped_child(char **command, int *f_in, int *f_out) { pid_t pid; int to_child_pipe[2]; int from_child_pipe[2]; if (DEBUG_GTE(CMD, 1)) print_child_argv("opening connection using:", command); if (fd_pair(to_child_pipe) < 0 || fd_pair(from_child_pipe) < 0) { rsyserr(FERROR, errno, "pipe"); exit_cleanup(RERR_IPC); } pid = do_fork(); if (pid == -1) { rsyserr(FERROR, errno, "fork"); exit_cleanup(RERR_IPC); } if (pid == 0) { if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 || close(to_child_pipe[1]) < 0 || close(from_child_pipe[0]) < 0 || dup2(from_child_pipe[1], STDOUT_FILENO) < 0) { rsyserr(FERROR, errno, "Failed to dup/close"); exit_cleanup(RERR_IPC); } if (to_child_pipe[0] != STDIN_FILENO) close(to_child_pipe[0]); if (from_child_pipe[1] != STDOUT_FILENO) close(from_child_pipe[1]); set_blocking(STDIN_FILENO); if (blocking_io > 0) set_blocking(STDOUT_FILENO); execvp(command[0], command); rsyserr(FERROR, errno, "Failed to exec %s", command[0]); exit_cleanup(RERR_IPC); } if (close(from_child_pipe[1]) < 0 || close(to_child_pipe[0]) < 0) { rsyserr(FERROR, errno, "Failed to close"); exit_cleanup(RERR_IPC); } *f_in = from_child_pipe[0]; *f_out = to_child_pipe[1]; if(pid == 0) { set_blocking(STDIN_FILENO); execvp(command[0], command); rsyserr(FERROR, errno, "Failed to exec %s", command[0]); } return pid; }
void match_report(void) { if (!DEBUG_GTE(DELTASUM, 1)) return; rprintf(FINFO, "total: matches=%d hash_hits=%d false_alarms=%d data=%s\n", total_matches, total_hash_hits, total_false_alarms, big_num(stats.literal_data)); }
/* This returns 0 for success, 1 for a symlink if symlink time-setting * is not possible, or -1 for any other error. */ int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode) { static int switch_step = 0; if (DEBUG_GTE(TIME, 1)) { rprintf(FINFO, "set modtime of %s to (%ld) %s", fname, (long)modtime, asctime(localtime(&modtime))); } switch (switch_step) { #ifdef HAVE_UTIMENSAT #include "case_N.h" if (do_utimensat(fname, modtime, mod_nsec) == 0) break; if (errno != ENOSYS) return -1; switch_step++; /* FALLTHROUGH */ #endif #ifdef HAVE_LUTIMES #include "case_N.h" if (do_lutimes(fname, modtime, mod_nsec) == 0) break; if (errno != ENOSYS) return -1; switch_step++; /* FALLTHROUGH */ #endif #include "case_N.h" switch_step++; if (preserve_times & PRESERVE_LINK_TIMES) { preserve_times &= ~PRESERVE_LINK_TIMES; if (S_ISLNK(mode)) return 1; } /* FALLTHROUGH */ #include "case_N.h" #ifdef HAVE_UTIMES if (do_utimes(fname, modtime, mod_nsec) == 0) break; #else if (do_utime(fname, modtime, mod_nsec) == 0) break; #endif return -1; } return 0; }
void send_protected_args(int fd, char *args[]) { int i; #ifdef ICONV_OPTION int convert = ic_send != (iconv_t)-1; xbuf outbuf, inbuf; if (convert) alloc_xbuf(&outbuf, 1024); #endif for (i = 0; args[i]; i++) {} /* find first NULL */ args[i] = "rsync"; /* set a new arg0 */ if (DEBUG_GTE(CMD, 1)) print_child_argv("protected args:", args + i + 1); do { if (!args[i][0]) write_buf(fd, ".", 2); #ifdef ICONV_OPTION else if (convert) { INIT_XBUF_STRLEN(inbuf, args[i]); iconvbufs(ic_send, &inbuf, &outbuf, ICB_EXPAND_OUT | ICB_INCLUDE_BAD | ICB_INCLUDE_INCOMPLETE | ICB_INIT); outbuf.buf[outbuf.len] = '\0'; write_buf(fd, outbuf.buf, outbuf.len + 1); outbuf.len = 0; } #endif else write_buf(fd, args[i], strlen(args[i]) + 1); } while (args[++i]); write_byte(fd, 0); #ifdef ICONV_OPTION if (convert) free(outbuf.buf); #endif }
/** * Scan through a origin file, looking for sections that match * checksums from the generator, and transmit either literal or token * data. * * Also calculates the MD4 checksum of the whole file, using the md * accumulator. This is transmitted with the file as protection * against corruption on the wire. * * @param s Checksums received from the generator. If <tt>s->count == * 0</tt>, then there are actually no checksums for this file. * * @param len Length of the file to send. **/ void match_sums(int f, struct sum_struct *s, struct map_struct *buf, OFF_T len) { int sum_len; last_match = 0; false_alarms = 0; hash_hits = 0; matches = 0; data_transfer = 0; sum_init(xfersum_type, checksum_seed); if (append_mode > 0) { if (append_mode == 2) { OFF_T j = 0; for (j = CHUNK_SIZE; j < s->flength; j += CHUNK_SIZE) { if (buf && INFO_GTE(PROGRESS, 1)) show_progress(last_match, buf->file_size); sum_update(map_ptr(buf, last_match, CHUNK_SIZE), CHUNK_SIZE); last_match = j; } if (last_match < s->flength) { int32 n = (int32)(s->flength - last_match); if (buf && INFO_GTE(PROGRESS, 1)) show_progress(last_match, buf->file_size); sum_update(map_ptr(buf, last_match, n), n); } } last_match = s->flength; s->count = 0; } if (len > 0 && s->count > 0) { build_hash_table(s); if (DEBUG_GTE(DELTASUM, 2)) rprintf(FINFO,"built hash table\n"); hash_search(f, s, buf, len); if (DEBUG_GTE(DELTASUM, 2)) rprintf(FINFO,"done hash search\n"); } else { OFF_T j; /* by doing this in pieces we avoid too many seeks */ for (j = last_match + CHUNK_SIZE; j < len; j += CHUNK_SIZE) matched(f, s, buf, j, -2); matched(f, s, buf, len, -1); } sum_len = sum_end(sender_file_sum); /* If we had a read error, send a bad checksum. We use all bits * off as long as the checksum doesn't happen to be that, in * which case we turn the last 0 bit into a 1. */ if (buf && buf->status != 0) { int i; for (i = 0; i < sum_len && sender_file_sum[i] == 0; i++) {} memset(sender_file_sum, 0, sum_len); if (i == sum_len) sender_file_sum[i-1]++; } if (DEBUG_GTE(DELTASUM, 2)) rprintf(FINFO,"sending file_sum\n"); write_buf(f, sender_file_sum, sum_len); if (DEBUG_GTE(DELTASUM, 2)) { rprintf(FINFO, "false_alarms=%d hash_hits=%d matches=%d\n", false_alarms, hash_hits, matches); } total_hash_hits += hash_hits; total_false_alarms += false_alarms; total_matches += matches; stats.literal_data += data_transfer; }
static void hash_search(int f,struct sum_struct *s, struct map_struct *buf, OFF_T len) { OFF_T offset, aligned_offset, end; int32 k, want_i, aligned_i, backup; char sum2[SUM_LENGTH]; uint32 s1, s2, sum; int more; schar *map; /* want_i is used to encourage adjacent matches, allowing the RLL * coding of the output to work more efficiently. */ want_i = 0; if (DEBUG_GTE(DELTASUM, 2)) { rprintf(FINFO, "hash search b=%ld len=%s\n", (long)s->blength, big_num(len)); } k = (int32)MIN(len, (OFF_T)s->blength); map = (schar *)map_ptr(buf, 0, k); sum = get_checksum1((char *)map, k); s1 = sum & 0xFFFF; s2 = sum >> 16; if (DEBUG_GTE(DELTASUM, 3)) rprintf(FINFO, "sum=%.8x k=%ld\n", sum, (long)k); offset = aligned_offset = aligned_i = 0; end = len + 1 - s->sums[s->count-1].len; if (DEBUG_GTE(DELTASUM, 3)) { rprintf(FINFO, "hash search s->blength=%ld len=%s count=%s\n", (long)s->blength, big_num(len), big_num(s->count)); } do { int done_csum2 = 0; uint32 hash_entry; int32 i, *prev; if (DEBUG_GTE(DELTASUM, 4)) { rprintf(FINFO, "offset=%s sum=%04x%04x\n", big_num(offset), s2 & 0xFFFF, s1 & 0xFFFF); } if (tablesize == TRADITIONAL_TABLESIZE) { hash_entry = SUM2HASH2(s1,s2); if ((i = hash_table[hash_entry]) < 0) goto null_hash; sum = (s1 & 0xffff) | (s2 << 16); } else { sum = (s1 & 0xffff) | (s2 << 16); hash_entry = BIG_SUM2HASH(sum); if ((i = hash_table[hash_entry]) < 0) goto null_hash; } prev = &hash_table[hash_entry]; hash_hits++; do { int32 l; /* When updating in-place, the chunk's offset must be * either >= our offset or identical data at that offset. * Remove any bypassed entries that we can never use. */ if (updating_basis_file && s->sums[i].offset < offset && !(s->sums[i].flags & SUMFLG_SAME_OFFSET)) { *prev = s->sums[i].chain; continue; } prev = &s->sums[i].chain; if (sum != s->sums[i].sum1) continue; /* also make sure the two blocks are the same length */ l = (int32)MIN((OFF_T)s->blength, len-offset); if (l != s->sums[i].len) continue; if (DEBUG_GTE(DELTASUM, 3)) { rprintf(FINFO, "potential match at %s i=%ld sum=%08x\n", big_num(offset), (long)i, sum); } if (!done_csum2) { map = (schar *)map_ptr(buf,offset,l); get_checksum2((char *)map,l,sum2); done_csum2 = 1; } if (memcmp(sum2,s->sums[i].sum2,s->s2length) != 0) { false_alarms++; continue; } /* When updating in-place, the best possible match is * one with an identical offset, so we prefer that over * the adjacent want_i optimization. */ if (updating_basis_file) { /* All the generator's chunks start at blength boundaries. */ while (aligned_offset < offset) { aligned_offset += s->blength; aligned_i++; } if ((offset == aligned_offset || (sum == 0 && l == s->blength && aligned_offset + l <= len)) && aligned_i < s->count) { if (i != aligned_i) { if (sum != s->sums[aligned_i].sum1 || l != s->sums[aligned_i].len || memcmp(sum2, s->sums[aligned_i].sum2, s->s2length) != 0) goto check_want_i; i = aligned_i; } if (offset != aligned_offset) { /* We've matched some zeros in a spot that is also zeros * further along in the basis file, if we find zeros ahead * in the sender's file, we'll output enough literal data * to re-align with the basis file, and get back to seeking * instead of writing. */ backup = (int32)(aligned_offset - last_match); if (backup < 0) backup = 0; map = (schar *)map_ptr(buf, aligned_offset - backup, l + backup) + backup; sum = get_checksum1((char *)map, l); if (sum != s->sums[i].sum1) goto check_want_i; get_checksum2((char *)map, l, sum2); if (memcmp(sum2, s->sums[i].sum2, s->s2length) != 0) goto check_want_i; /* OK, we have a re-alignment match. Bump the offset * forward to the new match point. */ offset = aligned_offset; } /* This identical chunk is in the same spot in the old and new file. */ s->sums[i].flags |= SUMFLG_SAME_OFFSET; want_i = i; } } check_want_i: /* we've found a match, but now check to see * if want_i can hint at a better match. */ if (i != want_i && want_i < s->count && (!updating_basis_file || s->sums[want_i].offset >= offset || s->sums[want_i].flags & SUMFLG_SAME_OFFSET) && sum == s->sums[want_i].sum1 && memcmp(sum2, s->sums[want_i].sum2, s->s2length) == 0) { /* we've found an adjacent match - the RLL coder * will be happy */ i = want_i; } want_i = i + 1; matched(f,s,buf,offset,i); offset += s->sums[i].len - 1; k = (int32)MIN((OFF_T)s->blength, len-offset); map = (schar *)map_ptr(buf, offset, k); sum = get_checksum1((char *)map, k); s1 = sum & 0xFFFF; s2 = sum >> 16; matches++; break; } while ((i = s->sums[i].chain) >= 0); null_hash: backup = (int32)(offset - last_match); /* We sometimes read 1 byte prior to last_match... */ if (backup < 0) backup = 0; /* Trim off the first byte from the checksum */ more = offset + k < len; map = (schar *)map_ptr(buf, offset - backup, k + more + backup) + backup; s1 -= map[0] + CHAR_OFFSET; s2 -= k * (map[0]+CHAR_OFFSET); /* Add on the next byte (if there is one) to the checksum */ if (more) { s1 += map[k] + CHAR_OFFSET; s2 += s1; } else --k; /* By matching early we avoid re-reading the data 3 times in the case where a token match comes a long way after last match. The 3 reads are caused by the running match, the checksum update and the literal send. */ if (backup >= s->blength+CHUNK_SIZE && end-offset > CHUNK_SIZE) matched(f, s, buf, offset - s->blength, -2); } while (++offset < end); matched(f, s, buf, len, -1); map_ptr(buf, len-1, 1); }
/** * Eventually calls exit(), passing @p code, therefore does not return. * * @param code one of the RERR_* codes from errcode.h. **/ NORETURN void _exit_cleanup(int code, const char *file, int line) { static int switch_step = 0; static int exit_code = 0, exit_line = 0; static const char *exit_file = NULL; static int first_code = 0; SIGACTION(SIGUSR1, SIG_IGN); SIGACTION(SIGUSR2, SIG_IGN); if (!exit_code) { /* Preserve first error exit info when recursing. */ exit_code = code; exit_file = file; exit_line = line < 0 ? -line : line; } /* If this is the exit at the end of the run, the server side * should not attempt to output a message (see log_exit()). */ if (am_server && code == 0) am_server = 2; /* Some of our actions might cause a recursive call back here, so we * keep track of where we are in the cleanup and never repeat a step. */ switch (switch_step) { #include "case_N.h" /* case 0: */ switch_step++; first_code = code; if (output_needs_newline) { fputc('\n', stdout); output_needs_newline = 0; } if (DEBUG_GTE(EXIT, 2)) { rprintf(FINFO, "[%s] _exit_cleanup(code=%d, file=%s, line=%d): entered\n", who_am_i(), code, file, line); } /* FALLTHROUGH */ #include "case_N.h" switch_step++; if (cleanup_child_pid != -1) { int status; int pid = wait_process(cleanup_child_pid, &status, WNOHANG); if (pid == cleanup_child_pid) { status = WEXITSTATUS(status); if (status > exit_code) exit_code = status; } } /* FALLTHROUGH */ #include "case_N.h" switch_step++; if (cleanup_got_literal && (cleanup_fname || cleanup_fd_w != -1)) { if (cleanup_fd_r != -1) { close(cleanup_fd_r); cleanup_fd_r = -1; } if (cleanup_fd_w != -1) { flush_write_file(cleanup_fd_w); close(cleanup_fd_w); cleanup_fd_w = -1; } if (cleanup_fname && cleanup_new_fname && keep_partial && handle_partial_dir(cleanup_new_fname, PDIR_CREATE)) { int tweak_modtime = 0; const char *fname = cleanup_fname; cleanup_fname = NULL; if (!partial_dir) { /* We don't want to leave a partial file with a modern time or it * could be skipped via --update. Setting the time to something * really old also helps it to stand out as unfinished in an ls. */ tweak_modtime = 1; cleanup_file->modtime = 0; } finish_transfer(cleanup_new_fname, fname, NULL, NULL, cleanup_file, tweak_modtime, !partial_dir); } } /* FALLTHROUGH */ #include "case_N.h" switch_step++; if (flush_ok_after_signal) { flush_ok_after_signal = False; if (code == RERR_SIGNAL) io_flush(FULL_FLUSH); } if (!exit_code && !code) io_flush(FULL_FLUSH); /* FALLTHROUGH */ #include "case_N.h" switch_step++; if (cleanup_fname) do_unlink(cleanup_fname); if (exit_code) kill_all(SIGUSR1); if (cleanup_pid && cleanup_pid == getpid()) { char *pidf = lp_pid_file(); if (pidf && *pidf) unlink(lp_pid_file()); } if (exit_code == 0) { if (code) exit_code = code; if (io_error & IOERR_DEL_LIMIT) exit_code = RERR_DEL_LIMIT; if (io_error & IOERR_VANISHED) exit_code = RERR_VANISHED; if (io_error & IOERR_GENERAL || got_xfer_error) exit_code = RERR_PARTIAL; } /* If line < 0, this exit is after a MSG_ERROR_EXIT event, so * we don't want to output a duplicate error. */ if ((exit_code && line > 0) || am_daemon || (logfile_name && (am_server || !INFO_GTE(STATS, 1)))) log_exit(exit_code, exit_file, exit_line); /* FALLTHROUGH */ #include "case_N.h" switch_step++; if (DEBUG_GTE(EXIT, 1)) { rprintf(FINFO, "[%s] _exit_cleanup(code=%d, file=%s, line=%d): " "about to call exit(%d)\n", who_am_i(), first_code, exit_file, exit_line, exit_code); } /* FALLTHROUGH */ #include "case_N.h" switch_step++; if (exit_code && exit_code != RERR_SOCKETIO && exit_code != RERR_STREAMIO && exit_code != RERR_SIGNAL1 && exit_code != RERR_TIMEOUT && !shutting_down && (protocol_version >= 31 || am_receiver)) { if (line > 0) { if (DEBUG_GTE(EXIT, 3)) { rprintf(FINFO, "[%s] sending MSG_ERROR_EXIT with exit_code %d\n", who_am_i(), exit_code); } send_msg_int(MSG_ERROR_EXIT, exit_code); } noop_io_until_death(); } /* FALLTHROUGH */ #include "case_N.h" switch_step++; if (am_server && exit_code) msleep(100); close_all(); /* FALLTHROUGH */ default: break; } exit(exit_code); }
void setup_protocol(int f_out,int f_in) { if (am_sender) file_extra_cnt += PTR_EXTRA_CNT; else file_extra_cnt++; if (preserve_uid) uid_ndx = ++file_extra_cnt; if (preserve_gid) gid_ndx = ++file_extra_cnt; if (preserve_acls && !am_sender) acls_ndx = ++file_extra_cnt; if (preserve_xattrs) xattrs_ndx = ++file_extra_cnt; if (am_server) set_allow_inc_recurse(); if (remote_protocol == 0) { if (am_server && !local_server) check_sub_protocol(); if (!read_batch) write_int(f_out, protocol_version); remote_protocol = read_int(f_in); if (protocol_version > remote_protocol) protocol_version = remote_protocol; } if (read_batch && remote_protocol > protocol_version) { rprintf(FERROR, "The protocol version in the batch file is too new (%d > %d).\n", remote_protocol, protocol_version); exit_cleanup(RERR_PROTOCOL); } if (DEBUG_GTE(PROTO, 1)) { rprintf(FINFO, "(%s) Protocol versions: remote=%d, negotiated=%d\n", am_server? "Server" : "Client", remote_protocol, protocol_version); } if (remote_protocol < MIN_PROTOCOL_VERSION || remote_protocol > MAX_PROTOCOL_VERSION) { rprintf(FERROR,"protocol version mismatch -- is your shell clean?\n"); rprintf(FERROR,"(see the rsync man page for an explanation)\n"); exit_cleanup(RERR_PROTOCOL); } if (remote_protocol < OLD_PROTOCOL_VERSION) { rprintf(FINFO,"%s is very old version of rsync, upgrade recommended.\n", am_server? "Client" : "Server"); } if (protocol_version < MIN_PROTOCOL_VERSION) { rprintf(FERROR, "--protocol must be at least %d on the %s.\n", MIN_PROTOCOL_VERSION, am_server? "Server" : "Client"); exit_cleanup(RERR_PROTOCOL); } if (protocol_version > PROTOCOL_VERSION) { rprintf(FERROR, "--protocol must be no more than %d on the %s.\n", PROTOCOL_VERSION, am_server? "Server" : "Client"); exit_cleanup(RERR_PROTOCOL); } if (read_batch) check_batch_flags(); #ifndef SUPPORT_PREALLOCATION if (preallocate_files && !am_sender) { rprintf(FERROR, "preallocation is not supported on this %s\n", am_server ? "Server" : "Client"); exit_cleanup(RERR_SYNTAX); } #endif if (protocol_version < 30) { if (append_mode == 1) append_mode = 2; if (preserve_acls && !local_server) { rprintf(FERROR, "--acls requires protocol 30 or higher" " (negotiated %d).\n", protocol_version); exit_cleanup(RERR_PROTOCOL); } if (preserve_xattrs && !local_server) { rprintf(FERROR, "--xattrs requires protocol 30 or higher" " (negotiated %d).\n", protocol_version); exit_cleanup(RERR_PROTOCOL); } } if (delete_mode && !(delete_before+delete_during+delete_after)) { if (protocol_version < 30) delete_before = 1; else delete_during = 1; } if (protocol_version < 29) { if (fuzzy_basis) { rprintf(FERROR, "--fuzzy requires protocol 29 or higher" " (negotiated %d).\n", protocol_version); exit_cleanup(RERR_PROTOCOL); } if (basis_dir_cnt && inplace) { rprintf(FERROR, "%s with --inplace requires protocol 29 or higher" " (negotiated %d).\n", dest_option, protocol_version); exit_cleanup(RERR_PROTOCOL); } if (basis_dir_cnt > 1) { rprintf(FERROR, "Using more than one %s option requires protocol" " 29 or higher (negotiated %d).\n", dest_option, protocol_version); exit_cleanup(RERR_PROTOCOL); } if (prune_empty_dirs) { rprintf(FERROR, "--prune-empty-dirs requires protocol 29 or higher" " (negotiated %d).\n", protocol_version); exit_cleanup(RERR_PROTOCOL); } } else if (protocol_version >= 30) { if (am_server) { compat_flags = allow_inc_recurse ? CF_INC_RECURSE : 0; #ifdef CAN_SET_SYMLINK_TIMES compat_flags |= CF_SYMLINK_TIMES; #endif #ifdef ICONV_OPTION compat_flags |= CF_SYMLINK_ICONV; #endif if (local_server || strchr(client_info, 'f') != NULL) compat_flags |= CF_SAFE_FLIST; write_byte(f_out, compat_flags); } else compat_flags = read_byte(f_in); /* The inc_recurse var MUST be set to 0 or 1. */ inc_recurse = compat_flags & CF_INC_RECURSE ? 1 : 0; if (am_sender) { receiver_symlink_times = am_server ? strchr(client_info, 'L') != NULL : !!(compat_flags & CF_SYMLINK_TIMES); } #ifdef CAN_SET_SYMLINK_TIMES else receiver_symlink_times = 1; #endif #ifdef ICONV_OPTION sender_symlink_iconv = iconv_opt && (am_server ? local_server || strchr(client_info, 's') != NULL : !!(compat_flags & CF_SYMLINK_ICONV)); #endif if (inc_recurse && !allow_inc_recurse) { /* This should only be able to happen in a batch. */ fprintf(stderr, "Incompatible options specified for inc-recursive %s.\n", read_batch ? "batch file" : "connection"); exit_cleanup(RERR_SYNTAX); } use_safe_inc_flist = (compat_flags & CF_SAFE_FLIST) || protocol_version >= 31; need_messages_from_generator = 1; #ifdef CAN_SET_SYMLINK_TIMES } else if (!am_sender) { receiver_symlink_times = 1; #endif } if (need_unsorted_flist && (!am_sender || inc_recurse)) unsort_ndx = ++file_extra_cnt; if (partial_dir && *partial_dir != '/' && (!am_server || local_server)) { int rflags = FILTRULE_NO_PREFIXES | FILTRULE_DIRECTORY; if (!am_sender || protocol_version >= 30) rflags |= FILTRULE_PERISHABLE; parse_filter_str(&filter_list, partial_dir, rule_template(rflags), 0); } #ifdef ICONV_OPTION if (protect_args && files_from) { if (am_sender) filesfrom_convert = filesfrom_host && ic_send != (iconv_t)-1; else filesfrom_convert = !filesfrom_host && ic_recv != (iconv_t)-1; } #endif if (am_server) { if (!checksum_seed) checksum_seed = time(NULL); write_int(f_out, checksum_seed); } else { checksum_seed = read_int(f_in); } }
/* The directory is about to be deleted: if DEL_RECURSE is given, delete all * its contents, otherwise just checks for content. Returns DR_SUCCESS or * DR_NOT_EMPTY. Note that fname must point to a MAXPATHLEN buffer! (The * buffer is used for recursion, but returned unchanged.) */ static enum delret delete_dir_contents(char *fname, uint16 flags) { struct file_list *dirlist; enum delret ret; unsigned remainder; void *save_filters; int j, dlen; char *p; if (DEBUG_GTE(DEL, 3)) { rprintf(FINFO, "delete_dir_contents(%s) flags=%d\n", fname, flags); } dlen = strlen(fname); save_filters = push_local_filters(fname, dlen); non_perishable_cnt = 0; dirlist = get_dirlist(fname, dlen, 0); ret = non_perishable_cnt ? DR_NOT_EMPTY : DR_SUCCESS; if (!dirlist->used) goto done; if (!(flags & DEL_RECURSE)) { ret = DR_NOT_EMPTY; goto done; } p = fname + dlen; if (dlen != 1 || *fname != '/') *p++ = '/'; remainder = MAXPATHLEN - (p - fname); /* We do our own recursion, so make delete_item() non-recursive. */ flags = (flags & ~(DEL_RECURSE|DEL_MAKE_ROOM|DEL_NO_UID_WRITE)) | DEL_DIR_IS_EMPTY; for (j = dirlist->used; j--; ) { struct file_struct *fp = dirlist->files[j]; if (fp->flags & FLAG_MOUNT_DIR && S_ISDIR(fp->mode)) { if (DEBUG_GTE(DEL, 1)) { rprintf(FINFO, "mount point, %s, pins parent directory\n", f_name(fp, NULL)); } ret = DR_NOT_EMPTY; continue; } strlcpy(p, fp->basename, remainder); if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US) do_chmod(fname, fp->mode | S_IWUSR); /* Save stack by recursing to ourself directly. */ if (S_ISDIR(fp->mode)) { if (delete_dir_contents(fname, flags | DEL_RECURSE) != DR_SUCCESS) ret = DR_NOT_EMPTY; } if (delete_item(fname, fp->mode, flags) != DR_SUCCESS) ret = DR_NOT_EMPTY; } fname[dlen] = '\0'; done: flist_free(dirlist); pop_local_filters(save_filters); if (ret == DR_NOT_EMPTY) { rprintf(FINFO, "cannot delete non-empty directory: %s\n", fname); } return ret; }
/* Delete a file or directory. If DEL_RECURSE is set in the flags, this will * delete recursively. * * Note that fbuf must point to a MAXPATHLEN buffer if the mode indicates it's * a directory! (The buffer is used for recursion, but returned unchanged.) */ enum delret delete_item(char *fbuf, uint16 mode, uint16 flags) { enum delret ret; char *what; int ok; if (DEBUG_GTE(DEL, 2)) { rprintf(FINFO, "delete_item(%s) mode=%o flags=%d\n", fbuf, (int)mode, (int)flags); } if (flags & DEL_NO_UID_WRITE) do_chmod(fbuf, mode | S_IWUSR); if (S_ISDIR(mode) && !(flags & DEL_DIR_IS_EMPTY)) { /* This only happens on the first call to delete_item() since * delete_dir_contents() always calls us w/DEL_DIR_IS_EMPTY. */ ignore_perishable = 1; /* If DEL_RECURSE is not set, this just reports emptiness. */ ret = delete_dir_contents(fbuf, flags); ignore_perishable = 0; if (ret == DR_NOT_EMPTY || ret == DR_AT_LIMIT) goto check_ret; /* OK: try to delete the directory. */ } if (!(flags & DEL_MAKE_ROOM) && max_delete >= 0 && stats.deleted_files >= max_delete) { skipped_deletes++; return DR_AT_LIMIT; } if (S_ISDIR(mode)) { what = "rmdir"; ok = do_rmdir(fbuf) == 0; } else { if (make_backups > 0 && !(flags & DEL_FOR_BACKUP) && (backup_dir || !is_backup_file(fbuf))) { what = "make_backup"; ok = make_backup(fbuf, True); if (ok == 2) { what = "unlink"; ok = robust_unlink(fbuf) == 0; } } else { what = "unlink"; ok = robust_unlink(fbuf) == 0; } } if (ok) { if (!(flags & DEL_MAKE_ROOM)) { log_delete(fbuf, mode); stats.deleted_files++; if (S_ISREG(mode)) { /* Nothing more to count */ } else if (S_ISDIR(mode)) stats.deleted_dirs++; #ifdef SUPPORT_LINKS else if (S_ISLNK(mode)) stats.deleted_symlinks++; #endif else if (IS_DEVICE(mode)) stats.deleted_symlinks++; else stats.deleted_specials++; } ret = DR_SUCCESS; } else { if (S_ISDIR(mode) && errno == ENOTEMPTY) { rprintf(FINFO, "cannot delete non-empty directory: %s\n", fbuf); ret = DR_NOT_EMPTY; } else if (errno != ENOENT) { rsyserr(FERROR, errno, "delete_file: %s(%s) failed", what, fbuf); ret = DR_FAILURE; } else ret = DR_SUCCESS; } check_ret: if (ret != DR_SUCCESS && flags & DEL_MAKE_ROOM) { const char *desc; switch (flags & DEL_MAKE_ROOM) { case DEL_FOR_FILE: desc = "regular file"; break; case DEL_FOR_DIR: desc = "directory"; break; case DEL_FOR_SYMLINK: desc = "symlink"; break; case DEL_FOR_DEVICE: desc = "device file"; break; case DEL_FOR_SPECIAL: desc = "special file"; break; default: exit_cleanup(RERR_UNSUPPORTED); /* IMPOSSIBLE */ } rprintf(FERROR_XFER, "could not make way for %s %s: %s\n", flags & DEL_FOR_BACKUP ? "backup" : "new", desc, fbuf); } return ret; }
void setup_iconv(void) { const char *defset = default_charset(); # ifdef ICONV_OPTION const char *charset; char *cp; # endif if (!am_server && !allow_8bit_chars) { /* It's OK if this fails... */ ic_chck = iconv_open(defset, defset); if (DEBUG_GTE(ICONV, 2)) { if (ic_chck == (iconv_t)-1) { rprintf(FINFO, "msg checking via isprint()" " (iconv_open(\"%s\", \"%s\") errno: %d)\n", defset, defset, errno); } else { rprintf(FINFO, "msg checking charset: %s\n", defset); } } } else ic_chck = (iconv_t)-1; # ifdef ICONV_OPTION if (!iconv_opt) return; if ((cp = strchr(iconv_opt, ',')) != NULL) { if (am_server) /* A local transfer needs this. */ iconv_opt = cp + 1; else *cp = '\0'; } if (!*iconv_opt || (*iconv_opt == '.' && iconv_opt[1] == '\0')) charset = defset; else charset = iconv_opt; if ((ic_send = iconv_open(UTF8_CHARSET, charset)) == (iconv_t)-1) { rprintf(FERROR, "iconv_open(\"%s\", \"%s\") failed\n", UTF8_CHARSET, charset); exit_cleanup(RERR_UNSUPPORTED); } if ((ic_recv = iconv_open(charset, UTF8_CHARSET)) == (iconv_t)-1) { rprintf(FERROR, "iconv_open(\"%s\", \"%s\") failed\n", charset, UTF8_CHARSET); exit_cleanup(RERR_UNSUPPORTED); } if (DEBUG_GTE(ICONV, 1)) { rprintf(FINFO, "[%s] charset: %s\n", who_am_i(), *charset ? charset : "[LOCALE]"); } # endif }
int read_ndx_and_attrs(int f_in, int f_out, int *iflag_ptr, uchar *type_ptr, char *buf, int *len_ptr) { int len, iflags = 0; struct file_list *flist; uchar fnamecmp_type = FNAMECMP_FNAME; int ndx; read_loop: while (1) { ndx = read_ndx(f_in); if (ndx >= 0) break; if (ndx == NDX_DONE) return ndx; if (ndx == NDX_DEL_STATS) { read_del_stats(f_in); if (am_sender && am_server) write_del_stats(f_out); continue; } if (!inc_recurse || am_sender) { int last; if (first_flist) last = first_flist->prev->ndx_start + first_flist->prev->used - 1; else last = -1; rprintf(FERROR, "Invalid file index: %d (%d - %d) [%s]\n", ndx, NDX_DONE, last, who_am_i()); exit_cleanup(RERR_PROTOCOL); } if (ndx == NDX_FLIST_EOF) { flist_eof = 1; if (DEBUG_GTE(FLIST, 3)) rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i()); write_int(f_out, NDX_FLIST_EOF); continue; } ndx = NDX_FLIST_OFFSET - ndx; if (ndx < 0 || ndx >= dir_flist->used) { ndx = NDX_FLIST_OFFSET - ndx; rprintf(FERROR, "Invalid dir index: %d (%d - %d) [%s]\n", ndx, NDX_FLIST_OFFSET, NDX_FLIST_OFFSET - dir_flist->used + 1, who_am_i()); exit_cleanup(RERR_PROTOCOL); } if (DEBUG_GTE(FLIST, 2)) { rprintf(FINFO, "[%s] receiving flist for dir %d\n", who_am_i(), ndx); } /* Send all the data we read for this flist to the generator. */ start_flist_forward(ndx); flist = recv_file_list(f_in); flist->parent_ndx = ndx; stop_flist_forward(); } iflags = protocol_version >= 29 ? read_shortint(f_in) : ITEM_TRANSFER | ITEM_MISSING_DATA; /* Support the protocol-29 keep-alive style. */ if (protocol_version < 30 && ndx == cur_flist->used && iflags == ITEM_IS_NEW) { if (am_sender) maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH); goto read_loop; } flist = flist_for_ndx(ndx, "read_ndx_and_attrs"); if (flist != cur_flist) { cur_flist = flist; if (am_sender) { file_old_total = cur_flist->used; for (flist = first_flist; flist != cur_flist; flist = flist->next) file_old_total += flist->used; } } if (iflags & ITEM_BASIS_TYPE_FOLLOWS) fnamecmp_type = read_byte(f_in); *type_ptr = fnamecmp_type; if (iflags & ITEM_XNAME_FOLLOWS) { if ((len = read_vstring(f_in, buf, MAXPATHLEN)) < 0) exit_cleanup(RERR_PROTOCOL); } else { *buf = '\0'; len = -1; } *len_ptr = len; if (iflags & ITEM_TRANSFER) { int i = ndx - cur_flist->ndx_start; if (i < 0 || !S_ISREG(cur_flist->files[i]->mode)) { rprintf(FERROR, "received request to transfer non-regular file: %d [%s]\n", ndx, who_am_i()); exit_cleanup(RERR_PROTOCOL); } } *iflag_ptr = iflags; return ndx; }
/* Hard-link, rename, or copy an item to the backup name. Returns 0 for * failure, 1 if item was moved, 2 if item was duplicated or hard linked * into backup area, or 3 if item doesn't exist or isn't a regular file. */ int make_backup(const char *fname, BOOL prefer_rename) { stat_x sx; struct file_struct *file; int save_preserve_xattrs; char *buf; int ret = 0; init_stat_x(&sx); /* Return success if no file to keep. */ if (x_lstat(fname, &sx.st, NULL) < 0) return 3; if (!(buf = get_backup_name(fname))) return 0; /* Try a hard-link or a rename first. Using rename is not atomic, but * is more efficient than forcing a copy for larger files when no hard- * linking is possible. */ if ((ret = link_or_rename(fname, buf, prefer_rename, &sx.st)) != 0) goto success; if (errno == EEXIST || errno == EISDIR) { STRUCT_STAT bakst; if (do_lstat(buf, &bakst) == 0) { int flags = get_del_for_flag(bakst.st_mode) | DEL_FOR_BACKUP | DEL_RECURSE; if (delete_item(buf, bakst.st_mode, flags) != 0) return 0; } if ((ret = link_or_rename(fname, buf, prefer_rename, &sx.st)) != 0) goto success; } /* Fall back to making a copy. */ if (!(file = make_file(fname, NULL, &sx.st, 0, NO_FILTERS))) return 3; /* the file could have disappeared */ #ifdef SUPPORT_ACLS if (preserve_acls && !S_ISLNK(file->mode)) { get_acl(fname, &sx); cache_tmp_acl(file, &sx); free_acl(&sx); } #endif #ifdef SUPPORT_XATTRS if (preserve_xattrs) { get_xattr(fname, &sx); cache_tmp_xattr(file, &sx); free_xattr(&sx); } #endif /* Check to see if this is a device file, or link */ if ((am_root && preserve_devices && IS_DEVICE(file->mode)) || (preserve_specials && IS_SPECIAL(file->mode))) { if (do_mknod(buf, file->mode, sx.st.st_rdev) < 0) rsyserr(FERROR, errno, "mknod %s failed", full_fname(buf)); else if (DEBUG_GTE(BACKUP, 1)) rprintf(FINFO, "make_backup: DEVICE %s successful.\n", fname); ret = 2; } #ifdef SUPPORT_LINKS if (!ret && preserve_links && S_ISLNK(file->mode)) { const char *sl = F_SYMLINK(file); if (safe_symlinks && unsafe_symlink(sl, fname)) { if (INFO_GTE(SYMSAFE, 1)) { rprintf(FINFO, "not backing up unsafe symlink \"%s\" -> \"%s\"\n", fname, sl); } ret = 2; } else { if (do_symlink(sl, buf) < 0) rsyserr(FERROR, errno, "link %s -> \"%s\"", full_fname(buf), sl); else if (DEBUG_GTE(BACKUP, 1)) rprintf(FINFO, "make_backup: SYMLINK %s successful.\n", fname); ret = 2; } } #endif if (!ret && !S_ISREG(file->mode)) { rprintf(FINFO, "make_bak: skipping non-regular file %s\n", fname); unmake_file(file); #ifdef SUPPORT_ACLS uncache_tmp_acls(); #endif #ifdef SUPPORT_XATTRS uncache_tmp_xattrs(); #endif return 3; } /* Copy to backup tree if a file. */ if (!ret) { if (copy_file(fname, buf, -1, file->mode) < 0) { rsyserr(FERROR, errno, "keep_backup failed: %s -> \"%s\"", full_fname(fname), buf); unmake_file(file); #ifdef SUPPORT_ACLS uncache_tmp_acls(); #endif #ifdef SUPPORT_XATTRS uncache_tmp_xattrs(); #endif return 0; } if (DEBUG_GTE(BACKUP, 1)) rprintf(FINFO, "make_backup: COPY %s successful.\n", fname); ret = 2; } save_preserve_xattrs = preserve_xattrs; preserve_xattrs = 0; set_file_attrs(buf, file, NULL, fname, ATTRS_SET_NANO); preserve_xattrs = save_preserve_xattrs; unmake_file(file); #ifdef SUPPORT_ACLS uncache_tmp_acls(); #endif #ifdef SUPPORT_XATTRS uncache_tmp_xattrs(); #endif success: if (INFO_GTE(BACKUP, 1)) rprintf(FINFO, "backed up %s to %s\n", fname, buf); return ret; }