From 1d94c403fd52eddd2c4d6c0123f64c56a79bfca7 Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Fri, 7 Jan 2011 17:34:33 +0100 Subject: [PATCH 1/5] Win32 dirent: remove unused dirent.d_ino member There are no proper inodes on Windows, so remove dirent.d_ino and #define NO_D_INO_IN_DIRENT in the Makefile (this skips e.g. an ineffective qsort in fsck.c). Signed-off-by: Karsten Blees Signed-off-by: Erik Faye-Lund Signed-off-by: Stepan Kasal Signed-off-by: Junio C Hamano --- compat/win32/dirent.h | 1 - config.mak.uname | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/compat/win32/dirent.h b/compat/win32/dirent.h index 927a25ca76..b38973b051 100644 --- a/compat/win32/dirent.h +++ b/compat/win32/dirent.h @@ -9,7 +9,6 @@ typedef struct DIR DIR; #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. */ diff --git a/config.mak.uname b/config.mak.uname index 23a8803656..f14c160f3c 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -349,6 +349,7 @@ ifeq ($(uname_S),Windows) NO_POSIX_GOODIES = UnfortunatelyYes NATIVE_CRLF = YesPlease DEFAULT_HELP_FORMAT = html + NO_D_INO_IN_DIRENT = YesPlease CC = compat/vcbuild/scripts/clink.pl AR = compat/vcbuild/scripts/lib.pl @@ -497,6 +498,7 @@ ifneq (,$(findstring MINGW,$(uname_S))) NO_INET_NTOP = YesPlease NO_POSIX_GOODIES = UnfortunatelyYes DEFAULT_HELP_FORMAT = html + NO_D_INO_IN_DIRENT = YesPlease COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -D_USE_32BIT_TIME_T -DNOGDI -Icompat -Icompat/win32 COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\" COMPAT_OBJS += compat/mingw.o compat/winansi.o \ From b0601e6564061aa21699ee0a523e969c228cb1b4 Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Fri, 7 Jan 2011 17:38:25 +0100 Subject: [PATCH 2/5] Win32 dirent: remove unused dirent.d_reclen member Remove the union around dirent.d_type and the unused dirent.d_reclen member (which was necessary for compatibility with the MinGW dirent runtime, which is no longer used). Signed-off-by: Karsten Blees Signed-off-by: Erik Faye-Lund Signed-off-by: Stepan Kasal Signed-off-by: Junio C Hamano --- compat/win32/dirent.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compat/win32/dirent.h b/compat/win32/dirent.h index b38973b051..7f4e6c71d9 100644 --- a/compat/win32/dirent.h +++ b/compat/win32/dirent.h @@ -10,10 +10,7 @@ typedef struct DIR DIR; struct dirent { char d_name[FILENAME_MAX]; /* File name. */ - union { - unsigned short d_reclen; /* Always zero. */ - unsigned char d_type; /* Reimplementation adds this */ - }; + unsigned char d_type; /* file type to prevent lstat after readdir */ }; DIR *opendir(const char *dirname); From fa9abe95bec0e12322baae3511678ccfeb97eebc Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Fri, 7 Jan 2011 17:43:14 +0100 Subject: [PATCH 3/5] Win32 dirent: change FILENAME_MAX to MAX_PATH FILENAME_MAX and MAX_PATH are both 260 on Windows, however, MAX_PATH is used throughout the other Win32 code in Git, and also defines the length of file name buffers in the Win32 API (e.g. WIN32_FIND_DATA.cFileName, from which we're copying the dirent data). Signed-off-by: Karsten Blees Signed-off-by: Erik Faye-Lund Signed-off-by: Stepan Kasal Signed-off-by: Junio C Hamano --- compat/win32/dirent.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compat/win32/dirent.h b/compat/win32/dirent.h index 7f4e6c71d9..8838cd61fc 100644 --- a/compat/win32/dirent.h +++ b/compat/win32/dirent.h @@ -9,8 +9,8 @@ typedef struct DIR DIR; #define DT_LNK 3 struct dirent { - char d_name[FILENAME_MAX]; /* File name. */ unsigned char d_type; /* file type to prevent lstat after readdir */ + char d_name[MAX_PATH]; /* file name */ }; DIR *opendir(const char *dirname); From a8248f4a8dfc43177bbbcd9b874fc4e0cbf6b322 Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Fri, 7 Jan 2011 17:47:41 +0100 Subject: [PATCH 4/5] Win32 dirent: clarify #include directives Git-compat-util.h is two dirs up, and already includes (which is the same as "dirent.h" due to -Icompat/win32 in the Makefile). Signed-off-by: Karsten Blees Signed-off-by: Erik Faye-Lund Signed-off-by: Stepan Kasal Signed-off-by: Junio C Hamano --- compat/win32/dirent.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compat/win32/dirent.c b/compat/win32/dirent.c index 7a0debe51b..fac7f25047 100644 --- a/compat/win32/dirent.c +++ b/compat/win32/dirent.c @@ -1,5 +1,4 @@ -#include "../git-compat-util.h" -#include "dirent.h" +#include "../../git-compat-util.h" struct DIR { struct dirent dd_dir; /* includes d_type */ From d8890ce72609b3f7a002ed38b4ed832b6a97fc5c Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Fri, 7 Jan 2011 17:57:02 +0100 Subject: [PATCH 5/5] Win32 dirent: improve dirent implementation Improve the dirent implementation by removing the relics that were once necessary to plug into the now unused MinGW runtime, in preparation for Unicode file name support. Move FindFirstFile to opendir, and FindClose to closedir, with the following implications: - DIR.dd_name is no longer needed - chdir(one); opendir(relative); chdir(two); readdir() works as expected (i.e. lists one/relative instead of two/relative) - DIR.dd_handle is a valid handle for the entire lifetime of the DIR struct - thus, all checks for dd_handle == INVALID_HANDLE_VALUE and dd_handle == 0 have been removed - the special case that the directory has been fully read (which was previously explicitly tracked with dd_handle == INVALID_HANDLE_VALUE && dd_stat != 0) is now handled implicitly by the FindNextFile error handling code (if a client continues to call readdir after receiving NULL, FindNextFile will continue to fail with ERROR_NO_MORE_FILES, to the same effect) - extracting dirent data from WIN32_FIND_DATA is needed in two places, so moved to its own method - GetFileAttributes is no longer needed. The same information can be obtained from the FindFirstFile error code, which is ERROR_DIRECTORY if the name is NOT a directory (-> ENOTDIR), otherwise we can use err_win_to_posix (e.g. ERROR_PATH_NOT_FOUND -> ENOENT). The ERROR_DIRECTORY case could be fixed in err_win_to_posix, but this probably breaks other functionality. Removes the ERROR_NO_MORE_FILES check after FindFirstFile (this was fortunately a NOOP (searching for '*' always finds '.' and '..'), otherwise the subsequent code would have copied data from an uninitialized buffer). Changes malloc to git support function xmalloc, so opendir will die() if out of memory, rather than failing with ENOMEM and letting git work on incomplete directory listings (error handling in dir.c is quite sparse). Signed-off-by: Karsten Blees Signed-off-by: Erik Faye-Lund Signed-off-by: Stepan Kasal Signed-off-by: Junio C Hamano --- compat/win32/dirent.c | 111 ++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 58 deletions(-) diff --git a/compat/win32/dirent.c b/compat/win32/dirent.c index fac7f25047..82a515c21b 100644 --- a/compat/win32/dirent.c +++ b/compat/win32/dirent.c @@ -4,92 +4,88 @@ 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 */ }; +static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAA *fdata) +{ + /* copy file name from WIN32_FIND_DATA to dirent */ + memcpy(ent->d_name, fdata->cFileName, sizeof(ent->d_name)); + + /* Set file type, based on WIN32_FIND_DATA */ + if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + ent->d_type = DT_DIR; + else + ent->d_type = DT_REG; +} + DIR *opendir(const char *name) { - DWORD attrs = GetFileAttributesA(name); + char pattern[MAX_PATH]; + WIN32_FIND_DATAA fdata; + HANDLE h; int len; - DIR *p; + DIR *dir; - /* check for valid path */ - if (attrs == INVALID_FILE_ATTRIBUTES) { - errno = ENOENT; + /* check that name is not NULL */ + if (!name) { + errno = EINVAL; 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; } + /* copy name to temp buffer */ + memcpy(pattern, name, len + 1); - p = malloc(sizeof(DIR) + len + 2); - if (!p) + /* append optional '/' and wildcard '*' */ + if (len && !is_dir_sep(pattern[len - 1])) + pattern[len++] = '/'; + pattern[len++] = '*'; + pattern[len] = 0; + + /* open find handle */ + h = FindFirstFileA(pattern, &fdata); + if (h == INVALID_HANDLE_VALUE) { + DWORD err = GetLastError(); + errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err); 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; + /* initialize DIR structure and copy first dir entry */ + dir = xmalloc(sizeof(DIR)); + dir->dd_handle = h; + dir->dd_stat = 0; + finddata2dirent(&dir->dd_dir, &fdata); + return dir; } struct dirent *readdir(DIR *dir) { - WIN32_FIND_DATAA buf; - HANDLE handle; - - if (!dir || !dir->dd_handle) { + if (!dir) { 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); + /* if first entry, dirent has already been set up by opendir */ + if (dir->dd_stat) { + /* get next entry and convert from WIN32_FIND_DATA to dirent */ + WIN32_FIND_DATAA fdata; + if (FindNextFileA(dir->dd_handle, &fdata)) { + finddata2dirent(&dir->dd_dir, &fdata); + } else { + DWORD lasterr = GetLastError(); + /* 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; } - } 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; } @@ -100,8 +96,7 @@ int closedir(DIR *dir) return -1; } - if (dir->dd_handle != INVALID_HANDLE_VALUE) - FindClose(dir->dd_handle); + FindClose(dir->dd_handle); free(dir); return 0; }