Merge branch 'nd/per-worktree-config'
[gitweb.git] / compat / mingw.c
index 18caf21969a5917e8e6c6d754beffbba2741e6c2..3b44dd99d79aed48a325b1e59b28d98d0c0790b9 100644 (file)
@@ -5,6 +5,7 @@
 #include "../strbuf.h"
 #include "../run-command.h"
 #include "../cache.h"
+#include "win32/lazyload.h"
 
 #define HCAST(type, handle) ((type)(intptr_t)handle)
 
@@ -202,6 +203,31 @@ static int ask_yes_no_if_possible(const char *format, ...)
        }
 }
 
+/* Normalizes NT paths as returned by some low-level APIs. */
+static wchar_t *normalize_ntpath(wchar_t *wbuf)
+{
+       int i;
+       /* fix absolute path prefixes */
+       if (wbuf[0] == '\\') {
+               /* strip NT namespace prefixes */
+               if (!wcsncmp(wbuf, L"\\??\\", 4) ||
+                   !wcsncmp(wbuf, L"\\\\?\\", 4))
+                       wbuf += 4;
+               else if (!wcsnicmp(wbuf, L"\\DosDevices\\", 12))
+                       wbuf += 12;
+               /* replace remaining '...UNC\' with '\\' */
+               if (!wcsnicmp(wbuf, L"UNC\\", 4)) {
+                       wbuf += 2;
+                       *wbuf = '\\';
+               }
+       }
+       /* convert backslashes to slashes */
+       for (i = 0; wbuf[i]; i++)
+               if (wbuf[i] == '\\')
+                       wbuf[i] = '/';
+       return wbuf;
+}
+
 int mingw_unlink(const char *pathname)
 {
        int ret, tries = 0;
@@ -592,9 +618,11 @@ static inline long long filetime_to_hnsec(const FILETIME *ft)
        return winTime - 116444736000000000LL;
 }
 
-static inline time_t filetime_to_time_t(const FILETIME *ft)
+static inline void filetime_to_timespec(const FILETIME *ft, struct timespec *ts)
 {
-       return (time_t)(filetime_to_hnsec(ft) / 10000000);
+       long long hnsec = filetime_to_hnsec(ft);
+       ts->tv_sec = (time_t)(hnsec / 10000000);
+       ts->tv_nsec = (hnsec % 10000000) * 100;
 }
 
 /**
@@ -653,9 +681,9 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
                buf->st_size = fdata.nFileSizeLow |
                        (((off_t)fdata.nFileSizeHigh)<<32);
                buf->st_dev = buf->st_rdev = 0; /* not used by Git */
-               buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
-               buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
-               buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
+               filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
+               filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
+               filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
                if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
                        WIN32_FIND_DATAW findbuf;
                        HANDLE handle = FindFirstFileW(wfilename, &findbuf);
@@ -736,6 +764,29 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
        return do_lstat(follow, alt_name, buf);
 }
 
+static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
+{
+       BY_HANDLE_FILE_INFORMATION fdata;
+
+       if (!GetFileInformationByHandle(hnd, &fdata)) {
+               errno = err_win_to_posix(GetLastError());
+               return -1;
+       }
+
+       buf->st_ino = 0;
+       buf->st_gid = 0;
+       buf->st_uid = 0;
+       buf->st_nlink = 1;
+       buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
+       buf->st_size = fdata.nFileSizeLow |
+               (((off_t)fdata.nFileSizeHigh)<<32);
+       buf->st_dev = buf->st_rdev = 0; /* not used by Git */
+       filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
+       filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
+       filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
+       return 0;
+}
+
 int mingw_lstat(const char *file_name, struct stat *buf)
 {
        return do_stat_internal(0, file_name, buf);
@@ -748,32 +799,31 @@ int mingw_stat(const char *file_name, struct stat *buf)
 int mingw_fstat(int fd, struct stat *buf)
 {
        HANDLE fh = (HANDLE)_get_osfhandle(fd);
-       BY_HANDLE_FILE_INFORMATION fdata;
+       DWORD avail, type = GetFileType(fh) & ~FILE_TYPE_REMOTE;
 
-       if (fh == INVALID_HANDLE_VALUE) {
-               errno = EBADF;
-               return -1;
-       }
-       /* direct non-file handles to MS's fstat() */
-       if (GetFileType(fh) != FILE_TYPE_DISK)
-               return _fstati64(fd, buf);
+       switch (type) {
+       case FILE_TYPE_DISK:
+               return get_file_info_by_handle(fh, buf);
 
-       if (GetFileInformationByHandle(fh, &fdata)) {
-               buf->st_ino = 0;
-               buf->st_gid = 0;
-               buf->st_uid = 0;
+       case FILE_TYPE_CHAR:
+       case FILE_TYPE_PIPE:
+               /* initialize stat fields */
+               memset(buf, 0, sizeof(*buf));
                buf->st_nlink = 1;
-               buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
-               buf->st_size = fdata.nFileSizeLow |
-                       (((off_t)fdata.nFileSizeHigh)<<32);
-               buf->st_dev = buf->st_rdev = 0; /* not used by Git */
-               buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
-               buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
-               buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
+
+               if (type == FILE_TYPE_CHAR) {
+                       buf->st_mode = _S_IFCHR;
+               } else {
+                       buf->st_mode = _S_IFIFO;
+                       if (PeekNamedPipe(fh, NULL, 0, NULL, &avail, NULL))
+                               buf->st_size = avail;
+               }
                return 0;
+
+       default:
+               errno = EBADF;
+               return -1;
        }
-       errno = EBADF;
-       return -1;
 }
 
 static inline void time_t_to_filetime(time_t t, FILETIME *ft)
@@ -917,8 +967,29 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
 
 char *mingw_getcwd(char *pointer, int len)
 {
-       wchar_t wpointer[MAX_PATH];
-       if (!_wgetcwd(wpointer, ARRAY_SIZE(wpointer)))
+       wchar_t cwd[MAX_PATH], wpointer[MAX_PATH];
+       DWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd);
+
+       if (!ret || ret >= ARRAY_SIZE(cwd)) {
+               errno = ret ? ENAMETOOLONG : err_win_to_posix(GetLastError());
+               return NULL;
+       }
+       ret = GetLongPathNameW(cwd, wpointer, ARRAY_SIZE(wpointer));
+       if (!ret && GetLastError() == ERROR_ACCESS_DENIED) {
+               HANDLE hnd = CreateFileW(cwd, 0,
+                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                       OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+               if (hnd == INVALID_HANDLE_VALUE)
+                       return NULL;
+               ret = GetFinalPathNameByHandleW(hnd, wpointer, ARRAY_SIZE(wpointer), 0);
+               CloseHandle(hnd);
+               if (!ret || ret >= ARRAY_SIZE(wpointer))
+                       return NULL;
+               if (xwcstoutf(pointer, normalize_ntpath(wpointer), len) < 0)
+                       return NULL;
+               return pointer;
+       }
+       if (!ret || ret >= ARRAY_SIZE(wpointer))
                return NULL;
        if (xwcstoutf(pointer, wpointer, len) < 0)
                return NULL;
@@ -1577,7 +1648,8 @@ static void ensure_socket_initialization(void)
                        WSAGetLastError());
 
        for (name = libraries; *name; name++) {
-               ipv6_dll = LoadLibrary(*name);
+               ipv6_dll = LoadLibraryExA(*name, NULL,
+                                         LOAD_LIBRARY_SEARCH_SYSTEM32);
                if (!ipv6_dll)
                        continue;
 
@@ -1798,18 +1870,63 @@ int mingw_getpagesize(void)
        return si.dwAllocationGranularity;
 }
 
+/* See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724435.aspx */
+enum EXTENDED_NAME_FORMAT {
+       NameDisplay = 3,
+       NameUserPrincipal = 8
+};
+
+static char *get_extended_user_info(enum EXTENDED_NAME_FORMAT type)
+{
+       DECLARE_PROC_ADDR(secur32.dll, BOOL, GetUserNameExW,
+               enum EXTENDED_NAME_FORMAT, LPCWSTR, PULONG);
+       static wchar_t wbuffer[1024];
+       DWORD len;
+
+       if (!INIT_PROC_ADDR(GetUserNameExW))
+               return NULL;
+
+       len = ARRAY_SIZE(wbuffer);
+       if (GetUserNameExW(type, wbuffer, &len)) {
+               char *converted = xmalloc((len *= 3));
+               if (xwcstoutf(converted, wbuffer, len) >= 0)
+                       return converted;
+               free(converted);
+       }
+
+       return NULL;
+}
+
+char *mingw_query_user_email(void)
+{
+       return get_extended_user_info(NameUserPrincipal);
+}
+
 struct passwd *getpwuid(int uid)
 {
+       static unsigned initialized;
        static char user_name[100];
-       static struct passwd p;
+       static struct passwd *p;
+       DWORD len;
+
+       if (initialized)
+               return p;
 
-       DWORD len = sizeof(user_name);
-       if (!GetUserName(user_name, &len))
+       len = sizeof(user_name);
+       if (!GetUserName(user_name, &len)) {
+               initialized = 1;
                return NULL;
-       p.pw_name = user_name;
-       p.pw_gecos = "unknown";
-       p.pw_dir = NULL;
-       return &p;
+       }
+
+       p = xmalloc(sizeof(*p));
+       p->pw_name = user_name;
+       p->pw_gecos = get_extended_user_info(NameDisplay);
+       if (!p->pw_gecos)
+               p->pw_gecos = "unknown";
+       p->pw_dir = NULL;
+
+       initialized = 1;
+       return p;
 }
 
 static HANDLE timer_event;