Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 171 additions & 10 deletions compat/mingw.c
Original file line number Diff line number Diff line change
Expand Up @@ -292,11 +292,23 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)

int mingw_unlink(const char *pathname)
{
int ret, tries = 0;
int ret, tries = 0, wlen = 0;
wchar_t wpathname[MAX_PATH];
if (xutftowcs_path(wpathname, pathname) < 0)

wlen = xutftowcs_path(wpathname, pathname);
if (wlen < 0)
return -1;

if (!protect_ntfs && is_path_ends_with_special_character(pathname)) {
wchar_t abspath[MAX_PATH];

if (convert_to_abs_path_for_special_path(abspath, wpathname,
wlen) < 0)
return -1;

wcscpy(wpathname, abspath);
}

if (DeleteFileW(wpathname))
return 0;

Expand Down Expand Up @@ -457,6 +469,7 @@ int mingw_mkdir(const char *path, int mode)

if (xutftowcs_path(wpath, path) < 0)
return -1;

ret = _wmkdir(wpath);
if (!ret && needs_hiding(path))
return set_hidden_flag(wpath, 1);
Expand Down Expand Up @@ -543,6 +556,7 @@ int mingw_open (const char *filename, int oflags, ...)
unsigned mode;
int fd, create = (oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL);
wchar_t wfilename[MAX_PATH];
int path_len = -1;
open_fn_t open_fn;

va_start(args, oflags);
Expand All @@ -561,8 +575,22 @@ int mingw_open (const char *filename, int oflags, ...)

if (filename && !strcmp(filename, "/dev/null"))
wcscpy(wfilename, L"nul");
else if (xutftowcs_path(wfilename, filename) < 0)
return -1;
else {
path_len = xutftowcs_path(wfilename, filename);
if (path_len < 0)
return -1;
}

if (!protect_ntfs && is_path_ends_with_special_character(filename)) {
wchar_t abspath[MAX_PATH];

if (convert_to_abs_path_for_special_path(abspath, wfilename,
path_len) < 0)
return -1;

wcscpy(wfilename, abspath);
}


fd = open_fn(wfilename, oflags, mode);

Expand Down Expand Up @@ -618,6 +646,7 @@ int mingw_fgetc(FILE *stream)
FILE *mingw_fopen (const char *filename, const char *otype)
{
int hide = needs_hiding(filename);
int wlen;
FILE *file;
wchar_t wfilename[MAX_PATH], wotype[4];
if (filename && !strcmp(filename, "/dev/null"))
Expand All @@ -626,8 +655,21 @@ FILE *mingw_fopen (const char *filename, const char *otype)
int create = otype && strchr(otype, 'w');
errno = create ? EINVAL : ENOENT;
return NULL;
} else if (xutftowcs_path(wfilename, filename) < 0)
return NULL;
} else {
wlen = xutftowcs_path(wfilename, filename);
if (wlen < 0)
return NULL;
if (!protect_ntfs && is_path_ends_with_special_character(filename)) {
wchar_t abspath[MAX_PATH];

if (convert_to_abs_path_for_special_path(
abspath, wfilename, wlen) < 0)
return NULL;

wcscpy(wfilename, abspath);
}

}

if (xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
return NULL;
Expand All @@ -647,16 +689,31 @@ FILE *mingw_fopen (const char *filename, const char *otype)
FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
{
int hide = needs_hiding(filename);
int wlen;
FILE *file;
wchar_t wfilename[MAX_PATH], wotype[4];

if (filename && !strcmp(filename, "/dev/null"))
wcscpy(wfilename, L"nul");
else if (!is_valid_win32_path(filename, 1)) {
int create = otype && strchr(otype, 'w');
errno = create ? EINVAL : ENOENT;
return NULL;
} else if (xutftowcs_path(wfilename, filename) < 0)
return NULL;
} else {
wlen = xutftowcs_path(wfilename, filename);
if (wlen < 0)
return NULL;

if (!protect_ntfs && is_path_ends_with_special_character(filename)) {
wchar_t abspath[MAX_PATH];

if (convert_to_abs_path_for_special_path(
abspath, wfilename, wlen) < 0)
return NULL;

wcscpy(wfilename, abspath);
}
}

if (xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
return NULL;
Expand Down Expand Up @@ -721,12 +778,25 @@ int mingw_access(const char *filename, int mode)
return _waccess(wfilename, mode & ~X_OK);
}

/* cached length of current directory for convert_to_abs_path_for_special_path */
static int current_directory_len = 0;

/* cached path of current directory for convert_to_abs_path_for_special_path */
static wchar_t *current_directory_path;


int mingw_chdir(const char *dirname)
{
int result;
wchar_t wdirname[MAX_PATH];
if (xutftowcs_path(wdirname, dirname) < 0)
return -1;
return _wchdir(wdirname);

result =_wchdir(wdirname);
current_directory_len = GetCurrentDirectoryW(0, NULL);
ALLOC_ARRAY(current_directory_path, current_directory_len);
GetCurrentDirectoryW(current_directory_len, current_directory_path);
return result;
}

int mingw_chmod(const char *filename, int mode)
Expand Down Expand Up @@ -799,9 +869,21 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
{
WIN32_FILE_ATTRIBUTE_DATA fdata;
wchar_t wfilename[MAX_PATH];
if (xutftowcs_path(wfilename, file_name) < 0)
int wlen = xutftowcs_path(wfilename, file_name);
if (wlen < 0)
return -1;

if (is_path_ends_with_special_character(file_name)) {
wchar_t abspath[MAX_PATH];

int len = convert_to_abs_path_for_special_path(abspath,
wfilename, wlen);
if (len < 0)
return -1;

wcscpy(wfilename, abspath);
}

if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
buf->st_ino = 0;
buf->st_gid = 0;
Expand Down Expand Up @@ -2909,6 +2991,79 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
}
}

int is_path_ends_with_special_character(const char *path)
{
int preceding_space_or_period = 0, i = 0, periods = 0;

skip_dos_drive_prefix((char **)&path);

for (;;) {
char c = *(path++);

switch (c) {
case '\0':
case '/':
case '\\':
if (preceding_space_or_period &&
(i != periods || periods > 2))
return 1;

if (!c)
return 0;

i = periods = preceding_space_or_period = 0;
continue;

case '.':
periods++;
/* fallthru */

case ' ':
preceding_space_or_period = 1;
i++;
continue;
}

preceding_space_or_period = 0;
i++;
}
}

int convert_to_abs_path_for_special_path(wchar_t *abspath, wchar_t *path,
int len)
{
int result;

/**
* if provided path is already absolute path, do not convert path again.
*/
if ((is_dir_sep(path[0])) || (!is_dir_sep(path[0]) && path[1] == ':')) {
wcscpy(abspath, path);
return len;
}

/* convert to absolute path **without** using GetFullPathNameW because
* GetFullPathNameW returns invalid path */
result = current_directory_len + len;
if (result >= MAX_PATH - 4) {
errno = ENAMETOOLONG;
return -1;
}

wcscpy(abspath, L"\\\\?\\");
wcscpy(abspath + 4, current_directory_path);
wcscpy(abspath + 4 + current_directory_len - 1, L"\\");
wcscpy(abspath + 4 + current_directory_len, path);

/* normalize slashes to backslashes because Windows does not recognize
* special paths as slashes */
for (; *abspath; abspath++)
if (*abspath == '/')
*abspath = '\\';

return result + 4;
}

#if !defined(_MSC_VER)
/*
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
Expand Down Expand Up @@ -3066,6 +3221,12 @@ int wmain(int argc, const wchar_t **wargv)
/* initialize Unicode console */
winansi_init();

/* init path and length of current directory for convert_to_abs_path_for_special_path */
current_directory_len = GetCurrentDirectoryW(0, NULL);
ALLOC_ARRAY(current_directory_path, current_directory_len);
GetCurrentDirectoryW(current_directory_len, current_directory_path);


/* invoke the real main() using our utf8 version of argv. */
exit_status = main(argc, argv);

Expand Down
12 changes: 12 additions & 0 deletions compat/mingw.h
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,18 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report);
int is_valid_win32_path(const char *path, int allow_literal_nul);
#define is_valid_path(path) is_valid_win32_path(path, 0)

/**
* Verifies that the given path is a ends with special characters (`.` or ` `).
*
* Returns 1 upon ends with special characters, otherwise 0.
*/
int is_path_ends_with_special_character(const char *path);

/**
* Convert to absolute path for special path.
*/
int convert_to_abs_path_for_special_path(wchar_t *abspath, wchar_t *path, int len);

/**
* Converts UTF-8 encoded string to UTF-16LE.
*
Expand Down