extern struct stats stats;
extern int verbose;
-extern int do_progress;
+extern int dry_run;
+extern int list_only;
extern int am_root;
extern int am_server;
extern int am_daemon;
extern int am_sender;
+extern int do_progress;
extern int always_checksum;
extern int module_id;
extern int ignore_errors;
extern int numeric_ids;
-
extern int recurse;
extern int xfer_dirs;
-extern char curr_dir[MAXPATHLEN];
-extern unsigned int curr_dir_len;
-extern char *backup_dir;
-extern char *backup_suffix;
extern int filesfrom_fd;
-
extern int one_file_system;
extern int keep_dirlinks;
extern int preserve_links;
extern int preserve_gid;
extern int relative_paths;
extern int implied_dirs;
-extern int make_backups;
-extern int backup_suffix_len;
extern int copy_links;
extern int copy_unsafe_links;
extern int protocol_version;
extern int sanitize_paths;
-extern int max_delete;
extern int orig_umask;
-extern int list_only;
+
+extern char curr_dir[MAXPATHLEN];
extern struct filter_list_struct filter_list;
extern struct filter_list_struct server_filter_list;
int io_error;
+dev_t filesystem_dev; /* used to implement -x */
static char empty_sum[MD4_SUM_LENGTH];
static unsigned int file_struct_len;
-static struct file_list *received_flist;
-static dev_t filesystem_dev; /* used to implement -x */
-static int deletion_count = 0; /* used to implement --max-delete */
+static struct file_list *received_flist, *sorting_flist;
static void clean_flist(struct file_list *flist, int strip_root, int no_dups);
-static void output_flist(struct file_list *flist, const char *whose_list);
+static void output_flist(struct file_list *flist);
void init_flist(void)
{
permstring(perms, f->mode);
-#if SUPPORT_LINKS
+#ifdef SUPPORT_LINKS
if (preserve_links && S_ISLNK(f->mode)) {
rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
perms,
**/
static int readlink_stat(const char *path, STRUCT_STAT *buffer, char *linkbuf)
{
-#if SUPPORT_LINKS
+#ifdef SUPPORT_LINKS
if (copy_links)
return do_stat(path, buffer);
if (link_stat(path, buffer, 0) < 0)
int link_stat(const char *path, STRUCT_STAT *buffer, int follow_dirlinks)
{
-#if SUPPORT_LINKS
+#ifdef SUPPORT_LINKS
if (copy_links)
return do_stat(path, buffer);
if (do_lstat(path, buffer) < 0)
static int to_wire_mode(mode_t mode)
{
-#if SUPPORT_LINKS
+#ifdef SUPPORT_LINKS
if (S_ISLNK(mode) && (_S_IFLNK != 0120000))
return (mode & ~(_S_IFMT)) | 0120000;
#endif
static void send_directory(int f, struct file_list *flist,
- char *fbuf, unsigned int offset);
+ char *fbuf, int len);
static char *flist_dir;
static int flist_dir_len;
char fname[MAXPATHLEN];
int l1, l2;
- if (f == -1)
+ if (f < 0)
return;
if (!file) {
else
modtime = file->modtime;
-#if SUPPORT_HARD_LINKS
+#ifdef SUPPORT_HARD_LINKS
if (file->link_u.idev) {
if (file->F_DEV == dev) {
if (protocol_version >= 28)
}
}
-#if SUPPORT_LINKS
+#ifdef SUPPORT_LINKS
if (preserve_links && S_ISLNK(mode)) {
int len = strlen(file->u.link);
write_int(f, len);
}
#endif
-#if SUPPORT_HARD_LINKS
+#ifdef SUPPORT_HARD_LINKS
if (flags & XMIT_HAS_IDEV_DATA) {
if (protocol_version < 26) {
/* 32-bit dev_t and ino_t */
}
}
-#if SUPPORT_LINKS
+#ifdef SUPPORT_LINKS
if (preserve_links && S_ISLNK(mode)) {
linkname_len = read_int(f) + 1; /* count the '\0' */
if (linkname_len <= 0 || linkname_len > MAXPATHLEN) {
if (preserve_devices && IS_DEVICE(mode))
file->u.rdev = rdev;
-#if SUPPORT_LINKS
+#ifdef SUPPORT_LINKS
if (linkname_len) {
file->u.link = bp;
read_sbuf(f, bp, linkname_len - 1);
}
#endif
-#if SUPPORT_HARD_LINKS
+#ifdef SUPPORT_HARD_LINKS
if (preserve_hard_links && protocol_version < 28 && S_ISREG(mode))
flags |= XMIT_HAS_IDEV_DATA;
if (flags & XMIT_HAS_IDEV_DATA) {
&& is_excluded(thisname, 0, filter_level))
return NULL;
if (save_errno == ENOENT) {
-#if SUPPORT_LINKS
+#ifdef SUPPORT_LINKS
/* Avoid "vanished" error if symlink points nowhere. */
if (copy_links && do_lstat(thisname, &st) == 0
&& S_ISLNK(st.st_mode)) {
return NULL;
if (lp_ignore_nonreadable(module_id)) {
-#if SUPPORT_LINKS
+#ifdef SUPPORT_LINKS
if (!S_ISLNK(st.st_mode))
#endif
if (access(thisname, R_OK) != 0)
}
basename_len = strlen(basename) + 1; /* count the '\0' */
-#if SUPPORT_LINKS
+#ifdef SUPPORT_LINKS
linkname_len = S_ISLNK(st.st_mode) ? strlen(linkname) + 1 : 0;
#else
linkname_len = 0;
file->uid = st.st_uid;
file->gid = st.st_gid;
-#if SUPPORT_HARD_LINKS
+#ifdef SUPPORT_HARD_LINKS
if (flist && flist->hlink_pool) {
if (protocol_version < 28) {
if (S_ISREG(st.st_mode))
memcpy(bp, basename, basename_len);
bp += basename_len;
-#if HAVE_STRUCT_STAT_ST_RDEV
+#ifdef HAVE_STRUCT_STAT_ST_RDEV
if (preserve_devices && IS_DEVICE(st.st_mode))
file->u.rdev = st.st_rdev;
#endif
-#if SUPPORT_LINKS
+#ifdef SUPPORT_LINKS
if (linkname_len) {
file->u.link = bp;
memcpy(bp, linkname, linkname_len);
file->mode = save_mode;
}
- if (!S_ISDIR(st.st_mode))
+ if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
stats.total_size += st.st_size;
return file;
struct file_struct *file;
char fbuf[MAXPATHLEN];
- if (!(file = make_file(fname, flist, ALL_FILTERS)))
+ file = make_file(fname, flist, f == -2 ? SERVER_FILTERS : ALL_FILTERS);
+ if (!file)
return;
maybe_emit_filelist_progress(flist);
}
-/* Note that the "recurse" value either contains -1, for infinite recursion,
- * or a number >= 0 indicating how many levels of recursion we will allow.
- * This function is normally called by the sender, but the receiving side
- * also calls it from delete_in_dir() with f set to -1 so that we just
- * construct the file list in memory without sending it over the wire. */
+/* Note that the "recurse" value either contains -1, for infinite recursion, or
+ * a number >= 0 indicating how many levels of recursion we will allow. This
+ * function is normally called by the sender, but the receiving side also calls
+ * it from delete_in_dir() with f set to -1 so that we just construct the file
+ * list in memory without sending it over the wire. Also, get_dirlist() might
+ * call this with f set to -2, which indicates that local filter rules should
+ * be ignored. */
static void send_directory(int f, struct file_list *flist,
- char *fbuf, unsigned int len)
+ char *fbuf, int len)
{
struct dirent *di;
+ unsigned remainder;
char *p;
DIR *d;
if (len != 1 || *fbuf != '/')
*p++ = '/';
*p = '\0';
+ remainder = MAXPATHLEN - (p - fbuf);
for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) {
char *dname = d_name(di);
if (dname[0] == '.' && (dname[1] == '\0'
|| (dname[1] == '.' && dname[2] == '\0')))
continue;
- if (strlcpy(p, dname, MAXPATHLEN - len) < MAXPATHLEN - len) {
+ if (strlcpy(p, dname, remainder) < remainder) {
int do_subdirs = recurse >= 1 ? recurse-- : recurse;
send_file_name(f, flist, fbuf, do_subdirs, 0);
} else {
full_fname(fbuf));
}
}
+
+ fbuf[len] = '\0';
+
if (errno) {
io_error |= IOERR_GENERAL;
- *p = '\0';
rsyserr(FERROR, errno, "readdir(%s)", full_fname(fbuf));
}
stats.num_files = flist->count;
if (verbose > 3)
- output_flist(flist, who_am_i());
+ output_flist(flist);
if (verbose > 2)
rprintf(FINFO, "send_file_list done\n");
clean_flist(flist, relative_paths, 1);
- if (f != -1) {
+ if (f >= 0) {
/* Now send the uid/gid list. This was introduced in
* protocol version 15 */
recv_uid_list(f, flist);
}
if (verbose > 3)
- output_flist(flist, who_am_i());
+ output_flist(flist);
if (list_only) {
int i;
out_of_memory, POOL_INTERN)))
out_of_memory(msg);
-#if SUPPORT_HARD_LINKS
+#ifdef SUPPORT_HARD_LINKS
if (with_hlink && preserve_hard_links) {
if (!(flist->hlink_pool = pool_create(HLINK_EXTENT,
sizeof (struct idev), out_of_memory, POOL_INTERN)))
if (!flist || flist->count == 0)
return;
+ sorting_flist = flist;
qsort(flist->files, flist->count,
sizeof flist->files[0], (int (*)())file_compare);
+ sorting_flist = NULL;
for (i = no_dups? 0 : flist->count; i < flist->count; i++) {
if (flist->files[i]->basename) {
}
flist->low = prev_i;
while (++i < flist->count) {
- int is_dup;
+ int j;
struct file_struct *file = flist->files[i];
if (!file->basename)
continue;
- is_dup = f_name_cmp(file, flist->files[prev_i]) == 0;
- if (!is_dup && protocol_version >= 29 && S_ISDIR(file->mode)) {
+ if (f_name_cmp(file, flist->files[prev_i]) == 0)
+ j = prev_i;
+ else if (protocol_version >= 29 && S_ISDIR(file->mode)) {
int save_mode = file->mode;
/* Make sure that this directory doesn't duplicate a
* non-directory earlier in the list. */
- file->mode = S_IFREG;
flist->high = prev_i;
- is_dup = flist_find(flist, file) >= 0;
+ file->mode = S_IFREG;
+ j = flist_find(flist, file);
file->mode = save_mode;
- }
- if (is_dup) {
+ } else
+ j = -1;
+ if (j >= 0) {
+ struct file_struct *fp = flist->files[j];
+ int keep, drop;
+ /* If one is a dir and the other is not, we want to
+ * keep the dir because it might have contents in the
+ * list. */
+ if (S_ISDIR(file->mode) != S_ISDIR(fp->mode)) {
+ if (S_ISDIR(file->mode))
+ keep = i, drop = j;
+ else
+ keep = j, drop = i;
+ } else
+ keep = j, drop = i;
if (verbose > 1 && !am_server) {
rprintf(FINFO,
- "removing duplicate name %s from file list %d\n",
- safe_fname(f_name(file)), i);
+ "removing duplicate name %s from file list (%d)\n",
+ safe_fname(f_name(file)), drop);
}
/* Make sure that if we unduplicate '.', that we don't
* lose track of a user-specified top directory. */
- if (file->flags & FLAG_TOP_DIR)
- flist->files[prev_i]->flags |= FLAG_TOP_DIR;
+ if (flist->files[drop]->flags & FLAG_TOP_DIR)
+ flist->files[keep]->flags |= FLAG_TOP_DIR;
+
+ clear_file(drop, flist);
- clear_file(i, flist);
+ if (keep == i) {
+ if (flist->low == drop) {
+ for (j = drop + 1;
+ j < i && !flist->files[j]->basename;
+ j++) {}
+ flist->low = j;
+ }
+ prev_i = i;
+ }
} else
prev_i = i;
}
}
}
-static void output_flist(struct file_list *flist, const char *whose_list)
+
+static void output_flist(struct file_list *flist)
{
char uidbuf[16], gidbuf[16], depthbuf[16];
struct file_struct *file;
+ const char *who = who_am_i();
int i;
for (i = 0; i < flist->count; i++) {
if (!am_sender)
sprintf(depthbuf, "%d", file->dir.depth);
rprintf(FINFO, "[%s] i=%d %s %s%s%s%s mode=0%o len=%.0f%s%s flags=%x\n",
- whose_list, i, am_sender ? NS(file->dir.root) : depthbuf,
+ who, i, am_sender ? NS(file->dir.root) : depthbuf,
file->dirname ? safe_fname(file->dirname) : "",
file->dirname ? "/" : "", NS(file->basename),
S_ISDIR(file->mode) ? "/" : "", (int)file->mode,
if (!*c2) {
switch (state2) {
case s_DIR:
+ if (state1 == s_SLASH && sorting_flist) {
+ int j;
+ /* Optimize for future comparisons. */
+ for (j = 0;
+ j < sorting_flist->count;
+ j++) {
+ struct file_struct *fp
+ = sorting_flist->files[j];
+ if (fp->dirname == f2->dirname)
+ fp->dirname = f1->dirname;
+ }
+ }
state2 = s_SLASH;
c2 = (uchar*)"/";
break;
}
-static int is_backup_file(char *fn)
+/* Do a non-recursive scan of the named directory, possibly ignoring all
+ * exclude rules except for the daemon's. If "dlen" is >=0, it is the length
+ * of the dirname string, and also indicates that "dirname" is a MAXPATHLEN
+ * buffer (the functions we call will append names onto the end, but the old
+ * dir value will be restored on exit). */
+struct file_list *get_dirlist(char *dirname, int dlen,
+ int ignore_filter_rules)
{
- int k = strlen(fn) - backup_suffix_len;
- return k > 0 && strcmp(fn+k, backup_suffix) == 0;
-}
-
-
-/* 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). */
-void delete_in_dir(struct file_list *flist, char *fbuf,
- struct file_struct *file)
-{
- static int min_depth = MAXPATHLEN, cur_depth = -1;
- static void *filt_array[MAXPATHLEN/2+1];
- struct file_list *dir_list;
- STRUCT_STAT st;
- int dlen;
-
- if (!flist) {
- while (cur_depth >= min_depth)
- pop_local_filters(filt_array[cur_depth--]);
- min_depth = MAXPATHLEN;
- cur_depth = -1;
- return;
- }
- if (file->dir.depth >= MAXPATHLEN/2+1)
- return; /* Impossible... */
+ struct file_list *dirlist;
+ char dirbuf[MAXPATHLEN];
+ int save_recurse = recurse;
- if (max_delete && deletion_count >= max_delete)
- return;
-
- if (io_error && !(lp_ignore_errors(module_id) || ignore_errors)) {
- rprintf(FINFO,
- "IO error encountered -- skipping file deletion\n");
- max_delete = -1; /* avoid duplicating the above warning */
- return;
+ if (dlen < 0) {
+ dlen = strlcpy(dirbuf, dirname, MAXPATHLEN);
+ if (dlen >= MAXPATHLEN)
+ return NULL;
+ dirname = dirbuf;
}
- while (cur_depth >= file->dir.depth && cur_depth >= min_depth)
- pop_local_filters(filt_array[cur_depth--]);
- cur_depth = file->dir.depth;
- if (min_depth > cur_depth)
- min_depth = cur_depth;
- dlen = strlen(fbuf);
- filt_array[cur_depth] = push_local_filters(fbuf, dlen);
-
- if (link_stat(fbuf, &st, keep_dirlinks) < 0)
- return;
-
- if (one_file_system && file->flags & FLAG_TOP_DIR)
- filesystem_dev = st.st_dev;
-
- dir_list = flist_new(WITHOUT_HLINK, "delete_in_dir");
+ dirlist = flist_new(WITHOUT_HLINK, "get_dirlist");
recurse = 0;
- send_directory(-1, dir_list, fbuf, dlen);
- recurse = -1;
- fbuf[dlen] = '\0';
-
- if (verbose > 3)
- output_flist(dir_list, "delete");
-
- delete_missing(flist, dir_list, fbuf);
+ send_directory(ignore_filter_rules ? -2 : -1, dirlist, dirname, dlen);
+ recurse = save_recurse;
- flist_free(dir_list);
-}
-
-
-/* If an item in dir_list is not found in full_list, delete it from the
- * filesystem. */
-void delete_missing(struct file_list *full_list, struct file_list *dir_list,
- const char *dirname)
-{
- int i, mode;
+ clean_flist(dirlist, 0, 0);
- if (max_delete && deletion_count >= max_delete)
- return;
-
- if (verbose > 2)
- rprintf(FINFO, "delete_missing(%s)\n", safe_fname(dirname));
+ if (verbose > 3)
+ output_flist(dirlist);
- for (i = dir_list->count; i--; ) {
- if (!dir_list->files[i]->basename)
- continue;
- mode = dir_list->files[i]->mode;
- if (flist_find(full_list, dir_list->files[i]) < 0) {
- char *f = f_name(dir_list->files[i]);
- if (make_backups && (backup_dir || !is_backup_file(f))
- && !S_ISDIR(mode)) {
- make_backup(f);
- if (verbose) {
- rprintf(FINFO, "deleting %s\n",
- safe_fname(f));
- }
- } else if (S_ISDIR(mode))
- delete_file(f, DEL_DIR | DEL_FORCE_RECURSE);
- else
- delete_file(f, 0);
- deletion_count++;
- if (max_delete && deletion_count >= max_delete)
- break;
- }
- }
+ return dirlist;
}