The robust_rename() function now takes a "partialptr" arg that, if
[rsync/rsync.git] / util.c
diff --git a/util.c b/util.c
index 03134e9..d37ab61 100644 (file)
--- a/util.c
+++ b/util.c
@@ -31,8 +31,10 @@ extern int verbose;
 extern int dry_run;
 extern int module_id;
 extern int modify_window;
+extern int relative_paths;
+extern int human_readable;
 extern char *partial_dir;
-extern struct exclude_list_struct server_exclude_list;
+extern struct filter_list_struct server_filter_list;
 
 int sanitize_paths = 0;
 
@@ -79,7 +81,7 @@ int fd_pair(int fd[2])
 {
        int ret;
 
-#if HAVE_SOCKETPAIR
+#ifdef HAVE_SOCKETPAIR
        ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
 #else
        ret = pipe(fd);
@@ -120,7 +122,7 @@ void out_of_memory(char *str)
        exit_cleanup(RERR_MALLOC);
 }
 
-void overflow(char *str)
+void overflow_exit(char *str)
 {
        rprintf(FERROR, "ERROR: buffer overflow in %s\n", str);
        exit_cleanup(RERR_MALLOC);
@@ -128,10 +130,12 @@ void overflow(char *str)
 
 
 
-int set_modtime(char *fname, time_t modtime)
+int set_modtime(char *fname, time_t modtime, mode_t mode)
 {
-       if (dry_run)
-               return 0;
+#if !defined HAVE_LUTIMES || !defined HAVE_UTIMES
+       if (S_ISLNK(mode))
+               return 1;
+#endif
 
        if (verbose > 2) {
                rprintf(FINFO, "set modtime of %s to (%ld) %s",
@@ -139,24 +143,33 @@ int set_modtime(char *fname, time_t modtime)
                        asctime(localtime(&modtime)));
        }
 
+       if (dry_run)
+               return 0;
+
        {
-#ifdef HAVE_UTIMBUF
+#ifdef HAVE_UTIMES
+               struct timeval t[2];
+               t[0].tv_sec = time(NULL);
+               t[0].tv_usec = 0;
+               t[1].tv_sec = modtime;
+               t[1].tv_usec = 0;
+# ifdef HAVE_LUTIMES
+               if (S_ISLNK(mode))
+                       return lutimes(fname, t);
+# endif
+               return utimes(fname, t);
+#elif defined HAVE_UTIMBUF
                struct utimbuf tbuf;
                tbuf.actime = time(NULL);
                tbuf.modtime = modtime;
                return utime(fname,&tbuf);
-#elif defined(HAVE_UTIME)
+#elif defined HAVE_UTIME
                time_t t[2];
                t[0] = time(NULL);
                t[1] = modtime;
                return utime(fname,t);
 #else
-               struct timeval t[2];
-               t[0].tv_sec = time(NULL);
-               t[0].tv_usec = 0;
-               t[1].tv_sec = modtime;
-               t[1].tv_usec = 0;
-               return utimes(fname,t);
+#error No file-time-modification routine found!
 #endif
        }
 }
@@ -197,7 +210,7 @@ int create_directory_path(char *fname, int base_umask)
  *
  * Derived from GNU C's cccp.c.
  */
-static int full_write(int desc, char *ptr, size_t len)
+int full_write(int desc, char *ptr, size_t len)
 {
        int total_written;
 
@@ -245,8 +258,9 @@ static int safe_read(int desc, char *ptr, size_t len)
 
 /** Copy a file.
  *
- * This is used in conjunction with the --temp-dir option */
-int copy_file(char *source, char *dest, mode_t mode)
+ * This is used in conjunction with the --temp-dir, --backup, and
+ * --copy-dest options. */
+int copy_file(const char *source, const char *dest, mode_t mode)
 {
        int ifd;
        int ofd;
@@ -315,7 +329,7 @@ int copy_file(char *source, char *dest, mode_t mode)
  * --delete trying to remove old .rsyncNNN files, hence it renames it
  * each time.
  **/
-int robust_unlink(char *fname)
+int robust_unlink(const char *fname)
 {
 #ifndef ETXTBSY
        return do_unlink(fname);
@@ -363,8 +377,11 @@ int robust_unlink(char *fname)
 }
 
 /* Returns 0 on successful rename, 1 if we successfully copied the file
- * across filesystems, -2 if copy_file() failed, and -1 on other errors. */
-int robust_rename(char *from, char *to, int mode)
+ * across filesystems, -2 if copy_file() failed, and -1 on other errors.
+ * If partialptr is not NULL and we need to do a copy, copy the file into
+ * the active partial-dir instead of over the destination file. */
+int robust_rename(char *from, char *to, char *partialptr,
+                 int mode)
 {
        int tries = 4;
 
@@ -380,6 +397,11 @@ int robust_rename(char *from, char *to, int mode)
                        break;
 #endif
                case EXDEV:
+                       if (partialptr) {
+                               if (!handle_partial_dir(partialptr,PDIR_CREATE))
+                                       return -1;
+                               to = partialptr;
+                       }
                        if (copy_file(from, to, mode) != 0)
                                return -2;
                        do_unlink(from);
@@ -479,14 +501,14 @@ int lock_range(int fd, int offset, int len)
        return fcntl(fd,F_SETLK,&lock) == 0;
 }
 
-static int exclude_server_path(char *arg)
+static int filter_server_path(char *arg)
 {
        char *s;
 
-       if (server_exclude_list.head) {
+       if (server_filter_list.head) {
                for (s = arg; (s = strchr(s, '/')) != NULL; ) {
                        *s = '\0';
-                       if (check_exclude(&server_exclude_list, arg, 1) < 0) {
+                       if (check_filter(&server_filter_list, arg, 1) < 0) {
                                /* We must leave arg truncated! */
                                return 1;
                        }
@@ -502,7 +524,7 @@ static void glob_expand_one(char *s, char ***argv_ptr, int *argc_ptr,
        char **argv = *argv_ptr;
        int argc = *argc_ptr;
        int maxargs = *maxargs_ptr;
-#if !(defined(HAVE_GLOB) && defined(HAVE_GLOB_H))
+#if !defined HAVE_GLOB || !defined HAVE_GLOB_H
        if (argc == maxargs) {
                maxargs += MAX_ARGS;
                if (!(argv = realloc_array(argv, char *, maxargs)))
@@ -513,10 +535,9 @@ static void glob_expand_one(char *s, char ***argv_ptr, int *argc_ptr,
        if (!*s)
                s = ".";
        s = argv[argc++] = strdup(s);
-       exclude_server_path(s);
+       filter_server_path(s);
 #else
        glob_t globbuf;
-       int i;
 
        if (maxargs <= argc)
                return;
@@ -529,7 +550,7 @@ static void glob_expand_one(char *s, char ***argv_ptr, int *argc_ptr,
                s = strdup(s);
 
        memset(&globbuf, 0, sizeof globbuf);
-       if (!exclude_server_path(s))
+       if (!filter_server_path(s))
                glob(s, 0, NULL, &globbuf);
        if (MAX((int)globbuf.gl_pathc, 1) > maxargs - argc) {
                maxargs += globbuf.gl_pathc + MAX_ARGS;
@@ -541,9 +562,9 @@ static void glob_expand_one(char *s, char ***argv_ptr, int *argc_ptr,
        if (globbuf.gl_pathc == 0)
                argv[argc++] = s;
        else {
-               int j = globbuf.gl_pathc;
+               int i;
                free(s);
-               for (i = 0; i < j; i++) {
+               for (i = 0; i < (int)globbuf.gl_pathc; i++) {
                        if (!(argv[argc++] = strdup(globbuf.gl_pathv[i])))
                                out_of_memory("glob_expand_one");
                }
@@ -664,11 +685,11 @@ int count_dir_elements(const char *p)
        return cnt;
 }
 
-/* Turns multiple adjacent slashes into a single slash; gets rid of "./"
- * elements; collapses ".." elements except for those at the start of the
- * string; removes a trailing slash.  If the resulting name would be empty,
- * change it into a ".". */
-unsigned int clean_fname(char *name)
+/* Turns multiple adjacent slashes into a single slash, gets rid of "./"
+ * elements (but not a trailing dot dir), removes a trailing slash, and
+ * optionally collapses ".." elements (except for those at the start of the
+ * string).  If the resulting name would be empty, change it into a ".". */
+unsigned int clean_fname(char *name, BOOL collapse_dot_dot)
 {
        char *limit = name - 1, *t = name, *f = name;
        int anchored;
@@ -691,7 +712,8 @@ unsigned int clean_fname(char *name)
                                continue;
                        }
                        /* collapse ".." dirs */
-                       if (f[1] == '.' && (f[2] == '/' || !f[2])) {
+                       if (collapse_dot_dot
+                           && f[1] == '.' && (f[2] == '/' || !f[2])) {
                                char *s = t - 1;
                                if (s == name && anchored) {
                                        f += 2;
@@ -703,9 +725,7 @@ unsigned int clean_fname(char *name)
                                        f += 2;
                                        continue;
                                }
-                               *t++ = *f++;
-                               *t++ = *f++;
-                               limit = t;
+                               limit = t + 2;
                        }
                }
                while (*f && (*t++ = *f++) != '/') {}
@@ -731,17 +751,21 @@ unsigned int clean_fname(char *name)
  * The rootdir string contains a value to use in place of a leading slash.
  * Specify NULL to get the default of lp_path(module_id).
  *
- * If depth is > 0, it is a count of how many '..'s to allow at the start
- * of the path.
+ * If depth is >= 0, it is a count of how many '..'s to allow at the start
+ * of the path.  Use -1 to allow unlimited depth.
  *
- * We call clean_fname() to clean up the path, but we preserve a trailing
- * slash because that is sometimes significant on command-line arguments.
- */
+ * We also clean the path in a manner similar to clean_fname() but with a
+ * few differences: 
+ *
+ * Turns multiple adjacent slashes into a single slash, gets rid of "." dir
+ * elements (INCLUDING a trailing dot dir), PRESERVES a trailing slash, and
+ * ALWAYS collapses ".." elements (except for those at the start of the
+ * string up to "depth" deep).  If the resulting name would be empty,
+ * change it into a ".". */
 char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth)
 {
        char *start, *sanp;
-       int allowdotdot = 0;
-       int rlen = 0;
+       int rlen = 0, leave_one_dotdir = relative_paths;
 
        if (dest != p) {
                int plen = strlen(p);
@@ -765,33 +789,29 @@ char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth)
        }
 
        start = sanp = dest + rlen;
-       while (*p == '/') {
-               /* remove leading slashes */
-               p++;
-       }
        while (*p != '\0') {
+               /* discard leading or extra slashes */
+               if (*p == '/') {
+                       p++;
+                       continue;
+               }
                /* this loop iterates once per filename component in p.
                 * both p (and sanp if the original had a slash) should
                 * always be left pointing after a slash
                 */
                if (*p == '.' && (p[1] == '/' || p[1] == '\0')) {
-                       /* skip "." component */
-                       while (*++p == '/') {
-                               /* skip following slashes */
-                               ;
+                       if (leave_one_dotdir && p[1])
+                               leave_one_dotdir = 0;
+                       else {
+                               /* skip "." component */
+                               p++;
+                               continue;
                        }
-                       continue;
                }
-               allowdotdot = 0;
                if (*p == '.' && p[1] == '.' && (p[2] == '/' || p[2] == '\0')) {
                        /* ".." component followed by slash or end */
-                       if (depth > 0 && sanp == start) {
-                               /* allow depth levels of .. at the beginning */
-                               --depth;
-                               allowdotdot = 1;
-                       } else {
+                       if (depth <= 0 || sanp != start) {
                                p += 2;
-                               while (*p == '/') p++;
                                if (sanp != start) {
                                        /* back up sanp one level */
                                        --sanp; /* now pointing at slash */
@@ -802,22 +822,13 @@ char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth)
                                }
                                continue;
                        }
-               }
-               while (1) {
-                       /* copy one component through next slash */
-                       *sanp++ = *p++;
-                       if (*p == '\0' || p[-1] == '/') {
-                               while (*p == '/') {
-                                       /* skip multiple slashes */
-                                       p++;
-                               }
-                               break;
-                       }
-               }
-               if (allowdotdot) {
+                       /* allow depth levels of .. at the beginning */
+                       depth--;
                        /* move the virtual beginning to leave the .. alone */
-                       start = sanp;
+                       start = sanp + 3;
                }
+               /* copy one component through next slash */
+               while (*p && (*sanp++ = *p++) != '/') {}
        }
        if (sanp == dest) {
                /* ended up with nothing, so put in "." component */
@@ -869,7 +880,7 @@ int push_dir(char *dir)
                curr_dir_len += len;
        }
 
-       curr_dir_len = clean_fname(curr_dir);
+       curr_dir_len = clean_fname(curr_dir, 1);
 
        return 1;
 }
@@ -890,30 +901,6 @@ int pop_dir(char *dir)
        return 1;
 }
 
-/**
- * Return the filename, turning any newlines into '?'s.  This ensures that
- * outputting it on a line of its own cannot generate an empty line.  This
- * function can handle only 2 names at a time!
- **/
-const char *safe_fname(const char *fname)
-{
-       static char fbuf1[MAXPATHLEN], fbuf2[MAXPATHLEN];
-       static char *fbuf = fbuf2;
-       char *nl = strchr(fname, '\n');
-
-       if (!nl)
-               return fname;
-
-       fbuf = fbuf == fbuf1 ? fbuf2 : fbuf1;
-       strlcpy(fbuf, fname, MAXPATHLEN);
-       nl = fbuf + (nl - (char *)fname);
-       do {
-               *nl = '?';
-       } while ((nl = strchr(nl+1, '\n')) != NULL);
-
-       return fbuf;
-}
-
 /**
  * Return a quoted string with the full pathname of the indicated filename.
  * The string " (in MODNAME)" may also be appended.  The returned pointer
@@ -928,30 +915,25 @@ char *full_fname(const char *fn)
        if (result)
                free(result);
 
-       fn = safe_fname(fn);
        if (*fn == '/')
                p1 = p2 = "";
        else {
                p1 = curr_dir;
-               p2 = "/";
+               for (p2 = p1; *p2 == '/'; p2++) {}
+               if (*p2)
+                       p2 = "/";
        }
        if (module_id >= 0) {
                m1 = " (in ";
                m2 = lp_name(module_id);
                m3 = ")";
-               if (*p1) {
+               if (p1 == curr_dir) {
                        if (!lp_use_chroot(module_id)) {
                                char *p = lp_path(module_id);
                                if (*p != '/' || p[1])
                                        p1 += strlen(p);
                        }
-                       if (!*p1)
-                               p2++;
-                       else
-                               p1++;
                }
-               else
-                       fn++;
        } else
                m1 = m2 = m3 = "";
 
@@ -980,9 +962,17 @@ char *partial_dir_fname(const char *fname)
                fn = fname;
        if ((int)pathjoin(t, sz, partial_dir, fn) >= sz)
                return NULL;
-       if (server_exclude_list.head
-           && check_exclude(&server_exclude_list, partial_fname, 0) < 0)
-               return NULL;
+       if (server_filter_list.head) {
+               static int len;
+               if (!len)
+                       len = strlen(partial_dir);
+               t[len] = '\0';
+               if (check_filter(&server_filter_list, partial_fname, 1) < 0)
+                       return NULL;
+               t[len] = '/';
+               if (check_filter(&server_filter_list, partial_fname, 0) < 0)
+                       return NULL;
+       }
 
        return partial_fname;
 }
@@ -1004,11 +994,7 @@ int handle_partial_dir(const char *fname, int create)
        dir = partial_fname;
        if (create) {
                STRUCT_STAT st;
-#if SUPPORT_LINKS
                int statret = do_lstat(dir, &st);
-#else
-               int statret = do_stat(dir, &st);
-#endif
                if (statret == 0 && !S_ISDIR(st.st_mode)) {
                        if (do_unlink(dir) < 0)
                                return 0;
@@ -1023,22 +1009,6 @@ int handle_partial_dir(const char *fname, int create)
        return 1;
 }
 
-/** We need to supply our own strcmp function for file list comparisons
-   to ensure that signed/unsigned usage is consistent between machines. */
-int u_strcmp(const char *cs1, const char *cs2)
-{
-       const uchar *s1 = (const uchar *)cs1;
-       const uchar *s2 = (const uchar *)cs2;
-
-       while (*s1 && *s2 && (*s1 == *s2)) {
-               s1++; s2++;
-       }
-
-       return (int)*s1 - (int)*s2;
-}
-
-
-
 /**
  * Determine if a symlink points outside the current directory tree.
  * This is considered "unsafe" because e.g. when mirroring somebody
@@ -1103,6 +1073,63 @@ int unsafe_symlink(const char *dest, const char *src)
        return (depth < 0);
 }
 
+/* Return the int64 number as a string.  If the --human-readable option was
+ * specified, we may output the number in K, M, or G units.  We can return
+ * up to 4 buffers at a time. */
+char *human_num(int64 num)
+{
+       static char bufs[4][128]; /* more than enough room */
+       static unsigned int n;
+       char *s;
+
+       n = (n + 1) % (sizeof bufs / sizeof bufs[0]);
+
+       if (human_readable) {
+               char units = '\0';
+               int mult = human_readable == 1 ? 1024 : 1000;
+               double dnum = 0;
+               if (num > mult*mult*mult) {
+                       dnum = (double)num / (mult*mult*mult);
+                       units = 'G';
+               } else if (num > mult*mult) {
+                       dnum = (double)num / (mult*mult);
+                       units = 'M';
+               } else if (num > mult) {
+                       dnum = (double)num / mult;
+                       units = 'K';
+               }
+               if (units) {
+                       sprintf(bufs[n], "%.2f%c", dnum, units);
+                       return bufs[n];
+               }
+       }
+
+       s = bufs[n] + sizeof bufs[0] - 1;
+       *s = '\0';
+
+       if (!num)
+               *--s = '0';
+       while (num) {
+               *--s = (num % 10) + '0';
+               num /= 10;
+       }
+       return s;
+}
+
+/* Return the double number as a string.  If the --human-readable option was
+ * specified, we may output the number in K, M, or G units.  We use a buffer
+ * from human_num() to return our result. */
+char *human_dnum(double dnum, int decimal_digits)
+{
+       char *buf = human_num(dnum);
+       int len = strlen(buf);
+       if (isdigit(*(uchar*)(buf+len-1))) {
+               /* There's extra room in buf prior to the start of the num. */
+               buf -= decimal_digits + 1;
+               snprintf(buf, len + decimal_digits + 2, "%.*f", decimal_digits, dnum);
+       }
+       return buf;
+}
 
 /**
  * Return the date and time as a string
@@ -1231,3 +1258,110 @@ void *_realloc_array(void *ptr, unsigned int size, unsigned long num)
                return malloc(size * num);
        return realloc(ptr, size * num);
 }
+
+/* Take a filename and filename length and return the most significant
+ * filename suffix we can find.  This ignores suffixes such as "~",
+ * ".bak", ".orig", ".~1~", etc. */
+const char *find_filename_suffix(const char *fn, int fn_len, int *len_ptr)
+{
+       const char *suf, *s;
+       BOOL had_tilde;
+       int s_len;
+
+       /* One or more dots at the start aren't a suffix. */
+       while (fn_len && *fn == '.') fn++, fn_len--;
+
+       /* Ignore the ~ in a "foo~" filename. */
+       if (fn_len > 1 && fn[fn_len-1] == '~')
+               fn_len--, had_tilde = True;
+       else
+               had_tilde = False;
+
+       /* Assume we don't find an suffix. */
+       suf = "";
+       *len_ptr = 0;
+
+       /* Find the last significant suffix. */
+       for (s = fn + fn_len; fn_len > 1; ) {
+               while (*--s != '.' && s != fn) {}
+               if (s == fn)
+                       break;
+               s_len = fn_len - (s - fn);
+               fn_len = s - fn;
+               if (s_len == 4) {
+                       if (strcmp(s+1, "bak") == 0
+                        || strcmp(s+1, "old") == 0)
+                               continue;
+               } else if (s_len == 5) {
+                       if (strcmp(s+1, "orig") == 0)
+                               continue;
+               } else if (s_len > 2 && had_tilde
+                   && s[1] == '~' && isdigit(*(uchar*)(s+2)))
+                       continue;
+               *len_ptr = s_len;
+               suf = s;
+               if (s_len == 1)
+                       break;
+               /* Determine if the suffix is all digits. */
+               for (s++, s_len--; s_len > 0; s++, s_len--) {
+                       if (!isdigit(*(uchar*)s))
+                               return suf;
+               }
+               /* An all-digit suffix may not be that signficant. */
+               s = suf;
+       }
+
+       return suf;
+}
+
+/* This is an implementation of the Levenshtein distance algorithm.  It
+ * was implemented to avoid needing a two-dimensional matrix (to save
+ * memory).  It was also tweaked to try to factor in the ASCII distance
+ * between changed characters as a minor distance quantity.  The normal
+ * Levenshtein units of distance (each signifying a single change between
+ * the two strings) are defined as a "UNIT". */
+
+#define UNIT (1 << 16)
+
+uint32 fuzzy_distance(const char *s1, int len1, const char *s2, int len2)
+{
+       uint32 a[MAXPATHLEN], diag, above, left, diag_inc, above_inc, left_inc;
+       int32 cost;
+       int i1, i2;
+
+       if (!len1 || !len2) {
+               if (!len1) {
+                       s1 = s2;
+                       len1 = len2;
+               }
+               for (i1 = 0, cost = 0; i1 < len1; i1++)
+                       cost += s1[i1];
+               return (int32)len1 * UNIT + cost;
+       }
+
+       for (i2 = 0; i2 < len2; i2++)
+               a[i2] = (i2+1) * UNIT;
+
+       for (i1 = 0; i1 < len1; i1++) {
+               diag = i1 * UNIT;
+               above = (i1+1) * UNIT;
+               for (i2 = 0; i2 < len2; i2++) {
+                       left = a[i2];
+                       if ((cost = *((uchar*)s1+i1) - *((uchar*)s2+i2)) != 0) {
+                               if (cost < 0)
+                                       cost = UNIT - cost;
+                               else
+                                       cost = UNIT + cost;
+                       }
+                       diag_inc = diag + cost;
+                       left_inc = left + UNIT + *((uchar*)s1+i1);
+                       above_inc = above + UNIT + *((uchar*)s2+i2);
+                       a[i2] = above = left < above
+                             ? (left_inc < diag_inc ? left_inc : diag_inc)
+                             : (above_inc < diag_inc ? above_inc : diag_inc);
+                       diag = left;
+               }
+       }
+
+       return a[len2-1];
+}