extern OFF_T max_size;
extern int io_timeout;
extern int protocol_version;
+extern int fuzzy_basis;
extern int always_checksum;
extern char *partial_dir;
extern char *basis_dir[];
unmap_file(mapbuf);
}
+/* Try to find a filename in the same dir as "fname" with a similar name. */
+static int find_fuzzy(struct file_struct *file, struct file_list *dirlist)
+{
+ int fname_len, fname_suf_len;
+ const char *fname_suf, *fname = file->basename;
+ uint32 lowest_dist = 0x7FFFFFFF;
+ int j, lowest_j = -1;
+
+ fname_len = strlen(fname);
+ fname_suf = find_filename_suffix(fname, fname_len, &fname_suf_len);
+
+ for (j = 0; j < dirlist->count; j++) {
+ struct file_struct *fp = dirlist->files[j];
+ const char *suf, *name;
+ int len, suf_len;
+ uint32 dist;
+
+ if (!S_ISREG(fp->mode) || !fp->length
+ || fp->flags & FLAG_NO_FUZZY)
+ continue;
+
+ name = fp->basename;
+
+ if (fp->length == file->length
+ && fp->modtime == file->modtime) {
+ if (verbose > 4) {
+ rprintf(FINFO,
+ "fuzzy size/modtime match for %s\n",
+ name);
+ }
+ return j;
+ }
+
+ len = strlen(name);
+ suf = find_filename_suffix(name, len, &suf_len);
+
+ dist = fuzzy_distance(name, len, fname, fname_len);
+ /* Add some extra weight to how well the suffixes match. */
+ dist += fuzzy_distance(suf, suf_len, fname_suf, fname_suf_len)
+ * 10;
+ if (verbose > 4) {
+ rprintf(FINFO, "fuzzy distance for %s = %d.%05d\n",
+ name, (int)(dist>>16), (int)(dist&0xFFFF));
+ }
+ if (dist <= lowest_dist) {
+ lowest_dist = dist;
+ lowest_j = j;
+ }
+ }
+
+ return lowest_j;
+}
+
/* Acts on flist->file's ndx'th item, whose name is fname. If a directory,
* make sure it exists, and has the right permissions/timestamp info. For
struct file_struct *file, int ndx,
int f_out, int f_out_name)
{
+ static int missing_below = -1;
+ static char *fuzzy_dirname = NULL;
+ static struct file_list *fuzzy_dirlist = NULL;
+ struct file_struct *fuzzy_file = NULL;
int fd = -1, f_copy = -1;
STRUCT_STAT st, partial_st;
struct file_struct *back_file = NULL;
if (list_only)
return;
+ if (!fname) {
+ if (fuzzy_dirlist) {
+ flist_free(fuzzy_dirlist);
+ fuzzy_dirlist = NULL;
+ fuzzy_dirname = NULL;
+ }
+ if (missing_below >= 0) {
+ dry_run--;
+ missing_below = -1;
+ }
+ return;
+ }
+
if (verbose > 2) {
rprintf(FINFO, "recv_generator(%s,%d)\n",
safe_fname(fname), ndx);
return;
}
+ if (missing_below >= 0 && file->dir.depth <= missing_below) {
+ dry_run--;
+ missing_below = -1;
+ }
if (dry_run > 1) {
statret = -1;
stat_errno = ENOENT;
} else {
+ if (fuzzy_basis && S_ISREG(file->mode)) {
+ char *dn = file->dirname ? file->dirname : ".";
+ /* Yes, identical dirnames are guaranteed to have
+ * identical pointers at this point. */
+ if (fuzzy_dirname != dn) {
+ if (fuzzy_dirlist)
+ flist_free(fuzzy_dirlist);
+ fuzzy_dirname = dn;
+ fuzzy_dirlist = get_dirlist(fuzzy_dirname, 1);
+ }
+ }
+
statret = link_stat(fname, &st,
keep_dirlinks && S_ISDIR(file->mode));
stat_errno = errno;
delete_file(fname, DEL_TERSE);
statret = -1;
}
+ if (dry_run && statret != 0 && missing_below < 0) {
+ missing_below = file->dir.depth;
+ dry_run++;
+ }
if (statret != 0 && do_mkdir(fname,file->mode) != 0 && errno != EEXIST) {
if (!relative_paths || errno != ENOENT
|| create_directory_path(fname, orig_umask) < 0
}
if (preserve_links && S_ISLNK(file->mode)) {
-#if SUPPORT_LINKS
+#ifdef SUPPORT_LINKS
if (safe_symlinks && unsafe_symlink(file->u.link, fname)) {
if (verbose) {
rprintf(FINFO,
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf,
basis_dir[i], fname);
}
-#if HAVE_LINK
+#ifdef HAVE_LINK
if (link_dest && match_level == 3 && !dry_run) {
if (do_link(fnamecmpbuf, fname) < 0) {
if (verbose) {
} else
partialptr = NULL;
+ if (statret == -1 && fuzzy_basis && dry_run <= 1) {
+ int j = find_fuzzy(file, fuzzy_dirlist);
+ if (j >= 0) {
+ fuzzy_file = fuzzy_dirlist->files[j];
+ f_name_to(fuzzy_file, fnamecmpbuf);
+ if (verbose > 2) {
+ rprintf(FINFO, "fuzzy basis selected for %s: %s\n",
+ safe_fname(fname), safe_fname(fnamecmpbuf));
+ }
+ st.st_mode = fuzzy_file->mode;
+ st.st_size = fuzzy_file->length;
+ st.st_mtime = fuzzy_file->modtime;
+ statret = 0;
+ fnamecmp = fnamecmpbuf;
+ fnamecmp_type = FNAMECMP_FUZZY;
+ }
+ }
+
if (statret == -1) {
if (preserve_hard_links && hard_link_check(file, HL_SKIP))
return;
if (!compare_dest && fnamecmp_type <= FNAMECMP_BASIS_DIR_HIGH)
;
+ else if (fnamecmp_type == FNAMECMP_FUZZY)
+ ;
else if (unchanged_file(fnamecmp, file, &st)) {
if (fnamecmp_type == FNAMECMP_FNAME)
set_perms(fname, file, &st, PERMS_REPORT);
goto notify_others;
}
+ if (fuzzy_basis) {
+ int j = flist_find(fuzzy_dirlist, file);
+ if (j >= 0) /* don't use changing file as future fuzzy basis */
+ fuzzy_dirlist->files[j]->flags |= FLAG_NO_FUZZY;
+ }
+
/* open the file */
fd = do_open(fnamecmp, O_RDONLY, 0);
write_int(f_out, ndx);
if (protocol_version >= 29 && inplace && !read_batch)
write_byte(f_out, fnamecmp_type);
- if (f_out_name >= 0)
+ if (f_out_name >= 0) {
write_byte(f_out_name, fnamecmp_type);
+ if (fnamecmp_type == FNAMECMP_FUZZY) {
+ uchar lenbuf[3], *lb = lenbuf;
+ int len = strlen(fuzzy_file->basename);
+ if (len > 0x7F) {
+#if MAXPATHLEN > 0x7FFF
+ *lb++ = len / 0x10000 + 0x80;
+ *lb++ = len / 0x100;
+#else
+ *lb++ = len / 0x100 + 0x80;
+#endif
+ }
+ *lb = len;
+ write_buf(f_out_name, lenbuf, lb - lenbuf + 1);
+ write_buf(f_out_name, fuzzy_file->basename, len);
+ }
+ }
if (dry_run || read_batch)
return;
recv_generator(local_name ? local_name : f_name_to(file, fbuf),
flist, file, i, f_out, f_out_name);
}
+ recv_generator(NULL, NULL, NULL, 0, -1, -1);
if (delete_during)
delete_in_dir(NULL, NULL, NULL);
flist, file, i, -1, -1);
}
}
+ recv_generator(NULL, NULL, NULL, 0, -1, -1);
if (verbose > 2)
rprintf(FINFO,"generate_files finished\n");