summaryrefslogtreecommitdiff
path: root/compat/mingw.c
diff options
context:
space:
mode:
Diffstat (limited to 'compat/mingw.c')
-rw-r--r--compat/mingw.c284
1 files changed, 205 insertions, 79 deletions
diff --git a/compat/mingw.c b/compat/mingw.c
index c5c37e53ce..3fbfda5978 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -6,6 +6,8 @@
#include "../run-command.h"
#include "../cache.h"
+#define HCAST(type, handle) ((type)(intptr_t)handle)
+
static const int delay[] = { 0, 1, 10, 20, 40 };
int err_win_to_posix(DWORD winerr)
@@ -284,6 +286,49 @@ int mingw_rmdir(const char *pathname)
return ret;
}
+static inline int needs_hiding(const char *path)
+{
+ const char *basename;
+
+ if (hide_dotfiles == HIDE_DOTFILES_FALSE)
+ return 0;
+
+ /* We cannot use basename(), as it would remove trailing slashes */
+ mingw_skip_dos_drive_prefix((char **)&path);
+ if (!*path)
+ return 0;
+
+ for (basename = path; *path; path++)
+ if (is_dir_sep(*path)) {
+ do {
+ path++;
+ } while (is_dir_sep(*path));
+ /* ignore trailing slashes */
+ if (*path)
+ basename = path;
+ }
+
+ if (hide_dotfiles == HIDE_DOTFILES_TRUE)
+ return *basename == '.';
+
+ assert(hide_dotfiles == HIDE_DOTFILES_DOTGITONLY);
+ return !strncasecmp(".git", basename, 4) &&
+ (!basename[4] || is_dir_sep(basename[4]));
+}
+
+static int set_hidden_flag(const wchar_t *path, int set)
+{
+ DWORD original = GetFileAttributesW(path), modified;
+ if (set)
+ modified = original | FILE_ATTRIBUTE_HIDDEN;
+ else
+ modified = original & ~FILE_ATTRIBUTE_HIDDEN;
+ if (original == modified || SetFileAttributesW(path, modified))
+ return 0;
+ errno = err_win_to_posix(GetLastError());
+ return -1;
+}
+
int mingw_mkdir(const char *path, int mode)
{
int ret;
@@ -291,6 +336,8 @@ 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);
return ret;
}
@@ -312,11 +359,26 @@ int mingw_open (const char *filename, int oflags, ...)
return -1;
fd = _wopen(wfilename, oflags, mode);
- if (fd < 0 && (oflags & O_CREAT) && errno == EACCES) {
+ if (fd < 0 && (oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) {
DWORD attrs = GetFileAttributesW(wfilename);
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
errno = EISDIR;
}
+ if ((oflags & O_CREAT) && needs_hiding(filename)) {
+ /*
+ * Internally, _wopen() uses the CreateFile() API which errors
+ * out with an ERROR_ACCESS_DENIED if CREATE_ALWAYS was
+ * specified and an already existing file's attributes do not
+ * match *exactly*. As there is no mode or flag we can set that
+ * would correspond to FILE_ATTRIBUTE_HIDDEN, let's just try
+ * again *without* the O_CREAT flag (that corresponds to the
+ * CREATE_ALWAYS flag of CreateFile()).
+ */
+ if (fd < 0 && errno == EACCES)
+ fd = _wopen(wfilename, oflags & ~O_CREAT, mode);
+ if (fd >= 0 && set_hidden_flag(wfilename, 1))
+ warning("could not mark '%s' as hidden.", filename);
+ }
return fd;
}
@@ -348,6 +410,7 @@ int mingw_fgetc(FILE *stream)
#undef fopen
FILE *mingw_fopen (const char *filename, const char *otype)
{
+ int hide = needs_hiding(filename);
FILE *file;
wchar_t wfilename[MAX_PATH], wotype[4];
if (filename && !strcmp(filename, "/dev/null"))
@@ -355,12 +418,19 @@ FILE *mingw_fopen (const char *filename, const char *otype)
if (xutftowcs_path(wfilename, filename) < 0 ||
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
return NULL;
+ if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
+ error("could not unhide %s", filename);
+ return NULL;
+ }
file = _wfopen(wfilename, wotype);
+ if (file && hide && set_hidden_flag(wfilename, 1))
+ warning("could not mark '%s' as hidden.", filename);
return file;
}
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 (filename && !strcmp(filename, "/dev/null"))
@@ -368,7 +438,13 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
if (xutftowcs_path(wfilename, filename) < 0 ||
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
return NULL;
+ if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
+ error("could not unhide %s", filename);
+ return NULL;
+ }
file = _wfreopen(wfilename, wotype, stream);
+ if (file && hide && set_hidden_flag(wfilename, 1))
+ warning("could not mark '%s' as hidden.", filename);
return file;
}
@@ -394,6 +470,23 @@ int mingw_fflush(FILE *stream)
return ret;
}
+#undef write
+ssize_t mingw_write(int fd, const void *buf, size_t len)
+{
+ ssize_t result = write(fd, buf, len);
+
+ if (result < 0 && errno == EINVAL && buf) {
+ /* check if fd is a pipe */
+ HANDLE h = (HANDLE) _get_osfhandle(fd);
+ if (GetFileType(h) == FILE_TYPE_PIPE)
+ errno = EPIPE;
+ else
+ errno = EINVAL;
+ }
+
+ return result;
+}
+
int mingw_access(const char *filename, int mode)
{
wchar_t wfilename[MAX_PATH];
@@ -435,6 +528,39 @@ static inline time_t filetime_to_time_t(const FILETIME *ft)
return (time_t)(filetime_to_hnsec(ft) / 10000000);
}
+/**
+ * Verifies that safe_create_leading_directories() would succeed.
+ */
+static int has_valid_directory_prefix(wchar_t *wfilename)
+{
+ int n = wcslen(wfilename);
+
+ while (n > 0) {
+ wchar_t c = wfilename[--n];
+ DWORD attributes;
+
+ if (!is_dir_sep(c))
+ continue;
+
+ wfilename[n] = L'\0';
+ attributes = GetFileAttributesW(wfilename);
+ wfilename[n] = c;
+ if (attributes == FILE_ATTRIBUTE_DIRECTORY ||
+ attributes == FILE_ATTRIBUTE_DEVICE)
+ return 1;
+ if (attributes == INVALID_FILE_ATTRIBUTES)
+ switch (GetLastError()) {
+ case ERROR_PATH_NOT_FOUND:
+ continue;
+ case ERROR_FILE_NOT_FOUND:
+ /* This implies parent directory exists. */
+ return 1;
+ }
+ return 0;
+ }
+ return 1;
+}
+
/* We keep the do_lstat code in a separate function to avoid recursion.
* When a path ends with a slash, the stat will fail with ENOENT. In
* this case, we strip the trailing slashes and stat again.
@@ -495,6 +621,12 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
case ERROR_NOT_ENOUGH_MEMORY:
errno = ENOMEM;
break;
+ case ERROR_PATH_NOT_FOUND:
+ if (!has_valid_directory_prefix(wfilename)) {
+ errno = ENOTDIR;
+ break;
+ }
+ /* fallthru */
default:
errno = ENOENT;
break;
@@ -674,14 +806,14 @@ int pipe(int filedes[2])
errno = err_win_to_posix(GetLastError());
return -1;
}
- filedes[0] = _open_osfhandle((int)h[0], O_NOINHERIT);
+ filedes[0] = _open_osfhandle(HCAST(int, h[0]), O_NOINHERIT);
if (filedes[0] < 0) {
CloseHandle(h[0]);
CloseHandle(h[1]);
return -1;
}
- filedes[1] = _open_osfhandle((int)h[1], O_NOINHERIT);
- if (filedes[0] < 0) {
+ filedes[1] = _open_osfhandle(HCAST(int, h[1]), O_NOINHERIT);
+ if (filedes[1] < 0) {
close(filedes[0]);
CloseHandle(h[1]);
return -1;
@@ -705,15 +837,12 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
char *mingw_getcwd(char *pointer, int len)
{
- int i;
wchar_t wpointer[MAX_PATH];
if (!_wgetcwd(wpointer, ARRAY_SIZE(wpointer)))
return NULL;
if (xwcstoutf(pointer, wpointer, len) < 0)
return NULL;
- for (i = 0; pointer[i]; i++)
- if (pointer[i] == '\\')
- pointer[i] = '/';
+ convert_slashes(pointer);
return pointer;
}
@@ -752,7 +881,7 @@ static const char *quote_arg(const char *arg)
return arg;
/* insert \ where necessary */
- d = q = xmalloc(len+n+3);
+ d = q = xmalloc(st_add3(len, n, 3));
*d++ = '"';
while (*arg) {
if (*arg == '"')
@@ -835,7 +964,7 @@ static char **get_path_split(void)
if (!n)
return NULL;
- path = xmalloc((n+1)*sizeof(char *));
+ ALLOC_ARRAY(path, n + 1);
p = envpath;
i = 0;
do {
@@ -920,7 +1049,7 @@ static wchar_t *make_environment_block(char **deltaenv)
i++;
/* copy the environment, leaving space for changes */
- tmpenv = xmalloc((size + i) * sizeof(char*));
+ ALLOC_ARRAY(tmpenv, size + i);
memcpy(tmpenv, environ, size * sizeof(char*));
/* merge supplied environment changes into the temporary environment */
@@ -1011,7 +1140,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
free(quoted);
}
- wargs = xmalloc((2 * args.len + 1) * sizeof(wchar_t));
+ ALLOC_ARRAY(wargs, st_add(st_mult(2, args.len), 1));
xutftowcs(wargs, args.buf, 2 * args.len + 1);
strbuf_release(&args);
@@ -1110,7 +1239,7 @@ static int try_shell_exec(const char *cmd, char *const *argv)
int argc = 0;
const char **argv2;
while (argv[argc]) argc++;
- argv2 = xmalloc(sizeof(*argv) * (argc+1));
+ ALLOC_ARRAY(argv2, argc + 1);
argv2[0] = (char *)cmd; /* full path to the script file */
memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc);
pid = mingw_spawnv(prog, argv2, 1);
@@ -1584,7 +1713,12 @@ repeat:
if (gle == ERROR_ACCESS_DENIED &&
(attrs = GetFileAttributesW(wpnew)) != INVALID_FILE_ATTRIBUTES) {
if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
- errno = EISDIR;
+ DWORD attrsold = GetFileAttributesW(wpold);
+ if (attrsold == INVALID_FILE_ATTRIBUTES ||
+ !(attrsold & FILE_ATTRIBUTE_DIRECTORY))
+ errno = EISDIR;
+ else if (!_wrmdir(wpnew))
+ goto repeat;
return -1;
}
if ((attrs & FILE_ATTRIBUTE_READONLY) &&
@@ -1796,47 +1930,6 @@ int mingw_raise(int sig)
}
}
-
-static const char *make_backslash_path(const char *path)
-{
- static char buf[PATH_MAX + 1];
- char *c;
-
- if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
- die("Too long path: %.*s", 60, path);
-
- for (c = buf; *c; c++) {
- if (*c == '/')
- *c = '\\';
- }
- return buf;
-}
-
-void mingw_open_html(const char *unixpath)
-{
- const char *htmlpath = make_backslash_path(unixpath);
- typedef HINSTANCE (WINAPI *T)(HWND, const char *,
- const char *, const char *, const char *, INT);
- T ShellExecute;
- HMODULE shell32;
- int r;
-
- shell32 = LoadLibrary("shell32.dll");
- if (!shell32)
- die("cannot load shell32.dll");
- ShellExecute = (T)GetProcAddress(shell32, "ShellExecuteA");
- if (!ShellExecute)
- die("cannot run browser");
-
- printf("Launching default browser to display HTML ...\n");
- r = (int)ShellExecute(NULL, "open", htmlpath, NULL, "\\", SW_SHOWNORMAL);
- FreeLibrary(shell32);
- /* see the MSDN documentation referring to the result codes here */
- if (r <= 32) {
- die("failed to launch browser for %.*s", MAX_PATH, unixpath);
- }
-}
-
int link(const char *oldpath, const char *newpath)
{
typedef BOOL (WINAPI *T)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
@@ -1915,28 +2008,31 @@ pid_t waitpid(pid_t pid, int *status, int options)
return -1;
}
+int mingw_skip_dos_drive_prefix(char **path)
+{
+ int ret = has_dos_drive_prefix(*path);
+ *path += ret;
+ return ret;
+}
+
int mingw_offset_1st_component(const char *path)
{
- int offset = 0;
- if (has_dos_drive_prefix(path))
- offset = 2;
+ char *pos = (char *)path;
/* unc paths */
- else if (is_dir_sep(path[0]) && is_dir_sep(path[1])) {
-
+ if (!skip_dos_drive_prefix(&pos) &&
+ is_dir_sep(pos[0]) && is_dir_sep(pos[1])) {
/* skip server name */
- char *pos = strpbrk(path + 2, "\\/");
+ pos = strpbrk(pos + 2, "\\/");
if (!pos)
return 0; /* Error: malformed unc path */
do {
pos++;
} while (*pos && !is_dir_sep(*pos));
-
- offset = pos - path;
}
- return offset + is_dir_sep(path[offset]);
+ return pos + is_dir_sep(*pos) - path;
}
int xutftowcsn(wchar_t *wcs, const char *utfs, size_t wcslen, int utflen)
@@ -2024,6 +2120,35 @@ int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen)
return -1;
}
+static void setup_windows_environment(void)
+{
+ char *tmp = getenv("TMPDIR");
+
+ /* on Windows it is TMP and TEMP */
+ if (!tmp) {
+ if (!(tmp = getenv("TMP")))
+ tmp = getenv("TEMP");
+ if (tmp) {
+ setenv("TMPDIR", tmp, 1);
+ tmp = getenv("TMPDIR");
+ }
+ }
+
+ if (tmp) {
+ /*
+ * Convert all dir separators to forward slashes,
+ * to help shell commands called from the Git
+ * executable (by not mistaking the dir separators
+ * for escape characters).
+ */
+ convert_slashes(tmp);
+ }
+
+ /* simulate TERM to enable auto-color (see color.c) */
+ if (!getenv("TERM"))
+ setenv("TERM", "cygwin", 1);
+}
+
/*
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
* mingw startup code, see init.c in mingw runtime).
@@ -2037,7 +2162,7 @@ typedef struct {
extern int __wgetmainargs(int *argc, wchar_t ***argv, wchar_t ***env, int glob,
_startupinfo *si);
-static NORETURN void die_startup()
+static NORETURN void die_startup(void)
{
fputs("fatal: not enough memory for initialization", stderr);
exit(128);
@@ -2057,7 +2182,7 @@ static char *wcstoutfdup_startup(char *buffer, const wchar_t *wcs, size_t len)
return memcpy(malloc_startup(len), buffer, len);
}
-void mingw_startup()
+void mingw_startup(void)
{
int i, maxlen, argc;
char *buffer;
@@ -2102,19 +2227,7 @@ void mingw_startup()
qsort(environ, i, sizeof(char*), compareenv);
/* fix Windows specific environment settings */
-
- /* on Windows it is TMP and TEMP */
- if (!mingw_getenv("TMPDIR")) {
- const char *tmp = mingw_getenv("TMP");
- if (!tmp)
- tmp = mingw_getenv("TEMP");
- if (tmp)
- setenv("TMPDIR", tmp, 1);
- }
-
- /* simulate TERM to enable auto-color (see color.c) */
- if (!getenv("TERM"))
- setenv("TERM", "cygwin", 1);
+ setup_windows_environment();
/* initialize critical section for waitpid pinfo_t list */
InitializeCriticalSection(&pinfo_cs);
@@ -2128,3 +2241,16 @@ void mingw_startup()
/* initialize Unicode console */
winansi_init();
}
+
+int uname(struct utsname *buf)
+{
+ unsigned v = (unsigned)GetVersion();
+ memset(buf, 0, sizeof(*buf));
+ xsnprintf(buf->sysname, sizeof(buf->sysname), "Windows");
+ xsnprintf(buf->release, sizeof(buf->release),
+ "%u.%u", v & 0xff, (v >> 8) & 0xff);
+ /* assuming NT variants only.. */
+ xsnprintf(buf->version, sizeof(buf->version),
+ "%u", (v >> 16) & 0x7fff);
+ return 0;
+}