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 <johannes.schindelin@gmx.de>
original-by: Thorvald Natvig <slicer@users.sourceforge.net>

Signed-off-by: Michael Geddes <michael@frog.wheelycreek.net>
This commit is contained in:
Michael Geddes
2014-05-14 13:59:48 +08:00
parent 7e872d24a9
commit 82cc187b95
4 changed files with 500 additions and 51 deletions

View File

@@ -1,5 +1,6 @@
#include "../git-compat-util.h"
#include "win32.h"
#include <winioctl.h>
#include <conio.h>
#include <wchar.h>
#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,

View File

@@ -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

View File

@@ -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;

View File

@@ -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)
{