Skip to content

Commit d8890ce

Browse files
kbleesgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Erik Faye-Lund <[email protected]> Signed-off-by: Stepan Kasal <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a8248f4 commit d8890ce

File tree

1 file changed

+54
-59
lines changed

1 file changed

+54
-59
lines changed

compat/win32/dirent.c

Lines changed: 54 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -4,92 +4,88 @@ struct DIR {
44
struct dirent dd_dir; /* includes d_type */
55
HANDLE dd_handle; /* FindFirstFile handle */
66
int dd_stat; /* 0-based index */
7-
char dd_name[1]; /* extend struct */
87
};
98

9+
static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAA *fdata)
10+
{
11+
/* copy file name from WIN32_FIND_DATA to dirent */
12+
memcpy(ent->d_name, fdata->cFileName, sizeof(ent->d_name));
13+
14+
/* Set file type, based on WIN32_FIND_DATA */
15+
if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
16+
ent->d_type = DT_DIR;
17+
else
18+
ent->d_type = DT_REG;
19+
}
20+
1021
DIR *opendir(const char *name)
1122
{
12-
DWORD attrs = GetFileAttributesA(name);
23+
char pattern[MAX_PATH];
24+
WIN32_FIND_DATAA fdata;
25+
HANDLE h;
1326
int len;
14-
DIR *p;
27+
DIR *dir;
1528

16-
/* check for valid path */
17-
if (attrs == INVALID_FILE_ATTRIBUTES) {
18-
errno = ENOENT;
29+
/* check that name is not NULL */
30+
if (!name) {
31+
errno = EINVAL;
1932
return NULL;
2033
}
21-
22-
/* check if it's a directory */
23-
if (!(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
24-
errno = ENOTDIR;
25-
return NULL;
26-
}
27-
2834
/* check that the pattern won't be too long for FindFirstFileA */
2935
len = strlen(name);
30-
if (is_dir_sep(name[len - 1]))
31-
len--;
3236
if (len + 2 >= MAX_PATH) {
3337
errno = ENAMETOOLONG;
3438
return NULL;
3539
}
36-
37-
p = malloc(sizeof(DIR) + len + 2);
38-
if (!p)
40+
/* copy name to temp buffer */
41+
memcpy(pattern, name, len + 1);
42+
43+
/* append optional '/' and wildcard '*' */
44+
if (len && !is_dir_sep(pattern[len - 1]))
45+
pattern[len++] = '/';
46+
pattern[len++] = '*';
47+
pattern[len] = 0;
48+
49+
/* open find handle */
50+
h = FindFirstFileA(pattern, &fdata);
51+
if (h == INVALID_HANDLE_VALUE) {
52+
DWORD err = GetLastError();
53+
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
3954
return NULL;
55+
}
4056

41-
memset(p, 0, sizeof(DIR) + len + 2);
42-
strcpy(p->dd_name, name);
43-
p->dd_name[len] = '/';
44-
p->dd_name[len+1] = '*';
45-
46-
p->dd_handle = INVALID_HANDLE_VALUE;
47-
return p;
57+
/* initialize DIR structure and copy first dir entry */
58+
dir = xmalloc(sizeof(DIR));
59+
dir->dd_handle = h;
60+
dir->dd_stat = 0;
61+
finddata2dirent(&dir->dd_dir, &fdata);
62+
return dir;
4863
}
4964

5065
struct dirent *readdir(DIR *dir)
5166
{
52-
WIN32_FIND_DATAA buf;
53-
HANDLE handle;
54-
55-
if (!dir || !dir->dd_handle) {
67+
if (!dir) {
5668
errno = EBADF; /* No set_errno for mingw */
5769
return NULL;
5870
}
5971

60-
if (dir->dd_handle == INVALID_HANDLE_VALUE && dir->dd_stat == 0) {
61-
DWORD lasterr;
62-
handle = FindFirstFileA(dir->dd_name, &buf);
63-
lasterr = GetLastError();
64-
dir->dd_handle = handle;
65-
if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) {
66-
errno = err_win_to_posix(lasterr);
72+
/* if first entry, dirent has already been set up by opendir */
73+
if (dir->dd_stat) {
74+
/* get next entry and convert from WIN32_FIND_DATA to dirent */
75+
WIN32_FIND_DATAA fdata;
76+
if (FindNextFileA(dir->dd_handle, &fdata)) {
77+
finddata2dirent(&dir->dd_dir, &fdata);
78+
} else {
79+
DWORD lasterr = GetLastError();
80+
/* POSIX says you shouldn't set errno when readdir can't
81+
find any more files; so, if another error we leave it set. */
82+
if (lasterr != ERROR_NO_MORE_FILES)
83+
errno = err_win_to_posix(lasterr);
6784
return NULL;
6885
}
69-
} else if (dir->dd_handle == INVALID_HANDLE_VALUE) {
70-
return NULL;
71-
} else if (!FindNextFileA(dir->dd_handle, &buf)) {
72-
DWORD lasterr = GetLastError();
73-
FindClose(dir->dd_handle);
74-
dir->dd_handle = INVALID_HANDLE_VALUE;
75-
/* POSIX says you shouldn't set errno when readdir can't
76-
find any more files; so, if another error we leave it set. */
77-
if (lasterr != ERROR_NO_MORE_FILES)
78-
errno = err_win_to_posix(lasterr);
79-
return NULL;
8086
}
8187

82-
/* We get here if `buf' contains valid data. */
83-
strcpy(dir->dd_dir.d_name, buf.cFileName);
8488
++dir->dd_stat;
85-
86-
/* Set file type, based on WIN32_FIND_DATA */
87-
dir->dd_dir.d_type = 0;
88-
if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
89-
dir->dd_dir.d_type |= DT_DIR;
90-
else
91-
dir->dd_dir.d_type |= DT_REG;
92-
9389
return &dir->dd_dir;
9490
}
9591

@@ -100,8 +96,7 @@ int closedir(DIR *dir)
10096
return -1;
10197
}
10298

103-
if (dir->dd_handle != INVALID_HANDLE_VALUE)
104-
FindClose(dir->dd_handle);
99+
FindClose(dir->dd_handle);
105100
free(dir);
106101
return 0;
107102
}

0 commit comments

Comments
 (0)