Sorry, I made a Git error and posted the wrong patch.  New patch attached.

Thanks,
Florian
>From dd609baac563e2367b9424f32eead35940f6bc33 Mon Sep 17 00:00:00 2001
From: Florian Weimer <fwei...@redhat.com>
Date: Tue, 6 Feb 2018 14:43:35 +0100
Subject: [PATCH] Support running with an unknown current directory (bug 6422)

If the current directory is needed later, an error is reported.

Delayed error reporting is needed in more cases because after the fix
for CVE-2018-1000001 in getcwd

  https://sourceware.org/bugzilla/show_bug.cgi?id=22679

the glibc implementation will no longer return a made-up relative
pathname (as supplied by the Linux kernel) in case the current
directory has no name. Without this patch, rsync will fail to start in
such cases, but the command line arguments might all use absolute
pathnames, so that the current directory is never actually needed.
---
 exclude.c | 23 ++++++++++++++---------
 flist.c   | 35 +++++++++++++++++++++++++++++++----
 log.c     |  1 +
 main.c    | 12 +++++++++---
 util.c    | 34 ++++++++++++++++++++++++++++++++--
 5 files changed, 87 insertions(+), 18 deletions(-)

diff --git a/exclude.c b/exclude.c
index 7989fb3e..bbd3b263 100644
--- a/exclude.c
+++ b/exclude.c
@@ -381,6 +381,7 @@ void set_filter_dir(const char *dir, unsigned int dirlen)
 {
 	unsigned int len;
 	if (*dir != '/') {
+		need_curr_dir(dir);
 		memcpy(dirbuf, curr_dir, curr_dir_len);
 		dirbuf[curr_dir_len] = '/';
 		len = curr_dir_len + 1;
@@ -644,15 +645,19 @@ static int rule_matches(const char *fname, filter_rule *ex, int name_flags)
 		 * just match the name portion of the path. */
 		if ((p = strrchr(name,'/')) != NULL)
 			name = p+1;
-	} else if (ex->rflags & FILTRULE_ABS_PATH && *fname != '/'
-	    && curr_dir_len > module_dirlen + 1) {
-		/* If we're matching against an absolute-path pattern,
-		 * we need to prepend our full path info. */
-		strings[str_cnt++] = curr_dir + module_dirlen + 1;
-		strings[str_cnt++] = "/";
-	} else if (ex->rflags & FILTRULE_WILD2_PREFIX && *fname != '/') {
-		/* Allow "**"+"/" to match at the start of the string. */
-		strings[str_cnt++] = "/";
+	} else if (*fname != '/') {
+		need_curr_dir(fname);
+		if (ex->rflags & FILTRULE_ABS_PATH
+		    && curr_dir_len > module_dirlen + 1) {
+			/* If we're matching against an absolute-path pattern,
+			 * we need to prepend our full path info. */
+			strings[str_cnt++] = curr_dir + module_dirlen + 1;
+			strings[str_cnt++] = "/";
+		} else if (ex->rflags & FILTRULE_WILD2_PREFIX) {
+			/* Allow "**"+"/" to match at the start of the
+			 * string. */
+			strings[str_cnt++] = "/";
+		}
 	}
 	strings[str_cnt++] = name;
 	if (name_flags & NAME_IS_DIR) {
diff --git a/flist.c b/flist.c
index 499440cc..0ef6e9a3 100644
--- a/flist.c
+++ b/flist.c
@@ -77,6 +77,7 @@ extern char *filesfrom_host;
 extern char *usermap, *groupmap;
 
 extern char curr_dir[MAXPATHLEN];
+extern int curr_dir_error;
 
 extern struct chmod_mode_struct *chmod_modes;
 
@@ -278,8 +279,31 @@ static void send_directory(int f, struct file_list *flist,
 			   char *fbuf, int len, int flags);
 
 static const char *pathname, *orig_dir;
+static int orig_dir_error;
 static int pathname_len;
 
+/* Save orig_dir and its associated error code. */
+static void
+save_orig_dir(void)
+{
+	if (!orig_dir) {
+		orig_dir = strdup(curr_dir);
+		if (orig_dir == NULL)
+			out_of_memory("save_orig_dir");
+		orig_dir_error = curr_dir_error;
+	}
+}
+
+/* To be called before using orig_dir. */
+static void
+need_orig_dir(void)
+{
+	if (orig_dir_error != 0) {
+		curr_dir_error = orig_dir_error;
+		need_curr_dir(NULL);
+	}
+}
+
 /* Make sure flist can hold at least flist->used + extra entries. */
 static void flist_expand(struct file_list *flist, int extra)
 {
@@ -356,15 +380,19 @@ int change_pathname(struct file_struct *file, const char *dir, int dirlen)
 	pathname = dir;
 	pathname_len = dirlen;
 
-	if (!dir)
+	if (!dir) {
+		need_orig_dir();
 		dir = orig_dir;
+	}
 
 	if (!change_dir(dir, CD_NORMAL)) {
 	  chdir_error:
 		io_error |= IOERR_GENERAL;
 		rsyserr(FERROR_XFER, errno, "change_dir %s failed", full_fname(dir));
-		if (dir != orig_dir)
+		if (dir != orig_dir) {
+			need_orig_dir();
 			change_dir(orig_dir, CD_NORMAL);
+		}
 		pathname = NULL;
 		pathname_len = 0;
 		return 0;
@@ -2108,8 +2136,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
 		use_ff_fd = 1;
 	}
 
-	if (!orig_dir)
-		orig_dir = strdup(curr_dir);
+	save_orig_dir();
 
 	while (1) {
 		char fbuf[MAXPATHLEN], *fn, name_type;
diff --git a/log.c b/log.c
index 6143349c..a1f85c3a 100644
--- a/log.c
+++ b/log.c
@@ -602,6 +602,7 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
 				} else
 					n = buf2;
 			} else if (am_daemon && *c != '/') {
+				need_curr_dir(c);
 				pathjoin(buf2, sizeof buf2,
 					 curr_dir + module_dirlen, c);
 				clean_fname(buf2, 0);
diff --git a/main.c b/main.c
index ee9630fc..e1f1f705 100644
--- a/main.c
+++ b/main.c
@@ -714,7 +714,6 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
 static void check_alt_basis_dirs(void)
 {
 	STRUCT_STAT st;
-	char *slash = strrchr(curr_dir, '/');
 	int j;
 
 	for (j = 0; j < basis_dir_cnt; j++) {
@@ -723,8 +722,14 @@ static void check_alt_basis_dirs(void)
 		if (bd_len > 1 && bdir[bd_len-1] == '/')
 			bdir[--bd_len] = '\0';
 		if (dry_run > 1 && *bdir != '/') {
-			int len = curr_dir_len + 1 + bd_len + 1;
-			char *new = new_array(char, len);
+			int len;
+			char *slash;
+			char *new;
+
+			need_curr_dir(bdir);
+			len = curr_dir_len + 1 + bd_len + 1;
+			slash = strrchr(curr_dir, '/');
+			new = new_array(char, len);
 			if (!new)
 				out_of_memory("check_alt_basis_dirs");
 			if (slash && strncmp(bdir, "../", 3) == 0) {
@@ -1051,6 +1056,7 @@ static void do_server_recv(int f_in, int f_out, int argc, char *argv[])
 	 * we can sanitize the --link-/copy-/compare-dest args correctly. */
 	if (sanitize_paths) {
 		char **dir_p;
+		need_curr_dir(NULL);
 		for (dir_p = basis_dir; *dir_p; dir_p++)
 			*dir_p = sanitize_path(NULL, *dir_p, NULL, curr_dir_depth, SP_DEFAULT);
 		if (partial_dir)
diff --git a/util.c b/util.c
index 8723248f..31502d63 100644
--- a/util.c
+++ b/util.c
@@ -43,6 +43,7 @@ int sanitize_paths = 0;
 char curr_dir[MAXPATHLEN];
 unsigned int curr_dir_len;
 int curr_dir_depth; /* This is only set for a sanitizing daemon. */
+int curr_dir_error; /* Errno error code from the initial getcwd call. */
 
 /* Set a fd into nonblocking mode. */
 void set_nonblocking(int fd)
@@ -1079,6 +1080,22 @@ char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth,
 	return dest;
 }
 
+/* This function has to be called before accessing curr_dir, to make
+ * sure that the current directory path is actually known. The path
+ * argument is used in the error message only. */
+void need_curr_dir(const char *path)
+{
+  if (curr_dir_error != 0) {
+	  if (path != NULL)
+		  rsyserr(FERROR, curr_dir_error,
+			  "unknown current directory for \"%s\"", path);
+	  else
+		  rsyserr(FERROR, curr_dir_error,
+			  "unknown current directory");
+	  exit_cleanup(RERR_FILESELECT);
+  }
+}
+
 /* Like chdir(), but it keeps track of the current directory (in the
  * global "curr_dir"), and ensures that the path size doesn't overflow.
  * Also cleans the path using the clean_fname() function. */
@@ -1090,8 +1107,15 @@ int change_dir(const char *dir, int set_path_only)
 	if (!initialised) {
 		initialised = 1;
 		if (getcwd(curr_dir, sizeof curr_dir - 1) == NULL) {
-			rsyserr(FERROR, errno, "getcwd()");
-			exit_cleanup(RERR_FILESELECT);
+			/* This is not a hard error because we might
+			 * not need the current directory later (if
+			 * all paths used are absolute). */
+			curr_dir_error = errno;
+			rprintf(FINFO,
+				"[%s] current directory unavailable: %s (%d)\n",
+				who_am_i(), strerror(curr_dir_error),
+				curr_dir_error);
+			curr_dir[0] = '\0';
 		}
 		curr_dir_len = strlen(curr_dir);
 	}
@@ -1112,7 +1136,11 @@ int change_dir(const char *dir, int set_path_only)
 			return 0;
 		skipped_chdir = set_path_only;
 		memcpy(curr_dir, dir, len + 1);
+		/* Changing to an absolute path hides a previous
+		 * getcwd error. */
+		curr_dir_error = 0;
 	} else {
+		need_curr_dir(dir);
 		if (curr_dir_len + 1 + len >= sizeof curr_dir) {
 			errno = ENAMETOOLONG;
 			return 0;
@@ -1149,6 +1177,7 @@ char *normalize_path(char *path, BOOL force_newbuf, unsigned int *len_ptr)
 
 	if (*path != '/') { /* Make path absolute. */
 		int len = strlen(path);
+		need_curr_dir(path);
 		if (curr_dir_len + 1 + len >= sizeof curr_dir)
 			return NULL;
 		curr_dir[curr_dir_len] = '/';
@@ -1186,6 +1215,7 @@ char *full_fname(const char *fn)
 	if (*fn == '/')
 		p1 = p2 = "";
 	else {
+		need_curr_dir(fn);
 		p1 = curr_dir + module_dirlen;
 		for (p2 = p1; *p2 == '/'; p2++) {}
 		if (*p2)
-- 
2.14.3

-- 
Please use reply-all for most replies to avoid omitting the mailing list.
To unsubscribe or change options: https://lists.samba.org/mailman/listinfo/rsync
Before posting, read: http://www.catb.org/~esr/faqs/smart-questions.html

Reply via email to