|
18 | 18 | #include "c.h"
|
19 | 19 | #include <windows.h>
|
20 | 20 |
|
21 |
| -/* |
22 |
| - * In order to support MinGW and MSVC2013 we use NtQueryInformationFile as an |
23 |
| - * alternative for GetFileInformationByHandleEx. It is loaded from the ntdll |
24 |
| - * library. |
25 |
| - */ |
26 |
| -#if _WIN32_WINNT < 0x0600 |
27 |
| -#include <winternl.h> |
28 |
| - |
29 |
| -#if !defined(__MINGW32__) && !defined(__MINGW64__) |
30 |
| -/* MinGW includes this in <winternl.h>, but it is missing in MSVC */ |
31 |
| -typedef struct _FILE_STANDARD_INFORMATION |
32 |
| -{ |
33 |
| - LARGE_INTEGER AllocationSize; |
34 |
| - LARGE_INTEGER EndOfFile; |
35 |
| - ULONG NumberOfLinks; |
36 |
| - BOOLEAN DeletePending; |
37 |
| - BOOLEAN Directory; |
38 |
| -} FILE_STANDARD_INFORMATION; |
39 |
| -#define FileStandardInformation 5 |
40 |
| -#endif /* !defined(__MINGW32__) && |
41 |
| - * !defined(__MINGW64__) */ |
42 |
| - |
43 |
| -typedef NTSTATUS(NTAPI * PFN_NTQUERYINFORMATIONFILE) |
44 |
| -( |
45 |
| - IN HANDLE FileHandle, |
46 |
| - OUT PIO_STATUS_BLOCK IoStatusBlock, |
47 |
| - OUT PVOID FileInformation, |
48 |
| - IN ULONG Length, |
49 |
| - IN FILE_INFORMATION_CLASS FileInformationClass |
50 |
| -); |
51 |
| - |
52 |
| -static PFN_NTQUERYINFORMATIONFILE _NtQueryInformationFile = NULL; |
53 |
| - |
54 |
| -static HMODULE ntdll = NULL; |
55 |
| - |
56 |
| -/* |
57 |
| - * Load DLL file just once regardless of how many functions we load/call in it. |
58 |
| - */ |
59 |
| -static void |
60 |
| -LoadNtdll(void) |
61 |
| -{ |
62 |
| - if (ntdll != NULL) |
63 |
| - return; |
64 |
| - ntdll = LoadLibraryEx("ntdll.dll", NULL, 0); |
65 |
| -} |
66 |
| - |
67 |
| -#endif /* _WIN32_WINNT < 0x0600 */ |
68 |
| - |
69 |
| - |
70 | 21 | /*
|
71 | 22 | * Convert a FILETIME struct into a 64 bit time_t.
|
72 | 23 | */
|
@@ -165,105 +116,79 @@ _pgstat64(const char *name, struct stat *buf)
|
165 | 116 | {
|
166 | 117 | /*
|
167 | 118 | * We must use a handle so lstat() returns the information of the target
|
168 |
| - * file. To have a reliable test for ERROR_DELETE_PENDING, we use |
169 |
| - * NtQueryInformationFile from Windows 2000 or |
170 |
| - * GetFileInformationByHandleEx from Server 2008 / Vista. |
| 119 | + * file. To have a reliable test for ERROR_DELETE_PENDING, this uses a |
| 120 | + * method similar to open() with a loop using stat() and some waits when |
| 121 | + * facing ERROR_ACCESS_DENIED. |
171 | 122 | */
|
172 | 123 | SECURITY_ATTRIBUTES sa;
|
173 | 124 | HANDLE hFile;
|
174 | 125 | int ret;
|
175 |
| -#if _WIN32_WINNT < 0x0600 |
176 |
| - IO_STATUS_BLOCK ioStatus; |
177 |
| - FILE_STANDARD_INFORMATION standardInfo; |
178 |
| -#else |
179 |
| - FILE_STANDARD_INFO standardInfo; |
180 |
| -#endif |
| 126 | + int loops = 0; |
181 | 127 |
|
182 | 128 | if (name == NULL || buf == NULL)
|
183 | 129 | {
|
184 | 130 | errno = EINVAL;
|
185 | 131 | return -1;
|
186 | 132 | }
|
187 |
| - |
188 | 133 | /* fast not-exists check */
|
189 | 134 | if (GetFileAttributes(name) == INVALID_FILE_ATTRIBUTES)
|
190 | 135 | {
|
191 |
| - _dosmaperr(GetLastError()); |
192 |
| - return -1; |
| 136 | + DWORD err = GetLastError(); |
| 137 | + |
| 138 | + if (err != ERROR_ACCESS_DENIED) |
| 139 | + { |
| 140 | + _dosmaperr(err); |
| 141 | + return -1; |
| 142 | + } |
193 | 143 | }
|
194 | 144 |
|
195 | 145 | /* get a file handle as lightweight as we can */
|
196 | 146 | sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
197 | 147 | sa.bInheritHandle = TRUE;
|
198 | 148 | sa.lpSecurityDescriptor = NULL;
|
199 |
| - hFile = CreateFile(name, |
200 |
| - GENERIC_READ, |
201 |
| - (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), |
202 |
| - &sa, |
203 |
| - OPEN_EXISTING, |
204 |
| - (FILE_FLAG_NO_BUFFERING | FILE_FLAG_BACKUP_SEMANTICS | |
205 |
| - FILE_FLAG_OVERLAPPED), |
206 |
| - NULL); |
207 |
| - if (hFile == INVALID_HANDLE_VALUE) |
| 149 | + while ((hFile = CreateFile(name, |
| 150 | + GENERIC_READ, |
| 151 | + (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), |
| 152 | + &sa, |
| 153 | + OPEN_EXISTING, |
| 154 | + (FILE_FLAG_NO_BUFFERING | FILE_FLAG_BACKUP_SEMANTICS | |
| 155 | + FILE_FLAG_OVERLAPPED), |
| 156 | + NULL)) == INVALID_HANDLE_VALUE) |
208 | 157 | {
|
209 |
| - CloseHandle(hFile); |
210 |
| - errno = ENOENT; |
211 |
| - return -1; |
212 |
| - } |
213 |
| - |
214 |
| - memset(&standardInfo, 0, sizeof(standardInfo)); |
| 158 | + DWORD err = GetLastError(); |
215 | 159 |
|
216 |
| -#if _WIN32_WINNT < 0x0600 |
217 |
| - if (_NtQueryInformationFile == NULL) |
218 |
| - { |
219 |
| - /* First time through: load ntdll.dll and find NtQueryInformationFile */ |
220 |
| - LoadNtdll(); |
221 |
| - if (ntdll == NULL) |
222 |
| - { |
223 |
| - _dosmaperr(GetLastError()); |
224 |
| - CloseHandle(hFile); |
225 |
| - return -1; |
226 |
| - } |
227 |
| - |
228 |
| - _NtQueryInformationFile = (PFN_NTQUERYINFORMATIONFILE) |
229 |
| - GetProcAddress(ntdll, "NtQueryInformationFile"); |
230 |
| - if (_NtQueryInformationFile == NULL) |
| 160 | + /* |
| 161 | + * ERROR_ACCESS_DENIED is returned if the file is deleted but not yet |
| 162 | + * gone (Windows NT status code is STATUS_DELETE_PENDING). In that |
| 163 | + * case we want to wait a bit and try again, giving up after 1 second |
| 164 | + * (since this condition should never persist very long). However, |
| 165 | + * there are other commonly-hit cases that return ERROR_ACCESS_DENIED, |
| 166 | + * so care is needed. In particular that happens if we try to open a |
| 167 | + * directory, or of course if there's an actual file-permissions |
| 168 | + * problem. To distinguish these cases, try a stat(). In the |
| 169 | + * delete-pending case, it will either also get STATUS_DELETE_PENDING, |
| 170 | + * or it will see the file as gone and fail with ENOENT. In other |
| 171 | + * cases it will usually succeed. The only somewhat-likely case where |
| 172 | + * this coding will uselessly wait is if there's a permissions problem |
| 173 | + * with a containing directory, which we hope will never happen in any |
| 174 | + * performance-critical code paths. |
| 175 | + */ |
| 176 | + if (err == ERROR_ACCESS_DENIED) |
231 | 177 | {
|
232 |
| - _dosmaperr(GetLastError()); |
233 |
| - CloseHandle(hFile); |
234 |
| - return -1; |
| 178 | + if (loops < 10) |
| 179 | + { |
| 180 | + struct microsoft_native_stat st; |
| 181 | + |
| 182 | + if (microsoft_native_stat(name, &st) != 0) |
| 183 | + { |
| 184 | + pg_usleep(100000); |
| 185 | + loops++; |
| 186 | + continue; |
| 187 | + } |
| 188 | + } |
235 | 189 | }
|
236 |
| - } |
237 | 190 |
|
238 |
| - if (!NT_SUCCESS(_NtQueryInformationFile(hFile, &ioStatus, &standardInfo, |
239 |
| - sizeof(standardInfo), |
240 |
| - FileStandardInformation))) |
241 |
| - { |
242 |
| - _dosmaperr(GetLastError()); |
243 |
| - CloseHandle(hFile); |
244 |
| - return -1; |
245 |
| - } |
246 |
| -#else |
247 |
| - if (!GetFileInformationByHandleEx(hFile, FileStandardInfo, &standardInfo, |
248 |
| - sizeof(standardInfo))) |
249 |
| - { |
250 |
| - _dosmaperr(GetLastError()); |
251 |
| - CloseHandle(hFile); |
252 |
| - return -1; |
253 |
| - } |
254 |
| -#endif /* _WIN32_WINNT < 0x0600 */ |
255 |
| - |
256 |
| - if (standardInfo.DeletePending) |
257 |
| - { |
258 |
| - /* |
259 |
| - * File has been deleted, but is not gone from the filesystem yet. |
260 |
| - * This can happen when some process with FILE_SHARE_DELETE has it |
261 |
| - * open, and it will be fully removed once that handle is closed. |
262 |
| - * Meanwhile, we can't open it, so indicate that the file just doesn't |
263 |
| - * exist. |
264 |
| - */ |
265 |
| - CloseHandle(hFile); |
266 |
| - errno = ENOENT; |
| 191 | + _dosmaperr(err); |
267 | 192 | return -1;
|
268 | 193 | }
|
269 | 194 |
|
|
0 commit comments