VirtualBox

source: kBuild/trunk/src/lib/nt/ntstat.c@ 3007

Last change on this file since 3007 was 3007, checked in by bird, 9 years ago

lib/nt: Deal better with NTFS mount points.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 33.9 KB
Line 
1/* $Id: ntstat.c 3007 2016-11-06 16:46:43Z bird $ */
2/** @file
3 * MSC + NT stat, lstat and fstat.
4 */
5
6/*
7 * Copyright (c) 2005-2013 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 * IN THE SOFTWARE.
26 *
27 * Alternatively, the content of this file may be used under the terms of the
28 * GPL version 2 or later, or LGPL version 2.1 or later.
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#include <stdio.h>
36#include <errno.h>
37#include <malloc.h>
38
39#include "ntstuff.h"
40#include "nthlp.h"
41#include "ntstat.h"
42
43
44#undef stat
45
46static int birdIsExecutableExtension(const char *pszExt)
47{
48 switch (pszExt[0])
49 {
50 default:
51 return 0;
52
53 case 'e': /* exe */
54 return pszExt[1] == 'x' && pszExt[2] == 'e' && pszExt[3] == '\0';
55
56 case 'b': /* bat */
57 return pszExt[1] == 'a' && pszExt[2] == 't' && pszExt[3] == '\0';
58
59 case 'v': /* vbs */
60 return pszExt[1] == 'v' && pszExt[2] == 's' && pszExt[3] == '\0';
61
62 case 'c': /* com and cmd */
63 return (pszExt[1] == 'o' && pszExt[2] == 'm' && pszExt[3] == '\0')
64 || (pszExt[1] == 'm' && pszExt[2] == 'd' && pszExt[3] == '\0');
65 }
66}
67
68
69static int birdIsFileExecutable(const char *pszName)
70{
71 const char *pszExt = NULL;
72 char szExt[8];
73 size_t cchExt;
74 unsigned i;
75 char ch;
76
77 /* Look for a 3 char extension. */
78 ch = *pszName++;
79 if (!ch)
80 return 0;
81
82 while ((ch = *pszName++) != '\0')
83 if (ch == '.')
84 pszExt = pszName;
85
86 if (!pszExt)
87 return 0;
88 pszExt++;
89 cchExt = pszName - pszExt;
90 if (cchExt != 3)
91 return 0;
92
93 /* Copy the extension out and lower case it. Fail immediately on non-alpha chars. */
94 for (i = 0; i < cchExt; i++, pszExt++)
95 {
96 ch = *pszExt;
97 if (ch >= 'a' && ch <= 'z')
98 { /* likely */ }
99 else if (ch >= 'A' && ch <= 'Z')
100 ch += 'a' - 'A';
101 else
102 return 0;
103 szExt[i] = ch;
104 }
105 szExt[i] = '\0';
106
107 return birdIsExecutableExtension(szExt);
108}
109
110
111/**
112 * @a pwcName could be the full path.
113 */
114static int birdIsFileExecutableW(WCHAR const *pwcName, size_t cwcName)
115{
116 char szExt[8];
117 unsigned cchExt;
118 unsigned i;
119 WCHAR const *pwc;
120
121 /* Look for a 3 char extension. */
122 if (cwcName > 2 && pwcName[cwcName - 2] == '.')
123 return 0;
124 else if (cwcName > 3 && pwcName[cwcName - 3] == '.')
125 return 0;
126 else if (cwcName > 4 && pwcName[cwcName - 4] == '.')
127 cchExt = 3;
128 else
129 return 0;
130
131 /* Copy the extension out and lower case it. Fail immediately on non-alpha chars. */
132 pwc = &pwcName[cwcName - cchExt];
133 for (i = 0; i < cchExt; i++, pwc++)
134 {
135 WCHAR wc = *pwc;
136 if (wc >= 'a' && wc <= 'z')
137 { /* likely */ }
138 else if (wc >= 'A' && wc <= 'Z')
139 wc += 'a' - 'A';
140 else
141 return 0;
142 szExt[i] = (char)wc;
143 }
144 szExt[i] = '\0';
145
146 return birdIsExecutableExtension(szExt);
147}
148
149
150static unsigned short birdFileInfoToMode(ULONG fAttribs, ULONG uReparseTag,
151 const char *pszName, const wchar_t *pwszName, size_t cbNameW,
152 unsigned __int8 *pfIsDirSymlink, unsigned __int8 *pfIsMountPoint)
153{
154 unsigned short fMode;
155
156 /* File type. */
157 *pfIsDirSymlink = 0;
158 *pfIsMountPoint = 0;
159 if (!(fAttribs & FILE_ATTRIBUTE_REPARSE_POINT))
160 {
161 if (fAttribs & FILE_ATTRIBUTE_DIRECTORY)
162 fMode = S_IFDIR;
163 else
164 fMode = S_IFREG;
165 }
166 else
167 {
168 switch (uReparseTag)
169 {
170 case IO_REPARSE_TAG_SYMLINK:
171 *pfIsDirSymlink = !!(fAttribs & FILE_ATTRIBUTE_DIRECTORY);
172 fMode = S_IFLNK;
173 break;
174
175 case IO_REPARSE_TAG_MOUNT_POINT:
176 *pfIsMountPoint = 1;
177 default:
178 if (fAttribs & FILE_ATTRIBUTE_DIRECTORY)
179 fMode = S_IFDIR;
180 else
181 fMode = S_IFREG;
182 break;
183 }
184 }
185
186 /* Access mask. */
187 fMode |= S_IROTH | S_IRGRP | S_IRUSR;
188 if (!(fAttribs & FILE_ATTRIBUTE_READONLY))
189 fMode |= S_IWOTH | S_IWGRP | S_IWUSR;
190 if ( (fAttribs & FILE_ATTRIBUTE_DIRECTORY)
191 || (pwszName
192 ? birdIsFileExecutableW(pwszName, cbNameW / sizeof(wchar_t))
193 : birdIsFileExecutable(pszName)) )
194 fMode |= S_IXOTH | S_IXGRP | S_IXUSR;
195
196 return fMode;
197}
198
199
200/**
201 * Fills in a stat structure from an MY_FILE_ID_FULL_DIR_INFORMATION entry.
202 *
203 * @param pStat The stat structure.
204 * @param pBuf The MY_FILE_ID_FULL_DIR_INFORMATION entry.
205 * @remarks Caller sets st_dev.
206 */
207void birdStatFillFromFileIdFullDirInfo(BirdStat_T *pStat, MY_FILE_ID_FULL_DIR_INFORMATION const *pBuf)
208{
209 pStat->st_mode = birdFileInfoToMode(pBuf->FileAttributes, pBuf->EaSize, NULL /*pszPath*/, pBuf->FileName,
210 pBuf->FileNameLength, &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
211 pStat->st_padding0[0] = 0;
212 pStat->st_padding0[1] = 0;
213 pStat->st_size = pBuf->EndOfFile.QuadPart;
214 birdNtTimeToTimeSpec(pBuf->CreationTime.QuadPart, &pStat->st_birthtim);
215 birdNtTimeToTimeSpec(pBuf->ChangeTime.QuadPart, &pStat->st_ctim);
216 birdNtTimeToTimeSpec(pBuf->LastWriteTime.QuadPart, &pStat->st_mtim);
217 birdNtTimeToTimeSpec(pBuf->LastAccessTime.QuadPart, &pStat->st_atim);
218 pStat->st_ino = pBuf->FileId.QuadPart;
219 pStat->st_nlink = 1;
220 pStat->st_rdev = 0;
221 pStat->st_uid = 0;
222 pStat->st_gid = 0;
223 pStat->st_padding1 = 0;
224 pStat->st_attribs = pBuf->FileAttributes;
225 pStat->st_blksize = 65536;
226 pStat->st_blocks = (pBuf->AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
227 / BIRD_STAT_BLOCK_SIZE;
228}
229
230
231/**
232 * Fills in a stat structure from an MY_FILE_ID_BOTH_DIR_INFORMATION entry.
233 *
234 * @param pStat The stat structure.
235 * @param pBuf The MY_FILE_ID_BOTH_DIR_INFORMATION entry.
236 * @remarks Caller sets st_dev.
237 */
238void birdStatFillFromFileIdBothDirInfo(BirdStat_T *pStat, MY_FILE_ID_BOTH_DIR_INFORMATION const *pBuf)
239{
240 pStat->st_mode = birdFileInfoToMode(pBuf->FileAttributes, pBuf->EaSize, NULL /*pszPath*/, pBuf->FileName,
241 pBuf->FileNameLength, &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
242 pStat->st_padding0[0] = 0;
243 pStat->st_padding0[1] = 0;
244 pStat->st_size = pBuf->EndOfFile.QuadPart;
245 birdNtTimeToTimeSpec(pBuf->CreationTime.QuadPart, &pStat->st_birthtim);
246 birdNtTimeToTimeSpec(pBuf->ChangeTime.QuadPart, &pStat->st_ctim);
247 birdNtTimeToTimeSpec(pBuf->LastWriteTime.QuadPart, &pStat->st_mtim);
248 birdNtTimeToTimeSpec(pBuf->LastAccessTime.QuadPart, &pStat->st_atim);
249 pStat->st_ino = pBuf->FileId.QuadPart;
250 pStat->st_nlink = 1;
251 pStat->st_rdev = 0;
252 pStat->st_uid = 0;
253 pStat->st_gid = 0;
254 pStat->st_padding1 = 0;
255 pStat->st_attribs = pBuf->FileAttributes;
256 pStat->st_blksize = 65536;
257 pStat->st_blocks = (pBuf->AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
258 / BIRD_STAT_BLOCK_SIZE;
259}
260
261
262/**
263 * Fills in a stat structure from an MY_FILE_BOTH_DIR_INFORMATION entry.
264 *
265 * @param pStat The stat structure.
266 * @param pBuf The MY_FILE_BOTH_DIR_INFORMATION entry.
267 * @remarks Caller sets st_dev.
268 */
269void birdStatFillFromFileBothDirInfo(BirdStat_T *pStat, MY_FILE_BOTH_DIR_INFORMATION const *pBuf)
270{
271 pStat->st_mode = birdFileInfoToMode(pBuf->FileAttributes, pBuf->EaSize, NULL /*pszPath*/, pBuf->FileName,
272 pBuf->FileNameLength, &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
273 pStat->st_padding0[0] = 0;
274 pStat->st_padding0[1] = 0;
275 pStat->st_size = pBuf->EndOfFile.QuadPart;
276 birdNtTimeToTimeSpec(pBuf->CreationTime.QuadPart, &pStat->st_birthtim);
277 birdNtTimeToTimeSpec(pBuf->ChangeTime.QuadPart, &pStat->st_ctim);
278 birdNtTimeToTimeSpec(pBuf->LastWriteTime.QuadPart, &pStat->st_mtim);
279 birdNtTimeToTimeSpec(pBuf->LastAccessTime.QuadPart, &pStat->st_atim);
280 pStat->st_ino = 0;
281 pStat->st_nlink = 1;
282 pStat->st_rdev = 0;
283 pStat->st_uid = 0;
284 pStat->st_gid = 0;
285 pStat->st_padding1 = 0;
286 pStat->st_attribs = pBuf->FileAttributes;
287 pStat->st_blksize = 65536;
288 pStat->st_blocks = (pBuf->AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
289 / BIRD_STAT_BLOCK_SIZE;
290}
291
292
293int birdStatHandle2(HANDLE hFile, BirdStat_T *pStat, const char *pszPath, const wchar_t *pwszPath)
294{
295 int rc;
296 MY_NTSTATUS rcNt;
297#if 0
298 ULONG cbAll = sizeof(MY_FILE_ALL_INFORMATION) + 0x10000;
299 MY_FILE_ALL_INFORMATION *pAll = (MY_FILE_ALL_INFORMATION *)birdTmpAlloc(cbAll);
300 if (pAll)
301 {
302 MY_IO_STATUS_BLOCK Ios;
303 Ios.Information = 0;
304 Ios.u.Status = -1;
305 rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, pAll, cbAll, MyFileAllInformation);
306 if (MY_NT_SUCCESS(rcNt))
307 rcNt = Ios.u.Status;
308 if (MY_NT_SUCCESS(rcNt))
309 {
310 pStat->st_mode = birdFileInfoToMode(pAll->BasicInformation.FileAttributes, pszPath,
311 pAll->NameInformation.FileNamepAll->NameInformation.FileNameLength,
312 hFile, &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
313 pStat->st_padding0[0] = 0;
314 pStat->st_padding0[1] = 0;
315 pStat->st_size = pAll->StandardInformation.EndOfFile.QuadPart;
316 birdNtTimeToTimeSpec(pAll->BasicInformation.CreationTime.QuadPart, &pStat->st_birthtim);
317 birdNtTimeToTimeSpec(pAll->BasicInformation.ChangeTime.QuadPart, &pStat->st_ctim);
318 birdNtTimeToTimeSpec(pAll->BasicInformation.LastWriteTime.QuadPart, &pStat->st_mtim);
319 birdNtTimeToTimeSpec(pAll->BasicInformation.LastAccessTime.QuadPart, &pStat->st_atim);
320 pStat->st_ino = pAll->InternalInformation.IndexNumber.QuadPart;
321 pStat->st_nlink = pAll->StandardInformation.NumberOfLinks;
322 pStat->st_rdev = 0;
323 pStat->st_uid = 0;
324 pStat->st_gid = 0;
325 pStat->st_padding1 = 0;
326 pStat->st_attribs = pAll->StandardInformation.FileAttributes;
327 pStat->st_blksize = 65536;
328 pStat->st_blocks = (pAll->StandardInformation.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
329 / BIRD_STAT_BLOCK_SIZE;
330
331 /* Get the serial number, reusing the buffer from above. */
332 rcNt = g_pfnNtQueryVolumeInformationFile(hFile, &Ios, pAll, cbAll, MyFileFsVolumeInformation);
333 if (MY_NT_SUCCESS(rcNt))
334 rcNt = Ios.u.Status;
335 if (MY_NT_SUCCESS(rcNt))
336 {
337 MY_FILE_FS_VOLUME_INFORMATION const *pVolInfo = (MY_FILE_FS_VOLUME_INFORMATION const *)pAll;
338 pStat->st_dev = pVolInfo->VolumeSerialNumber
339 | (pVolInfo->VolumeCreationTime.QuadPart << 32);
340 rc = 0;
341 }
342 else
343 {
344 pStat->st_dev = 0;
345 rc = birdSetErrnoFromNt(rcNt);
346 }
347 }
348 else
349 rc = birdSetErrnoFromNt(rcNt);
350 }
351 else
352 rc = birdSetErrnoToNoMem();
353#else
354 ULONG cbNameInfo = 0;
355 MY_FILE_NAME_INFORMATION *pNameInfo = NULL;
356 MY_FILE_STANDARD_INFORMATION StdInfo;
357 MY_FILE_BASIC_INFORMATION BasicInfo;
358 MY_FILE_INTERNAL_INFORMATION InternalInfo;
359 MY_FILE_ATTRIBUTE_TAG_INFORMATION TagInfo;
360 MY_IO_STATUS_BLOCK Ios;
361
362 Ios.Information = 0;
363 Ios.u.Status = -1;
364 rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &StdInfo, sizeof(StdInfo), MyFileStandardInformation);
365 if (MY_NT_SUCCESS(rcNt))
366 rcNt = Ios.u.Status;
367
368 if (MY_NT_SUCCESS(rcNt))
369 rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &BasicInfo, sizeof(BasicInfo), MyFileBasicInformation);
370 if (MY_NT_SUCCESS(rcNt))
371 rcNt = Ios.u.Status;
372
373 if (MY_NT_SUCCESS(rcNt))
374 rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &InternalInfo, sizeof(InternalInfo), MyFileInternalInformation);
375 if (MY_NT_SUCCESS(rcNt))
376 rcNt = Ios.u.Status;
377
378 if (MY_NT_SUCCESS(rcNt))
379 {
380 if (!(BasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
381 TagInfo.ReparseTag = 0;
382 else
383 {
384 MY_NTSTATUS rcNt2 = g_pfnNtQueryInformationFile(hFile, &Ios, &TagInfo, sizeof(TagInfo), MyFileAttributeTagInformation);
385 if ( !MY_NT_SUCCESS(rcNt2)
386 || !MY_NT_SUCCESS(Ios.u.Status))
387 TagInfo.ReparseTag = 0;
388 }
389 }
390
391 if (MY_NT_SUCCESS(rcNt) && !pszPath && !pwszPath)
392 {
393 cbNameInfo = 0x10020;
394 pNameInfo = (MY_FILE_NAME_INFORMATION *)alloca(cbNameInfo);
395 rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, pNameInfo, cbNameInfo, MyFileNameInformation);
396 if (MY_NT_SUCCESS(rcNt))
397 rcNt = Ios.u.Status;
398 }
399
400 if (MY_NT_SUCCESS(rcNt))
401 {
402 pStat->st_mode = birdFileInfoToMode(BasicInfo.FileAttributes, TagInfo.ReparseTag, pszPath,
403 pNameInfo ? pNameInfo->FileName : pwszPath,
404 pNameInfo ? pNameInfo->FileNameLength
405 : pwszPath ? wcslen(pwszPath) * sizeof(wchar_t) : 0,
406 &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
407 pStat->st_padding0[0] = 0;
408 pStat->st_padding0[1] = 0;
409 pStat->st_size = StdInfo.EndOfFile.QuadPart;
410 birdNtTimeToTimeSpec(BasicInfo.CreationTime.QuadPart, &pStat->st_birthtim);
411 birdNtTimeToTimeSpec(BasicInfo.ChangeTime.QuadPart, &pStat->st_ctim);
412 birdNtTimeToTimeSpec(BasicInfo.LastWriteTime.QuadPart, &pStat->st_mtim);
413 birdNtTimeToTimeSpec(BasicInfo.LastAccessTime.QuadPart, &pStat->st_atim);
414 pStat->st_ino = InternalInfo.IndexNumber.QuadPart;
415 pStat->st_nlink = StdInfo.NumberOfLinks;
416 pStat->st_rdev = 0;
417 pStat->st_uid = 0;
418 pStat->st_gid = 0;
419 pStat->st_padding1 = 0;
420 pStat->st_attribs = BasicInfo.FileAttributes;
421 pStat->st_blksize = 65536;
422 pStat->st_blocks = (StdInfo.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
423 / BIRD_STAT_BLOCK_SIZE;
424
425 /* Get the serial number, reusing the buffer from above. */
426 if (!cbNameInfo)
427 {
428 cbNameInfo = sizeof(MY_FILE_FS_VOLUME_INFORMATION) + 1024;
429 pNameInfo = (MY_FILE_NAME_INFORMATION *)alloca(cbNameInfo);
430 }
431 rcNt = g_pfnNtQueryVolumeInformationFile(hFile, &Ios, pNameInfo, cbNameInfo, MyFileFsVolumeInformation);
432 if (MY_NT_SUCCESS(rcNt))
433 rcNt = Ios.u.Status;
434 if (MY_NT_SUCCESS(rcNt))
435 {
436 MY_FILE_FS_VOLUME_INFORMATION const *pVolInfo = (MY_FILE_FS_VOLUME_INFORMATION const *)pNameInfo;
437 pStat->st_dev = pVolInfo->VolumeSerialNumber
438 | (pVolInfo->VolumeCreationTime.QuadPart << 32);
439 rc = 0;
440 }
441 else
442 {
443 pStat->st_dev = 0;
444 rc = birdSetErrnoFromNt(rcNt);
445 }
446 }
447 else
448 rc = birdSetErrnoFromNt(rcNt);
449
450#endif
451 return rc;
452}
453
454
455int birdStatHandle(HANDLE hFile, BirdStat_T *pStat, const char *pszPath)
456{
457 return birdStatHandle2(hFile, pStat, pszPath, NULL);
458}
459
460
461/**
462 * Generates a device number from the volume information.
463 *
464 * @returns Device number.
465 * @param pVolInfo Volume information.
466 */
467unsigned __int64 birdVolumeInfoToDeviceNumber(const MY_FILE_FS_VOLUME_INFORMATION *pVolInfo)
468{
469 return pVolInfo->VolumeSerialNumber
470 | (pVolInfo->VolumeCreationTime.QuadPart << 32);
471}
472
473
474/**
475 * Quries the volume information and generates a device number from it.
476 *
477 * @returns NT status code.
478 * @param hFile The file/dir/whatever to query the volume info
479 * and device number for.
480 * @param pVolInfo User provided buffer for volume information.
481 * @param cbVolInfo The size of the buffer.
482 * @param puDevNo Where to return the device number. This is set
483 * to zero on failure.
484 */
485MY_NTSTATUS birdQueryVolumeDeviceNumber(HANDLE hFile, MY_FILE_FS_VOLUME_INFORMATION *pVolInfo, size_t cbVolInfo,
486 unsigned __int64 *puDevNo)
487{
488 MY_IO_STATUS_BLOCK Ios;
489 MY_NTSTATUS rcNt;
490
491 Ios.u.Status = -1;
492 Ios.Information = -1;
493
494 pVolInfo->VolumeSerialNumber = 0;
495 pVolInfo->VolumeCreationTime.QuadPart = 0;
496
497 rcNt = g_pfnNtQueryVolumeInformationFile(hFile, &Ios, pVolInfo, (LONG)cbVolInfo, MyFileFsVolumeInformation);
498 if (MY_NT_SUCCESS(rcNt))
499 {
500 *puDevNo = birdVolumeInfoToDeviceNumber(pVolInfo);
501 return Ios.u.Status;
502 }
503 *puDevNo = 0;
504 return rcNt;
505}
506
507
508static int birdStatInternal(HANDLE hRoot, const char *pszPath, BirdStat_T *pStat, int fFollow)
509{
510 int rc;
511 HANDLE hFile = birdOpenFileEx(hRoot, pszPath,
512 FILE_READ_ATTRIBUTES,
513 FILE_ATTRIBUTE_NORMAL,
514 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
515 FILE_OPEN,
516 FILE_OPEN_FOR_BACKUP_INTENT | (fFollow ? 0 : FILE_OPEN_REPARSE_POINT),
517 OBJ_CASE_INSENSITIVE);
518 if (hFile != INVALID_HANDLE_VALUE)
519 {
520 rc = birdStatHandle2(hFile, pStat, pszPath, NULL);
521 birdCloseFile(hFile);
522
523 if (rc || !pStat->st_ismountpoint)
524 { /* very likely */ }
525 else
526 {
527 /*
528 * If we hit a mount point (NTFS volume mounted under an empty NTFS directory),
529 * we should return information about what's mounted there rather than the
530 * directory it is mounted at as this is what UNIX does.
531 */
532 hFile = birdOpenFileEx(hRoot, pszPath,
533 FILE_READ_ATTRIBUTES,
534 FILE_ATTRIBUTE_NORMAL,
535 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
536 FILE_OPEN,
537 FILE_OPEN_FOR_BACKUP_INTENT,
538 OBJ_CASE_INSENSITIVE);
539 if (hFile != INVALID_HANDLE_VALUE)
540 {
541 rc = birdStatHandle2(hFile, pStat, pszPath, NULL);
542 pStat->st_ismountpoint = 2;
543 birdCloseFile(hFile);
544 }
545 }
546
547#if 0
548 {
549 static char s_szPrev[256];
550 size_t cchPath = strlen(pszPath);
551 if (memcmp(s_szPrev, pszPath, cchPath >= 255 ? 255 : cchPath + 1) == 0)
552 fprintf(stderr, "stat: %s -> rc/errno=%d/%u\n", pszPath, rc, errno);
553 else
554 memcpy(s_szPrev, pszPath, cchPath + 1);
555 }
556#endif
557 //fprintf(stderr, "stat: %s -> rc/errno=%d/%u\n", pszPath, rc, errno);
558 }
559 else
560 {
561 //fprintf(stderr, "stat: %s -> %u\n", pszPath, GetLastError());
562
563 /*
564 * On things like pagefile.sys we may get sharing violation. We fall
565 * back on directory enumeration for dealing with that.
566 */
567 if ( errno == ETXTBSY
568 && strchr(pszPath, '*') == NULL /* Serious paranoia... */
569 && strchr(pszPath, '?') == NULL)
570 {
571 MY_UNICODE_STRING NameUniStr;
572 hFile = birdOpenParentDir(hRoot, pszPath,
573 FILE_READ_DATA | SYNCHRONIZE,
574 FILE_ATTRIBUTE_NORMAL,
575 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
576 FILE_OPEN,
577 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
578 OBJ_CASE_INSENSITIVE,
579 &NameUniStr);
580 if (hFile != INVALID_HANDLE_VALUE)
581 {
582 MY_FILE_ID_FULL_DIR_INFORMATION *pBuf;
583 ULONG cbBuf = sizeof(*pBuf) + NameUniStr.MaximumLength + 1024;
584 MY_IO_STATUS_BLOCK Ios;
585 MY_NTSTATUS rcNt;
586
587 pBuf = (MY_FILE_ID_FULL_DIR_INFORMATION *)alloca(cbBuf);
588 Ios.u.Status = -1;
589 Ios.Information = -1;
590 rcNt = g_pfnNtQueryDirectoryFile(hFile, NULL, NULL, NULL, &Ios, pBuf, cbBuf,
591 MyFileIdFullDirectoryInformation, FALSE, &NameUniStr, TRUE);
592 if (MY_NT_SUCCESS(rcNt))
593 rcNt = Ios.u.Status;
594 if (MY_NT_SUCCESS(rcNt))
595 {
596 /*
597 * Convert the data.
598 */
599 birdStatFillFromFileIdFullDirInfo(pStat, pBuf);
600
601 /* Get the serial number, reusing the buffer from above. */
602 rcNt = birdQueryVolumeDeviceNumber(hFile, (MY_FILE_FS_VOLUME_INFORMATION *)pBuf, cbBuf, &pStat->st_dev);
603 if (MY_NT_SUCCESS(rcNt))
604 rc = 0;
605 else
606 rc = birdSetErrnoFromNt(rcNt);
607 }
608
609 birdFreeNtPath(&NameUniStr);
610 birdCloseFile(hFile);
611
612 if (MY_NT_SUCCESS(rcNt))
613 return 0;
614 birdSetErrnoFromNt(rcNt);
615 }
616 }
617 rc = -1;
618 }
619
620 return rc;
621}
622
623
624static int birdStatInternalW(HANDLE hRoot, const wchar_t *pwszPath, BirdStat_T *pStat, int fFollow)
625{
626 int rc;
627 HANDLE hFile = birdOpenFileExW(hRoot, pwszPath,
628 FILE_READ_ATTRIBUTES,
629 FILE_ATTRIBUTE_NORMAL,
630 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
631 FILE_OPEN,
632 FILE_OPEN_FOR_BACKUP_INTENT | (fFollow ? 0 : FILE_OPEN_REPARSE_POINT),
633 OBJ_CASE_INSENSITIVE);
634 if (hFile != INVALID_HANDLE_VALUE)
635 {
636 rc = birdStatHandle2(hFile, pStat, NULL, pwszPath);
637 birdCloseFile(hFile);
638
639 /*
640 * If we hit a mount point (NTFS volume mounted under an empty NTFS directory),
641 * we should return information about what's mounted there rather than the
642 * directory it is mounted at as this is what UNIX does.
643 */
644 hFile = birdOpenFileExW(hRoot, pwszPath,
645 FILE_READ_ATTRIBUTES,
646 FILE_ATTRIBUTE_NORMAL,
647 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
648 FILE_OPEN,
649 FILE_OPEN_FOR_BACKUP_INTENT,
650 OBJ_CASE_INSENSITIVE);
651 if (hFile != INVALID_HANDLE_VALUE)
652 {
653 rc = birdStatHandle2(hFile, pStat, NULL, pwszPath);
654 pStat->st_ismountpoint = 2;
655 birdCloseFile(hFile);
656 }
657 }
658 else
659 {
660 /*
661 * On things like pagefile.sys we may get sharing violation. We fall
662 * back on directory enumeration for dealing with that.
663 */
664 if ( errno == ETXTBSY
665 && wcschr(pwszPath, '*') == NULL /* Serious paranoia... */
666 && wcschr(pwszPath, '?') == NULL)
667 {
668 MY_UNICODE_STRING NameUniStr;
669 hFile = birdOpenParentDirW(hRoot, pwszPath,
670 FILE_READ_DATA | SYNCHRONIZE,
671 FILE_ATTRIBUTE_NORMAL,
672 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
673 FILE_OPEN,
674 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
675 OBJ_CASE_INSENSITIVE,
676 &NameUniStr);
677 if (hFile != INVALID_HANDLE_VALUE)
678 {
679 MY_FILE_ID_FULL_DIR_INFORMATION *pBuf;
680 ULONG cbBuf = sizeof(*pBuf) + NameUniStr.MaximumLength + 1024;
681 MY_IO_STATUS_BLOCK Ios;
682 MY_NTSTATUS rcNt;
683
684 pBuf = (MY_FILE_ID_FULL_DIR_INFORMATION *)alloca(cbBuf);
685 Ios.u.Status = -1;
686 Ios.Information = -1;
687 rcNt = g_pfnNtQueryDirectoryFile(hFile, NULL, NULL, NULL, &Ios, pBuf, cbBuf,
688 MyFileIdFullDirectoryInformation, FALSE, &NameUniStr, TRUE);
689 if (MY_NT_SUCCESS(rcNt))
690 rcNt = Ios.u.Status;
691 if (MY_NT_SUCCESS(rcNt))
692 {
693 /*
694 * Convert the data.
695 */
696 birdStatFillFromFileIdFullDirInfo(pStat, pBuf);
697
698 /* Get the serial number, reusing the buffer from above. */
699 rcNt = birdQueryVolumeDeviceNumber(hFile, (MY_FILE_FS_VOLUME_INFORMATION *)pBuf, cbBuf, &pStat->st_dev);
700 if (MY_NT_SUCCESS(rcNt))
701 rc = 0;
702 else
703 rc = birdSetErrnoFromNt(rcNt);
704 }
705
706 birdFreeNtPath(&NameUniStr);
707 birdCloseFile(hFile);
708
709 if (MY_NT_SUCCESS(rcNt))
710 return 0;
711 birdSetErrnoFromNt(rcNt);
712 }
713 }
714 rc = -1;
715 }
716
717 return rc;
718}
719
720
721/**
722 * Implements UNIX fstat().
723 */
724int birdStatOnFd(int fd, BirdStat_T *pStat)
725{
726 int rc;
727 HANDLE hFile = (HANDLE)_get_osfhandle(fd);
728 if (hFile != INVALID_HANDLE_VALUE)
729 {
730 DWORD fFileType;
731
732 birdResolveImports();
733
734 SetLastError(NO_ERROR);
735 fFileType = GetFileType(hFile) & ~FILE_TYPE_REMOTE;
736 switch (fFileType)
737 {
738 case FILE_TYPE_DISK:
739 rc = birdStatHandle2(hFile, pStat, NULL, NULL);
740 break;
741
742 case FILE_TYPE_CHAR:
743 case FILE_TYPE_PIPE:
744 if (fFileType == FILE_TYPE_PIPE)
745 pStat->st_mode = S_IFIFO | 0666;
746 else
747 pStat->st_mode = S_IFCHR | 0666;
748 pStat->st_padding0[0] = 0;
749 pStat->st_padding0[1] = 0;
750 pStat->st_size = 0;
751 pStat->st_atim.tv_sec = 0;
752 pStat->st_atim.tv_nsec = 0;
753 pStat->st_mtim.tv_sec = 0;
754 pStat->st_mtim.tv_nsec = 0;
755 pStat->st_ctim.tv_sec = 0;
756 pStat->st_ctim.tv_nsec = 0;
757 pStat->st_birthtim.tv_sec = 0;
758 pStat->st_birthtim.tv_nsec = 0;
759 pStat->st_ino = 0;
760 pStat->st_dev = 0;
761 pStat->st_rdev = 0;
762 pStat->st_uid = 0;
763 pStat->st_gid = 0;
764 pStat->st_padding1 = 0;
765 pStat->st_attribs = fFileType == FILE_TYPE_PIPE ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_DEVICE;
766 pStat->st_blksize = 512;
767 pStat->st_blocks = 0;
768 if (fFileType == FILE_TYPE_PIPE)
769 {
770 DWORD cbAvail;
771 if (PeekNamedPipe(hFile, NULL, 0, NULL, &cbAvail, NULL))
772 pStat->st_size = cbAvail;
773 }
774 rc = 0;
775 break;
776
777 case FILE_TYPE_UNKNOWN:
778 default:
779 if (GetLastError() == NO_ERROR)
780 rc = birdSetErrnoToBadFileNo();
781 else
782 rc = birdSetErrnoFromWin32(GetLastError());
783 break;
784 }
785 }
786 else
787 rc = -1;
788 return rc;
789}
790
791
792/**
793 * Special case that only gets the file size and nothing else.
794 */
795int birdStatOnFdJustSize(int fd, __int64 *pcbFile)
796{
797 int rc;
798 HANDLE hFile = (HANDLE)_get_osfhandle(fd);
799 if (hFile != INVALID_HANDLE_VALUE)
800 {
801 LARGE_INTEGER cbLocal;
802 if (GetFileSizeEx(hFile, &cbLocal))
803 {
804 *pcbFile = cbLocal.QuadPart;
805 rc = 0;
806 }
807 else
808 {
809 BirdStat_T Stat;
810 rc = birdStatOnFd(fd, &Stat);
811 if (rc == 0)
812 *pcbFile = Stat.st_size;
813 }
814 }
815 else
816 rc = -1;
817 return rc;
818}
819
820
821/**
822 * Implements UNIX stat().
823 */
824int birdStatFollowLink(const char *pszPath, BirdStat_T *pStat)
825{
826 return birdStatInternal(NULL, pszPath, pStat, 1 /*fFollow*/);
827}
828
829
830/**
831 * Implements UNIX stat().
832 */
833int birdStatFollowLinkW(const wchar_t *pwszPath, BirdStat_T *pStat)
834{
835 return birdStatInternalW(NULL, pwszPath, pStat, 1 /*fFollow*/);
836}
837
838
839/**
840 * Implements UNIX lstat().
841 */
842int birdStatOnLink(const char *pszPath, BirdStat_T *pStat)
843{
844 return birdStatInternal(NULL, pszPath, pStat, 0 /*fFollow*/);
845}
846
847
848/**
849 * Implements UNIX lstat().
850 */
851int birdStatOnLinkW(const wchar_t *pwszPath, BirdStat_T *pStat)
852{
853 return birdStatInternalW(NULL, pwszPath, pStat, 0 /*fFollow*/);
854}
855
856
857/**
858 * Implements an API like UNIX fstatat().
859 *
860 * @returns 0 on success, -1 and errno on failure.
861 * @param hRoot NT handle pwszPath is relative to.
862 * @param pszPath The path.
863 * @param pStat Where to return stats.
864 * @param fFollowLink Whether to follow links.
865 */
866int birdStatAt(HANDLE hRoot, const char *pszPath, BirdStat_T *pStat, int fFollowLink)
867{
868 return birdStatInternal(hRoot, pszPath, pStat, fFollowLink != 0);
869}
870
871
872/**
873 * Implements an API like UNIX fstatat().
874 *
875 * @returns 0 on success, -1 and errno on failure.
876 * @param hRoot NT handle pwszPath is relative to.
877 * @param pwszPath The path.
878 * @param pStat Where to return stats.
879 * @param fFollowLink Whether to follow links.
880 */
881int birdStatAtW(HANDLE hRoot, const wchar_t *pwszPath, BirdStat_T *pStat, int fFollowLink)
882{
883 return birdStatInternalW(hRoot, pwszPath, pStat, fFollowLink != 0);
884}
885
886
887/**
888 * Internal worker for birdStatModTimeOnly.
889 */
890static int birdStatOnlyInternal(const char *pszPath, int fFollowLink, MY_FILE_BASIC_INFORMATION *pBasicInfo)
891{
892 int rc;
893 HANDLE hFile = birdOpenFile(pszPath,
894 FILE_READ_ATTRIBUTES,
895 FILE_ATTRIBUTE_NORMAL,
896 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
897 FILE_OPEN,
898 FILE_OPEN_FOR_BACKUP_INTENT | (fFollowLink ? 0 : FILE_OPEN_REPARSE_POINT),
899 OBJ_CASE_INSENSITIVE);
900 if (hFile != INVALID_HANDLE_VALUE)
901 {
902 MY_NTSTATUS rcNt = 0;
903 MY_IO_STATUS_BLOCK Ios;
904 Ios.Information = 0;
905 Ios.u.Status = -1;
906
907 if (pBasicInfo)
908 {
909 rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, pBasicInfo, sizeof(*pBasicInfo), MyFileBasicInformation);
910 if (MY_NT_SUCCESS(rcNt))
911 rcNt = Ios.u.Status;
912 }
913 birdCloseFile(hFile);
914
915 if (MY_NT_SUCCESS(rcNt))
916 rc = 0;
917 else
918 {
919 birdSetErrnoFromNt(rcNt);
920 rc = -1;
921 }
922 }
923 else
924 {
925 //fprintf(stderr, "stat: %s -> %u\n", pszPath, GetLastError());
926
927 /* On things like pagefile.sys we may get sharing violation. */
928 if (GetLastError() == ERROR_SHARING_VIOLATION)
929 {
930 /** @todo Fall back on the parent directory enum if we run into a sharing
931 * violation. */
932 }
933 rc = -1;
934 }
935 return rc;
936}
937
938
939/**
940 * Special function for getting the modification time.
941 */
942int birdStatModTimeOnly(const char *pszPath, BirdTimeSpec_T *pTimeSpec, int fFollowLink)
943{
944 MY_FILE_BASIC_INFORMATION BasicInfo;
945 int rc = birdStatOnlyInternal(pszPath, fFollowLink, &BasicInfo);
946 if (!rc)
947 birdNtTimeToTimeSpec(BasicInfo.LastWriteTime.QuadPart, pTimeSpec);
948 return rc;
949}
950
951
952
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette