From 82cc187b952b8d6539bcb72ea5c63469409c919a Mon Sep 17 00:00:00 2001 From: Michael Geddes Date: Wed, 14 May 2014 13:59:48 +0800 Subject: [PATCH] MinGW: Add symlink support for NTFS on windows This patch implements git support for NTFS symbolic link type reparse points. * There is a specific privelege required to create symbolic links that is not generally associated with a standard user. This part is up to the user to worry about. * NTFS reparse points differentiate between file and directory links. This patch assumes file links are meant. (A separate patch will develop this further). * This patch is not intended to implement symbolic links in the shell utilities. This means that as of when this was written, bash and gnu utilities do not handle them. * Windows chdir behaves differently to *nix, and we need to unravel symbolic links for various operations to work as expected. * For efficiency, as much as possible of the calls are done with wchar_t, before being converted to utf-8. This is as much about avoiding dealing with windows default encoding as anything else. * resolve_symlink needed to be replaced in lockfile.c since there are some issues with recognising absolute paths, as well as for efficiency with wchar_t. This work was based on a combination of patches developed by the following people: original-by: Johannes Schindelin original-by: Thorvald Natvig Signed-off-by: Michael Geddes --- compat/mingw.c | 527 ++++++++++++++++++++++++++++++++++++++---- compat/mingw.h | 9 +- compat/win32/dirent.c | 9 +- lockfile.c | 6 + 4 files changed, 500 insertions(+), 51 deletions(-) diff --git a/compat/mingw.c b/compat/mingw.c index d87a0e03b6..7bc7fff8f7 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1,5 +1,6 @@ #include "../git-compat-util.h" #include "win32.h" +#include #include #include #include "../strbuf.h" @@ -201,16 +202,46 @@ static int ask_yes_no_if_possible(const char *format, ...) } } -int mingw_unlink(const char *pathname) +int do_wunlink(const wchar_t *wpathname) { - int ret, tries = 0; - wchar_t wpathname[MAX_LONG_PATH]; - if (xutftowcs_long_path(wpathname, pathname) < 0) + int ret, tries; + WIN32_FIND_DATAW findbuf; + HANDLE handle; + + /* Check for directories and symlinks */ + handle = FindFirstFileW(wpathname, &findbuf); + if (handle == INVALID_HANDLE_VALUE) { + errno = ENOENT; return -1; + } + FindClose(handle); /* read-only files cannot be removed */ _wchmod(wpathname, 0666); - while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) { + + tries = 0; + + do { + if (findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + BOOL bres; + if (findbuf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + bres = RemoveDirectoryW(wpathname); + else + bres = DeleteFileW(wpathname); + if (!bres) + ret = -1; + else + ret = 0; + } + else if (findbuf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + ret = _wrmdir(wpathname); + } + else + ret = _wunlink(wpathname); + + if (ret == 0) + break; + if (!is_file_in_use_error(GetLastError())) break; /* @@ -220,16 +251,61 @@ int mingw_unlink(const char *pathname) * complete its operation, we give up our time slice now. * If we have to retry again, we do sleep a bit. */ + if (tries >= ARRAY_SIZE(delay)) + break; Sleep(delay[tries]); tries++; - } - while (ret == -1 && is_file_in_use_error(GetLastError()) && - ask_yes_no_if_possible("Unlink of file '%s' failed. " - "Should I try again?", pathname)) - ret = _wunlink(wpathname); + } while (TRUE); + return ret; } +int mingw_wunlink(const wchar_t *wpathname) +{ + char pathname[PATH_MAX]; + int ret; + + do { + + ret = do_wunlink(wpathname); + + if (ret == 0) + break; + + if (xwcstoutf(pathname, wpathname, PATH_MAX) < 0) + return -1; + + } while (is_file_in_use_error(GetLastError()) && + ask_yes_no_if_possible("Unlink of file '%s' failed. " + "Should I try again?", pathname)); + + return ret; + + +} + +static inline wchar_t *to_backslash_wpath(wchar_t *path); + +int mingw_unlink(const char *pathname) +{ + wchar_t wpathname[MAX_LONG_PATH]; + int ret; + + do { + + if (xutftowcs_long_path(wpathname, pathname) < 0) + return -1; + + ret = do_wunlink(to_backslash_wpath(wpathname)); + + } while (ret != 0 && is_file_in_use_error(GetLastError()) && + ask_yes_no_if_possible("Unlink of file '%s' failed. " + "Should I try again?", pathname)); + + return ret; +} + + static int is_dir_empty(const wchar_t *wpath) { WIN32_FIND_DATAW findbuf; @@ -258,6 +334,7 @@ int mingw_rmdir(const char *pathname) wchar_t wpathname[MAX_LONG_PATH]; if (xutftowcs_long_path(wpathname, pathname) < 0) return -1; + to_backslash_wpath(wpathname); while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) { if (!is_file_in_use_error(GetLastError())) @@ -461,6 +538,46 @@ int mingw_access(const char *filename, int mode) return _waccess(wfilename, mode & ~X_OK); } +static int do_wlstat(int follow, const wchar_t *wfilename, struct stat *buf, wchar_t *wbuffer, int buffersize); +static int do_readlink(const wchar_t *path, wchar_t *buf, size_t bufsiz); +static wchar_t *do_resolve_symlink(wchar_t *pathname, size_t bufsize); +static inline int is_absolute_pathw(const wchar_t *path); +static wchar_t *do_getcwd(wchar_t *wpointer, int len); + +/* + * When changing to a directory that contains symbolic links in the path, + * we need to follow the unix behaviour, which is to unravel the symbolic links. + * + * Windows seems to leave the symbolic links intact. + * This breaks functions like make_absolute_path() that requires the unix behaviour to work. + * + */ +static int do_wchdir(wchar_t *dirname) +{ + wchar_t resolved[MAX_PATH]; + int ret; + + if (! is_absolute_pathw(dirname)) { + + /* + * Change to real, symlink-resolved, CWD first. + * This enforces unix behaviour when CWD is a symlink. + */ + if (!do_getcwd(resolved, MAX_PATH)) { + errno = ENOENT; /* CWD is not a path. */ + return -1; + } + + ret = _wchdir(resolved); + if (ret) + return ret; + } + wcscpy(resolved, dirname); + do_resolve_symlink(resolved, MAX_PATH); + return _wchdir(resolved); +} + + /* cached length of current directory for handle_long_path */ static int current_directory_len = 0; @@ -471,7 +588,7 @@ int mingw_chdir(const char *dirname) /* SetCurrentDirectoryW doesn't support long paths */ if (xutftowcs_path(wdirname, dirname) < 0) return -1; - result = _wchdir(wdirname); + return do_wchdir(to_backslash_wpath(wdirname)); current_directory_len = GetCurrentDirectoryW(0, NULL); return result; } @@ -484,21 +601,187 @@ int mingw_chmod(const char *filename, int mode) return _wchmod(wfilename, mode); } -/* 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. +#ifndef FSCTL_GET_REPARSE_POINT +#define FSCTL_GET_REPARSE_POINT 0x000900a8 +#endif + +static int do_readlink(const wchar_t *path, wchar_t *buf, size_t bufsiz) +{ + HANDLE handle = CreateFileW(path, GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, + NULL); + + if (handle != INVALID_HANDLE_VALUE) { + unsigned char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + DWORD dummy = 0; + if (DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0, buffer, + MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &dummy, NULL)) { + REPARSE_DATA_BUFFER *b = (REPARSE_DATA_BUFFER *) buffer; + if (b->ReparseTag == IO_REPARSE_TAG_SYMLINK) { + int len = b->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t); + int offset = b->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t); + len = (bufsiz < len) ? bufsiz : len; + /* Get rid of the \??\ prefix that gets put into absolute reparse points, something + * to do with the NT drive namespace. + */ + if (len >4 && wcsncmp(b->SymbolicLinkReparseBuffer.PathBuffer+offset,L"\\??\\",4) == 0) { + offset += 4; + len -= 4; + } + wcsncpy(buf, & b->SymbolicLinkReparseBuffer.PathBuffer[offset], len); + buf[len] = 0; + CloseHandle(handle); + return len; + } + } + + CloseHandle(handle); + } + + errno = EINVAL; + return -1; +} + +#define has_dos_drive_prefixw(path) (iswalpha(*(path)) && (path)[1] == ':') +static inline int is_absolute_pathw(const wchar_t *path) +{ + return is_dir_sep(path[0]) || has_dos_drive_prefixw(path); +} + +char *mingw_resolve_symlink(char *pathname, size_t bufsize) { + wchar_t wpathname[MAX_PATH+1]; + if (xutftowcs(wpathname, pathname, MAX_PATH) >= 0) { + do_resolve_symlink(to_backslash_wpath(wpathname), MAX_PATH); + xwcstoutf(pathname, wpathname, bufsize); + } + return pathname; +} + +/* + * This resolves symlinks with support for symlinks in the path along the way. * - * If follow is true then act like stat() and report on the link - * target. Otherwise report on the link itself. + * It is based on resolve_symlink in lockfile.c with support for widechars. + * + * To do this, it uses a left-to-right approach, checking each part of the path + * for a symlink and then replacing that section (depending on whether it is + * absolute or relative) with the link. + * + * This implementation is required to pass tests for make_absolute_path. */ -static int do_lstat(int follow, const char *file_name, struct stat *buf) +const int MAXDEPTH = 10; +static wchar_t *do_resolve_symlink(wchar_t *pathname, size_t bufsize) +{ + /* Limit the number of links we resolve to prevent recursion */ + int depth = MAXDEPTH; + + wchar_t *start=pathname; + wchar_t *from=pathname; + wchar_t *last; + wchar_t link[MAX_PATH+1]; + + while (*from && depth > 0) { + wchar_t endch; + int link_len; + /* Find the next section. */ + from = wcschr(start+1, '\\'); + if (!from) + from = start+wcslen(start); + else { + /* Skip drive letters */ + if (from > start && from[-1] == ':') { + from = wcschr(from+1, '\\'); + if (!from) + from = start+wcslen(start); + } + } + if (start > pathname && from-start == 3 && start[1]=='.' && start[2] == '.' ) { + /* Handle /../ */ + if (start[-1] != ':') { + for (last = start-1; last > pathname; --last) + if (*last == '\\') + break; + if (last > pathname) { + memmove(last, from, (pathname-from)+bufsize ); + } + } + } + + /* Temporarily replace pathsep with \0 */ + endch = *from; + *from = L'\0'; + /* and read the link up to that point */ + link_len = do_readlink(pathname, link, MAX_PATH); + *from = endch; + + if (link_len >=0) { + /* It's a link - make sure it will fit */ + if ((link_len + (from-pathname)) > MAX_PATH) { + char path[MAX_PATH]; + xwcstoutf(path, pathname, MAX_PATH); + warning("%s: symlink too long", path); + return pathname; + } + /* readlink() never null-terminates */ + link[link_len] = L'\0'; + + if (is_absolute_pathw(link)) { + /* Concat rest onto link */ + wcscat(link, from); + if (wcslen(link) > bufsize) { + char path[MAX_PATH]; + xwcstoutf(path, pathname, MAX_PATH); + warning("%s: symlink too long", path); + return pathname; + } + /* Absolute path replace all*/ + wcscpy(pathname, link); + start = pathname; + } else { + if ((wcslen(link)+(start-pathname) > bufsize)) { + char path[MAX_PATH]; + xwcstoutf(path, pathname, MAX_PATH); + warning("%s: symlink too long", path); + return pathname; + } + /* Concat rest onto link */ + wcscat(link, from); + + if (start[0] == L'.' && start[1] == L'\0') { + /* Special case of '.' and a relative symlink, + since this has to be actually relative to + one directory up. + */ + wcscpy(start, L"..\\"); + /* Concat with link and rest of filename. + */ + wcscpy(start+3, link); + } else { + /* replace bit from start + * with link and rest of filename. + */ + wcscpy(start, link); + } + } + --depth; + } else { + /* Move to next section. + */ + start = from+1; + while (*start == '\\') + ++start; + } + } + return pathname; +} + +static int do_wlstat(int follow, const wchar_t *wfilename, struct stat *buf, wchar_t *wbuffer, int buffersize) { WIN32_FILE_ATTRIBUTE_DATA fdata; - wchar_t wfilename[MAX_LONG_PATH]; - if (xutftowcs_long_path(wfilename, file_name) < 0) - return -1; + int usebuffer = 0; - if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) { + while (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) { buf->st_ino = 0; buf->st_gid = 0; buf->st_uid = 0; @@ -512,24 +795,54 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf) buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { WIN32_FIND_DATAW findbuf; + int len; + /* Check for trailing pathsep and remove it. */ + len = wcslen(wfilename); + if (len > 0 && wfilename[len-1] == L'\\') { + if (!usebuffer) { + /* don't modify original */ + usebuffer = 1; + wcscpy(wbuffer, wfilename); + /* Now continuing using passed-in buffer. */ + wfilename = wbuffer; + } + wbuffer[len-1] = L'\0'; + } + HANDLE handle = FindFirstFileW(wfilename, &findbuf); - if (handle != INVALID_HANDLE_VALUE) { + if (handle == INVALID_HANDLE_VALUE) { + /* Prevent infinite loop */ + break; + } else { if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && (findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) { - if (follow) { - char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; - buf->st_size = readlink(file_name, buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + if (!follow || !wbuffer) { + wchar_t buffer[MAX_PATH]; + buf->st_size = do_readlink(wfilename, buffer, MAX_PATH); + buf->st_mode = S_IFLNK; + buf->st_mode |= S_IREAD; + if (!(findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) + buf->st_mode |= S_IWRITE; + FindClose(handle); + return 0; } else { buf->st_mode = S_IFLNK; + wcscpy(wbuffer, wfilename); + if (do_resolve_symlink(wbuffer, buffersize) <= 0) { + FindClose(handle); + break; + } + /* Now continuing using link in passed-in buffer. + */ + wfilename = wbuffer; + usebuffer = 1; } - buf->st_mode |= S_IREAD; - if (!(findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) - buf->st_mode |= S_IWRITE; } FindClose(handle); } } - return 0; + else + return 0; } switch (GetLastError()) { case ERROR_ACCESS_DENIED: @@ -551,6 +864,22 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf) 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. + * + * If follow is true then act like stat() and report on the link + * target. Otherwise report on the link itself. + */ +static int do_lstat(int follow, const char *file_name, struct stat *buf) +{ + wchar_t wfilename[MAX_LONG_PATH]; + if (xutftowcs_long_path(wfilename, file_name) < 0) + return -1; + to_backslash_wpath(wfilename); + return do_wlstat(follow, wfilename, buf, wfilename, MAX_PATH); +} + /* We provide our own lstat/fstat functions, since the provided * lstat/fstat functions are so slow. These stat functions are * tailored for Git's usage (read: fast), and are not meant to be @@ -755,17 +1084,35 @@ struct tm *localtime_r(const time_t *timep, struct tm *result) return result; } -char *mingw_getcwd(char *pointer, int len) +static wchar_t *do_getcwd(wchar_t *wpointer, int len) { int i; + if (!_wgetcwd(wpointer, len)) + return NULL; + + /* Unix getcwd resolves symlinks + */ + do_resolve_symlink(wpointer, len); + i = wcslen(wpointer); + /* Unix getcwd doesn't appear ever to have a trailing / + * which do_resolve_symlink can append. + */ + if (wpointer[i-1] == L'\\') + wpointer[i-1] = L'\0'; + return wpointer; +} + +static inline wchar_t *to_unix_wpath(wchar_t *path); + +char *mingw_getcwd(char *pointer, int len) +{ wchar_t wpointer[MAX_PATH]; - if (!_wgetcwd(wpointer, ARRAY_SIZE(wpointer))) + if (!do_getcwd(wpointer, ARRAY_SIZE(wpointer))) return NULL; - if (xwcstoutf(pointer, wpointer, len) < 0) + + if (xwcstoutf(pointer, to_unix_wpath(wpointer), len) < 0) return NULL; - for (i = 0; pointer[i]; i++) - if (pointer[i] == '\\') - pointer[i] = '/'; + return pointer; } @@ -1856,25 +2203,49 @@ int mingw_raise(int sig) } } - -static const char *make_backslash_path(const char *path) +static inline wchar_t *to_unix_wpath(wchar_t *path) { - static char buf[PATH_MAX + 1]; - char *c; + wchar_t *c; + for (c = path; *c; c++) { + if (*c == '\\') + *c = '/'; + } + return path; +} - if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) - die("Too long path: %.*s", 60, path); - - for (c = buf; *c; c++) { +static inline wchar_t *to_backslash_wpath(wchar_t *path) +{ + wchar_t *c; + for (c = path; *c; c++) { if (*c == '/') *c = '\\'; } - return buf; + return path; +} + + +static inline char *backslash_path(char *path) +{ + char *c; + for (c = path; *c; c++) { + if (*c == '/') + *c = '\\'; + } + return path; +} + +static const char *make_backslash_path(const char *path, char *buf) +{ + if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) + die("Too long path: %.*s", 60, path); + + return backslash_path(buf); } void mingw_open_html(const char *unixpath) { - const char *htmlpath = make_backslash_path(unixpath); + char buf[PATH_MAX + 1]; + const char *htmlpath = make_backslash_path(unixpath, buf); typedef HINSTANCE (WINAPI *T)(HWND, const char *, const char *, const char *, const char *, INT); T ShellExecute; @@ -1901,10 +2272,21 @@ int link(const char *oldpath, const char *newpath) { typedef BOOL (WINAPI *T)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES); static T create_hard_link = NULL; - wchar_t woldpath[MAX_LONG_PATH], wnewpath[MAX_LONG_PATH]; + wchar_t woldpath[MAX_LONG_PATH], wnewpath[MAX_LONG_PATH], wbuf[MAX_LONG_PATH]; + struct stat st; + if (xutftowcs_long_path(woldpath, oldpath) < 0 || xutftowcs_long_path(wnewpath, newpath) < 0) return -1; + to_backslash_wpath(woldpath); + to_backslash_wpath(wnewpath); + + if (!do_wlstat(0, wnewpath, &st, wbuf, MAX_PATH)) { + /* Delete the file if it exists. + */ + if (mingw_wunlink(wnewpath)) + return -1; + } if (!create_hard_link) { create_hard_link = (T) GetProcAddress( @@ -1923,6 +2305,59 @@ int link(const char *oldpath, const char *newpath) return 0; } +int symlink(const char *oldpath, const char *newpath) +{ + typedef BOOL WINAPI (*symlink_fn)(const wchar_t*, const wchar_t*, DWORD); + static symlink_fn create_symbolic_link = NULL; + wchar_t woldpath[MAX_PATH], wnewpath[MAX_PATH], wbuf[MAX_PATH]; + struct stat st; + + if (xutftowcs(woldpath, oldpath, MAX_PATH) < 0) + return -1; + if (xutftowcs(wnewpath, newpath, MAX_PATH) < 0) + return -1; + to_backslash_wpath(woldpath); + to_backslash_wpath(wnewpath); + if (!do_wlstat(0, wnewpath, &st, wbuf, MAX_PATH)) { + /* Delete the file if it exists. + */ + if (mingw_wunlink(wnewpath)) + return -1; + } + + if (!create_symbolic_link) { + create_symbolic_link = (symlink_fn) GetProcAddress( + GetModuleHandle("kernel32.dll"), "CreateSymbolicLinkW"); + if (!create_symbolic_link) + create_symbolic_link = (symlink_fn)-1; + } + if (create_symbolic_link == (symlink_fn)-1) { + errno = ENOSYS; + return -1; + } + + if (!create_symbolic_link(wnewpath, woldpath, 0)) { + errno = err_win_to_posix(GetLastError()); + return -1; + } + return 0; +} + +int do_readlink(const wchar_t *path, wchar_t *buf, size_t bufsiz); + +int readlink(const char *path, char *buf, size_t bufsiz) +{ + wchar_t wpath[MAX_PATH], wbuffer[MAX_PATH]; + int result; + if (xutftowcs(wpath, path, MAX_PATH) < 0) + return -1; + result = do_readlink(wpath, wbuffer, MAX_PATH); + if (result >= 0) { + return xwcstoutf(buf, to_unix_wpath(wbuffer), bufsiz); + } + return -1; +} + pid_t waitpid(pid_t pid, int *status, int options) { HANDLE h = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, diff --git a/compat/mingw.h b/compat/mingw.h index 08b83fe1de..8be34d4890 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -84,10 +84,6 @@ struct itimerval { * trivial stubs */ -static inline int readlink(const char *path, char *buf, size_t bufsiz) -{ errno = ENOSYS; return -1; } -static inline int symlink(const char *oldpath, const char *newpath) -{ errno = ENOSYS; return -1; } static inline int fchmod(int fildes, mode_t mode) { errno = ENOSYS; return -1; } static inline pid_t fork(void) @@ -163,6 +159,8 @@ struct passwd *getpwuid(uid_t uid); int setitimer(int type, struct itimerval *in, struct itimerval *out); int sigaction(int sig, struct sigaction *in, struct sigaction *out); int link(const char *oldpath, const char *newpath); +int symlink(const char *oldpath, const char *newpath); +int readlink(const char *path, char *buf, size_t bufsiz); /* * replacements of existing functions @@ -376,6 +374,9 @@ void mingw_open_html(const char *path); void mingw_mark_as_git_dir(const char *dir); #define mark_as_git_dir mingw_mark_as_git_dir +char *mingw_resolve_symlink(char *p, size_t s); +#define resolve_symlink mingw_resolve_symlink + /** * Max length of long paths (exceeding MAX_PATH). The actual maximum supported * by NTFS is 32,767 (* sizeof(wchar_t)), but we choose an arbitrary smaller diff --git a/compat/win32/dirent.c b/compat/win32/dirent.c index b3bd8d7af7..8cf96ff42b 100644 --- a/compat/win32/dirent.c +++ b/compat/win32/dirent.c @@ -16,7 +16,14 @@ static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata) xwcstoutf(ent->d_name, fdata->cFileName, MAX_PATH * 3); /* Set file type, based on WIN32_FIND_DATA */ - if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + /* First check for symlinks since a directory symlink has the FILE_ATTRIBUTE_DIRECTORY + * attribute as well. Posix doesn't distinguish between directory/file symlinks, but + * NTFS does. + */ + if (fdata->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT + && (fdata->dwReserved0 == IO_REPARSE_TAG_SYMLINK)) + ent->d_type = DT_LNK; + else if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ent->d_type = DT_DIR; else ent->d_type = DT_REG; diff --git a/lockfile.c b/lockfile.c index 8fbcb6a98a..a812d06052 100644 --- a/lockfile.c +++ b/lockfile.c @@ -29,6 +29,11 @@ static void remove_lock_file_on_signal(int signo) raise(signo); } +/* mingw requires its own version of resolve_symlink to be use, + * including in lock_file below + */ +#ifndef resolve_symlink + /* * p = absolute or relative path name * @@ -121,6 +126,7 @@ static char *resolve_symlink(char *p, size_t s) return p; } +#endif static int lock_file(struct lock_file *lk, const char *path, int flags) {