Browse Source
The mingw-runtime implemenation of opendir, readdir and closedir
sets errno to 0 on success, something that POSIX explicitly
forbids. 3ba7a06
("A loose object is not corrupt if it cannot be
read due to EMFILE") introduce a dependency on this behaviour,
leading to a broken "git clone" on Windows.
compat/mingw.c contains an implementation of readdir, and
compat/msvc.c contains implementations of opendir and closedir.
Move these to compat/win32/dirent.[ch], and change to our own DIR
structure at the same time.
This provides a generic Win32-implementation of opendir, readdir
and closedir which works on both MinGW and MSVC and does not reset
errno, and as a result git clone is working again on Windows.
Signed-off-by: Erik Faye-Lund <kusmabite@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
maint


7 changed files with 137 additions and 271 deletions
@ -1,128 +0,0 @@
@@ -1,128 +0,0 @@
|
||||
/* |
||||
* DIRENT.H (formerly DIRLIB.H) |
||||
* This file has no copyright assigned and is placed in the Public Domain. |
||||
* This file is a part of the mingw-runtime package. |
||||
* |
||||
* The mingw-runtime package and its code is distributed in the hope that it |
||||
* will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR |
||||
* IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to |
||||
* warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
||||
* |
||||
* You are free to use this package and its code without limitation. |
||||
*/ |
||||
#ifndef _DIRENT_H_ |
||||
#define _DIRENT_H_ |
||||
#include <io.h> |
||||
|
||||
#define PATH_MAX 512 |
||||
|
||||
#define __MINGW_NOTHROW |
||||
|
||||
#ifndef RC_INVOKED |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
struct dirent |
||||
{ |
||||
long d_ino; /* Always zero. */ |
||||
unsigned short d_reclen; /* Always zero. */ |
||||
unsigned short d_namlen; /* Length of name in d_name. */ |
||||
char d_name[FILENAME_MAX]; /* File name. */ |
||||
}; |
||||
|
||||
/* |
||||
* This is an internal data structure. Good programmers will not use it |
||||
* except as an argument to one of the functions below. |
||||
* dd_stat field is now int (was short in older versions). |
||||
*/ |
||||
typedef struct |
||||
{ |
||||
/* disk transfer area for this dir */ |
||||
struct _finddata_t dd_dta; |
||||
|
||||
/* dirent struct to return from dir (NOTE: this makes this thread |
||||
* safe as long as only one thread uses a particular DIR struct at |
||||
* a time) */ |
||||
struct dirent dd_dir; |
||||
|
||||
/* _findnext handle */ |
||||
long dd_handle; |
||||
|
||||
/* |
||||
* Status of search: |
||||
* 0 = not started yet (next entry to read is first entry) |
||||
* -1 = off the end |
||||
* positive = 0 based index of next entry |
||||
*/ |
||||
int dd_stat; |
||||
|
||||
/* given path for dir with search pattern (struct is extended) */ |
||||
char dd_name[PATH_MAX+3]; |
||||
} DIR; |
||||
|
||||
DIR* __cdecl __MINGW_NOTHROW opendir (const char*); |
||||
struct dirent* __cdecl __MINGW_NOTHROW readdir (DIR*); |
||||
int __cdecl __MINGW_NOTHROW closedir (DIR*); |
||||
void __cdecl __MINGW_NOTHROW rewinddir (DIR*); |
||||
long __cdecl __MINGW_NOTHROW telldir (DIR*); |
||||
void __cdecl __MINGW_NOTHROW seekdir (DIR*, long); |
||||
|
||||
|
||||
/* wide char versions */ |
||||
|
||||
struct _wdirent |
||||
{ |
||||
long d_ino; /* Always zero. */ |
||||
unsigned short d_reclen; /* Always zero. */ |
||||
unsigned short d_namlen; /* Length of name in d_name. */ |
||||
wchar_t d_name[FILENAME_MAX]; /* File name. */ |
||||
}; |
||||
|
||||
/* |
||||
* This is an internal data structure. Good programmers will not use it |
||||
* except as an argument to one of the functions below. |
||||
*/ |
||||
typedef struct |
||||
{ |
||||
/* disk transfer area for this dir */ |
||||
//struct _wfinddata_t dd_dta; |
||||
|
||||
/* dirent struct to return from dir (NOTE: this makes this thread |
||||
* safe as long as only one thread uses a particular DIR struct at |
||||
* a time) */ |
||||
struct _wdirent dd_dir; |
||||
|
||||
/* _findnext handle */ |
||||
long dd_handle; |
||||
|
||||
/* |
||||
* Status of search: |
||||
* 0 = not started yet (next entry to read is first entry) |
||||
* -1 = off the end |
||||
* positive = 0 based index of next entry |
||||
*/ |
||||
int dd_stat; |
||||
|
||||
/* given path for dir with search pattern (struct is extended) */ |
||||
wchar_t dd_name[1]; |
||||
} _WDIR; |
||||
|
||||
|
||||
|
||||
_WDIR* __cdecl __MINGW_NOTHROW _wopendir (const wchar_t*); |
||||
struct _wdirent* __cdecl __MINGW_NOTHROW _wreaddir (_WDIR*); |
||||
int __cdecl __MINGW_NOTHROW _wclosedir (_WDIR*); |
||||
void __cdecl __MINGW_NOTHROW _wrewinddir (_WDIR*); |
||||
long __cdecl __MINGW_NOTHROW _wtelldir (_WDIR*); |
||||
void __cdecl __MINGW_NOTHROW _wseekdir (_WDIR*, long); |
||||
|
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif /* Not RC_INVOKED */ |
||||
|
||||
#endif /* Not _DIRENT_H_ */ |
@ -0,0 +1,108 @@
@@ -0,0 +1,108 @@
|
||||
#include "../git-compat-util.h" |
||||
#include "dirent.h" |
||||
|
||||
struct DIR { |
||||
struct dirent dd_dir; /* includes d_type */ |
||||
HANDLE dd_handle; /* FindFirstFile handle */ |
||||
int dd_stat; /* 0-based index */ |
||||
char dd_name[1]; /* extend struct */ |
||||
}; |
||||
|
||||
DIR *opendir(const char *name) |
||||
{ |
||||
DWORD attrs = GetFileAttributesA(name); |
||||
int len; |
||||
DIR *p; |
||||
|
||||
/* check for valid path */ |
||||
if (attrs == INVALID_FILE_ATTRIBUTES) { |
||||
errno = ENOENT; |
||||
return NULL; |
||||
} |
||||
|
||||
/* check if it's a directory */ |
||||
if (!(attrs & FILE_ATTRIBUTE_DIRECTORY)) { |
||||
errno = ENOTDIR; |
||||
return NULL; |
||||
} |
||||
|
||||
/* check that the pattern won't be too long for FindFirstFileA */ |
||||
len = strlen(name); |
||||
if (is_dir_sep(name[len - 1])) |
||||
len--; |
||||
if (len + 2 >= MAX_PATH) { |
||||
errno = ENAMETOOLONG; |
||||
return NULL; |
||||
} |
||||
|
||||
p = malloc(sizeof(DIR) + len + 2); |
||||
if (!p) |
||||
return NULL; |
||||
|
||||
memset(p, 0, sizeof(DIR) + len + 2); |
||||
strcpy(p->dd_name, name); |
||||
p->dd_name[len] = '/'; |
||||
p->dd_name[len+1] = '*'; |
||||
|
||||
p->dd_handle = INVALID_HANDLE_VALUE; |
||||
return p; |
||||
} |
||||
|
||||
struct dirent *readdir(DIR *dir) |
||||
{ |
||||
WIN32_FIND_DATAA buf; |
||||
HANDLE handle; |
||||
|
||||
if (!dir || !dir->dd_handle) { |
||||
errno = EBADF; /* No set_errno for mingw */ |
||||
return NULL; |
||||
} |
||||
|
||||
if (dir->dd_handle == INVALID_HANDLE_VALUE && dir->dd_stat == 0) { |
||||
DWORD lasterr; |
||||
handle = FindFirstFileA(dir->dd_name, &buf); |
||||
lasterr = GetLastError(); |
||||
dir->dd_handle = handle; |
||||
if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) { |
||||
errno = err_win_to_posix(lasterr); |
||||
return NULL; |
||||
} |
||||
} else if (dir->dd_handle == INVALID_HANDLE_VALUE) { |
||||
return NULL; |
||||
} else if (!FindNextFileA(dir->dd_handle, &buf)) { |
||||
DWORD lasterr = GetLastError(); |
||||
FindClose(dir->dd_handle); |
||||
dir->dd_handle = INVALID_HANDLE_VALUE; |
||||
/* POSIX says you shouldn't set errno when readdir can't |
||||
find any more files; so, if another error we leave it set. */ |
||||
if (lasterr != ERROR_NO_MORE_FILES) |
||||
errno = err_win_to_posix(lasterr); |
||||
return NULL; |
||||
} |
||||
|
||||
/* We get here if `buf' contains valid data. */ |
||||
strcpy(dir->dd_dir.d_name, buf.cFileName); |
||||
++dir->dd_stat; |
||||
|
||||
/* Set file type, based on WIN32_FIND_DATA */ |
||||
dir->dd_dir.d_type = 0; |
||||
if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) |
||||
dir->dd_dir.d_type |= DT_DIR; |
||||
else |
||||
dir->dd_dir.d_type |= DT_REG; |
||||
|
||||
return &dir->dd_dir; |
||||
} |
||||
|
||||
int closedir(DIR *dir) |
||||
{ |
||||
if (!dir) { |
||||
errno = EBADF; |
||||
return -1; |
||||
} |
||||
|
||||
if (dir->dd_handle != INVALID_HANDLE_VALUE) |
||||
FindClose(dir->dd_handle); |
||||
free(dir); |
||||
return 0; |
||||
} |
@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
|
||||
#ifndef DIRENT_H |
||||
#define DIRENT_H |
||||
|
||||
typedef struct DIR DIR; |
||||
|
||||
#define DT_UNKNOWN 0 |
||||
#define DT_DIR 1 |
||||
#define DT_REG 2 |
||||
#define DT_LNK 3 |
||||
|
||||
struct dirent { |
||||
long d_ino; /* Always zero. */ |
||||
char d_name[FILENAME_MAX]; /* File name. */ |
||||
union { |
||||
unsigned short d_reclen; /* Always zero. */ |
||||
unsigned char d_type; /* Reimplementation adds this */ |
||||
}; |
||||
}; |
||||
|
||||
DIR *opendir(const char *dirname); |
||||
struct dirent *readdir(DIR *dir); |
||||
int closedir(DIR *dir); |
||||
|
||||
#endif /* DIRENT_H */ |
Loading…
Reference in new issue