diff options
Diffstat (limited to 'src/win32/posix_w32.c')
-rw-r--r-- | src/win32/posix_w32.c | 411 |
1 files changed, 257 insertions, 154 deletions
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index 10de70da8..4d56299f7 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2012 the libgit2 contributors + * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. @@ -7,23 +7,18 @@ #include "../posix.h" #include "path.h" #include "utf-conv.h" +#include "repository.h" #include <errno.h> #include <io.h> #include <fcntl.h> - +#include <ws2tcpip.h> int p_unlink(const char *path) { - int ret = 0; - wchar_t* buf; - - if ((buf = gitwin_to_utf16(path)) != NULL) { - _wchmod(buf, 0666); - ret = _wunlink(buf); - git__free(buf); - } - - return ret; + wchar_t buf[GIT_WIN_PATH]; + git__utf8_to_16(buf, GIT_WIN_PATH, path); + _wchmod(buf, 0666); + return _wunlink(buf); } int p_fsync(int fd) @@ -57,16 +52,32 @@ GIT_INLINE(time_t) filetime_to_time_t(const FILETIME *ft) return (time_t)winTime; } -static int do_lstat(const char *file_name, struct stat *buf) +#define WIN32_IS_WSEP(CH) ((CH) == L'/' || (CH) == L'\\') + +static int do_lstat( + const char *file_name, struct stat *buf, int posix_enotdir) { WIN32_FILE_ATTRIBUTE_DATA fdata; - wchar_t* fbuf = gitwin_to_utf16(file_name); - if (!fbuf) - return -1; + wchar_t fbuf[GIT_WIN_PATH], lastch; + int flen; + + flen = git__utf8_to_16(fbuf, GIT_WIN_PATH, file_name); + + /* truncate trailing slashes */ + for (; flen > 0; --flen) { + lastch = fbuf[flen - 1]; + if (WIN32_IS_WSEP(lastch)) + fbuf[flen - 1] = L'\0'; + else if (lastch != L'\0') + break; + } if (GetFileAttributesExW(fbuf, GetFileExInfoStandard, &fdata)) { int fMode = S_IREAD; + if (!buf) + return 0; + if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) fMode |= S_IFDIR; else @@ -89,44 +100,59 @@ static int do_lstat(const char *file_name, struct stat *buf) buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); - git__free(fbuf); - return 0; - } + /* Windows symlinks have zero file size, call readlink to determine + * the length of the path pointed to, which we expect everywhere else + */ + if (S_ISLNK(fMode)) { + char target[GIT_WIN_PATH]; + int readlink_result; - git__free(fbuf); - return -1; -} + readlink_result = p_readlink(file_name, target, GIT_WIN_PATH); -int p_lstat(const char *file_name, struct stat *buf) -{ - int error; - size_t namelen; - char *alt_name; + if (readlink_result == -1) + return -1; - if (do_lstat(file_name, buf) == 0) - return 0; + buf->st_size = strlen(target); + } - /* if file_name ended in a '/', Windows returned ENOENT; - * try again without trailing slashes - */ - namelen = strlen(file_name); - if (namelen && file_name[namelen-1] != '/') - return -1; + return 0; + } - while (namelen && file_name[namelen-1] == '/') - --namelen; + errno = ENOENT; - if (!namelen) - return -1; + /* We need POSIX behavior, then ENOTDIR must set when any of the folders in the + * file path is a regular file,otherwise ENOENT must be set. + */ + if (posix_enotdir) { + /* scan up path until we find an existing item */ + while (1) { + /* remove last directory component */ + for (--flen; flen > 0 && !WIN32_IS_WSEP(fbuf[flen]); --flen); + + if (flen <= 0) + break; + + fbuf[flen] = L'\0'; + + if (GetFileAttributesExW(fbuf, GetFileExInfoStandard, &fdata)) { + if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + errno = ENOTDIR; + break; + } + } + } - alt_name = git__strndup(file_name, namelen); - if (!alt_name) - return -1; + return -1; +} - error = do_lstat(alt_name, buf); +int p_lstat(const char *filename, struct stat *buf) +{ + return do_lstat(filename, buf, 0); +} - git__free(alt_name); - return error; +int p_lstat_posixly(const char *filename, struct stat *buf) +{ + return do_lstat(filename, buf, 1); } int p_readlink(const char *link, char *target, size_t target_len) @@ -135,7 +161,7 @@ int p_readlink(const char *link, char *target, size_t target_len) static fpath_func pGetFinalPath = NULL; HANDLE hFile; DWORD dwRet; - wchar_t* link_w; + wchar_t link_w[GIT_WIN_PATH]; wchar_t* target_w; int error = 0; @@ -146,10 +172,10 @@ int p_readlink(const char *link, char *target, size_t target_len) * it is not available in platforms older than Vista */ if (pGetFinalPath == NULL) { - HINSTANCE library = LoadLibrary("kernel32"); + HMODULE module = GetModuleHandle("kernel32"); - if (library != NULL) - pGetFinalPath = (fpath_func)GetProcAddress(library, "GetFinalPathNameByHandleW"); + if (module != NULL) + pGetFinalPath = (fpath_func)GetProcAddress(module, "GetFinalPathNameByHandleW"); if (pGetFinalPath == NULL) { giterr_set(GITERR_OS, @@ -158,8 +184,7 @@ int p_readlink(const char *link, char *target, size_t target_len) } } - link_w = gitwin_to_utf16(link); - GITERR_CHECK_ALLOC(link_w); + git__utf8_to_16(link_w, GIT_WIN_PATH, link); hFile = CreateFileW(link_w, // file to open GENERIC_READ, // open for reading @@ -169,8 +194,6 @@ int p_readlink(const char *link, char *target, size_t target_len) FILE_FLAG_BACKUP_SEMANTICS, // normal file NULL); // no attr. template - git__free(link_w); - if (hFile == INVALID_HANDLE_VALUE) { giterr_set(GITERR_OS, "Cannot open '%s' for reading", link); return -1; @@ -217,46 +240,43 @@ int p_readlink(const char *link, char *target, size_t target_len) return dwRet; } +int p_symlink(const char *old, const char *new) +{ + /* Real symlinks on NTFS require admin privileges. Until this changes, + * libgit2 just creates a text file with the link target in the contents. + */ + return git_futils_fake_symlink(old, new); +} + int p_open(const char *path, int flags, ...) { - int fd; - wchar_t* buf; + wchar_t buf[GIT_WIN_PATH]; mode_t mode = 0; - buf = gitwin_to_utf16(path); - if (!buf) - return -1; + git__utf8_to_16(buf, GIT_WIN_PATH, path); - if (flags & O_CREAT) - { + if (flags & O_CREAT) { va_list arg_list; va_start(arg_list, flags); - mode = va_arg(arg_list, mode_t); + mode = (mode_t)va_arg(arg_list, int); va_end(arg_list); } - fd = _wopen(buf, flags | _O_BINARY, mode); - - git__free(buf); - return fd; + return _wopen(buf, flags | _O_BINARY, mode); } int p_creat(const char *path, mode_t mode) { - int fd; - wchar_t* buf = gitwin_to_utf16(path); - if (!buf) - return -1; - fd = _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode); - git__free(buf); - return fd; + wchar_t buf[GIT_WIN_PATH]; + git__utf8_to_16(buf, GIT_WIN_PATH, path); + return _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode); } int p_getcwd(char *buffer_out, size_t size) { int ret; - wchar_t* buf; + wchar_t *buf; if ((size_t)((int)size) != size) return -1; @@ -275,104 +295,78 @@ int p_getcwd(char *buffer_out, size_t size) int p_stat(const char* path, struct stat* buf) { - return do_lstat(path, buf); + return do_lstat(path, buf, 0); } int p_chdir(const char* path) { - wchar_t* buf = gitwin_to_utf16(path); - int ret; - if (!buf) - return -1; - ret = _wchdir(buf); - git__free(buf); - return ret; + wchar_t buf[GIT_WIN_PATH]; + git__utf8_to_16(buf, GIT_WIN_PATH, path); + return _wchdir(buf); } int p_chmod(const char* path, mode_t mode) { - wchar_t* buf = gitwin_to_utf16(path); - int ret; - if (!buf) - return -1; - ret = _wchmod(buf, mode); - git__free(buf); - return ret; + wchar_t buf[GIT_WIN_PATH]; + git__utf8_to_16(buf, GIT_WIN_PATH, path); + return _wchmod(buf, mode); } int p_rmdir(const char* path) { - wchar_t* buf = gitwin_to_utf16(path); - int ret; - if (!buf) - return -1; - ret = _wrmdir(buf); - git__free(buf); - return ret; + wchar_t buf[GIT_WIN_PATH]; + git__utf8_to_16(buf, GIT_WIN_PATH, path); + return _wrmdir(buf); } int p_hide_directory__w32(const char *path) { - int res; - wchar_t* buf = gitwin_to_utf16(path); - if (!buf) - return -1; - - res = SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN); - git__free(buf); - - return (res != 0) ? 0 : -1; /* MSDN states a "non zero" value indicates a success */ + wchar_t buf[GIT_WIN_PATH]; + git__utf8_to_16(buf, GIT_WIN_PATH, path); + return (SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN) != 0) ? 0 : -1; } char *p_realpath(const char *orig_path, char *buffer) { - int ret, buffer_sz = 0; - wchar_t* orig_path_w = gitwin_to_utf16(orig_path); - wchar_t* buffer_w = (wchar_t*)git__malloc(GIT_PATH_MAX * sizeof(wchar_t)); + int ret; + wchar_t orig_path_w[GIT_WIN_PATH]; + wchar_t buffer_w[GIT_WIN_PATH]; - if (!orig_path_w || !buffer_w) - return NULL; + git__utf8_to_16(orig_path_w, GIT_WIN_PATH, orig_path); - ret = GetFullPathNameW(orig_path_w, GIT_PATH_MAX, buffer_w, NULL); - git__free(orig_path_w); + /* Implicitly use GetCurrentDirectory which can be a threading issue */ + ret = GetFullPathNameW(orig_path_w, GIT_WIN_PATH, buffer_w, NULL); /* According to MSDN, a return value equals to zero means a failure. */ - if (ret == 0 || ret > GIT_PATH_MAX) { + if (ret == 0 || ret > GIT_WIN_PATH) buffer = NULL; - goto done; + + else if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) { + buffer = NULL; + errno = ENOENT; } - if (buffer == NULL) { - buffer_sz = WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, NULL, 0, NULL, NULL); + else if (buffer == NULL) { + int buffer_sz = WideCharToMultiByte( + CP_UTF8, 0, buffer_w, -1, NULL, 0, NULL, NULL); if (!buffer_sz || !(buffer = (char *)git__malloc(buffer_sz)) || - !WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, buffer, buffer_sz, NULL, NULL)) + !WideCharToMultiByte( + CP_UTF8, 0, buffer_w, -1, buffer, buffer_sz, NULL, NULL)) { git__free(buffer); buffer = NULL; - goto done; - } - } else { - if (!WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, buffer, GIT_PATH_MAX, NULL, NULL)) { - buffer = NULL; - goto done; } } - if (!git_path_exists(buffer)) - { - if (buffer_sz > 0) - git__free(buffer); - + else if (!WideCharToMultiByte( + CP_UTF8, 0, buffer_w, -1, buffer, GIT_PATH_MAX, NULL, NULL)) buffer = NULL; - errno = ENOENT; - } -done: - git__free(buffer_w); if (buffer) git_path_mkposix(buffer); + return buffer; } @@ -381,7 +375,8 @@ int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr) #ifdef _MSC_VER int len; - if (count == 0 || (len = _vsnprintf(buffer, count, format, argptr)) < 0) + if (count == 0 || + (len = _vsnprintf_s(buffer, count, _TRUNCATE, format, argptr)) < 0) return _vscprintf(format, argptr); return len; @@ -414,7 +409,7 @@ int p_mkstemp(char *tmp_path) return -1; #endif - return p_creat(tmp_path, 0744); + return p_creat(tmp_path, 0744); //-V536 } int p_setenv(const char* name, const char* value, int overwrite) @@ -427,32 +422,19 @@ int p_setenv(const char* name, const char* value, int overwrite) int p_access(const char* path, mode_t mode) { - wchar_t *buf = gitwin_to_utf16(path); - int ret; - if (!buf) - return -1; - - ret = _waccess(buf, mode); - git__free(buf); - - return ret; + wchar_t buf[GIT_WIN_PATH]; + git__utf8_to_16(buf, GIT_WIN_PATH, path); + return _waccess(buf, mode); } int p_rename(const char *from, const char *to) { - wchar_t *wfrom = gitwin_to_utf16(from); - wchar_t *wto = gitwin_to_utf16(to); - int ret; - - if (!wfrom || !wto) - return -1; - - ret = MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? 0 : -1; - - git__free(wfrom); - git__free(wto); + wchar_t wfrom[GIT_WIN_PATH]; + wchar_t wto[GIT_WIN_PATH]; - return ret; + git__utf8_to_16(wfrom, GIT_WIN_PATH, from); + git__utf8_to_16(wto, GIT_WIN_PATH, to); + return MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? 0 : -1; } int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags) @@ -470,3 +452,124 @@ int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags) return send(socket, buffer, (int)length, flags); } + +/** + * Borrowed from http://old.nabble.com/Porting-localtime_r-and-gmtime_r-td15282276.html + * On Win32, `gmtime_r` doesn't exist but `gmtime` is threadsafe, so we can use that + */ +struct tm * +p_localtime_r (const time_t *timer, struct tm *result) +{ + struct tm *local_result; + local_result = localtime (timer); + + if (local_result == NULL || result == NULL) + return NULL; + + memcpy (result, local_result, sizeof (struct tm)); + return result; +} +struct tm * +p_gmtime_r (const time_t *timer, struct tm *result) +{ + struct tm *local_result; + local_result = gmtime (timer); + + if (local_result == NULL || result == NULL) + return NULL; + + memcpy (result, local_result, sizeof (struct tm)); + return result; +} + +#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) +#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 +#else +#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL +#endif + +#ifndef _TIMEZONE_DEFINED +#define _TIMEZONE_DEFINED +struct timezone +{ + int tz_minuteswest; /* minutes W of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; +#endif + +int p_gettimeofday(struct timeval *tv, struct timezone *tz) +{ + FILETIME ft; + unsigned __int64 tmpres = 0; + static int tzflag; + + if (NULL != tv) + { + GetSystemTimeAsFileTime(&ft); + + tmpres |= ft.dwHighDateTime; + tmpres <<= 32; + tmpres |= ft.dwLowDateTime; + + /*converting file time to unix epoch*/ + tmpres /= 10; /*convert into microseconds*/ + tmpres -= DELTA_EPOCH_IN_MICROSECS; + tv->tv_sec = (long)(tmpres / 1000000UL); + tv->tv_usec = (long)(tmpres % 1000000UL); + } + + if (NULL != tz) + { + if (!tzflag) + { + _tzset(); + tzflag++; + } + tz->tz_minuteswest = _timezone / 60; + tz->tz_dsttime = _daylight; + } + + return 0; +} + +int p_inet_pton(int af, const char* src, void* dst) +{ + union { + struct sockaddr_in6 sin6; + struct sockaddr_in sin; + } sa; + int srcsize; + + switch(af) + { + case AF_INET: + sa.sin.sin_family = AF_INET; + srcsize = (int)sizeof(sa.sin); + break; + case AF_INET6: + sa.sin6.sin6_family = AF_INET6; + srcsize = (int)sizeof(sa.sin6); + break; + default: + errno = WSAEPFNOSUPPORT; + return -1; + } + + if (WSAStringToAddress((LPSTR)src, af, NULL, (struct sockaddr *) &sa, &srcsize) != 0) + { + errno = WSAGetLastError(); + return -1; + } + + switch(af) + { + case AF_INET: + memcpy(dst, &sa.sin.sin_addr, sizeof(sa.sin.sin_addr)); + break; + case AF_INET6: + memcpy(dst, &sa.sin6.sin6_addr, sizeof(sa.sin6.sin6_addr)); + break; + } + + return 1; +} |