summaryrefslogtreecommitdiff
path: root/compat
diff options
context:
space:
mode:
authorJohannes Schindelin <johannes.schindelin@gmx.de>2019-12-04 21:46:37 (GMT)
committerJohannes Schindelin <johannes.schindelin@gmx.de>2019-12-06 15:31:12 (GMT)
commitfc346cb2925d4647ccd65177660bffab13cbe253 (patch)
tree414c061e84002c2f65ef9b3d50646128217dcac4 /compat
parent8104ec994ea3849a968b4667d072fedd1e688642 (diff)
parent4cd1cf31efed9b16db5035c377bfa222f5272458 (diff)
downloadgit-fc346cb2925d4647ccd65177660bffab13cbe253.zip
git-fc346cb2925d4647ccd65177660bffab13cbe253.tar.gz
git-fc346cb2925d4647ccd65177660bffab13cbe253.tar.bz2
Sync with 2.20.2
* maint-2.20: (36 commits) Git 2.20.2 t7415: adjust test for dubiously-nested submodule gitdirs for v2.20.x Git 2.19.3 Git 2.18.2 Git 2.17.3 Git 2.16.6 test-drop-caches: use `has_dos_drive_prefix()` Git 2.15.4 Git 2.14.6 mingw: handle `subst`-ed "DOS drives" mingw: refuse to access paths with trailing spaces or periods mingw: refuse to access paths with illegal characters unpack-trees: let merged_entry() pass through do_add_entry()'s errors quote-stress-test: offer to test quoting arguments for MSYS2 sh t6130/t9350: prepare for stringent Win32 path validation quote-stress-test: allow skipping some trials quote-stress-test: accept arguments to test via the command-line tests: add a helper to stress test argument quoting mingw: fix quoting of arguments Disallow dubiously-nested submodule git directories ...
Diffstat (limited to 'compat')
-rw-r--r--compat/mingw.c76
-rw-r--r--compat/mingw.h14
-rw-r--r--compat/win32/path-utils.c24
-rw-r--r--compat/win32/path-utils.h5
4 files changed, 113 insertions, 6 deletions
diff --git a/compat/mingw.c b/compat/mingw.c
index 8141f77..0e14cab 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -390,6 +390,12 @@ int mingw_mkdir(const char *path, int mode)
{
int ret;
wchar_t wpath[MAX_PATH];
+
+ if (!is_valid_win32_path(path)) {
+ errno = EINVAL;
+ return -1;
+ }
+
if (xutftowcs_path(wpath, path) < 0)
return -1;
ret = _wmkdir(wpath);
@@ -463,7 +469,7 @@ int mingw_open (const char *filename, int oflags, ...)
typedef int (*open_fn_t)(wchar_t const *wfilename, int oflags, ...);
va_list args;
unsigned mode;
- int fd;
+ int fd, create = (oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL);
wchar_t wfilename[MAX_PATH];
open_fn_t open_fn;
@@ -471,6 +477,11 @@ int mingw_open (const char *filename, int oflags, ...)
mode = va_arg(args, int);
va_end(args);
+ if (!is_valid_win32_path(filename)) {
+ errno = create ? EINVAL : ENOENT;
+ return -1;
+ }
+
if (filename && !strcmp(filename, "/dev/null"))
filename = "nul";
@@ -537,6 +548,11 @@ FILE *mingw_fopen (const char *filename, const char *otype)
int hide = needs_hiding(filename);
FILE *file;
wchar_t wfilename[MAX_PATH], wotype[4];
+ if (!is_valid_win32_path(filename)) {
+ int create = otype && strchr(otype, 'w');
+ errno = create ? EINVAL : ENOENT;
+ return NULL;
+ }
if (filename && !strcmp(filename, "/dev/null"))
filename = "nul";
if (xutftowcs_path(wfilename, filename) < 0 ||
@@ -559,6 +575,11 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
int hide = needs_hiding(filename);
FILE *file;
wchar_t wfilename[MAX_PATH], wotype[4];
+ if (!is_valid_win32_path(filename)) {
+ int create = otype && strchr(otype, 'w');
+ errno = create ? EINVAL : ENOENT;
+ return NULL;
+ }
if (filename && !strcmp(filename, "/dev/null"))
filename = "nul";
if (xutftowcs_path(wfilename, filename) < 0 ||
@@ -1052,7 +1073,7 @@ static const char *quote_arg_msvc(const char *arg)
p++;
len++;
}
- if (*p == '"')
+ if (*p == '"' || !*p)
n += count*2 + 1;
continue;
}
@@ -1074,16 +1095,19 @@ static const char *quote_arg_msvc(const char *arg)
count++;
*d++ = *arg++;
}
- if (*arg == '"') {
+ if (*arg == '"' || !*arg) {
while (count-- > 0)
*d++ = '\\';
+ /* don't escape the surrounding end quote */
+ if (!*arg)
+ break;
*d++ = '\\';
}
}
*d++ = *arg++;
}
*d++ = '"';
- *d++ = 0;
+ *d++ = '\0';
return q;
}
@@ -2464,6 +2488,50 @@ static void setup_windows_environment(void)
setenv("TERM", "cygwin", 1);
}
+int is_valid_win32_path(const char *path)
+{
+ int preceding_space_or_period = 0, i = 0, periods = 0;
+
+ if (!protect_ntfs)
+ return 1;
+
+ skip_dos_drive_prefix((char **)&path);
+
+ for (;;) {
+ char c = *(path++);
+ switch (c) {
+ case '\0':
+ case '/': case '\\':
+ /* cannot end in ` ` or `.`, except for `.` and `..` */
+ if (preceding_space_or_period &&
+ (i != periods || periods > 2))
+ return 0;
+ if (!c)
+ return 1;
+
+ i = periods = preceding_space_or_period = 0;
+ continue;
+ case '.':
+ periods++;
+ /* fallthru */
+ case ' ':
+ preceding_space_or_period = 1;
+ i++;
+ continue;
+ case ':': /* DOS drive prefix was already skipped */
+ case '<': case '>': case '"': case '|': case '?': case '*':
+ /* illegal character */
+ return 0;
+ default:
+ if (c > '\0' && c < '\x20')
+ /* illegal character */
+ return 0;
+ }
+ preceding_space_or_period = 0;
+ i++;
+ }
+}
+
/*
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
* mingw startup code, see init.c in mingw runtime).
diff --git a/compat/mingw.h b/compat/mingw.h
index 30d9fb3..ec9fc1c 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -460,6 +460,20 @@ extern char *mingw_query_user_email(void);
#endif
/**
+ * Verifies that the given path is a valid one on Windows.
+ *
+ * In particular, path segments are disallowed which
+ *
+ * - end in a period or a space (except the special directories `.` and `..`).
+ *
+ * - contain any of the reserved characters, e.g. `:`, `;`, `*`, etc
+ *
+ * Returns 1 upon success, otherwise 0.
+ */
+int is_valid_win32_path(const char *path);
+#define is_valid_path(path) is_valid_win32_path(path)
+
+/**
* Converts UTF-8 encoded string to UTF-16LE.
*
* To support repositories with legacy-encoded file names, invalid UTF-8 bytes
diff --git a/compat/win32/path-utils.c b/compat/win32/path-utils.c
index d9d3641..ebf2f12 100644
--- a/compat/win32/path-utils.c
+++ b/compat/win32/path-utils.c
@@ -1,5 +1,29 @@
#include "../../git-compat-util.h"
+int win32_has_dos_drive_prefix(const char *path)
+{
+ int i;
+
+ /*
+ * Does it start with an ASCII letter (i.e. highest bit not set),
+ * followed by a colon?
+ */
+ if (!(0x80 & (unsigned char)*path))
+ return *path && path[1] == ':' ? 2 : 0;
+
+ /*
+ * While drive letters must be letters of the English alphabet, it is
+ * possible to assign virtually _any_ Unicode character via `subst` as
+ * a drive letter to "virtual drives". Even `1`, or `ä`. Or fun stuff
+ * like this:
+ *
+ * subst ֍: %USERPROFILE%\Desktop
+ */
+ for (i = 1; i < 4 && (0x80 & (unsigned char)path[i]); i++)
+ ; /* skip first UTF-8 character */
+ return path[i] == ':' ? i + 1 : 0;
+}
+
int win32_skip_dos_drive_prefix(char **path)
{
int ret = has_dos_drive_prefix(*path);
diff --git a/compat/win32/path-utils.h b/compat/win32/path-utils.h
index 0f70d43..3403681 100644
--- a/compat/win32/path-utils.h
+++ b/compat/win32/path-utils.h
@@ -1,5 +1,6 @@
-#define has_dos_drive_prefix(path) \
- (isalpha(*(path)) && (path)[1] == ':' ? 2 : 0)
+int win32_has_dos_drive_prefix(const char *path);
+#define has_dos_drive_prefix win32_has_dos_drive_prefix
+
int win32_skip_dos_drive_prefix(char **path);
#define skip_dos_drive_prefix win32_skip_dos_drive_prefix
static inline int win32_is_dir_sep(int c)