Use the new sanitize_path() calling syntax.
[rsync/rsync.git] / util.c
diff --git a/util.c b/util.c
index f2158a0..528efd4 100644 (file)
--- a/util.c
+++ b/util.c
@@ -31,6 +31,7 @@ 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;
@@ -495,12 +496,20 @@ static int exclude_server_path(char *arg)
        return 0;
 }
 
-static void glob_expand_one(char *s, char **argv, int *argc_ptr, 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 (maxargs <= argc)
-               return;
+       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);
@@ -521,12 +530,17 @@ static void glob_expand_one(char *s, char **argv, int *argc_ptr, int maxargs)
        memset(&globbuf, 0, sizeof globbuf);
        if (!exclude_server_path(s))
                glob(s, 0, NULL, &globbuf);
+       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;
+       }
        if (globbuf.gl_pathc == 0)
                argv[argc++] = s;
        else {
                int j = globbuf.gl_pathc;
-               if (j > maxargs - argc)
-                       j = maxargs - argc;
                free(s);
                for (i = 0; i < j; i++) {
                        if (!(argv[argc++] = strdup(globbuf.gl_pathv[i])))
@@ -539,9 +553,9 @@ static void glob_expand_one(char *s, char **argv, int *argc_ptr, int maxargs)
 }
 
 /* This routine is only used in daemon mode. */
-void glob_expand(char *base1, char **argv, int *argc_ptr, int maxargs)
+void glob_expand(char *base1, char ***argv_ptr, int *argc_ptr, int *maxargs_ptr)
 {
-       char *s = argv[*argc_ptr];
+       char *s = (*argv_ptr)[*argc_ptr];
        char *p, *q;
        char *base = base1;
        int base_len = strlen(base);
@@ -562,7 +576,7 @@ void glob_expand(char *base1, char **argv, int *argc_ptr, int maxargs)
        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, argc_ptr, maxargs);
+               glob_expand_one(q, argv_ptr, argc_ptr, maxargs_ptr);
                if (!p)
                        break;
        }
@@ -887,6 +901,30 @@ 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
@@ -901,6 +939,7 @@ char *full_fname(const char *fn)
        if (result)
                free(result);
 
+       fn = safe_fname(fn);
        if (*fn == '/')
                p1 = p2 = "";
        else {
@@ -932,6 +971,69 @@ char *full_fname(const 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)