|
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 | + |
21 | 68 | /*
|
22 | 69 | * Convert a FILETIME struct into a 64 bit time_t.
|
23 | 70 | */
|
@@ -116,81 +163,117 @@ _pgstat64(const char *name, struct stat *buf)
|
116 | 163 | {
|
117 | 164 | /*
|
118 | 165 | * We must use a handle so lstat() returns the information of the target
|
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. |
| 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. |
122 | 169 | */
|
123 | 170 | SECURITY_ATTRIBUTES sa;
|
124 | 171 | HANDLE hFile;
|
125 | 172 | int ret;
|
126 |
| - int loops = 0; |
| 173 | +#if _WIN32_WINNT < 0x0600 |
| 174 | + IO_STATUS_BLOCK ioStatus; |
| 175 | + FILE_STANDARD_INFORMATION standardInfo; |
| 176 | +#else |
| 177 | + FILE_STANDARD_INFO standardInfo; |
| 178 | +#endif |
127 | 179 |
|
128 | 180 | if (name == NULL || buf == NULL)
|
129 | 181 | {
|
130 | 182 | errno = EINVAL;
|
131 | 183 | return -1;
|
132 | 184 | }
|
| 185 | + |
133 | 186 | /* fast not-exists check */
|
134 | 187 | if (GetFileAttributes(name) == INVALID_FILE_ATTRIBUTES)
|
135 | 188 | {
|
136 |
| - DWORD err = GetLastError(); |
137 |
| - |
138 |
| - if (err != ERROR_ACCESS_DENIED) |
139 |
| - { |
140 |
| - _dosmaperr(err); |
141 |
| - return -1; |
142 |
| - } |
| 189 | + _dosmaperr(GetLastError()); |
| 190 | + return -1; |
143 | 191 | }
|
144 | 192 |
|
145 | 193 | /* get a file handle as lightweight as we can */
|
146 | 194 | sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
147 | 195 | sa.bInheritHandle = TRUE;
|
148 | 196 | 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) |
| 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) |
157 | 206 | {
|
158 | 207 | DWORD err = GetLastError();
|
159 | 208 |
|
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) |
| 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) |
177 | 233 | {
|
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 |
| - } |
| 234 | + DWORD err = GetLastError(); |
| 235 | + |
| 236 | + CloseHandle(hFile); |
| 237 | + _dosmaperr(err); |
| 238 | + return -1; |
189 | 239 | }
|
| 240 | + } |
190 | 241 |
|
| 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))) |
| 255 | + { |
| 256 | + DWORD err = GetLastError(); |
| 257 | + |
| 258 | + CloseHandle(hFile); |
191 | 259 | _dosmaperr(err);
|
192 | 260 | return -1;
|
193 | 261 | }
|
| 262 | +#endif /* _WIN32_WINNT < 0x0600 */ |
| 263 | + |
| 264 | + if (standardInfo.DeletePending) |
| 265 | + { |
| 266 | + /* |
| 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. |
| 272 | + */ |
| 273 | + CloseHandle(hFile); |
| 274 | + errno = ENOENT; |
| 275 | + return -1; |
| 276 | + } |
194 | 277 |
|
195 | 278 | /* At last we can invoke fileinfo_to_stat */
|
196 | 279 | ret = fileinfo_to_stat(hFile, buf);
|
|
0 commit comments