src/common.h | 1 + src/patch.c | 32 ++++++++++++++++++++++++++++++++ src/pch.c | 3 +++ src/util.c | 11 ----------- tests/bad-filenames | 38 ++++++++++++++++++++++++++++++-------- 5 files changed, 66 insertions(+), 19 deletions(-) diff --git a/src/common.h b/src/common.h index b38f1b2..8e8486e 100644 --- a/src/common.h +++ b/src/common.h @@ -143,6 +143,7 @@ XTERN char *revision; /* prerequisite revision, if any */ #endif void fatal_exit (int) __attribute__ ((noreturn)); +void validate_target_name (char const *n); #include #if !STDC_HEADERS && !defined errno diff --git a/src/patch.c b/src/patch.c index 77be499..f07fafb 100644 --- a/src/patch.c +++ b/src/patch.c @@ -31,6 +31,7 @@ #include #include #include +#include /* procedures */ @@ -105,6 +106,7 @@ main (int argc, char **argv) mode_t file_type; int outfd = -1; + outst.st_size = -1; exit_failure = 2; program_name = argv[0]; init_time (); @@ -500,6 +502,16 @@ main (int argc, char **argv) set_file_attributes (TMPOUTNAME, attr, inname, &instat, mode, &new_time); + /* We may have useful data in outst, via spew_output. + If not, get it now, via the file descriptor when + possible. */ + if (outst.st_size == -1) + { + if (0 <= outfd + ? fstat (outfd, &outst) + : stat (TMPOUTNAME, &outst)) + fatal ("failed to stat %s", quotearg (TMPOUTNAME)); + } move_file (TMPOUTNAME, &TMPOUTNAME_needs_removal, &outst, outname, mode, backup); @@ -1004,6 +1016,26 @@ numeric_string (char const *string, return value; } +void +validate_target_name (char const *n) +{ + char const *p = n; + if (explicit_inname) + return; + if (IS_ABSOLUTE_FILE_NAME (p)) + fatal ("rejecting absolute target file name: %s", quotearg (p)); + while (*p) + { + if (*p == '.' && *++p == '.' && ( ! *++p || ISSLASH (*p))) + fatal ("rejecting target file name with \"..\" component: %s", + quotearg (n)); + while (*p && ! ISSLASH (*p)) + p++; + while (ISSLASH (*p)) + p++; + } +} + /* Attempt to find the right place to apply this hunk of patch. */ static lin diff --git a/src/pch.c b/src/pch.c index 68f7bc8..bdfe0d4 100644 --- a/src/pch.c +++ b/src/pch.c @@ -194,6 +194,8 @@ maybe_reverse (char const *name, bool nonexistent, bool is_empty) { bool looks_reversed = (! is_empty) < p_says_nonexistent[reverse ^ is_empty]; + validate_target_name (name); + /* Allow to create and delete empty files when we know that they are empty: in the "diff --git" format, we know that from the index header. */ if (is_empty @@ -929,6 +931,7 @@ intuit_diff_type (bool need_header, mode_t *p_file_type) inerrno = stat_errno[i]; invc = version_controlled[i]; instat = st[i]; + validate_target_name (inname); } return retval; diff --git a/src/util.c b/src/util.c index 553cfbd..f1187ff 100644 --- a/src/util.c +++ b/src/util.c @@ -1415,17 +1415,6 @@ strip_leading_slashes (char *name, int strip_leading) n = p+1; } } - if (IS_ABSOLUTE_FILE_NAME (n)) - fatal ("rejecting absolute file name: %s", quotearg (n)); - for (p = n; *p; ) - { - if (*p == '.' && *++p == '.' && ( ! *++p || ISSLASH (*p))) - fatal ("rejecting file name with \"..\" component: %s", quotearg (n)); - while (*p && ! ISSLASH (*p)) - p++; - while (ISSLASH (*p)) - p++; - } if ((strip_leading < 0 || s <= 0) && *n) { memmove (name, n, strlen (n) + 1); diff --git a/tests/bad-filenames b/tests/bad-filenames index f53a613..44d02ba 100644 --- a/tests/bad-filenames +++ b/tests/bad-filenames @@ -7,43 +7,65 @@ . $srcdir/test-lib.sh use_local_patch +use_tmpdir # ================================================================ -emit_patch() +emit_2() { cat < target +check 'emit_2 /abs/path target | patch -R -p0; echo status: $?' <