@matheusbsbs / matheus-bagliano / commits / d1b6e6e015
Fork 0

win32: use our own dirent.h

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>

Erik Faye-Lund committed Nov 23, 2010 at 19:38 UTC d1b6e6e015501272c7491b3a4adf3cd3904edefa
7 files changed +137 -271
Makefile
+5 -2
@@ -499,6 +499,7 @@ LIB_H += compat/mingw.h
499 LIB_H += compat/win32/pthread.h
500 LIB_H += compat/win32/syslog.h
501 LIB_H += compat/win32/sys/poll.h
502 +LIB_H += compat/win32/dirent.h
503 LIB_H += csum-file.h
504 LIB_H += decorate.h
505 LIB_H += delta.h
@@ -1084,7 +1085,9 @@ ifeq ($(uname_S),Windows)
1085 AR = compat/vcbuild/scripts/lib.pl
1086 CFLAGS =
1087 BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE
1087 - COMPAT_OBJS = compat/msvc.o compat/fnmatch/fnmatch.o compat/winansi.o compat/win32/pthread.o compat/win32/syslog.o compat/win32/sys/poll.o
1088 + COMPAT_OBJS = compat/msvc.o compat/fnmatch/fnmatch.o compat/winansi.o \
1089 + compat/win32/pthread.o compat/win32/syslog.o \
1090 + compat/win32/sys/poll.o compat/win32/dirent.o
1091 COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/fnmatch -Icompat/regex -Icompat/fnmatch -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
1092 BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib
1093 EXTLIBS = advapi32.lib shell32.lib wininet.lib ws2_32.lib
@@ -1137,7 +1140,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
1140 COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
1141 COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o \
1142 compat/win32/pthread.o compat/win32/syslog.o \
1140 - compat/win32/sys/poll.o
1143 + compat/win32/sys/poll.o compat/win32/dirent.o
1144 EXTLIBS += -lws2_32
1145 PTHREAD_LIBS =
1146 X = .exe
compat/mingw.c
-60
@@ -1564,63 +1564,3 @@ pid_t waitpid(pid_t pid, int *status, unsigned options)
1564 errno = EINVAL;
1565 return -1;
1566 }
1567 -
1568 -#ifndef NO_MINGW_REPLACE_READDIR
1569 -/* MinGW readdir implementation to avoid extra lstats for Git */
1570 -struct mingw_DIR
1571 -{
1572 - struct _finddata_t dd_dta; /* disk transfer area for this dir */
1573 - struct mingw_dirent dd_dir; /* Our own implementation, including d_type */
1574 - long dd_handle; /* _findnext handle */
1575 - int dd_stat; /* 0 = next entry to read is first entry, -1 = off the end, positive = 0 based index of next entry */
1576 - char dd_name[1]; /* given path for dir with search pattern (struct is extended) */
1577 -};
1578 -
1579 -struct dirent *mingw_readdir(DIR *dir)
1580 -{
1581 - WIN32_FIND_DATAA buf;
1582 - HANDLE handle;
1583 - struct mingw_DIR *mdir = (struct mingw_DIR*)dir;
1584 -
1585 - if (!dir || !dir->dd_handle) {
1586 - errno = EBADF; /* No set_errno for mingw */
1587 - return NULL;
1588 - }
1589 -
1590 - if (dir->dd_handle == (long)INVALID_HANDLE_VALUE && dir->dd_stat == 0)
1591 - {
1592 - DWORD lasterr;
1593 - handle = FindFirstFileA(dir->dd_name, &buf);
1594 - lasterr = GetLastError();
1595 - dir->dd_handle = (long)handle;
1596 - if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) {
1597 - errno = err_win_to_posix(lasterr);
1598 - return NULL;
1599 - }
1600 - } else if (dir->dd_handle == (long)INVALID_HANDLE_VALUE) {
1601 - return NULL;
1602 - } else if (!FindNextFileA((HANDLE)dir->dd_handle, &buf)) {
1603 - DWORD lasterr = GetLastError();
1604 - FindClose((HANDLE)dir->dd_handle);
1605 - dir->dd_handle = (long)INVALID_HANDLE_VALUE;
1606 - /* POSIX says you shouldn't set errno when readdir can't
1607 - find any more files; so, if another error we leave it set. */
1608 - if (lasterr != ERROR_NO_MORE_FILES)
1609 - errno = err_win_to_posix(lasterr);
1610 - return NULL;
1611 - }
1612 -
1613 - /* We get here if `buf' contains valid data. */
1614 - strcpy(dir->dd_dir.d_name, buf.cFileName);
1615 - ++dir->dd_stat;
1616 -
1617 - /* Set file type, based on WIN32_FIND_DATA */
1618 - mdir->dd_dir.d_type = 0;
1619 - if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1620 - mdir->dd_dir.d_type |= DT_DIR;
1621 - else
1622 - mdir->dd_dir.d_type |= DT_REG;
1623 -
1624 - return (struct dirent*)&dir->dd_dir;
1625 -}
1626 -#endif // !NO_MINGW_REPLACE_READDIR
compat/mingw.h
-29
@@ -319,35 +319,6 @@ int main(int argc, const char **argv) \
319 } \
320 static int mingw_main(c,v)
321
322 -#ifndef NO_MINGW_REPLACE_READDIR
323 -/*
324 - * A replacement of readdir, to ensure that it reads the file type at
325 - * the same time. This avoid extra unneeded lstats in git on MinGW
326 - */
327 -#undef DT_UNKNOWN
328 -#undef DT_DIR
329 -#undef DT_REG
330 -#undef DT_LNK
331 -#define DT_UNKNOWN 0
332 -#define DT_DIR 1
333 -#define DT_REG 2
334 -#define DT_LNK 3
335 -
336 -struct mingw_dirent
337 -{
338 - long d_ino; /* Always zero. */
339 - union {
340 - unsigned short d_reclen; /* Always zero. */
341 - unsigned char d_type; /* Reimplementation adds this */
342 - };
343 - unsigned short d_namlen; /* Length of name in d_name. */
344 - char d_name[FILENAME_MAX]; /* File name. */
345 -};
346 -#define dirent mingw_dirent
347 -#define readdir(x) mingw_readdir(x)
348 -struct dirent *mingw_readdir(DIR *dir);
349 -#endif // !NO_MINGW_REPLACE_READDIR
350 -
322 /*
323 * Used by Pthread API implementation for Windows
324 */
compat/msvc.c
-52
@@ -3,56 +3,4 @@
3 #include <conio.h>
4 #include "../strbuf.h"
5
6 -DIR *opendir(const char *name)
7 -{
8 - DWORD attrs = GetFileAttributes(name);
9 - int len;
10 - DIR *p;
11 -
12 - /* check for valid path */
13 - if (attrs == INVALID_FILE_ATTRIBUTES) {
14 - errno = ENOENT;
15 - return NULL;
16 - }
17 -
18 - /* check if it's a directory */
19 - if (!(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
20 - errno = ENOTDIR;
21 - return NULL;
22 - }
23 -
24 - /* check that the pattern won't be too long for FindFirstFileA */
25 - len = strlen(name);
26 - if (is_dir_sep(name[len - 1]))
27 - len--;
28 - if (len + 2 >= MAX_PATH) {
29 - errno = ENAMETOOLONG;
30 - return NULL;
31 - }
32 -
33 - p = malloc(sizeof(DIR) + len + 2);
34 - if (!p)
35 - return NULL;
36 -
37 - memset(p, 0, sizeof(DIR) + len + 2);
38 - strcpy(p->dd_name, name);
39 - p->dd_name[len] = '/';
40 - p->dd_name[len+1] = '*';
41 -
42 - p->dd_handle = (long)INVALID_HANDLE_VALUE;
43 - return p;
44 -}
45 -int closedir(DIR *dir)
46 -{
47 - if (!dir) {
48 - errno = EBADF;
49 - return -1;
50 - }
51 -
52 - if (dir->dd_handle != (long)INVALID_HANDLE_VALUE)
53 - FindClose((HANDLE)dir->dd_handle);
54 - free(dir);
55 - return 0;
56 -}
57 -
6 #include "mingw.c"
compat/vcbuild/include/dirent.h deleted
-128
@@ -1,128 +0,0 @@
1 -/*
2 - * DIRENT.H (formerly DIRLIB.H)
3 - * This file has no copyright assigned and is placed in the Public Domain.
4 - * This file is a part of the mingw-runtime package.
5 - *
6 - * The mingw-runtime package and its code is distributed in the hope that it
7 - * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR
8 - * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to
9 - * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 - *
11 - * You are free to use this package and its code without limitation.
12 - */
13 -#ifndef _DIRENT_H_
14 -#define _DIRENT_H_
15 -#include <io.h>
16 -
17 -#define PATH_MAX 512
18 -
19 -#define __MINGW_NOTHROW
20 -
21 -#ifndef RC_INVOKED
22 -
23 -#ifdef __cplusplus
24 -extern "C" {
25 -#endif
26 -
27 -struct dirent
28 -{
29 - long d_ino; /* Always zero. */
30 - unsigned short d_reclen; /* Always zero. */
31 - unsigned short d_namlen; /* Length of name in d_name. */
32 - char d_name[FILENAME_MAX]; /* File name. */
33 -};
34 -
35 -/*
36 - * This is an internal data structure. Good programmers will not use it
37 - * except as an argument to one of the functions below.
38 - * dd_stat field is now int (was short in older versions).
39 - */
40 -typedef struct
41 -{
42 - /* disk transfer area for this dir */
43 - struct _finddata_t dd_dta;
44 -
45 - /* dirent struct to return from dir (NOTE: this makes this thread
46 - * safe as long as only one thread uses a particular DIR struct at
47 - * a time) */
48 - struct dirent dd_dir;
49 -
50 - /* _findnext handle */
51 - long dd_handle;
52 -
53 - /*
54 - * Status of search:
55 - * 0 = not started yet (next entry to read is first entry)
56 - * -1 = off the end
57 - * positive = 0 based index of next entry
58 - */
59 - int dd_stat;
60 -
61 - /* given path for dir with search pattern (struct is extended) */
62 - char dd_name[PATH_MAX+3];
63 -} DIR;
64 -
65 -DIR* __cdecl __MINGW_NOTHROW opendir (const char*);
66 -struct dirent* __cdecl __MINGW_NOTHROW readdir (DIR*);
67 -int __cdecl __MINGW_NOTHROW closedir (DIR*);
68 -void __cdecl __MINGW_NOTHROW rewinddir (DIR*);
69 -long __cdecl __MINGW_NOTHROW telldir (DIR*);
70 -void __cdecl __MINGW_NOTHROW seekdir (DIR*, long);
71 -
72 -
73 -/* wide char versions */
74 -
75 -struct _wdirent
76 -{
77 - long d_ino; /* Always zero. */
78 - unsigned short d_reclen; /* Always zero. */
79 - unsigned short d_namlen; /* Length of name in d_name. */
80 - wchar_t d_name[FILENAME_MAX]; /* File name. */
81 -};
82 -
83 -/*
84 - * This is an internal data structure. Good programmers will not use it
85 - * except as an argument to one of the functions below.
86 - */
87 -typedef struct
88 -{
89 - /* disk transfer area for this dir */
90 - //struct _wfinddata_t dd_dta;
91 -
92 - /* dirent struct to return from dir (NOTE: this makes this thread
93 - * safe as long as only one thread uses a particular DIR struct at
94 - * a time) */
95 - struct _wdirent dd_dir;
96 -
97 - /* _findnext handle */
98 - long dd_handle;
99 -
100 - /*
101 - * Status of search:
102 - * 0 = not started yet (next entry to read is first entry)
103 - * -1 = off the end
104 - * positive = 0 based index of next entry
105 - */
106 - int dd_stat;
107 -
108 - /* given path for dir with search pattern (struct is extended) */
109 - wchar_t dd_name[1];
110 -} _WDIR;
111 -
112 -
113 -
114 -_WDIR* __cdecl __MINGW_NOTHROW _wopendir (const wchar_t*);
115 -struct _wdirent* __cdecl __MINGW_NOTHROW _wreaddir (_WDIR*);
116 -int __cdecl __MINGW_NOTHROW _wclosedir (_WDIR*);
117 -void __cdecl __MINGW_NOTHROW _wrewinddir (_WDIR*);
118 -long __cdecl __MINGW_NOTHROW _wtelldir (_WDIR*);
119 -void __cdecl __MINGW_NOTHROW _wseekdir (_WDIR*, long);
120 -
121 -
122 -#ifdef __cplusplus
123 -}
124 -#endif
125 -
126 -#endif /* Not RC_INVOKED */
127 -
128 -#endif /* Not _DIRENT_H_ */
compat/win32/dirent.c new
+108
@@ -0,0 +1,108 @@
1 +#include "../git-compat-util.h"
2 +#include "dirent.h"
3 +
4 +struct DIR {
5 + struct dirent dd_dir; /* includes d_type */
6 + HANDLE dd_handle; /* FindFirstFile handle */
7 + int dd_stat; /* 0-based index */
8 + char dd_name[1]; /* extend struct */
9 +};
10 +
11 +DIR *opendir(const char *name)
12 +{
13 + DWORD attrs = GetFileAttributesA(name);
14 + int len;
15 + DIR *p;
16 +
17 + /* check for valid path */
18 + if (attrs == INVALID_FILE_ATTRIBUTES) {
19 + errno = ENOENT;
20 + return NULL;
21 + }
22 +
23 + /* check if it's a directory */
24 + if (!(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
25 + errno = ENOTDIR;
26 + return NULL;
27 + }
28 +
29 + /* check that the pattern won't be too long for FindFirstFileA */
30 + len = strlen(name);
31 + if (is_dir_sep(name[len - 1]))
32 + len--;
33 + if (len + 2 >= MAX_PATH) {
34 + errno = ENAMETOOLONG;
35 + return NULL;
36 + }
37 +
38 + p = malloc(sizeof(DIR) + len + 2);
39 + if (!p)
40 + return NULL;
41 +
42 + memset(p, 0, sizeof(DIR) + len + 2);
43 + strcpy(p->dd_name, name);
44 + p->dd_name[len] = '/';
45 + p->dd_name[len+1] = '*';
46 +
47 + p->dd_handle = INVALID_HANDLE_VALUE;
48 + return p;
49 +}
50 +
51 +struct dirent *readdir(DIR *dir)
52 +{
53 + WIN32_FIND_DATAA buf;
54 + HANDLE handle;
55 +
56 + if (!dir || !dir->dd_handle) {
57 + errno = EBADF; /* No set_errno for mingw */
58 + return NULL;
59 + }
60 +
61 + if (dir->dd_handle == INVALID_HANDLE_VALUE && dir->dd_stat == 0) {
62 + DWORD lasterr;
63 + handle = FindFirstFileA(dir->dd_name, &buf);
64 + lasterr = GetLastError();
65 + dir->dd_handle = handle;
66 + if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) {
67 + errno = err_win_to_posix(lasterr);
68 + return NULL;
69 + }
70 + } else if (dir->dd_handle == INVALID_HANDLE_VALUE) {
71 + return NULL;
72 + } else if (!FindNextFileA(dir->dd_handle, &buf)) {
73 + DWORD lasterr = GetLastError();
74 + FindClose(dir->dd_handle);
75 + dir->dd_handle = INVALID_HANDLE_VALUE;
76 + /* POSIX says you shouldn't set errno when readdir can't
77 + find any more files; so, if another error we leave it set. */
78 + if (lasterr != ERROR_NO_MORE_FILES)
79 + errno = err_win_to_posix(lasterr);
80 + return NULL;
81 + }
82 +
83 + /* We get here if `buf' contains valid data. */
84 + strcpy(dir->dd_dir.d_name, buf.cFileName);
85 + ++dir->dd_stat;
86 +
87 + /* Set file type, based on WIN32_FIND_DATA */
88 + dir->dd_dir.d_type = 0;
89 + if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
90 + dir->dd_dir.d_type |= DT_DIR;
91 + else
92 + dir->dd_dir.d_type |= DT_REG;
93 +
94 + return &dir->dd_dir;
95 +}
96 +
97 +int closedir(DIR *dir)
98 +{
99 + if (!dir) {
100 + errno = EBADF;
101 + return -1;
102 + }
103 +
104 + if (dir->dd_handle != INVALID_HANDLE_VALUE)
105 + FindClose(dir->dd_handle);
106 + free(dir);
107 + return 0;
108 +}
compat/win32/dirent.h new
+24
@@ -0,0 +1,24 @@
1 +#ifndef DIRENT_H
2 +#define DIRENT_H
3 +
4 +typedef struct DIR DIR;
5 +
6 +#define DT_UNKNOWN 0
7 +#define DT_DIR 1
8 +#define DT_REG 2
9 +#define DT_LNK 3
10 +
11 +struct dirent {
12 + long d_ino; /* Always zero. */
13 + char d_name[FILENAME_MAX]; /* File name. */
14 + union {
15 + unsigned short d_reclen; /* Always zero. */
16 + unsigned char d_type; /* Reimplementation adds this */
17 + };
18 +};
19 +
20 +DIR *opendir(const char *dirname);
21 +struct dirent *readdir(DIR *dir);
22 +int closedir(DIR *dir);
23 +
24 +#endif /* DIRENT_H */