diff options
Diffstat (limited to 'compat')
-rw-r--r-- | compat/mingw.c | 260 | ||||
-rw-r--r-- | compat/mingw.h | 38 | ||||
-rw-r--r-- | compat/mmap.c | 2 | ||||
-rw-r--r-- | compat/poll/poll.c | 6 | ||||
-rw-r--r-- | compat/poll/poll.h | 15 | ||||
-rw-r--r-- | compat/precompose_utf8.h | 3 | ||||
-rw-r--r-- | compat/vcbuild/README | 4 |
7 files changed, 273 insertions, 55 deletions
diff --git a/compat/mingw.c b/compat/mingw.c index 6ded1c859f..3b44dd99d7 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -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; @@ -341,12 +367,74 @@ int mingw_mkdir(const char *path, int mode) return ret; } +/* + * Calling CreateFile() using FILE_APPEND_DATA and without FILE_WRITE_DATA + * is documented in [1] as opening a writable file handle in append mode. + * (It is believed that) this is atomic since it is maintained by the + * kernel unlike the O_APPEND flag which is racily maintained by the CRT. + * + * [1] https://docs.microsoft.com/en-us/windows/desktop/fileio/file-access-rights-constants + * + * This trick does not appear to work for named pipes. Instead it creates + * a named pipe client handle that cannot be written to. Callers should + * just use the regular _wopen() for them. (And since client handle gets + * bound to a unique server handle, it isn't really an issue.) + */ +static int mingw_open_append(wchar_t const *wfilename, int oflags, ...) +{ + HANDLE handle; + int fd; + DWORD create = (oflags & O_CREAT) ? OPEN_ALWAYS : OPEN_EXISTING; + + /* only these flags are supported */ + if ((oflags & ~O_CREAT) != (O_WRONLY | O_APPEND)) + return errno = ENOSYS, -1; + + /* + * FILE_SHARE_WRITE is required to permit child processes + * to append to the file. + */ + handle = CreateFileW(wfilename, FILE_APPEND_DATA, + FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, create, FILE_ATTRIBUTE_NORMAL, NULL); + if (handle == INVALID_HANDLE_VALUE) + return errno = err_win_to_posix(GetLastError()), -1; + + /* + * No O_APPEND here, because the CRT uses it only to reset the + * file pointer to EOF before each write(); but that is not + * necessary (and may lead to races) for a file created with + * FILE_APPEND_DATA. + */ + fd = _open_osfhandle((intptr_t)handle, O_BINARY); + if (fd < 0) + CloseHandle(handle); + return fd; +} + +/* + * Does the pathname map to the local named pipe filesystem? + * That is, does it have a "//./pipe/" prefix? + */ +static int is_local_named_pipe_path(const char *filename) +{ + return (is_dir_sep(filename[0]) && + is_dir_sep(filename[1]) && + filename[2] == '.' && + is_dir_sep(filename[3]) && + !strncasecmp(filename+4, "pipe", 4) && + is_dir_sep(filename[8]) && + filename[9]); +} + 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; wchar_t wfilename[MAX_PATH]; + open_fn_t open_fn; va_start(args, oflags); mode = va_arg(args, int); @@ -355,9 +443,14 @@ int mingw_open (const char *filename, int oflags, ...) if (filename && !strcmp(filename, "/dev/null")) filename = "nul"; + if ((oflags & O_APPEND) && !is_local_named_pipe_path(filename)) + open_fn = mingw_open_append; + else + open_fn = _wopen; + if (xutftowcs_path(wfilename, filename) < 0) return -1; - fd = _wopen(wfilename, oflags, mode); + fd = open_fn(wfilename, oflags, mode); if (fd < 0 && (oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) { DWORD attrs = GetFileAttributesW(wfilename); @@ -375,7 +468,7 @@ int mingw_open (const char *filename, int oflags, ...) * CREATE_ALWAYS flag of CreateFile()). */ if (fd < 0 && errno == EACCES) - fd = _wopen(wfilename, oflags & ~O_CREAT, mode); + fd = open_fn(wfilename, oflags & ~O_CREAT, mode); if (fd >= 0 && set_hidden_flag(wfilename, 1)) warning("could not mark '%s' as hidden.", filename); } @@ -525,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; } /** @@ -586,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); @@ -669,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); @@ -681,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) @@ -850,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; @@ -1510,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; @@ -1731,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; diff --git a/compat/mingw.h b/compat/mingw.h index 571019d0bd..4c14e30835 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -327,18 +327,41 @@ static inline int getrlimit(int resource, struct rlimit *rlp) } /* - * Use mingw specific stat()/lstat()/fstat() implementations on Windows. + * Use mingw specific stat()/lstat()/fstat() implementations on Windows, + * including our own struct stat with 64 bit st_size and nanosecond-precision + * file times. */ #ifndef __MINGW64_VERSION_MAJOR #define off_t off64_t #define lseek _lseeki64 +struct timespec { + time_t tv_sec; + long tv_nsec; +}; #endif -/* use struct stat with 64 bit st_size */ +struct mingw_stat { + _dev_t st_dev; + _ino_t st_ino; + _mode_t st_mode; + short st_nlink; + short st_uid; + short st_gid; + _dev_t st_rdev; + off64_t st_size; + struct timespec st_atim; + struct timespec st_mtim; + struct timespec st_ctim; +}; + +#define st_atime st_atim.tv_sec +#define st_mtime st_mtim.tv_sec +#define st_ctime st_ctim.tv_sec + #ifdef stat #undef stat #endif -#define stat _stati64 +#define stat mingw_stat int mingw_lstat(const char *file_name, struct stat *buf); int mingw_stat(const char *file_name, struct stat *buf); int mingw_fstat(int fd, struct stat *buf); @@ -351,13 +374,6 @@ int mingw_fstat(int fd, struct stat *buf); #endif #define lstat mingw_lstat -#ifndef _stati64 -# define _stati64(x,y) mingw_stat(x,y) -#elif defined (_USE_32BIT_TIME_T) -# define _stat32i64(x,y) mingw_stat(x,y) -#else -# define _stat64(x,y) mingw_stat(x,y) -#endif int mingw_utime(const char *file_name, const struct utimbuf *times); #define utime mingw_utime @@ -424,6 +440,8 @@ static inline void convert_slashes(char *path) int mingw_offset_1st_component(const char *path); #define offset_1st_component mingw_offset_1st_component #define PATH_SEP ';' +extern char *mingw_query_user_email(void); +#define query_user_email mingw_query_user_email #if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800) #define PRIuMAX "I64u" #define PRId64 "I64d" diff --git a/compat/mmap.c b/compat/mmap.c index 7f662fef7b..14d31010df 100644 --- a/compat/mmap.c +++ b/compat/mmap.c @@ -4,7 +4,7 @@ void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t of { size_t n = 0; - if (start != NULL || !(flags & MAP_PRIVATE)) + if (start != NULL || flags != MAP_PRIVATE || prot != PROT_READ) die("Invalid usage of mmap when built with NO_MMAP"); start = xmalloc(length); diff --git a/compat/poll/poll.c b/compat/poll/poll.c index 7ed3fbbea1..ad5dcde439 100644 --- a/compat/poll/poll.c +++ b/compat/poll/poll.c @@ -29,9 +29,6 @@ #include <sys/types.h> -/* Specification. */ -#include <poll.h> - #include <errno.h> #include <limits.h> #include <assert.h> @@ -55,6 +52,9 @@ # include <unistd.h> #endif +/* Specification. */ +#include "poll.h" + #ifdef HAVE_SYS_IOCTL_H # include <sys/ioctl.h> #endif diff --git a/compat/poll/poll.h b/compat/poll/poll.h index cd1995292a..1e1597360f 100644 --- a/compat/poll/poll.h +++ b/compat/poll/poll.h @@ -21,6 +21,21 @@ #ifndef _GL_POLL_H #define _GL_POLL_H +#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 +/* Vista has its own, socket-only poll() */ +#undef POLLIN +#undef POLLPRI +#undef POLLOUT +#undef POLLERR +#undef POLLHUP +#undef POLLNVAL +#undef POLLRDNORM +#undef POLLRDBAND +#undef POLLWRNORM +#undef POLLWRBAND +#define pollfd compat_pollfd +#endif + /* fake a poll(2) environment */ #define POLLIN 0x0001 /* any readable data available */ #define POLLPRI 0x0002 /* OOB/Urgent readable data */ diff --git a/compat/precompose_utf8.h b/compat/precompose_utf8.h index a94e7c4342..6f843d3e1a 100644 --- a/compat/precompose_utf8.h +++ b/compat/precompose_utf8.h @@ -1,4 +1,6 @@ #ifndef PRECOMPOSE_UNICODE_H +#define PRECOMPOSE_UNICODE_H + #include <sys/stat.h> #include <sys/types.h> #include <dirent.h> @@ -41,5 +43,4 @@ int precompose_utf8_closedir(PREC_DIR *dirp); #define DIR PREC_DIR #endif /* PRECOMPOSE_UNICODE_C */ -#define PRECOMPOSE_UNICODE_H #endif /* PRECOMPOSE_UNICODE_H */ diff --git a/compat/vcbuild/README b/compat/vcbuild/README index df8a6574c9..60fd873fe8 100644 --- a/compat/vcbuild/README +++ b/compat/vcbuild/README @@ -30,8 +30,8 @@ The Steps of Build Git with VS2008 the git operations. 3. Inside Git's directory run the command: - make common-cmds.h - to generate the common-cmds.h file needed to compile git. + make command-list.h + to generate the command-list.h file needed to compile git. 4. Then either build Git with the GNU Make Makefile in the Git projects root |