Use the new sanitize_path() calling syntax.
[rsync/rsync.git] / util.c
diff --git a/util.c b/util.c
index be639e4..528efd4 100644 (file)
--- a/util.c
+++ b/util.c
 #include "rsync.h"
 
 extern int verbose;
+extern int dry_run;
+extern int module_id;
+extern int modify_window;
+extern char *partial_dir;
 extern struct exclude_list_struct server_exclude_list;
 
 int sanitize_paths = 0;
@@ -126,13 +130,12 @@ void overflow(char *str)
 
 int set_modtime(char *fname, time_t modtime)
 {
-       extern int dry_run;
        if (dry_run)
                return 0;
 
        if (verbose > 2) {
                rprintf(FINFO, "set modtime of %s to (%ld) %s",
-                       fname, (long) modtime,
+                       fname, (long)modtime,
                        asctime(localtime(&modtime)));
        }
 
@@ -252,41 +255,46 @@ int copy_file(char *source, char *dest, mode_t mode)
 
        ifd = do_open(source, O_RDONLY, 0);
        if (ifd == -1) {
-               rprintf(FERROR,"open %s: %s\n",
-                       source,strerror(errno));
+               rsyserr(FERROR, errno, "open %s", full_fname(source));
                return -1;
        }
 
        if (robust_unlink(dest) && errno != ENOENT) {
-               rprintf(FERROR,"unlink %s: %s\n",
-                       dest,strerror(errno));
+               rsyserr(FERROR, errno, "unlink %s", full_fname(dest));
                return -1;
        }
 
        ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode);
        if (ofd == -1) {
-               rprintf(FERROR,"open %s: %s\n",
-                       dest,strerror(errno));
+               rsyserr(FERROR, errno, "open %s", full_fname(dest));
                close(ifd);
                return -1;
        }
 
        while ((len = safe_read(ifd, buf, sizeof buf)) > 0) {
                if (full_write(ofd, buf, len) < 0) {
-                       rprintf(FERROR,"write %s: %s\n",
-                               dest,strerror(errno));
+                       rsyserr(FERROR, errno, "write %s", full_fname(dest));
                        close(ifd);
                        close(ofd);
                        return -1;
                }
        }
 
-       close(ifd);
-       close(ofd);
-
        if (len < 0) {
-               rprintf(FERROR,"read %s: %s\n",
-                       source,strerror(errno));
+               rsyserr(FERROR, errno, "read %s", full_fname(source));
+               close(ifd);
+               close(ofd);
+               return -1;
+       }
+
+       if (close(ifd) < 0) {
+               rsyserr(FINFO, errno, "close failed on %s",
+                       full_fname(source));
+       }
+
+       if (close(ofd) < 0) {
+               rsyserr(FERROR, errno, "close failed on %s",
+                       full_fname(dest));
                return -1;
        }
 
@@ -432,7 +440,8 @@ void kill_all(int sig)
 int name_to_uid(char *name, uid_t *uid)
 {
        struct passwd *pass;
-       if (!name || !*name) return 0;
+       if (!name || !*name)
+               return 0;
        pass = getpwnam(name);
        if (pass) {
                *uid = pass->pw_uid;
@@ -445,7 +454,8 @@ int name_to_uid(char *name, uid_t *uid)
 int name_to_gid(char *name, gid_t *gid)
 {
        struct group *grp;
-       if (!name || !*name) return 0;
+       if (!name || !*name)
+               return 0;
        grp = getgrnam(name);
        if (grp) {
                *gid = grp->gr_gid;
@@ -476,7 +486,7 @@ static int exclude_server_path(char *arg)
        if (server_exclude_list.head) {
                for (s = arg; (s = strchr(s, '/')) != NULL; ) {
                        *s = '\0';
-                       if (check_exclude(&server_exclude_list, arg, 1)) {
+                       if (check_exclude(&server_exclude_list, arg, 1) < 0) {
                                /* We must leave arg truncated! */
                                return 1;
                        }
@@ -486,75 +496,91 @@ static int exclude_server_path(char *arg)
        return 0;
 }
 
-static void glob_expand_one(char *s, char **argv, int *argc, int maxargs)
+static void glob_expand_one(char *s, char ***argv_ptr, int *argc_ptr,
+                           int *maxargs_ptr)
 {
+       char **argv = *argv_ptr;
+       int argc = *argc_ptr;
+       int maxargs = *maxargs_ptr;
 #if !(defined(HAVE_GLOB) && defined(HAVE_GLOB_H))
-       if (!*s) s = ".";
-       s = argv[*argc] = strdup(s);
+       if (argc == maxargs) {
+               maxargs += MAX_ARGS;
+               if (!(argv = realloc_array(argv, char *, maxargs)))
+                       out_of_memory("glob_expand_one");
+               *argv_ptr = argv;
+               *maxargs_ptr = maxargs;
+       }
+       if (!*s)
+               s = ".";
+       s = argv[argc++] = strdup(s);
        exclude_server_path(s);
-       (*argc)++;
 #else
-       extern int sanitize_paths;
        glob_t globbuf;
        int i;
 
-       if (!*s) s = ".";
+       if (maxargs <= argc)
+               return;
+       if (!*s)
+               s = ".";
 
-       s = argv[*argc] = strdup(s);
-       if (sanitize_paths) {
+       s = strdup(s);
+       if (sanitize_paths)
                sanitize_path(s, NULL);
-       }
 
        memset(&globbuf, 0, sizeof globbuf);
        if (!exclude_server_path(s))
                glob(s, 0, NULL, &globbuf);
-       if (globbuf.gl_pathc == 0) {
-               (*argc)++;
-               globfree(&globbuf);
-               return;
+       if (MAX((int)globbuf.gl_pathc, 1) > maxargs - argc) {
+               maxargs += globbuf.gl_pathc + MAX_ARGS;
+               if (!(argv = realloc_array(argv, char *, maxargs)))
+                       out_of_memory("glob_expand_one");
+               *argv_ptr = argv;
+               *maxargs_ptr = maxargs;
        }
-       for (i = 0; i < maxargs - *argc && i < (int)globbuf.gl_pathc; i++) {
-               if (i == 0)
-                       free(s);
-               argv[*argc + i] = strdup(globbuf.gl_pathv[i]);
-               if (!argv[*argc + i])
-                       out_of_memory("glob_expand");
+       if (globbuf.gl_pathc == 0)
+               argv[argc++] = s;
+       else {
+               int j = globbuf.gl_pathc;
+               free(s);
+               for (i = 0; i < j; i++) {
+                       if (!(argv[argc++] = strdup(globbuf.gl_pathv[i])))
+                               out_of_memory("glob_expand_one");
+               }
        }
        globfree(&globbuf);
-       *argc += i;
 #endif
+       *argc_ptr = argc;
 }
 
 /* This routine is only used in daemon mode. */
-void glob_expand(char *base1, char **argv, int *argc, int maxargs)
+void glob_expand(char *base1, char ***argv_ptr, int *argc_ptr, int *maxargs_ptr)
 {
-       char *s = argv[*argc];
+       char *s = (*argv_ptr)[*argc_ptr];
        char *p, *q;
        char *base = base1;
        int base_len = strlen(base);
 
-       if (!s || !*s) return;
+       if (!s || !*s)
+               return;
 
        if (strncmp(s, base, base_len) == 0)
                s += base_len;
 
-       s = strdup(s);
-       if (!s) out_of_memory("glob_expand");
+       if (!(s = strdup(s)))
+               out_of_memory("glob_expand");
 
-       if (asprintf(&base," %s/", base1) <= 0) out_of_memory("glob_expand");
+       if (asprintf(&base," %s/", base1) <= 0)
+               out_of_memory("glob_expand");
        base_len++;
 
-       q = s;
-       while ((p = strstr(q,base)) != NULL && *argc < maxargs) {
-               /* split it at this point */
-               *p = 0;
-               glob_expand_one(q, argv, argc, maxargs);
-               q = p + base_len;
+       for (q = s; *q; q = p + base_len) {
+               if ((p = strstr(q, base)) != NULL)
+                       *p = '\0'; /* split it at this point */
+               glob_expand_one(q, argv_ptr, argc_ptr, maxargs_ptr);
+               if (!p)
+                       break;
        }
 
-       if (*q && *argc < maxargs)
-               glob_expand_one(q, argv, argc, maxargs);
-
        free(s);
        free(base);
 }
@@ -565,8 +591,8 @@ void glob_expand(char *base1, char **argv, int *argc, int maxargs)
 void strlower(char *s)
 {
        while (*s) {
-               if (isupper(* (unsigned char *) s))
-                       *s = tolower(* (unsigned char *) s);
+               if (isupper(*(unsigned char *)s))
+                       *s = tolower(*(unsigned char *)s);
                s++;
        }
 }
@@ -629,7 +655,8 @@ void clean_fname(char *name)
        int l;
        int modified = 1;
 
-       if (!name) return;
+       if (!name)
+               return;
 
        while (modified) {
                modified = 0;
@@ -698,9 +725,12 @@ void sanitize_path(char *p, char *reldir)
        int allowdotdot = 0;
 
        if (reldir) {
-               depth++;
+               int new_component = 1;
                while (*reldir) {
-                       if (*reldir++ == '/') {
+                       if (*reldir++ == '/')
+                               new_component = 1;
+                       else if (new_component) {
+                               new_component = 0;
                                depth++;
                        }
                }
@@ -805,7 +835,6 @@ char *alloc_sanitize_path(const char *path, const char *rootdir)
                        rlen--;
                buf[rlen] = '\0';
        }
-       fprintf(stderr, "p:`%s'\n", buf);
 
        return buf;
 }
@@ -872,14 +901,37 @@ 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
  * remains valid until the next time full_fname() is called.
  **/
-char *full_fname(char *fn)
+char *full_fname(const char *fn)
 {
-       extern int module_id;
        static char *result = NULL;
        char *m1, *m2, *m3;
        char *p1, *p2;
@@ -887,6 +939,7 @@ char *full_fname(char *fn)
        if (result)
                free(result);
 
+       fn = safe_fname(fn);
        if (*fn == '/')
                p1 = p2 = "";
        else {
@@ -918,6 +971,69 @@ char *full_fname(char *fn)
        return result;
 }
 
+static char partial_fname[MAXPATHLEN];
+
+char *partial_dir_fname(const char *fname)
+{
+       char *t = partial_fname;
+       int sz = sizeof partial_fname;
+       const char *fn;
+
+       if ((fn = strrchr(fname, '/')) != NULL) {
+               fn++;
+               if (*partial_dir != '/') {
+                       int len = fn - fname;
+                       strncpy(t, fname, len); /* safe */
+                       t += len;
+                       sz -= len;
+               }
+       } else
+               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;
+
+       return partial_fname;
+}
+
+/* If no --partial-dir option was specified, we don't need to do anything
+ * (the partial-dir is essentially '.'), so just return success. */
+int handle_partial_dir(const char *fname, int create)
+{
+       char *fn, *dir;
+
+       if (fname != partial_fname)
+               return 1;
+       if (!create && *partial_dir == '/')
+               return 1;
+       if (!(fn = strrchr(partial_fname, '/')))
+               return 1;
+
+       *fn = '\0';
+       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;
+                       statret = -1;
+               }
+               if (statret < 0 && do_mkdir(dir, 0700) < 0)
+                       return 0;
+       } else
+               do_rmdir(dir);
+       *fn = '/';
+
+       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)
@@ -964,7 +1080,8 @@ int unsafe_symlink(const char *dest, const char *src)
        int depth = 0;
 
        /* all absolute and null symlinks are unsafe */
-       if (!dest || !*dest || *dest == '/') return 1;
+       if (!dest || !*dest || *dest == '/')
+               return 1;
 
        /* find out what our safety margin is */
        for (name = src; (slash = strchr(name, '/')) != 0; name = slash+1) {
@@ -1032,7 +1149,6 @@ int msleep(int t)
        struct timeval tval, t1, t2;
 
        gettimeofday(&t1, NULL);
-       gettimeofday(&t2, NULL);
 
        while (tdiff < t) {
                tval.tv_sec = (t-tdiff)/1000;
@@ -1063,13 +1179,13 @@ int msleep(int t)
  **/
 int cmp_modtime(time_t file1, time_t file2)
 {
-       extern int modify_window;
-
        if (file2 > file1) {
-               if (file2 - file1 <= modify_window) return 0;
+               if (file2 - file1 <= modify_window)
+                       return 0;
                return -1;
        }
-       if (file1 - file2 <= modify_window) return 0;
+       if (file1 - file2 <= modify_window)
+               return 0;
        return 1;
 }