📄 generator.c
字号:
/* * Routines that are exclusive to the generator process. * * Copyright (C) 1996-2000 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2002 Martin Pool <mbp@samba.org> * Copyright (C) 2003-2008 Wayne Davison * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, visit the http://fsf.org website. */#include "rsync.h"extern int verbose;extern int dry_run;extern int do_xfers;extern int stdout_format_has_i;extern int logfile_format_has_i;extern int am_root;extern int am_server;extern int am_daemon;extern int inc_recurse;extern int do_progress;extern int relative_paths;extern int implied_dirs;extern int keep_dirlinks;extern int preserve_acls;extern int preserve_xattrs;extern int preserve_links;extern int preserve_devices;extern int preserve_specials;extern int preserve_hard_links;extern int preserve_executability;extern int preserve_perms;extern int preserve_times;extern int uid_ndx;extern int gid_ndx;extern int delete_mode;extern int delete_before;extern int delete_during;extern int delete_after;extern int msgdone_cnt;extern int ignore_errors;extern int remove_source_files;extern int delay_updates;extern int update_only;extern int ignore_existing;extern int ignore_non_existing;extern int inplace;extern int append_mode;extern int make_backups;extern int csum_length;extern int ignore_times;extern int size_only;extern OFF_T max_size;extern OFF_T min_size;extern int io_error;extern int flist_eof;extern int allowed_lull;extern int sock_f_out;extern int ignore_timeout;extern int protocol_version;extern int file_total;extern int fuzzy_basis;extern int always_checksum;extern int checksum_len;extern char *partial_dir;extern char *basis_dir[];extern int compare_dest;extern int copy_dest;extern int link_dest;extern int whole_file;extern int list_only;extern int read_batch;extern int safe_symlinks;extern long block_size; /* "long" because popt can't set an int32. */extern int unsort_ndx;extern int max_delete;extern int force_delete;extern int one_file_system;extern struct stats stats;extern dev_t filesystem_dev;extern mode_t orig_umask;extern uid_t our_uid;extern char *backup_dir;extern char *backup_suffix;extern int backup_suffix_len;extern struct file_list *cur_flist, *first_flist, *dir_flist;extern struct filter_list_struct daemon_filter_list;int ignore_perishable = 0;int non_perishable_cnt = 0;int maybe_ATTRS_REPORT = 0;static dev_t dev_zero;static int deletion_count = 0; /* used to implement --max-delete */static int deldelay_size = 0, deldelay_cnt = 0;static char *deldelay_buf = NULL;static int deldelay_fd = -1;static int loopchk_limit;static int dir_tweaking;static int symlink_timeset_failed_flags;static int need_retouch_dir_times;static int need_retouch_dir_perms;static const char *solo_file = NULL;/* For calling delete_item() and delete_dir_contents(). */#define DEL_NO_UID_WRITE (1<<0) /* file/dir has our uid w/o write perm */#define DEL_RECURSE (1<<1) /* if dir, delete all contents */#define DEL_DIR_IS_EMPTY (1<<2) /* internal delete_FUNCTIONS use only */#define DEL_FOR_FILE (1<<3) /* making room for a replacement file */#define DEL_FOR_DIR (1<<4) /* making room for a replacement dir */#define DEL_FOR_SYMLINK (1<<5) /* making room for a replacement symlink */#define DEL_FOR_DEVICE (1<<6) /* making room for a replacement device */#define DEL_FOR_SPECIAL (1<<7) /* making room for a replacement special */#define DEL_MAKE_ROOM (DEL_FOR_FILE|DEL_FOR_DIR|DEL_FOR_SYMLINK|DEL_FOR_DEVICE|DEL_FOR_SPECIAL)enum nonregtype { TYPE_DIR, TYPE_SPECIAL, TYPE_DEVICE, TYPE_SYMLINK};enum delret { DR_SUCCESS = 0, DR_FAILURE, DR_AT_LIMIT, DR_NOT_EMPTY};/* Forward declarations. */static enum delret delete_dir_contents(char *fname, uint16 flags);#ifdef SUPPORT_HARD_LINKSstatic void handle_skipped_hlink(struct file_struct *file, int itemizing, enum logcode code, int f_out);#endifstatic int is_backup_file(char *fn){ int k = strlen(fn) - backup_suffix_len; return k > 0 && strcmp(fn+k, backup_suffix) == 0;}/* 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.) */static enum delret delete_item(char *fbuf, uint16 mode, uint16 flags){ enum delret ret; char *what; int ok; if (verbose > 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)) { int save_uid_ndx = uid_ndx; /* This only happens on the first call to delete_item() since * delete_dir_contents() always calls us w/DEL_DIR_IS_EMPTY. */ if (!uid_ndx) uid_ndx = ++file_extra_cnt; ignore_perishable = 1; /* If DEL_RECURSE is not set, this just reports emptiness. */ ret = delete_dir_contents(fbuf, flags); ignore_perishable = 0; if (!save_uid_ndx) { --file_extra_cnt; uid_ndx = 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 && ++deletion_count > max_delete) return DR_AT_LIMIT; if (S_ISDIR(mode)) { what = "rmdir"; ok = do_rmdir(fbuf) == 0; } else if (make_backups > 0 && (backup_dir || !is_backup_file(fbuf))) { what = "make_backup"; ok = make_backup(fbuf); } else { what = "unlink"; ok = robust_unlink(fbuf) == 0; } if (ok) { if (!(flags & DEL_MAKE_ROOM)) log_delete(fbuf, mode); 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 { deletion_count--; 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 new %s: %s\n", desc, fbuf); } return ret;}/* 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 (verbose > 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 (verbose > 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 && (uid_t)F_OWNER(fp) == our_uid) 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;}static int start_delete_delay_temp(void){ char fnametmp[MAXPATHLEN]; int save_dry_run = dry_run; dry_run = 0; if (!get_tmpname(fnametmp, "deldelay") || (deldelay_fd = do_mkstemp(fnametmp, 0600)) < 0) { rprintf(FINFO, "NOTE: Unable to create delete-delay temp file%s.\n", inc_recurse ? "" : " -- switching to --delete-after"); delete_during = 0; delete_after = !inc_recurse; dry_run = save_dry_run; return 0; } unlink(fnametmp); dry_run = save_dry_run; return 1;}static int flush_delete_delay(void){ if (deldelay_fd < 0 && !start_delete_delay_temp()) return 0; if (write(deldelay_fd, deldelay_buf, deldelay_cnt) != deldelay_cnt) { rsyserr(FERROR, errno, "flush of delete-delay buffer"); delete_during = 0; delete_after = !inc_recurse; close(deldelay_fd); return 0; } deldelay_cnt = 0; return 1;}static int remember_delete(struct file_struct *file, const char *fname, int flags){ int len; if (deldelay_cnt == deldelay_size && !flush_delete_delay()) return 0; if (flags & DEL_NO_UID_WRITE) deldelay_buf[deldelay_cnt++] = '!'; while (1) { len = snprintf(deldelay_buf + deldelay_cnt, deldelay_size - deldelay_cnt, "%x %s%c", (int)file->mode, fname, '\0'); if ((deldelay_cnt += len) <= deldelay_size) break; deldelay_cnt -= len; if (!flush_delete_delay()) return 0; } return 1;}static int read_delay_line(char *buf, int *flags_p){ static int read_pos = 0; int j, len, mode; char *bp, *past_space; while (1) { for (j = read_pos; j < deldelay_cnt && deldelay_buf[j]; j++) {} if (j < deldelay_cnt) break; if (deldelay_fd < 0) { if (j > read_pos) goto invalid_data; return -1; } deldelay_cnt -= read_pos; if (deldelay_cnt == deldelay_size) goto invalid_data; if (deldelay_cnt && read_pos) { memmove(deldelay_buf, deldelay_buf + read_pos, deldelay_cnt); } len = read(deldelay_fd, deldelay_buf + deldelay_cnt, deldelay_size - deldelay_cnt); if (len == 0) { if (deldelay_cnt) { rprintf(FERROR, "ERROR: unexpected EOF in delete-delay file.\n"); } return -1; } if (len < 0) { rsyserr(FERROR, errno, "reading delete-delay file"); return -1; } deldelay_cnt += len; read_pos = 0; } bp = deldelay_buf + read_pos; if (*bp == '!') { bp++; *flags_p = DEL_NO_UID_WRITE; } else *flags_p = 0; if (sscanf(bp, "%x ", &mode) != 1) { invalid_data: rprintf(FERROR, "ERROR: invalid data in delete-delay file.\n"); return -1; } past_space = strchr(bp, ' ') + 1; len = j - read_pos - (past_space - bp) + 1; /* count the '\0' */ read_pos = j + 1; if (len > MAXPATHLEN) { rprintf(FERROR, "ERROR: filename too long in delete-delay file.\n"); return -1; } /* The caller needs the name in a MAXPATHLEN buffer, so we copy it * instead of returning a pointer to our buffer. */ memcpy(buf, past_space, len); return mode;}static void do_delayed_deletions(char *delbuf){ int mode, flags; if (deldelay_fd >= 0) { if (deldelay_cnt && !flush_delete_delay()) return; lseek(deldelay_fd, 0, 0); } while ((mode = read_delay_line(delbuf, &flags)) >= 0) delete_item(delbuf, mode, flags | DEL_RECURSE); if (deldelay_fd >= 0) close(deldelay_fd);}/* This function is used to implement per-directory deletion, and is used by * all the --delete-WHEN options. Note that the fbuf pointer must point to a * MAXPATHLEN buffer with the name of the directory in it (the functions we * call will append names onto the end, but the old dir value will be restored * on exit). */static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev){ static int already_warned = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -