📄 main.c
字号:
} if (!dest_path || list_only) return NULL; if (daemon_filter_list.head) { char *slash = strrchr(dest_path, '/'); if (slash && (slash[1] == '\0' || (slash[1] == '.' && slash[2] == '\0'))) *slash = '\0'; else slash = NULL; if ((*dest_path != '.' || dest_path[1] != '\0') && (check_filter(&daemon_filter_list, FLOG, dest_path, 0) < 0 || check_filter(&daemon_filter_list, FLOG, dest_path, 1) < 0)) { rprintf(FERROR, "skipping daemon-excluded destination \"%s\"\n", dest_path); exit_cleanup(RERR_FILESELECT); } if (slash) *slash = '/'; } /* See what currently exists at the destination. */ if ((statret = do_stat(dest_path, &st)) == 0) { /* If the destination is a dir, enter it and use mode 1. */ if (S_ISDIR(st.st_mode)) { if (!change_dir(dest_path, CD_NORMAL)) { rsyserr(FERROR, errno, "change_dir#1 %s failed", full_fname(dest_path)); exit_cleanup(RERR_FILESELECT); } return NULL; } if (file_total > 1) { rprintf(FERROR, "ERROR: destination must be a directory when" " copying more than 1 file\n"); exit_cleanup(RERR_FILESELECT); } if (file_total == 1 && S_ISDIR(flist->files[0]->mode)) { rprintf(FERROR, "ERROR: cannot overwrite non-directory" " with a directory\n"); exit_cleanup(RERR_FILESELECT); } } else if (errno != ENOENT) { /* If we don't know what's at the destination, fail. */ rsyserr(FERROR, errno, "ERROR: cannot stat destination %s", full_fname(dest_path)); exit_cleanup(RERR_FILESELECT); } cp = strrchr(dest_path, '/'); /* If we need a destination directory because the transfer is not * of a single non-directory or the user has requested one via a * destination path ending in a slash, create one and use mode 1. */ if (file_total > 1 || (cp && !cp[1])) { /* Lop off the final slash (if any). */ if (cp && !cp[1]) *cp = '\0'; if (statret == 0) { rprintf(FERROR, "ERROR: destination path is not a directory\n"); exit_cleanup(RERR_SYNTAX); } if (mkdir_defmode(dest_path) != 0) { rsyserr(FERROR, errno, "mkdir %s failed", full_fname(dest_path)); exit_cleanup(RERR_FILEIO); } if (flist->high >= flist->low && strcmp(flist->files[flist->low]->basename, ".") == 0) flist->files[0]->flags |= FLAG_DIR_CREATED; if (verbose) rprintf(FINFO, "created directory %s\n", dest_path); if (dry_run) { /* Indicate that dest dir doesn't really exist. */ dry_run++; } if (!change_dir(dest_path, dry_run > 1 ? CD_SKIP_CHDIR : CD_NORMAL)) { rsyserr(FERROR, errno, "change_dir#2 %s failed", full_fname(dest_path)); exit_cleanup(RERR_FILESELECT); } return NULL; } /* Otherwise, we are writing a single file, possibly on top of an * existing non-directory. Change to the item's parent directory * (if it has a path component), return the basename of the * destination file as the local name, and use mode 2. */ if (!cp) return dest_path; if (cp == dest_path) dest_path = "/"; *cp = '\0'; if (!change_dir(dest_path, CD_NORMAL)) { rsyserr(FERROR, errno, "change_dir#3 %s failed", full_fname(dest_path)); exit_cleanup(RERR_FILESELECT); } *cp = '/'; return cp + 1;}/* This function checks on our alternate-basis directories. If we're in * dry-run mode and the destination dir does not yet exist, we'll try to * tweak any dest-relative paths to make them work for a dry-run (the * destination dir must be in curr_dir[] when this function is called). * We also warn about any arg that is non-existent or not a directory. */static void check_alt_basis_dirs(void){ STRUCT_STAT st; char **dir_p, *slash = strrchr(curr_dir, '/'); for (dir_p = basis_dir; *dir_p; dir_p++) { if (dry_run > 1 && **dir_p != '/') { int len = curr_dir_len + 1 + strlen(*dir_p) + 1; char *new = new_array(char, len); if (!new) out_of_memory("check_alt_basis_dirs"); if (slash && strncmp(*dir_p, "../", 3) == 0) { /* We want to remove only one leading "../" prefix for * the directory we couldn't create in dry-run mode: * this ensures that any other ".." references get * evaluated the same as they would for a live copy. */ *slash = '\0'; pathjoin(new, len, curr_dir, *dir_p + 3); *slash = '/'; } else pathjoin(new, len, curr_dir, *dir_p); *dir_p = new; } if (do_stat(*dir_p, &st) < 0) { rprintf(FWARNING, "%s arg does not exist: %s\n", dest_option, *dir_p); } else if (!S_ISDIR(st.st_mode)) { rprintf(FWARNING, "%s arg is not a dir: %s\n", dest_option, *dir_p); } }}/* This is only called by the sender. */static void read_final_goodbye(int f_in){ int i, iflags, xlen; uchar fnamecmp_type; char xname[MAXPATHLEN]; if (protocol_version < 29) i = read_int(f_in); else { i = read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type, xname, &xlen); } if (i != NDX_DONE) { rprintf(FERROR, "Invalid packet at end of run (%d) [%s]\n", i, who_am_i()); exit_cleanup(RERR_PROTOCOL); }}static void do_server_sender(int f_in, int f_out, int argc, char *argv[]){ struct file_list *flist; char *dir = argv[0]; if (verbose > 2) { rprintf(FINFO, "server_sender starting pid=%ld\n", (long)getpid()); } if (am_daemon && lp_write_only(module_id)) { rprintf(FERROR, "ERROR: module is write only\n"); exit_cleanup(RERR_SYNTAX); return; } if (am_daemon && lp_read_only(module_id) && remove_source_files) { rprintf(FERROR, "ERROR: --remove-%s-files cannot be used with a read-only module\n", remove_source_files == 1 ? "source" : "sent"); exit_cleanup(RERR_SYNTAX); return; } if (!relative_paths) { if (!change_dir(dir, CD_NORMAL)) { rsyserr(FERROR, errno, "change_dir#3 %s failed", full_fname(dir)); exit_cleanup(RERR_FILESELECT); } } argc--; argv++; if (argc == 0 && (recurse || xfer_dirs || list_only)) { argc = 1; argv--; argv[0] = "."; } flist = send_file_list(f_out,argc,argv); if (!flist || flist->used == 0) exit_cleanup(0); io_start_buffering_in(f_in); send_files(f_in, f_out); io_flush(FULL_FLUSH); handle_stats(f_out); if (protocol_version >= 24) read_final_goodbye(f_in); io_flush(FULL_FLUSH); exit_cleanup(0);}static int do_recv(int f_in, int f_out, char *local_name){ int pid; int exit_code = 0; int error_pipe[2]; /* The receiving side mustn't obey this, or an existing symlink that * points to an identical file won't be replaced by the referent. */ copy_links = copy_dirlinks = copy_unsafe_links = 0;#ifdef SUPPORT_HARD_LINKS if (preserve_hard_links && !inc_recurse) match_hard_links(first_flist);#endif if (fd_pair(error_pipe) < 0) { rsyserr(FERROR, errno, "pipe failed in do_recv"); exit_cleanup(RERR_IPC); } io_flush(NORMAL_FLUSH); if ((pid = do_fork()) == -1) { rsyserr(FERROR, errno, "fork failed in do_recv"); exit_cleanup(RERR_IPC); } if (pid == 0) { close(error_pipe[0]); if (f_in != f_out) close(f_out); /* we can't let two processes write to the socket at one time */ io_end_multiplex_out(); /* set place to send errors */ set_msg_fd_out(error_pipe[1]); io_start_buffering_out(error_pipe[1]); recv_files(f_in, local_name); io_flush(FULL_FLUSH); handle_stats(f_in); send_msg(MSG_DONE, "", 1, 0); write_varlong(error_pipe[1], stats.total_read, 3); io_flush(FULL_FLUSH); /* Handle any keep-alive packets from the post-processing work * that the generator does. */ if (protocol_version >= 29) { int iflags, xlen; uchar fnamecmp_type; char xname[MAXPATHLEN]; kluge_around_eof = -1; /* This should only get stopped via a USR2 signal. */ read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type, xname, &xlen); rprintf(FERROR, "Invalid packet at end of run [%s]\n", who_am_i()); exit_cleanup(RERR_PROTOCOL); } /* Finally, we go to sleep until our parent kills us with a * USR2 signal. We sleep for a short time, as on some OSes * a signal won't interrupt a sleep! */ while (1) msleep(20); } am_generator = 1; io_end_multiplex_in(); if (write_batch && !am_server) stop_write_batch(); close(error_pipe[1]); if (f_in != f_out) close(f_in); io_start_buffering_out(f_out); set_msg_fd_in(error_pipe[0]); io_start_buffering_in(error_pipe[0]);#ifdef SUPPORT_HARD_LINKS if (preserve_hard_links && inc_recurse) { struct file_list *flist; for (flist = first_flist; flist; flist = flist->next) match_hard_links(flist); }#endif generate_files(f_out, local_name); handle_stats(-1); io_flush(FULL_FLUSH); if (protocol_version >= 24) { /* send a final goodbye message */ write_ndx(f_out, NDX_DONE); } io_flush(FULL_FLUSH); set_msg_fd_in(-1); kill(pid, SIGUSR2); wait_process_with_flush(pid, &exit_code); return exit_code;}static void do_server_recv(int f_in, int f_out, int argc, char *argv[]){ int exit_code; struct file_list *flist; char *local_name = NULL; int save_verbose = verbose; if (filesfrom_fd >= 0) { /* We can't mix messages with files-from data on the socket, * so temporarily turn off verbose messages. */ verbose = 0; } if (verbose > 2) { rprintf(FINFO, "server_recv(%d) starting pid=%ld\n", argc, (long)getpid()); } if (am_daemon && lp_read_only(module_id)) { rprintf(FERROR,"ERROR: module is read only\n"); exit_cleanup(RERR_SYNTAX); return; } if (argc > 0) { char *dir = argv[0]; argc--; argv++; if (!am_daemon && !change_dir(dir, CD_NORMAL)) { rsyserr(FERROR, errno, "change_dir#4 %s failed", full_fname(dir)); exit_cleanup(RERR_FILESELECT); } } if (protocol_version >= 30) io_start_multiplex_in(); else io_start_buffering_in(f_in); recv_filter_list(f_in); if (filesfrom_fd >= 0) { /* We need to send the files-from names to the sender at the * same time that we receive the file-list from them, so we * need the IO routines to automatically write out the names * onto our f_out socket as we read the file-list. This * avoids both deadlock and extra delays/buffers. */ io_set_filesfrom_fds(filesfrom_fd, f_out); filesfrom_fd = -1; } flist = recv_file_list(f_in); if (!flist) { rprintf(FERROR,"server_recv: recv_file_list error\n"); exit_cleanup(RERR_FILESELECT); } if (inc_recurse && file_total == 1) recv_additional_file_list(f_in); verbose = save_verbose; if (argc > 0) local_name = get_local_name(flist,argv[0]); /* Now that we know what our destination directory turned out to be, * we can sanitize the --link-/copy-/compare-dest args correctly. */ if (sanitize_paths) { char **dir_p; for (dir_p = basis_dir; *dir_p; dir_p++) *dir_p = sanitize_path(NULL, *dir_p, NULL, curr_dir_depth, SP_DEFAULT); if (partial_dir) partial_dir = sanitize_path(NULL, partial_dir, NULL, curr_dir_depth, SP_DEFAULT); } check_alt_basis_dirs(); if (daemon_filter_list.head) { char **dir_p; struct filter_list_struct *elp = &daemon_filter_list; for (dir_p = basis_dir; *dir_p; dir_p++) { char *dir = *dir_p; if (*dir == '/') dir += module_dirlen; if (check_filter(elp, FLOG, dir, 1) < 0) goto options_rejected; } if (partial_dir && *partial_dir == '/' && check_filter(elp, FLOG, partial_dir + module_dirlen, 1) < 0) { options_rejected: rprintf(FERROR, "Your options have been rejected by the server.\n"); exit_cleanup(RERR_SYNTAX); } } exit_code = do_recv(f_in, f_out, local_name); exit_cleanup(exit_code);}int child_main(int argc, char *argv[]){ start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv); return 0;}void start_server(int f_in, int f_out, int argc, char *argv[]){ set_nonblocking(f_in); set_nonblocking(f_out); io_set_sock_fds(f_in, f_out); setup_protocol(f_out, f_in); if (protocol_version >= 23) io_start_multiplex_out(); if (am_sender) { keep_dirlinks = 0; /* Must be disabled on the sender. */ if (need_messages_from_generator) io_start_multiplex_in(); recv_filter_list(f_in); do_server_sender(f_in, f_out, argc, argv); } else do_server_recv(f_in, f_out, argc, argv); exit_cleanup(0);}/* * This is called once the connection has been negotiated. It is used * for rsyncd, remote-shell, and local connections. */int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[]){ struct file_list *flist = NULL; int exit_code = 0, exit_code2 = 0; char *local_name = NULL; cleanup_child_pid = pid; if (!read_batch) { set_nonblocking(f_in); set_nonblocking(f_out); } io_set_sock_fds(f_in, f_out); setup_protocol(f_out,f_in); /* We set our stderr file handle to blocking because ssh might have * set it to non-blocking. This can be particularly troublesome if * stderr is a clone of stdout, because ssh would have set our stdout * to non-blocking at the same time (which can easily cause us to lose * output from our print statements). This kluge shouldn't cause ssh * any problems for how we use it. Note also that we delayed setting * this until after the above protocol setup so that we know for sure * that ssh is done twiddling its file descriptors. */ set_blocking(STDERR_FILENO); if (am_sender) { keep_dirlinks = 0; /* Must be disabled on the sender. */ if (protocol_version >= 30) io_start_multiplex_out(); else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -