|
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 |
| - (IN HANDLE FileHandle, |
45 |
| - OUT PIO_STATUS_BLOCK IoStatusBlock, |
46 |
| - OUT PVOID FileInformation, |
47 |
| - IN ULONG Length, |
48 |
| - IN FILE_INFORMATION_CLASS FileInformationClass); |
49 |
| - |
50 |
| -static PFN_NTQUERYINFORMATIONFILE _NtQueryInformationFile = NULL; |
51 |
| - |
52 |
| -static HMODULE ntdll = NULL; |
53 |
| - |
54 |
| -/* |
55 |
| - * Load DLL file just once regardless of how many functions we load/call in it. |
56 |
| - */ |
57 |
| -static void |
58 |
| -LoadNtdll(void) |
59 |
| -{ |
60 |
| - if (ntdll != NULL) |
61 |
| - return; |
62 |
| - ntdll = LoadLibraryEx("ntdll.dll", NULL, 0); |
63 |
| -} |
64 |
| - |
65 |
| -#endif /* _WIN32_WINNT < 0x0600 */ |
66 |
| - |
67 |
| - |
68 | 21 | /*
|
69 | 22 | * Convert a FILETIME struct into a 64 bit time_t.
|
70 | 23 | */
|
@@ -163,115 +116,79 @@ _pgstat64(const char *name, struct stat *buf)
|
163 | 116 | {
|
164 | 117 | /*
|
165 | 118 | * We must use a handle so lstat() returns the information of the target
|
166 |
| - * file. To have a reliable test for ERROR_DELETE_PENDING, we use |
167 |
| - * NtQueryInformationFile from Windows 2000 or |
168 |
| - * 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. |
169 | 122 | */
|
170 | 123 | SECURITY_ATTRIBUTES sa;
|
171 | 124 | HANDLE hFile;
|
172 | 125 | int ret;
|
173 |
| -#if _WIN32_WINNT < 0x0600 |
174 |
| - IO_STATUS_BLOCK ioStatus; |
175 |
| - FILE_STANDARD_INFORMATION standardInfo; |
176 |
| -#else |
177 |
| - FILE_STANDARD_INFO standardInfo; |
178 |
| -#endif |
| 126 | + int loops = 0; |
179 | 127 |
|
180 | 128 | if (name == NULL || buf == NULL)
|
181 | 129 | {
|
182 | 130 | errno = EINVAL;
|
183 | 131 | return -1;
|
184 | 132 | }
|
185 |
| - |
186 | 133 | /* fast not-exists check */
|
187 | 134 | if (GetFileAttributes(name) == INVALID_FILE_ATTRIBUTES)
|
188 |
| - { |
189 |
| - _dosmaperr(GetLastError()); |
190 |
| - return -1; |
191 |
| - } |
192 |
| - |
193 |
| - /* get a file handle as lightweight as we can */ |
194 |
| - sa.nLength = sizeof(SECURITY_ATTRIBUTES); |
195 |
| - sa.bInheritHandle = TRUE; |
196 |
| - sa.lpSecurityDescriptor = NULL; |
197 |
| - hFile = CreateFile(name, |
198 |
| - GENERIC_READ, |
199 |
| - (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), |
200 |
| - &sa, |
201 |
| - OPEN_EXISTING, |
202 |
| - (FILE_FLAG_NO_BUFFERING | FILE_FLAG_BACKUP_SEMANTICS | |
203 |
| - FILE_FLAG_OVERLAPPED), |
204 |
| - NULL); |
205 |
| - if (hFile == INVALID_HANDLE_VALUE) |
206 | 135 | {
|
207 | 136 | DWORD err = GetLastError();
|
208 | 137 |
|
209 |
| - CloseHandle(hFile); |
210 |
| - _dosmaperr(err); |
211 |
| - return -1; |
212 |
| - } |
213 |
| - |
214 |
| - memset(&standardInfo, 0, sizeof(standardInfo)); |
215 |
| - |
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 |
| - DWORD err = GetLastError(); |
224 |
| - |
225 |
| - CloseHandle(hFile); |
226 |
| - _dosmaperr(err); |
227 |
| - return -1; |
228 |
| - } |
229 |
| - |
230 |
| - _NtQueryInformationFile = (PFN_NTQUERYINFORMATIONFILE) (pg_funcptr_t) |
231 |
| - GetProcAddress(ntdll, "NtQueryInformationFile"); |
232 |
| - if (_NtQueryInformationFile == NULL) |
| 138 | + if (err != ERROR_ACCESS_DENIED) |
233 | 139 | {
|
234 |
| - DWORD err = GetLastError(); |
235 |
| - |
236 |
| - CloseHandle(hFile); |
237 | 140 | _dosmaperr(err);
|
238 | 141 | return -1;
|
239 | 142 | }
|
240 | 143 | }
|
241 | 144 |
|
242 |
| - if (!NT_SUCCESS(_NtQueryInformationFile(hFile, &ioStatus, &standardInfo, |
243 |
| - sizeof(standardInfo), |
244 |
| - FileStandardInformation))) |
245 |
| - { |
246 |
| - DWORD err = GetLastError(); |
247 |
| - |
248 |
| - CloseHandle(hFile); |
249 |
| - _dosmaperr(err); |
250 |
| - return -1; |
251 |
| - } |
252 |
| -#else |
253 |
| - if (!GetFileInformationByHandleEx(hFile, FileStandardInfo, &standardInfo, |
254 |
| - sizeof(standardInfo))) |
| 145 | + /* get a file handle as lightweight as we can */ |
| 146 | + sa.nLength = sizeof(SECURITY_ATTRIBUTES); |
| 147 | + sa.bInheritHandle = TRUE; |
| 148 | + sa.lpSecurityDescriptor = NULL; |
| 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) |
255 | 157 | {
|
256 | 158 | DWORD err = GetLastError();
|
257 | 159 |
|
258 |
| - CloseHandle(hFile); |
259 |
| - _dosmaperr(err); |
260 |
| - return -1; |
261 |
| - } |
262 |
| -#endif /* _WIN32_WINNT < 0x0600 */ |
263 |
| - |
264 |
| - if (standardInfo.DeletePending) |
265 |
| - { |
266 | 160 | /*
|
267 |
| - * File has been deleted, but is not gone from the filesystem yet. |
268 |
| - * This can happen when some process with FILE_SHARE_DELETE has it |
269 |
| - * open, and it will be fully removed once that handle is closed. |
270 |
| - * Meanwhile, we can't open it, so indicate that the file just doesn't |
271 |
| - * exist. |
| 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. |
272 | 175 | */
|
273 |
| - CloseHandle(hFile); |
274 |
| - errno = ENOENT; |
| 176 | + if (err == ERROR_ACCESS_DENIED) |
| 177 | + { |
| 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 | + } |
| 189 | + } |
| 190 | + |
| 191 | + _dosmaperr(err); |
275 | 192 | return -1;
|
276 | 193 | }
|
277 | 194 |
|
|
0 commit comments