/* Try to open a keymap with fopen() */ FILE * xkeymap_open(const char *filename) { char *path1, *path2; char *home; FILE *fp; /* Try ~/.rdesktop/keymaps */ home = getenv("HOME"); if (home) { path1 = pathjoin(home, ".rdesktop/keymaps"); path2 = pathjoin(path1, filename); xfree(path1); fp = fopen(path2, "r"); xfree(path2); if (fp) return fp; } /* Try KEYMAP_PATH */ path1 = pathjoin(KEYMAP_PATH, filename); fp = fopen(path1, "r"); xfree(path1); if (fp) return fp; /* Try current directory, in case we are running from the source tree */ path1 = pathjoin("keymaps", filename); fp = fopen(path1, "r"); xfree(path1); if (fp) return fp; return NULL; }
int chat_register(const char *hostname, const char *username) { char *passwd = chat_register_rpc(hostname, username); if (strlen(passwd) == 0) { fprintf(stderr, "Register failed\n"); exit(EXIT_SUCCESS); } char *workdir = getworkdir(); char *passwdfile = pathjoin(workdir, PASSWDFILE); free(workdir); FILE *fpasswd = fopen(passwdfile, "w"); fprintf(fpasswd, "%s\n", passwd); fclose(fpasswd); chmod(passwdfile, 0600); fprintf(stderr, "%s\n", passwd); return 0; }
sqlite3 * getdb () { char *workdir = getworkdir(); char *dbfile = pathjoin(workdir, DBFILE); free(workdir); int notfound = 0; int rc; struct stat st; rc = stat(dbfile, &st); if (rc == -1) { if (errno == ENOENT) { notfound = 1; } else { fprintf(stderr, "Error stating %s: %d\n", dbfile, errno); exit(EXIT_FAILURE); } } sqlite3 *db; rc = sqlite3_open(dbfile, &db); if (rc != SQLITE_OK) { fprintf(stderr, "Can't open database %s.\n", sqlite3_errmsg(db)); sqlite3_close(db); exit(EXIT_FAILURE); } if (notfound) { fprintf(stderr, "Initializing database.\n"); initdb(db); } return db; }
/* A generic logging routine for send/recv, with parameter substitiution. */ static void log_formatted(enum logcode code, const char *format, const char *op, struct file_struct *file, const char *fname, int iflags, const char *hlink) { char buf[MAXPATHLEN+1024], buf2[MAXPATHLEN], fmt[32]; char *p, *s, *c; const char *n; size_t len, total; int64 b; *fmt = '%'; /* We expand % codes one by one in place in buf. We don't * copy in the terminating null of the inserted strings, but * rather keep going until we reach the null of the format. */ total = strlcpy(buf, format, sizeof buf); if (total > MAXPATHLEN) { rprintf(FERROR, "log-format string is WAY too long!\n"); exit_cleanup(RERR_MESSAGEIO); } buf[total++] = '\n'; buf[total] = '\0'; for (p = buf; (p = strchr(p, '%')) != NULL; ) { int humanize = 0; s = p++; c = fmt + 1; while (*p == '\'') { humanize++; p++; } if (*p == '-') *c++ = *p++; while (isDigit(p) && c - fmt < (int)(sizeof fmt) - 8) *c++ = *p++; while (*p == '\'') { humanize++; p++; } if (!*p) break; *c = '\0'; n = NULL; /* Note for %h and %a: it doesn't matter what fd we pass to * client_{name,addr} because rsync_module will already have * forced the answer to be cached (assuming, of course, for %h * that lp_reverse_lookup(module_id) is true). */ switch (*p) { case 'h': if (am_daemon) { n = lp_reverse_lookup(module_id) ? client_name(0) : undetermined_hostname; } break; case 'a': if (am_daemon) n = client_addr(0); break; case 'l': strlcat(fmt, "s", sizeof fmt); snprintf(buf2, sizeof buf2, fmt, do_big_num(F_LENGTH(file), humanize, NULL)); n = buf2; break; case 'U': strlcat(fmt, "u", sizeof fmt); snprintf(buf2, sizeof buf2, fmt, uid_ndx ? F_OWNER(file) : 0); n = buf2; break; case 'G': if (!gid_ndx || file->flags & FLAG_SKIP_GROUP) n = "DEFAULT"; else { strlcat(fmt, "u", sizeof fmt); snprintf(buf2, sizeof buf2, fmt, F_GROUP(file)); n = buf2; } break; case 'p': strlcat(fmt, "d", sizeof fmt); snprintf(buf2, sizeof buf2, fmt, (int)getpid()); n = buf2; break; case 'M': n = c = timestring(file->modtime); while ((c = strchr(c, ' ')) != NULL) *c = '-'; break; case 'B': c = buf2 + MAXPATHLEN - PERMSTRING_SIZE - 1; permstring(c, file->mode); n = c + 1; /* skip the type char */ break; case 'o': n = op; break; case 'f': if (fname) { c = f_name_buf(); strlcpy(c, fname, MAXPATHLEN); } else c = f_name(file, NULL); if (am_sender && F_PATHNAME(file)) { pathjoin(buf2, sizeof buf2, F_PATHNAME(file), c); clean_fname(buf2, 0); if (fmt[1]) { strlcpy(c, buf2, MAXPATHLEN); n = c; } else n = buf2; } else if (am_daemon && *c != '/') { pathjoin(buf2, sizeof buf2, curr_dir + module_dirlen, c); clean_fname(buf2, 0); if (fmt[1]) { strlcpy(c, buf2, MAXPATHLEN); n = c; } else n = buf2; } else { clean_fname(c, 0); n = c; } if (*n == '/') n++; break; case 'n': if (fname) { c = f_name_buf(); strlcpy(c, fname, MAXPATHLEN); } else c = f_name(file, NULL); if (S_ISDIR(file->mode)) strlcat(c, "/", MAXPATHLEN); n = c; break; case 'L': if (hlink && *hlink) { n = hlink; strlcpy(buf2, " => ", sizeof buf2); } else if (S_ISLNK(file->mode) && !fname) { n = F_SYMLINK(file); strlcpy(buf2, " -> ", sizeof buf2); } else { n = ""; if (!fmt[1]) break; strlcpy(buf2, " ", sizeof buf2); } strlcat(fmt, "s", sizeof fmt); snprintf(buf2 + 4, sizeof buf2 - 4, fmt, n); n = buf2; break; case 'm': n = lp_name(module_id); break; case 't': n = timestring(time(NULL)); break; case 'P': n = full_module_path; break; case 'u': n = auth_user; break; case 'b': case 'c': if (!(iflags & ITEM_TRANSFER)) b = 0; else if ((!!am_sender) ^ (*p == 'c')) b = total_data_written - initial_data_written; else b = total_data_read - initial_data_read; strlcat(fmt, "s", sizeof fmt); snprintf(buf2, sizeof buf2, fmt, do_big_num(b, humanize, NULL)); n = buf2; break; case 'C': n = NULL; if (S_ISREG(file->mode)) { if (always_checksum && canonical_checksum(checksum_type)) n = sum_as_hex(checksum_type, F_SUM(file)); else if (iflags & ITEM_TRANSFER && canonical_checksum(xfersum_type)) n = sum_as_hex(xfersum_type, sender_file_sum); } if (!n) { int checksum_len = csum_len_for_type(always_checksum ? checksum_type : xfersum_type); memset(buf2, ' ', checksum_len*2); buf2[checksum_len*2] = '\0'; n = buf2; } break; case 'i': if (iflags & ITEM_DELETED) { n = "*deleting "; break; } n = c = buf2 + MAXPATHLEN - 32; c[0] = iflags & ITEM_LOCAL_CHANGE ? iflags & ITEM_XNAME_FOLLOWS ? 'h' : 'c' : !(iflags & ITEM_TRANSFER) ? '.' : !local_server && *op == 's' ? '<' : '>'; if (S_ISLNK(file->mode)) { c[1] = 'L'; c[3] = '.'; c[4] = !(iflags & ITEM_REPORT_TIME) ? '.' : !preserve_times || !receiver_symlink_times || (iflags & ITEM_REPORT_TIMEFAIL) ? 'T' : 't'; } else { c[1] = S_ISDIR(file->mode) ? 'd' : IS_SPECIAL(file->mode) ? 'S' : IS_DEVICE(file->mode) ? 'D' : 'f'; c[3] = !(iflags & ITEM_REPORT_SIZE) ? '.' : 's'; c[4] = !(iflags & ITEM_REPORT_TIME) ? '.' : !preserve_times ? 'T' : 't'; } c[2] = !(iflags & ITEM_REPORT_CHANGE) ? '.' : 'c'; c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p'; c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o'; c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g'; c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.' : 'u'; c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a'; c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x'; c[11] = '\0'; if (iflags & (ITEM_IS_NEW|ITEM_MISSING_DATA)) { char ch = iflags & ITEM_IS_NEW ? '+' : '?'; int i; for (i = 2; c[i]; i++) c[i] = ch; } else if (c[0] == '.' || c[0] == 'h' || c[0] == 'c') { int i; for (i = 2; c[i]; i++) { if (c[i] != '.') break; } if (!c[i]) { for (i = 2; c[i]; i++) c[i] = ' '; } } break; } /* "n" is the string to be inserted in place of this % code. */ if (!n) continue; if (n != buf2 && fmt[1]) { strlcat(fmt, "s", sizeof fmt); snprintf(buf2, sizeof buf2, fmt, n); n = buf2; } len = strlen(n); /* Subtract the length of the escape from the string's size. */ total -= p - s + 1; if (len + total >= (size_t)sizeof buf) { rprintf(FERROR, "buffer overflow expanding %%%c -- exiting\n", p[0]); exit_cleanup(RERR_MESSAGEIO); } /* Shuffle the rest of the string along to make space for n */ if (len != (size_t)(p - s + 1)) memmove(s + len, p + 1, total - (s - buf) + 1); total += len; /* Insert the contents of string "n", but NOT its null. */ if (len) memcpy(s, n, len); /* Skip over inserted string; continue looking */ p = s + len; } rwrite(code, buf, total, 0); }
/** * main routine for receiver process. * * Receiver process runs on the same host as the generator process. */ int recv_files(int f_in, char *local_name) { int fd1,fd2; STRUCT_STAT st; int iflags, xlen; char *fname, fbuf[MAXPATHLEN]; char xname[MAXPATHLEN]; char fnametmp[MAXPATHLEN]; char *fnamecmp, *partialptr; char fnamecmpbuf[MAXPATHLEN]; uchar fnamecmp_type; struct file_struct *file; struct stats initial_stats; int itemizing = am_server ? logfile_format_has_i : stdout_format_has_i; enum logcode log_code = log_before_transfer ? FLOG : FINFO; int max_phase = protocol_version >= 29 ? 2 : 1; int dflt_perms = (ACCESSPERMS & ~orig_umask); #ifdef SUPPORT_ACLS const char *parent_dirname = ""; #endif int ndx, recv_ok; if (verbose > 2) rprintf(FINFO, "recv_files(%d) starting\n", cur_flist->used); if (delay_updates) delayed_bits = bitbag_create(cur_flist->used + 1); while (1) { cleanup_disable(); /* This call also sets cur_flist. */ ndx = read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type, xname, &xlen); if (ndx == NDX_DONE) { if (inc_recurse && first_flist) { if (read_batch) gen_wants_ndx(first_flist->used + first_flist->ndx_start); flist_free(first_flist); if (first_flist) continue; } else if (read_batch && first_flist) gen_wants_ndx(first_flist->used); if (++phase > max_phase) break; if (verbose > 2) rprintf(FINFO, "recv_files phase=%d\n", phase); if (phase == 2 && delay_updates) handle_delayed_updates(local_name); send_msg(MSG_DONE, "", 0, 0); continue; } if (ndx - cur_flist->ndx_start >= 0) file = cur_flist->files[ndx - cur_flist->ndx_start]; else file = dir_flist->files[cur_flist->parent_ndx]; fname = local_name ? local_name : f_name(file, fbuf); if (verbose > 2) rprintf(FINFO, "recv_files(%s)\n", fname); #ifdef SUPPORT_XATTRS if (iflags & ITEM_REPORT_XATTR && !dry_run) recv_xattr_request(file, f_in); #endif if (!(iflags & ITEM_TRANSFER)) { maybe_log_item(file, iflags, itemizing, xname); #ifdef SUPPORT_XATTRS if (preserve_xattrs && iflags & ITEM_REPORT_XATTR && !dry_run) set_file_attrs(fname, file, NULL, fname, 0); #endif continue; } if (phase == 2) { rprintf(FERROR, "got transfer request in phase 2 [%s]\n", who_am_i()); exit_cleanup(RERR_PROTOCOL); } if (file->flags & FLAG_FILE_SENT) { if (csum_length == SHORT_SUM_LENGTH) { if (keep_partial && !partial_dir) make_backups = -make_backups; /* prevents double backup */ if (append_mode) sparse_files = -sparse_files; append_mode = -append_mode; csum_length = SUM_LENGTH; redoing = 1; } } else { if (csum_length != SHORT_SUM_LENGTH) { if (keep_partial && !partial_dir) make_backups = -make_backups; if (append_mode) sparse_files = -sparse_files; append_mode = -append_mode; csum_length = SHORT_SUM_LENGTH; redoing = 0; } } if (!am_server && do_progress) set_current_file_index(file, ndx); stats.num_transferred_files++; stats.total_transferred_size += F_LENGTH(file); cleanup_got_literal = 0; if (daemon_filter_list.head && check_filter(&daemon_filter_list, FLOG, fname, 0) < 0) { rprintf(FERROR, "attempt to hack rsync failed.\n"); exit_cleanup(RERR_PROTOCOL); } if (!do_xfers) { /* log the transfer */ log_item(FCLIENT, file, &stats, iflags, NULL); if (read_batch) discard_receive_data(f_in, F_LENGTH(file)); continue; } if (write_batch < 0) { log_item(FCLIENT, file, &stats, iflags, NULL); if (!am_server) discard_receive_data(f_in, F_LENGTH(file)); continue; } if (read_batch) { if (!(redoing ? we_want_redo(ndx) : gen_wants_ndx(ndx))) { rprintf(FINFO, "(Skipping batched update for%s \"%s\")\n", redoing ? " resend of" : "", fname); discard_receive_data(f_in, F_LENGTH(file)); file->flags |= FLAG_FILE_SENT; continue; } } partialptr = partial_dir ? partial_dir_fname(fname) : fname; if (protocol_version >= 29) { switch (fnamecmp_type) { case FNAMECMP_FNAME: fnamecmp = fname; break; case FNAMECMP_PARTIAL_DIR: fnamecmp = partialptr; break; case FNAMECMP_BACKUP: fnamecmp = get_backup_name(fname); break; case FNAMECMP_FUZZY: if (file->dirname) { pathjoin(fnamecmpbuf, MAXPATHLEN, file->dirname, xname); fnamecmp = fnamecmpbuf; } else fnamecmp = xname; break; default: if (fnamecmp_type >= basis_dir_cnt) { rprintf(FERROR, "invalid basis_dir index: %d.\n", fnamecmp_type); exit_cleanup(RERR_PROTOCOL); } pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], fname); fnamecmp = fnamecmpbuf; break; } if (!fnamecmp || (daemon_filter_list.head && check_filter(&daemon_filter_list, FLOG, fname, 0) < 0)) { fnamecmp = fname; fnamecmp_type = FNAMECMP_FNAME; } } else { /* Reminder: --inplace && --partial-dir are never * enabled at the same time. */ if (inplace && make_backups > 0) { if (!(fnamecmp = get_backup_name(fname))) fnamecmp = fname; else fnamecmp_type = FNAMECMP_BACKUP; } else if (partial_dir && partialptr) fnamecmp = partialptr; else fnamecmp = fname; } initial_stats = stats; /* open the file */ fd1 = do_open(fnamecmp, O_RDONLY, 0); if (fd1 == -1 && protocol_version < 29) { if (fnamecmp != fname) { fnamecmp = fname; fd1 = do_open(fnamecmp, O_RDONLY, 0); } if (fd1 == -1 && basis_dir[0]) { /* pre-29 allowed only one alternate basis */ pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[0], fname); fnamecmp = fnamecmpbuf; fd1 = do_open(fnamecmp, O_RDONLY, 0); } } updating_basis_or_equiv = inplace && (fnamecmp == fname || fnamecmp_type == FNAMECMP_BACKUP); if (fd1 == -1) { st.st_mode = 0; st.st_size = 0; } else if (do_fstat(fd1,&st) != 0) { rsyserr(FERROR_XFER, errno, "fstat %s failed", full_fname(fnamecmp)); discard_receive_data(f_in, F_LENGTH(file)); close(fd1); if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); continue; } if (fd1 != -1 && S_ISDIR(st.st_mode) && fnamecmp == fname) { /* this special handling for directories * wouldn't be necessary if robust_rename() * and the underlying robust_unlink could cope * with directories */ rprintf(FERROR_XFER, "recv_files: %s is a directory\n", full_fname(fnamecmp)); discard_receive_data(f_in, F_LENGTH(file)); close(fd1); if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); continue; } if (fd1 != -1 && !S_ISREG(st.st_mode)) { close(fd1); fd1 = -1; } /* If we're not preserving permissions, change the file-list's * mode based on the local permissions and some heuristics. */ if (!preserve_perms) { int exists = fd1 != -1; #ifdef SUPPORT_ACLS const char *dn = file->dirname ? file->dirname : "."; if (parent_dirname != dn && strcmp(parent_dirname, dn) != 0) { dflt_perms = default_perms_for_dir(dn); parent_dirname = dn; } #endif file->mode = dest_mode(file->mode, st.st_mode, dflt_perms, exists); } /* We now check to see if we are writing the file "inplace" */ if (inplace) { fd2 = do_open(fname, O_WRONLY|O_CREAT, 0600); if (fd2 == -1) { rsyserr(FERROR_XFER, errno, "open %s failed", full_fname(fname)); } } else { fd2 = open_tmpfile(fnametmp, fname, file); if (fd2 != -1) cleanup_set(fnametmp, partialptr, file, fd1, fd2); } if (fd2 == -1) { discard_receive_data(f_in, F_LENGTH(file)); if (fd1 != -1) close(fd1); if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); continue; } /* log the transfer */ if (log_before_transfer) log_item(FCLIENT, file, &initial_stats, iflags, NULL); else if (!am_server && verbose && do_progress) rprintf(FINFO, "%s\n", fname); /* recv file data */ recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size, fname, fd2, F_LENGTH(file)); log_item(log_code, file, &initial_stats, iflags, NULL); if (fd1 != -1) close(fd1); if (close(fd2) < 0) { rsyserr(FERROR, errno, "close failed on %s", full_fname(fnametmp)); exit_cleanup(RERR_FILEIO); } if ((recv_ok && (!delay_updates || !partialptr)) || inplace) { if (partialptr == fname) partialptr = NULL; if (!finish_transfer(fname, fnametmp, fnamecmp, partialptr, file, recv_ok, 1)) recv_ok = -1; else if (fnamecmp == partialptr) { do_unlink(partialptr); handle_partial_dir(partialptr, PDIR_DELETE); } } else if (keep_partial && partialptr) { if (!handle_partial_dir(partialptr, PDIR_CREATE)) { rprintf(FERROR, "Unable to create partial-dir for %s -- discarding %s.\n", local_name ? local_name : f_name(file, NULL), recv_ok ? "completed file" : "partial file"); do_unlink(fnametmp); recv_ok = -1; } else if (!finish_transfer(partialptr, fnametmp, fnamecmp, NULL, file, recv_ok, !partial_dir)) recv_ok = -1; else if (delay_updates && recv_ok) { bitbag_set_bit(delayed_bits, ndx); recv_ok = 2; } else partialptr = NULL; } else do_unlink(fnametmp); cleanup_disable(); if (read_batch) file->flags |= FLAG_FILE_SENT; switch (recv_ok) { case 2: break; case 1: if (remove_source_files || inc_recurse || (preserve_hard_links && F_IS_HLINKED(file))) send_msg_int(MSG_SUCCESS, ndx); break; case 0: { enum logcode msgtype = redoing ? FERROR_XFER : FWARNING; if (msgtype == FERROR_XFER || verbose) { char *errstr, *redostr, *keptstr; if (!(keep_partial && partialptr) && !inplace) keptstr = "discarded"; else if (partial_dir) keptstr = "put into partial-dir"; else keptstr = "retained"; if (msgtype == FERROR_XFER) { errstr = "ERROR"; redostr = ""; } else { errstr = "WARNING"; redostr = read_batch ? " (may try again)" : " (will try again)"; } rprintf(msgtype, "%s: %s failed verification -- update %s%s.\n", errstr, local_name ? f_name(file, NULL) : fname, keptstr, redostr); } if (!redoing) { if (read_batch) flist_ndx_push(&batch_redo_list, ndx); send_msg_int(MSG_REDO, ndx); file->flags |= FLAG_FILE_SENT; } else if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); break; } case -1: if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); break; } } if (make_backups < 0) make_backups = -make_backups; if (phase == 2 && delay_updates) /* for protocol_version < 29 */ handle_delayed_updates(local_name); if (verbose > 2) rprintf(FINFO,"recv_files finished\n"); return 0; }
/** * main routine for receiver process. * * Receiver process runs on the same host as the generator process. */ int recv_files(int f_in, char *local_name) { int fd1,fd2; STRUCT_STAT st; int iflags, xlen; char *fname, fbuf[MAXPATHLEN]; char xname[MAXPATHLEN]; char fnametmp[MAXPATHLEN]; char *fnamecmp, *partialptr; char fnamecmpbuf[MAXPATHLEN]; uchar fnamecmp_type; struct file_struct *file; struct stats initial_stats; int itemizing = am_server ? logfile_format_has_i : stdout_format_has_i; enum logcode log_code = log_before_transfer ? FLOG : FINFO; int max_phase = protocol_version >= 29 ? 2 : 1; int dflt_perms = (ACCESSPERMS & ~orig_umask); #if 0 /* was SUPPORT_ACLS */ const char *parent_dirname = ""; #endif int ndx, recv_ok; if (verbose > 2) rprintf(FINFO, "recv_files(%d) starting\n", cur_flist->used); if (delay_updates) delayed_bits = bitbag_create(cur_flist->used + 1); while (1) { struct sum_struct sum; int numMatchTokens = -1, nextToken = 0; char *nextData = NULL; static char file_sum[MAX_DIGEST_LEN]; cleanup_disable(); /* This call also sets cur_flist. */ ndx = read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type, xname, &xlen); if (ndx == NDX_DONE) { if (inc_recurse && first_flist) { if (read_batch) gen_wants_ndx(first_flist->used + first_flist->ndx_start); flist_free(first_flist); if (first_flist) continue; } else if (read_batch && first_flist) gen_wants_ndx(first_flist->used); if (++phase > max_phase) break; if (verbose > 2) rprintf(FINFO, "recv_files phase=%d\n", phase); if (phase == 2 && delay_updates) handle_delayed_updates(local_name); send_msg(MSG_DONE, "", 0, 0); continue; } if (ndx - cur_flist->ndx_start >= 0) file = cur_flist->files[ndx - cur_flist->ndx_start]; else file = dir_flist->files[cur_flist->parent_ndx]; fname = local_name ? local_name : f_name(file, fbuf); if (verbose > 2) rprintf(FINFO, "recv_files(%s)\n", fname); #ifdef SUPPORT_XATTRS if (preserve_xattrs && iflags & ITEM_REPORT_XATTR && do_xfers) recv_xattr_request(file, f_in); #endif if (!(iflags & ITEM_TRANSFER)) { maybe_log_item(file, iflags, itemizing, xname); #ifdef SUPPORT_XATTRS if (preserve_xattrs && iflags & ITEM_REPORT_XATTR && do_xfers && !BITS_SET(iflags, ITEM_XNAME_FOLLOWS|ITEM_LOCAL_CHANGE)) set_file_attrs(fname, file, NULL, fname, 0); #endif continue; } if (phase == 2) { rprintf(FERROR, "got transfer request in phase 2 [%s]\n", who_am_i()); exit_cleanup(RERR_PROTOCOL); } if (file->flags & FLAG_FILE_SENT) { if (csum_length == SHORT_SUM_LENGTH) { if (keep_partial && !partial_dir) make_backups = -make_backups; /* prevents double backup */ if (append_mode) sparse_files = -sparse_files; append_mode = -append_mode; csum_length = SUM_LENGTH; redoing = 1; } } else { if (csum_length != SHORT_SUM_LENGTH) { if (keep_partial && !partial_dir) make_backups = -make_backups; if (append_mode) sparse_files = -sparse_files; append_mode = -append_mode; csum_length = SHORT_SUM_LENGTH; redoing = 0; } } if (!am_server && do_progress) set_current_file_index(file, ndx); stats.num_transferred_files++; stats.total_transferred_size += F_LENGTH(file); cleanup_got_literal = 0; if (daemon_filter_list.head && check_filter(&daemon_filter_list, FLOG, fname, 0) < 0) { rprintf(FERROR, "attempt to hack rsync failed.\n"); exit_cleanup(RERR_PROTOCOL); } read_sum_head(f_in, &sum); if (read_batch) { int wanted = redoing ? we_want_redo(ndx) : gen_wants_ndx(ndx); if (!wanted) { rprintf(FINFO, "(Skipping batched update for%s \"%s\")\n", redoing ? " resend of" : "", fname); discard_receive_data(f_in, F_LENGTH(file), &sum, numMatchTokens, nextToken, nextData, file_sum); file->flags |= FLAG_FILE_SENT; continue; } } if (!do_xfers) { /* log the transfer */ log_item(FCLIENT, file, &stats, iflags, NULL); if (read_batch) discard_receive_data(f_in, F_LENGTH(file), &sum, numMatchTokens, nextToken, nextData, file_sum); continue; } if (write_batch < 0) { log_item(FCLIENT, file, &stats, iflags, NULL); if (!am_server) discard_receive_data(f_in, F_LENGTH(file), &sum, numMatchTokens, nextToken, nextData, file_sum); continue; } partialptr = partial_dir ? partial_dir_fname(fname) : fname; if (protocol_version >= 29) { switch (fnamecmp_type) { case FNAMECMP_FNAME: fnamecmp = fname; break; case FNAMECMP_PARTIAL_DIR: fnamecmp = partialptr; break; case FNAMECMP_BACKUP: fnamecmp = get_backup_name(fname); break; case FNAMECMP_FUZZY: if (file->dirname) { pathjoin(fnamecmpbuf, MAXPATHLEN, file->dirname, xname); fnamecmp = fnamecmpbuf; } else fnamecmp = xname; break; default: if (fnamecmp_type >= basis_dir_cnt) { rprintf(FERROR, "invalid basis_dir index: %d.\n", fnamecmp_type); exit_cleanup(RERR_PROTOCOL); } pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], fname); fnamecmp = fnamecmpbuf; break; } if (!fnamecmp || (daemon_filter_list.head && check_filter(&daemon_filter_list, FLOG, fname, 0) < 0)) { fnamecmp = fname; fnamecmp_type = FNAMECMP_FNAME; } } else { /* Reminder: --inplace && --partial-dir are never * enabled at the same time. */ if (inplace && make_backups > 0) { if (!(fnamecmp = get_backup_name(fname))) fnamecmp = fname; else fnamecmp_type = FNAMECMP_BACKUP; } else if (partial_dir && partialptr) fnamecmp = partialptr; else fnamecmp = fname; } initial_stats = stats; /* * Opening/reading/writing files in BackupPC is quite expensive, given the * compression. Before we open the file and call mkstemp, we peek ahead at * the delta tokens to see if the file is unchanged. If so, we skip the file * open, mkstemp and writing. The file is unchanged if the tokens * are sequential matching block numbers, which are encoded as * negative integers (ie: -1, -2, -3...), with no literal data * tokens (which have positive numbers). * * If the file has changed, at some point a matching block token * will be out of order, or we will encounter a literal data token. * At that point, we stop reading ahead and proceed as normal. * We pass the number of matching block tokens, and the first * unexpected token, to receive_data(), so it can process all * the tokens back (basically playing them back). * * Of course, this only applies if the file exists already, * and protocol_version 30 is required since older protocols * use MD4 for the full-file digest, not MD5. * * First we need to read the sum struct, then start reading the * tokens. */ recv_ok = 0; if ( protocol_version >= 30 ) { numMatchTokens = 0; while ( (nextToken = recv_token(f_in, &nextData)) != 0 ) { if ( nextToken != -numMatchTokens - 1 ) break; numMatchTokens++; } if ( nextToken == 0 ) { OFF_T flength = (OFF_T)numMatchTokens * sum.blength; if ( sum.remainder && numMatchTokens > 0 ) flength -= sum.blength - sum.remainder; /* * Likely exact match - read the final file digest and make * sure the digest and file size match the existing file. * If so, this call creates the temporary file so the * attributes can be checked/set, and then renamed as * before. */ read_buf(f_in, file_sum, MD5_DIGEST_LEN); if ( !bpc_sysCall_checkFileMatch(fnamecmp, fnametmp, file, file_sum, flength) ) { recv_ok = 1; if ( log_before_transfer ) { iflags &= ~ITEM_REPORT_CHANGE; log_item(FCLIENT, file, &initial_stats, iflags, NULL); } } } } if ( !recv_ok ) { /* * proceed as normal, remembering to replay the tokens we read ahead above: * first, numMatchTokens: -1, -2, ..., -numMatchTokens, * then (nextToken,nextData). After that we go back to reading the * remaining tokens from f_in. */ /* open the file */ fd1 = do_open(fnamecmp, O_RDONLY, 0); if ( fd1 < 0 && protocol_version >= 30 && always_checksum ) { /* * For protocol_version >= 30 and if always_checksum is set, we can use the * MD5 whole-file digest to check for a potential match via the pool. * Use that as the basis if the file is there. The generator does * the same in recv_generator(). */ if ( S_ISREG(file->mode) && !bpc_sysCall_poolFileCheck(fnamecmp, file) ) { fd1 = do_open(fnamecmp, O_RDONLY, 0); } } if (fd1 == -1 && protocol_version < 29) { if (fnamecmp != fname) { fnamecmp = fname; fd1 = do_open(fnamecmp, O_RDONLY, 0); } if (fd1 == -1 && basis_dir[0]) { /* pre-29 allowed only one alternate basis */ pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[0], fname); fnamecmp = fnamecmpbuf; fd1 = do_open(fnamecmp, O_RDONLY, 0); } } updating_basis_or_equiv = inplace && (fnamecmp == fname || fnamecmp_type == FNAMECMP_BACKUP); if (fd1 == -1) { st.st_mode = 0; st.st_size = 0; } else if (do_fstat(fd1,&st) != 0) { rsyserr(FERROR_XFER, errno, "fstat %s failed", full_fname(fnamecmp)); discard_receive_data(f_in, F_LENGTH(file), &sum, numMatchTokens, nextToken, nextData, file_sum); bpc_close(fd1); if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); continue; } if (fd1 != -1 && S_ISDIR(st.st_mode) && fnamecmp == fname) { /* this special handling for directories * wouldn't be necessary if robust_rename() * and the underlying robust_unlink could cope * with directories */ rprintf(FERROR_XFER, "recv_files: %s is a directory\n", full_fname(fnamecmp)); discard_receive_data(f_in, F_LENGTH(file), &sum, numMatchTokens, nextToken, nextData, file_sum); bpc_close(fd1); if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); continue; } if (fd1 != -1 && !S_ISREG(st.st_mode)) { bpc_close(fd1); fd1 = -1; } /* If we're not preserving permissions, change the file-list's * mode based on the local permissions and some heuristics. */ if (!preserve_perms) { int exists = fd1 != -1; #if 0 /* was SUPPORT_ACLS */ const char *dn = file->dirname ? file->dirname : "."; if (parent_dirname != dn && strcmp(parent_dirname, dn) != 0) { dflt_perms = default_perms_for_dir(dn); parent_dirname = dn; } #endif file->mode = dest_mode(file->mode, st.st_mode, dflt_perms, exists); } /* We now check to see if we are writing the file "inplace" */ if (inplace) { fd2 = do_open(fname, O_WRONLY|O_CREAT, 0600); if (fd2 == -1) { rsyserr(FERROR_XFER, errno, "open %s failed", full_fname(fname)); } } else { fd2 = open_tmpfile(fnametmp, fname, file); if (fd2 != -1) cleanup_set(fnametmp, partialptr, file, fd1, fd2); } if (fd2 == -1) { discard_receive_data(f_in, F_LENGTH(file), &sum, numMatchTokens, nextToken, nextData, file_sum); if (fd1 != -1) bpc_close(fd1); if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); continue; } /* log the transfer */ if (log_before_transfer) log_item(FCLIENT, file, &initial_stats, iflags, NULL); else if (!am_server && verbose && do_progress) rprintf(FINFO, "%s\n", fname); /* recv file data */ recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size, fname, fd2, F_LENGTH(file), &sum, numMatchTokens, nextToken, nextData, file_sum); if (fd1 != -1) bpc_close(fd1); if (bpc_close(fd2) < 0) { rsyserr(FERROR, errno, "close failed on %s", full_fname(fnametmp)); exit_cleanup(RERR_FILEIO); } } log_item(log_code, file, &initial_stats, iflags, NULL); if ((recv_ok && (!delay_updates || !partialptr)) || inplace) { if (partialptr == fname) partialptr = NULL; if (!finish_transfer(fname, fnametmp, fnamecmp, partialptr, file, recv_ok, 1)) recv_ok = -1; else if (fnamecmp == partialptr) { do_unlink(partialptr); handle_partial_dir(partialptr, PDIR_DELETE); } } else if (keep_partial && partialptr) { if (!handle_partial_dir(partialptr, PDIR_CREATE)) { rprintf(FERROR, "Unable to create partial-dir for %s -- discarding %s.\n", local_name ? local_name : f_name(file, NULL), recv_ok ? "completed file" : "partial file"); do_unlink(fnametmp); recv_ok = -1; } else if (!finish_transfer(partialptr, fnametmp, fnamecmp, NULL, file, recv_ok, !partial_dir)) recv_ok = -1; else if (delay_updates && recv_ok) { bitbag_set_bit(delayed_bits, ndx); recv_ok = 2; } else partialptr = NULL; } else do_unlink(fnametmp); cleanup_disable(); if (read_batch) file->flags |= FLAG_FILE_SENT; switch (recv_ok) { case 2: break; case 1: if (remove_source_files || inc_recurse || (preserve_hard_links && F_IS_HLINKED(file))) send_msg_int(MSG_SUCCESS, ndx); break; case 0: { enum logcode msgtype = redoing ? FERROR_XFER : FWARNING; if (msgtype == FERROR_XFER || verbose) { char *errstr, *redostr, *keptstr; if (!(keep_partial && partialptr) && !inplace) keptstr = "discarded"; else if (partial_dir) keptstr = "put into partial-dir"; else keptstr = "retained"; if (msgtype == FERROR_XFER) { errstr = "ERROR"; redostr = ""; } else { errstr = "WARNING"; redostr = read_batch ? " (may try again)" : " (will try again)"; } rprintf(msgtype, "%s: %s failed verification -- update %s%s.\n", errstr, local_name ? f_name(file, NULL) : fname, keptstr, redostr); } if (!redoing) { if (read_batch) flist_ndx_push(&batch_redo_list, ndx); send_msg_int(MSG_REDO, ndx); file->flags |= FLAG_FILE_SENT; bpc_sysCall_printfileStatus(fname, "retry"); } else if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); bpc_sysCall_printfileStatus(fname, "fail"); break; } case -1: if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); break; } } if (make_backups < 0) make_backups = -make_backups; if (phase == 2 && delay_updates) /* for protocol_version < 29 */ handle_delayed_updates(local_name); if (verbose > 2) rprintf(FINFO,"recv_files finished\n"); return 0; }
/** * main routine for receiver process. * * Receiver process runs on the same host as the generator process. */ int recv_files(int f_in, struct file_list *flist, char *local_name) { int next_gen_i = -1; int fd1,fd2; STRUCT_STAT st; int iflags, xlen; char *fname, fbuf[MAXPATHLEN]; char xname[MAXPATHLEN]; char fnametmp[MAXPATHLEN]; char *fnamecmp, *partialptr, numbuf[4]; char fnamecmpbuf[MAXPATHLEN]; uchar fnamecmp_type; struct file_struct *file; struct stats initial_stats; int save_make_backups = make_backups; int itemizing = am_daemon ? daemon_log_format_has_i : !am_server && log_format_has_i; int max_phase = protocol_version >= 29 ? 2 : 1; int i, recv_ok; if (verbose > 2) rprintf(FINFO,"recv_files(%d) starting\n",flist->count); if (flist->hlink_pool) { pool_destroy(flist->hlink_pool); flist->hlink_pool = NULL; } if (delay_updates) init_delayed_bits(flist->count); while (1) { cleanup_disable(); i = read_int(f_in); if (i == -1) { if (read_batch) { get_next_gen_i(batch_gen_fd, next_gen_i, flist->count); next_gen_i = -1; } if (++phase > max_phase) break; csum_length = SUM_LENGTH; if (verbose > 2) rprintf(FINFO, "recv_files phase=%d\n", phase); if (phase == 2 && delay_updates) handle_delayed_updates(flist, local_name); send_msg(MSG_DONE, "", 0); if (keep_partial && !partial_dir) make_backups = 0; /* prevents double backup */ continue; } iflags = read_item_attrs(f_in, -1, i, &fnamecmp_type, xname, &xlen); if (iflags == ITEM_IS_NEW) /* no-op packet */ continue; file = flist->files[i]; fname = local_name ? local_name : f_name_to(file, fbuf); if (verbose > 2) rprintf(FINFO, "recv_files(%s)\n", safe_fname(fname)); if (!(iflags & ITEM_TRANSFER)) { maybe_log_item(file, iflags, itemizing, xname); continue; } if (phase == 2) { rprintf(FERROR, "got transfer request in phase 2 [%s]\n", who_am_i()); exit_cleanup(RERR_PROTOCOL); } stats.current_file_index = i; stats.num_transferred_files++; stats.total_transferred_size += file->length; cleanup_got_literal = 0; if (server_filter_list.head && check_filter(&server_filter_list, fname, 0) < 0) { rprintf(FERROR, "attempt to hack rsync failed.\n"); exit_cleanup(RERR_PROTOCOL); } if (!do_xfers) { /* log the transfer */ if (!am_server && log_format) log_item(file, &stats, iflags, NULL); if (read_batch) discard_receive_data(f_in, file->length); continue; } if (write_batch < 0) { log_item(file, &stats, iflags, NULL); if (!am_server) discard_receive_data(f_in, file->length); continue; } if (read_batch) { next_gen_i = get_next_gen_i(batch_gen_fd, next_gen_i, i); if (i < next_gen_i) { rprintf(FINFO, "(Skipping batched update for \"%s\")\n", safe_fname(fname)); discard_receive_data(f_in, file->length); continue; } next_gen_i = -1; } partialptr = partial_dir ? partial_dir_fname(fname) : fname; if (protocol_version >= 29) { switch (fnamecmp_type) { case FNAMECMP_FNAME: fnamecmp = fname; break; case FNAMECMP_PARTIAL_DIR: fnamecmp = partialptr; break; case FNAMECMP_BACKUP: fnamecmp = get_backup_name(fname); break; case FNAMECMP_FUZZY: if (file->dirname) { pathjoin(fnamecmpbuf, MAXPATHLEN, file->dirname, xname); fnamecmp = fnamecmpbuf; } else fnamecmp = xname; break; default: if (fnamecmp_type >= basis_dir_cnt) { rprintf(FERROR, "invalid basis_dir index: %d.\n", fnamecmp_type); exit_cleanup(RERR_PROTOCOL); } pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], fname); fnamecmp = fnamecmpbuf; break; } if (!fnamecmp || (server_filter_list.head && check_filter(&server_filter_list, fname, 0) < 0)) fnamecmp = fname; } else { /* Reminder: --inplace && --partial-dir are never * enabled at the same time. */ if (inplace && make_backups) { if (!(fnamecmp = get_backup_name(fname))) fnamecmp = fname; } else if (partial_dir && partialptr) fnamecmp = partialptr; else fnamecmp = fname; } initial_stats = stats; /* open the file */ fd1 = do_open(fnamecmp, O_RDONLY, 0); if (fd1 == -1 && protocol_version < 29) { if (fnamecmp != fname) { fnamecmp = fname; fd1 = do_open(fnamecmp, O_RDONLY, 0); } if (fd1 == -1 && basis_dir[0]) { /* pre-29 allowed only one alternate basis */ pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[0], fname); fnamecmp = fnamecmpbuf; fd1 = do_open(fnamecmp, O_RDONLY, 0); } } if (fd1 != -1 && do_fstat(fd1,&st) != 0) { rsyserr(FERROR, errno, "fstat %s failed", full_fname(fnamecmp)); discard_receive_data(f_in, file->length); close(fd1); continue; } if (fd1 != -1 && S_ISDIR(st.st_mode) && fnamecmp == fname) { /* this special handling for directories * wouldn't be necessary if robust_rename() * and the underlying robust_unlink could cope * with directories */ rprintf(FERROR,"recv_files: %s is a directory\n", full_fname(fnamecmp)); discard_receive_data(f_in, file->length); close(fd1); continue; } if (fd1 != -1 && !S_ISREG(st.st_mode)) { close(fd1); fd1 = -1; } if (fd1 != -1 && !preserve_perms) { /* if the file exists already and we aren't preserving * permissions then act as though the remote end sent * us the file permissions we already have */ file->mode = st.st_mode; } /* We now check to see if we are writing file "inplace" */ if (inplace) { fd2 = do_open(fname, O_WRONLY|O_CREAT, 0); if (fd2 == -1) { rsyserr(FERROR, errno, "open %s failed", full_fname(fname)); discard_receive_data(f_in, file->length); if (fd1 != -1) close(fd1); continue; } } else { if (!get_tmpname(fnametmp,fname)) { discard_receive_data(f_in, file->length); if (fd1 != -1) close(fd1); continue; } /* we initially set the perms without the * setuid/setgid bits to ensure that there is no race * condition. They are then correctly updated after * the lchown. Thanks to [email protected] for pointing * this out. We also set it initially without group * access because of a similar race condition. */ fd2 = do_mkstemp(fnametmp, file->mode & INITACCESSPERMS); /* in most cases parent directories will already exist * because their information should have been previously * transferred, but that may not be the case with -R */ if (fd2 == -1 && relative_paths && errno == ENOENT && create_directory_path(fnametmp, orig_umask) == 0) { /* Get back to name with XXXXXX in it. */ get_tmpname(fnametmp, fname); fd2 = do_mkstemp(fnametmp, file->mode & INITACCESSPERMS); } if (fd2 == -1) { rsyserr(FERROR, errno, "mkstemp %s failed", full_fname(fnametmp)); discard_receive_data(f_in, file->length); if (fd1 != -1) close(fd1); continue; } if (partialptr) cleanup_set(fnametmp, partialptr, file, fd1, fd2); } /* log the transfer */ if (log_before_transfer) log_item(file, &initial_stats, iflags, NULL); else if (!am_server && verbose && do_progress) rprintf(FINFO, "%s\n", safe_fname(fname)); /* recv file data */ recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size, fname, fd2, file->length); if (!log_before_transfer) log_item(file, &initial_stats, iflags, NULL); if (fd1 != -1) close(fd1); if (close(fd2) < 0) { rsyserr(FERROR, errno, "close failed on %s", full_fname(fnametmp)); exit_cleanup(RERR_FILEIO); } if ((recv_ok && (!delay_updates || !partialptr)) || inplace) { finish_transfer(fname, fnametmp, file, recv_ok, 1); if (partialptr != fname && fnamecmp == partialptr) { do_unlink(partialptr); handle_partial_dir(partialptr, PDIR_DELETE); } } else if (keep_partial && partialptr && handle_partial_dir(partialptr, PDIR_CREATE)) { finish_transfer(partialptr, fnametmp, file, recv_ok, !partial_dir); if (delay_updates && recv_ok) { set_delayed_bit(i); recv_ok = -1; } } else { partialptr = NULL; do_unlink(fnametmp); } cleanup_disable(); if (recv_ok > 0) { if (remove_sent_files || (preserve_hard_links && file->link_u.links)) { SIVAL(numbuf, 0, i); send_msg(MSG_SUCCESS, numbuf, 4); } } else if (!recv_ok) { int msgtype = phase || read_batch ? FERROR : FINFO; if (msgtype == FERROR || verbose) { char *errstr, *redostr, *keptstr; if (!(keep_partial && partialptr) && !inplace) keptstr = "discarded"; else if (partial_dir) keptstr = "put into partial-dir"; else keptstr = "retained"; if (msgtype == FERROR) { errstr = "ERROR"; redostr = ""; } else { errstr = "WARNING"; redostr = " (will try again)"; } rprintf(msgtype, "%s: %s failed verification -- update %s%s.\n", errstr, safe_fname(fname), keptstr, redostr); } if (!phase) { SIVAL(numbuf, 0, i); send_msg(MSG_REDO, numbuf, 4); } } } make_backups = save_make_backups; if (phase == 2 && delay_updates) /* for protocol_version < 29 */ handle_delayed_updates(flist, local_name); if (verbose > 2) rprintf(FINFO,"recv_files finished\n"); return 0; }