VirtualBox

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

Last change on this file was 3485, checked in by bird, 4 years ago

lib/nt/ntstat.h|c: Forgot to commit birdStatModeOnly the other day.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 37.8 KB
Line 
1/* $Id: ntstat.c 3485 2020-09-21 12:25:08Z bird $ */
2/** @file
3 * MSC + NT stat, lstat and fstat.
4 */
5
6/*
7 * Copyright (c) 2005-2013 knut st. osmundsen <[email protected]>
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] == 'b' && 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 if (pszName)
72 {
73 const char *pszExt = NULL;
74 char szExt[8];
75 size_t cchExt;
76 unsigned i;
77 char ch;
78
79 /* Look for a 3 char extension. */
80 ch = *pszName++;
81 if (!ch)
82 return 0;
83
84 while ((ch = *pszName++) != '\0')
85 if (ch == '.')
86 pszExt = pszName;
87
88 if (!pszExt)
89 return 0;
90 pszExt++;
91 cchExt = pszName - pszExt;
92 if (cchExt != 3)
93 return 0;
94
95 /* Copy the extension out and lower case it. Fail immediately on non-alpha chars. */
96 for (i = 0; i < cchExt; i++, pszExt++)
97 {
98 ch = *pszExt;
99 if (ch >= 'a' && ch <= 'z')
100 { /* likely */ }
101 else if (ch >= 'A' && ch <= 'Z')
102 ch += 'a' - 'A';
103 else
104 return 0;
105 szExt[i] = ch;
106 }
107 szExt[i] = '\0';
108
109 return birdIsExecutableExtension(szExt);
110 }
111
112 return 0;
113}
114
115
116/**
117 * @a pwcName could be the full path.
118 */
119static int birdIsFileExecutableW(WCHAR const *pwcName, size_t cwcName)
120{
121 char szExt[8];
122 unsigned cchExt;
123 unsigned i;
124 WCHAR const *pwc;
125
126 /* Look for a 3 char extension. */
127 if (cwcName > 2 && pwcName[cwcName - 2] == '.')
128 return 0;
129 else if (cwcName > 3 && pwcName[cwcName - 3] == '.')
130 return 0;
131 else if (cwcName > 4 && pwcName[cwcName - 4] == '.')
132 cchExt = 3;
133 else
134 return 0;
135
136 /* Copy the extension out and lower case it. Fail immediately on non-alpha chars. */
137 pwc = &pwcName[cwcName - cchExt];
138 for (i = 0; i < cchExt; i++, pwc++)
139 {
140 WCHAR wc = *pwc;
141 if (wc >= 'a' && wc <= 'z')
142 { /* likely */ }
143 else if (wc >= 'A' && wc <= 'Z')
144 wc += 'a' - 'A';
145 else
146 return 0;
147 szExt[i] = (char)wc;
148 }
149 szExt[i] = '\0';
150
151 return birdIsExecutableExtension(szExt);
152}
153
154
155static unsigned short birdFileInfoToMode(ULONG fAttribs, ULONG uReparseTag,
156 const char *pszName, const wchar_t *pwszName, size_t cbNameW,
157 unsigned __int8 *pfIsDirSymlink, unsigned __int8 *pfIsMountPoint)
158{
159 unsigned short fMode;
160
161 /* File type. */
162 *pfIsDirSymlink = 0;
163 *pfIsMountPoint = 0;
164 if (!(fAttribs & FILE_ATTRIBUTE_REPARSE_POINT))
165 {
166 if (fAttribs & FILE_ATTRIBUTE_DIRECTORY)
167 fMode = S_IFDIR;
168 else
169 fMode = S_IFREG;
170 }
171 else
172 {
173 switch (uReparseTag)
174 {
175 case IO_REPARSE_TAG_SYMLINK:
176 *pfIsDirSymlink = !!(fAttribs & FILE_ATTRIBUTE_DIRECTORY);
177 fMode = S_IFLNK;
178 break;
179
180 case IO_REPARSE_TAG_MOUNT_POINT:
181 *pfIsMountPoint = 1;
182 default:
183 if (fAttribs & FILE_ATTRIBUTE_DIRECTORY)
184 fMode = S_IFDIR;
185 else
186 fMode = S_IFREG;
187 break;
188 }
189 }
190
191 /* Access mask. */
192 fMode |= S_IROTH | S_IRGRP | S_IRUSR;
193 if (!(fAttribs & FILE_ATTRIBUTE_READONLY))
194 fMode |= S_IWOTH | S_IWGRP | S_IWUSR;
195 if ( (fAttribs & FILE_ATTRIBUTE_DIRECTORY)
196 || (pwszName
197 ? birdIsFileExecutableW(pwszName, cbNameW / sizeof(wchar_t))
198 : birdIsFileExecutable(pszName)) )
199 fMode |= S_IXOTH | S_IXGRP | S_IXUSR;
200
201 return fMode;
202}
203
204
205/**
206 * Fills in a stat structure from an MY_FILE_ID_FULL_DIR_INFORMATION entry.
207 *
208 * @param pStat The stat structure.
209 * @param pBuf The MY_FILE_ID_FULL_DIR_INFORMATION entry.
210 * @remarks Caller sets st_dev.
211 */
212void birdStatFillFromFileIdFullDirInfo(BirdStat_T *pStat, MY_FILE_ID_FULL_DIR_INFORMATION const *pBuf)
213{
214 pStat->st_mode = birdFileInfoToMode(pBuf->FileAttributes, pBuf->EaSize, NULL /*pszPath*/, pBuf->FileName,
215 pBuf->FileNameLength, &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
216 pStat->st_padding0[0] = 0;
217 pStat->st_padding0[1] = 0;
218 pStat->st_size = pBuf->EndOfFile.QuadPart;
219 birdNtTimeToTimeSpec(pBuf->CreationTime.QuadPart, &pStat->st_birthtim);
220 birdNtTimeToTimeSpec(pBuf->ChangeTime.QuadPart, &pStat->st_ctim);
221 birdNtTimeToTimeSpec(pBuf->LastWriteTime.QuadPart, &pStat->st_mtim);
222 birdNtTimeToTimeSpec(pBuf->LastAccessTime.QuadPart, &pStat->st_atim);
223 pStat->st_ino = pBuf->FileId.QuadPart;
224 pStat->st_nlink = 1;
225 pStat->st_rdev = 0;
226 pStat->st_uid = 0;
227 pStat->st_gid = 0;
228 pStat->st_padding1 = 0;
229 pStat->st_attribs = pBuf->FileAttributes;
230 pStat->st_blksize = 65536;
231 pStat->st_blocks = (pBuf->AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
232 / BIRD_STAT_BLOCK_SIZE;
233}
234
235
236/**
237 * Fills in a stat structure from an MY_FILE_ID_BOTH_DIR_INFORMATION entry.
238 *
239 * @param pStat The stat structure.
240 * @param pBuf The MY_FILE_ID_BOTH_DIR_INFORMATION entry.
241 * @remarks Caller sets st_dev.
242 */
243void birdStatFillFromFileIdBothDirInfo(BirdStat_T *pStat, MY_FILE_ID_BOTH_DIR_INFORMATION const *pBuf)
244{
245 pStat->st_mode = birdFileInfoToMode(pBuf->FileAttributes, pBuf->EaSize, NULL /*pszPath*/, pBuf->FileName,
246 pBuf->FileNameLength, &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
247 pStat->st_padding0[0] = 0;
248 pStat->st_padding0[1] = 0;
249 pStat->st_size = pBuf->EndOfFile.QuadPart;
250 birdNtTimeToTimeSpec(pBuf->CreationTime.QuadPart, &pStat->st_birthtim);
251 birdNtTimeToTimeSpec(pBuf->ChangeTime.QuadPart, &pStat->st_ctim);
252 birdNtTimeToTimeSpec(pBuf->LastWriteTime.QuadPart, &pStat->st_mtim);
253 birdNtTimeToTimeSpec(pBuf->LastAccessTime.QuadPart, &pStat->st_atim);
254 pStat->st_ino = pBuf->FileId.QuadPart;
255 pStat->st_nlink = 1;
256 pStat->st_rdev = 0;
257 pStat->st_uid = 0;
258 pStat->st_gid = 0;
259 pStat->st_padding1 = 0;
260 pStat->st_attribs = pBuf->FileAttributes;
261 pStat->st_blksize = 65536;
262 pStat->st_blocks = (pBuf->AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
263 / BIRD_STAT_BLOCK_SIZE;
264}
265
266
267/**
268 * Fills in a stat structure from an MY_FILE_BOTH_DIR_INFORMATION entry.
269 *
270 * @param pStat The stat structure.
271 * @param pBuf The MY_FILE_BOTH_DIR_INFORMATION entry.
272 * @remarks Caller sets st_dev.
273 */
274void birdStatFillFromFileBothDirInfo(BirdStat_T *pStat, MY_FILE_BOTH_DIR_INFORMATION const *pBuf)
275{
276 pStat->st_mode = birdFileInfoToMode(pBuf->FileAttributes, pBuf->EaSize, NULL /*pszPath*/, pBuf->FileName,
277 pBuf->FileNameLength, &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
278 pStat->st_padding0[0] = 0;
279 pStat->st_padding0[1] = 0;
280 pStat->st_size = pBuf->EndOfFile.QuadPart;
281 birdNtTimeToTimeSpec(pBuf->CreationTime.QuadPart, &pStat->st_birthtim);
282 birdNtTimeToTimeSpec(pBuf->ChangeTime.QuadPart, &pStat->st_ctim);
283 birdNtTimeToTimeSpec(pBuf->LastWriteTime.QuadPart, &pStat->st_mtim);
284 birdNtTimeToTimeSpec(pBuf->LastAccessTime.QuadPart, &pStat->st_atim);
285 pStat->st_ino = 0;
286 pStat->st_nlink = 1;
287 pStat->st_rdev = 0;
288 pStat->st_uid = 0;
289 pStat->st_gid = 0;
290 pStat->st_padding1 = 0;
291 pStat->st_attribs = pBuf->FileAttributes;
292 pStat->st_blksize = 65536;
293 pStat->st_blocks = (pBuf->AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
294 / BIRD_STAT_BLOCK_SIZE;
295}
296
297
298int birdStatHandle2(HANDLE hFile, BirdStat_T *pStat, const char *pszPath, const wchar_t *pwszPath)
299{
300 int rc;
301 MY_NTSTATUS rcNt;
302#if 0
303 ULONG cbAll = sizeof(MY_FILE_ALL_INFORMATION) + 0x10000;
304 MY_FILE_ALL_INFORMATION *pAll = (MY_FILE_ALL_INFORMATION *)birdTmpAlloc(cbAll);
305 if (pAll)
306 {
307 MY_IO_STATUS_BLOCK Ios;
308 Ios.Information = 0;
309 Ios.u.Status = -1;
310 rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, pAll, cbAll, MyFileAllInformation);
311 if (MY_NT_SUCCESS(rcNt))
312 rcNt = Ios.u.Status;
313 if (MY_NT_SUCCESS(rcNt))
314 {
315 pStat->st_mode = birdFileInfoToMode(pAll->BasicInformation.FileAttributes, pszPath,
316 pAll->NameInformation.FileNamepAll->NameInformation.FileNameLength,
317 hFile, &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
318 pStat->st_padding0[0] = 0;
319 pStat->st_padding0[1] = 0;
320 pStat->st_size = pAll->StandardInformation.EndOfFile.QuadPart;
321 birdNtTimeToTimeSpec(pAll->BasicInformation.CreationTime.QuadPart, &pStat->st_birthtim);
322 birdNtTimeToTimeSpec(pAll->BasicInformation.ChangeTime.QuadPart, &pStat->st_ctim);
323 birdNtTimeToTimeSpec(pAll->BasicInformation.LastWriteTime.QuadPart, &pStat->st_mtim);
324 birdNtTimeToTimeSpec(pAll->BasicInformation.LastAccessTime.QuadPart, &pStat->st_atim);
325 pStat->st_ino = pAll->InternalInformation.IndexNumber.QuadPart;
326 pStat->st_nlink = pAll->StandardInformation.NumberOfLinks;
327 pStat->st_rdev = 0;
328 pStat->st_uid = 0;
329 pStat->st_gid = 0;
330 pStat->st_padding1 = 0;
331 pStat->st_attribs = pAll->StandardInformation.FileAttributes;
332 pStat->st_blksize = 65536;
333 pStat->st_blocks = (pAll->StandardInformation.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
334 / BIRD_STAT_BLOCK_SIZE;
335
336 /* Get the serial number, reusing the buffer from above. */
337 rcNt = g_pfnNtQueryVolumeInformationFile(hFile, &Ios, pAll, cbAll, MyFileFsVolumeInformation);
338 if (MY_NT_SUCCESS(rcNt))
339 rcNt = Ios.u.Status;
340 if (MY_NT_SUCCESS(rcNt))
341 {
342 MY_FILE_FS_VOLUME_INFORMATION const *pVolInfo = (MY_FILE_FS_VOLUME_INFORMATION const *)pAll;
343 pStat->st_dev = pVolInfo->VolumeSerialNumber
344 | (pVolInfo->VolumeCreationTime.QuadPart << 32);
345 rc = 0;
346 }
347 else
348 {
349 pStat->st_dev = 0;
350 rc = birdSetErrnoFromNt(rcNt);
351 }
352 }
353 else
354 rc = birdSetErrnoFromNt(rcNt);
355 }
356 else
357 rc = birdSetErrnoToNoMem();
358#else
359 ULONG cbNameInfo = 0;
360 MY_FILE_NAME_INFORMATION *pNameInfo = NULL;
361 MY_FILE_STANDARD_INFORMATION StdInfo;
362 MY_FILE_BASIC_INFORMATION BasicInfo;
363 MY_FILE_INTERNAL_INFORMATION InternalInfo;
364 MY_FILE_ATTRIBUTE_TAG_INFORMATION TagInfo;
365 MY_IO_STATUS_BLOCK Ios;
366
367 Ios.Information = 0;
368 Ios.u.Status = -1;
369 rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &StdInfo, sizeof(StdInfo), MyFileStandardInformation);
370 if (MY_NT_SUCCESS(rcNt))
371 rcNt = Ios.u.Status;
372
373 if (MY_NT_SUCCESS(rcNt))
374 rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &BasicInfo, sizeof(BasicInfo), MyFileBasicInformation);
375 if (MY_NT_SUCCESS(rcNt))
376 rcNt = Ios.u.Status;
377
378 if (MY_NT_SUCCESS(rcNt))
379 rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &InternalInfo, sizeof(InternalInfo), MyFileInternalInformation);
380 if (MY_NT_SUCCESS(rcNt))
381 rcNt = Ios.u.Status;
382
383 if (MY_NT_SUCCESS(rcNt))
384 {
385 if (!(BasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
386 TagInfo.ReparseTag = 0;
387 else
388 {
389 MY_NTSTATUS rcNt2 = g_pfnNtQueryInformationFile(hFile, &Ios, &TagInfo, sizeof(TagInfo), MyFileAttributeTagInformation);
390 if ( !MY_NT_SUCCESS(rcNt2)
391 || !MY_NT_SUCCESS(Ios.u.Status))
392 TagInfo.ReparseTag = 0;
393 }
394 }
395
396 if ( MY_NT_SUCCESS(rcNt)
397 && !pszPath
398 && !pwszPath
399 && !(BasicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
400 {
401 cbNameInfo = 0x10020;
402 pNameInfo = (MY_FILE_NAME_INFORMATION *)alloca(cbNameInfo);
403 rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, pNameInfo, cbNameInfo, MyFileNameInformation);
404 if (MY_NT_SUCCESS(rcNt))
405 rcNt = Ios.u.Status;
406 }
407
408 if (MY_NT_SUCCESS(rcNt))
409 {
410 pStat->st_mode = birdFileInfoToMode(BasicInfo.FileAttributes, TagInfo.ReparseTag, pszPath,
411 pNameInfo ? pNameInfo->FileName : pwszPath,
412 pNameInfo ? pNameInfo->FileNameLength
413 : pwszPath ? wcslen(pwszPath) * sizeof(wchar_t) : 0,
414 &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
415 pStat->st_padding0[0] = 0;
416 pStat->st_padding0[1] = 0;
417 pStat->st_size = StdInfo.EndOfFile.QuadPart;
418 birdNtTimeToTimeSpec(BasicInfo.CreationTime.QuadPart, &pStat->st_birthtim);
419 birdNtTimeToTimeSpec(BasicInfo.ChangeTime.QuadPart, &pStat->st_ctim);
420 birdNtTimeToTimeSpec(BasicInfo.LastWriteTime.QuadPart, &pStat->st_mtim);
421 birdNtTimeToTimeSpec(BasicInfo.LastAccessTime.QuadPart, &pStat->st_atim);
422 pStat->st_ino = InternalInfo.IndexNumber.QuadPart;
423 pStat->st_nlink = StdInfo.NumberOfLinks;
424 pStat->st_rdev = 0;
425 pStat->st_uid = 0;
426 pStat->st_gid = 0;
427 pStat->st_padding1 = 0;
428 pStat->st_attribs = BasicInfo.FileAttributes;
429 pStat->st_blksize = 65536;
430 pStat->st_blocks = (StdInfo.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
431 / BIRD_STAT_BLOCK_SIZE;
432
433 /* Get the serial number, reusing the buffer from above. */
434 if (!cbNameInfo)
435 {
436 cbNameInfo = sizeof(MY_FILE_FS_VOLUME_INFORMATION) + 1024;
437 pNameInfo = (MY_FILE_NAME_INFORMATION *)alloca(cbNameInfo);
438 }
439 rcNt = g_pfnNtQueryVolumeInformationFile(hFile, &Ios, pNameInfo, cbNameInfo, MyFileFsVolumeInformation);
440 if (MY_NT_SUCCESS(rcNt))
441 rcNt = Ios.u.Status;
442 if (MY_NT_SUCCESS(rcNt))
443 {
444 MY_FILE_FS_VOLUME_INFORMATION const *pVolInfo = (MY_FILE_FS_VOLUME_INFORMATION const *)pNameInfo;
445 pStat->st_dev = pVolInfo->VolumeSerialNumber
446 | (pVolInfo->VolumeCreationTime.QuadPart << 32);
447 rc = 0;
448 }
449 else
450 {
451 pStat->st_dev = 0;
452 rc = birdSetErrnoFromNt(rcNt);
453 }
454 }
455 else
456 rc = birdSetErrnoFromNt(rcNt);
457
458#endif
459 return rc;
460}
461
462
463int birdStatHandle(HANDLE hFile, BirdStat_T *pStat, const char *pszPath)
464{
465 return birdStatHandle2(hFile, pStat, pszPath, NULL);
466}
467
468
469/**
470 * Generates a device number from the volume information.
471 *
472 * @returns Device number.
473 * @param pVolInfo Volume information.
474 */
475unsigned __int64 birdVolumeInfoToDeviceNumber(const MY_FILE_FS_VOLUME_INFORMATION *pVolInfo)
476{
477 return pVolInfo->VolumeSerialNumber
478 | (pVolInfo->VolumeCreationTime.QuadPart << 32);
479}
480
481
482/**
483 * Quries the volume information and generates a device number from it.
484 *
485 * @returns NT status code.
486 * @param hFile The file/dir/whatever to query the volume info
487 * and device number for.
488 * @param pVolInfo User provided buffer for volume information.
489 * @param cbVolInfo The size of the buffer.
490 * @param puDevNo Where to return the device number. This is set
491 * to zero on failure.
492 */
493MY_NTSTATUS birdQueryVolumeDeviceNumber(HANDLE hFile, MY_FILE_FS_VOLUME_INFORMATION *pVolInfo, size_t cbVolInfo,
494 unsigned __int64 *puDevNo)
495{
496 MY_IO_STATUS_BLOCK Ios;
497 MY_NTSTATUS rcNt;
498
499 Ios.u.Status = -1;
500 Ios.Information = -1;
501
502 pVolInfo->VolumeSerialNumber = 0;
503 pVolInfo->VolumeCreationTime.QuadPart = 0;
504
505 rcNt = g_pfnNtQueryVolumeInformationFile(hFile, &Ios, pVolInfo, (LONG)cbVolInfo, MyFileFsVolumeInformation);
506 if (MY_NT_SUCCESS(rcNt))
507 {
508 *puDevNo = birdVolumeInfoToDeviceNumber(pVolInfo);
509 return Ios.u.Status;
510 }
511 *puDevNo = 0;
512 return rcNt;
513}
514
515
516static int birdStatInternal(HANDLE hRoot, const char *pszPath, BirdStat_T *pStat, int fFollow)
517{
518 int rc;
519 HANDLE hFile = birdOpenFileEx(hRoot, pszPath,
520 FILE_READ_ATTRIBUTES,
521 FILE_ATTRIBUTE_NORMAL,
522 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
523 FILE_OPEN,
524 FILE_OPEN_FOR_BACKUP_INTENT | (fFollow ? 0 : FILE_OPEN_REPARSE_POINT),
525 OBJ_CASE_INSENSITIVE);
526 if (hFile != INVALID_HANDLE_VALUE)
527 {
528 rc = birdStatHandle2(hFile, pStat, pszPath, NULL);
529 birdCloseFile(hFile);
530
531 if (rc || !pStat->st_ismountpoint)
532 { /* very likely */ }
533 else
534 {
535 /*
536 * If we hit a mount point (NTFS volume mounted under an empty NTFS directory),
537 * we should return information about what's mounted there rather than the
538 * directory it is mounted at as this is what UNIX does.
539 */
540 hFile = birdOpenFileEx(hRoot, pszPath,
541 FILE_READ_ATTRIBUTES,
542 FILE_ATTRIBUTE_NORMAL,
543 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
544 FILE_OPEN,
545 FILE_OPEN_FOR_BACKUP_INTENT,
546 OBJ_CASE_INSENSITIVE);
547 if (hFile != INVALID_HANDLE_VALUE)
548 {
549 rc = birdStatHandle2(hFile, pStat, pszPath, NULL);
550 pStat->st_ismountpoint = 2;
551 birdCloseFile(hFile);
552 }
553 }
554
555#if 0
556 {
557 static char s_szPrev[256];
558 size_t cchPath = strlen(pszPath);
559 if (memcmp(s_szPrev, pszPath, cchPath >= 255 ? 255 : cchPath + 1) == 0)
560 fprintf(stderr, "stat: %s -> rc/errno=%d/%u\n", pszPath, rc, errno);
561 else
562 memcpy(s_szPrev, pszPath, cchPath + 1);
563 }
564#endif
565 //fprintf(stderr, "stat: %s -> rc/errno=%d/%u\n", pszPath, rc, errno);
566 }
567 else
568 {
569 //fprintf(stderr, "stat: %s -> %u\n", pszPath, GetLastError());
570
571 /*
572 * On things like pagefile.sys we may get sharing violation. We fall
573 * back on directory enumeration for dealing with that.
574 */
575 if ( errno == ETXTBSY
576 && strchr(pszPath, '*') == NULL /* Serious paranoia... */
577 && strchr(pszPath, '?') == NULL)
578 {
579 MY_UNICODE_STRING NameUniStr;
580 hFile = birdOpenParentDir(hRoot, pszPath,
581 FILE_READ_DATA | SYNCHRONIZE,
582 FILE_ATTRIBUTE_NORMAL,
583 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
584 FILE_OPEN,
585 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
586 OBJ_CASE_INSENSITIVE,
587 &NameUniStr);
588 if (hFile != INVALID_HANDLE_VALUE)
589 {
590 MY_FILE_ID_FULL_DIR_INFORMATION *pBuf;
591 ULONG cbBuf = sizeof(*pBuf) + NameUniStr.MaximumLength + 1024;
592 MY_IO_STATUS_BLOCK Ios;
593 MY_NTSTATUS rcNt;
594
595 pBuf = (MY_FILE_ID_FULL_DIR_INFORMATION *)alloca(cbBuf);
596 Ios.u.Status = -1;
597 Ios.Information = -1;
598 rcNt = g_pfnNtQueryDirectoryFile(hFile, NULL, NULL, NULL, &Ios, pBuf, cbBuf,
599 MyFileIdFullDirectoryInformation, FALSE, &NameUniStr, TRUE);
600 if (MY_NT_SUCCESS(rcNt))
601 rcNt = Ios.u.Status;
602 if (MY_NT_SUCCESS(rcNt))
603 {
604 /*
605 * Convert the data.
606 */
607 birdStatFillFromFileIdFullDirInfo(pStat, pBuf);
608
609 /* Get the serial number, reusing the buffer from above. */
610 rcNt = birdQueryVolumeDeviceNumber(hFile, (MY_FILE_FS_VOLUME_INFORMATION *)pBuf, cbBuf, &pStat->st_dev);
611 if (MY_NT_SUCCESS(rcNt))
612 rc = 0;
613 else
614 rc = birdSetErrnoFromNt(rcNt);
615 }
616
617 birdFreeNtPath(&NameUniStr);
618 birdCloseFile(hFile);
619
620 if (MY_NT_SUCCESS(rcNt))
621 return 0;
622 birdSetErrnoFromNt(rcNt);
623 }
624 }
625 rc = -1;
626 }
627
628 return rc;
629}
630
631
632static int birdStatInternalW(HANDLE hRoot, const wchar_t *pwszPath, BirdStat_T *pStat, int fFollow)
633{
634 int rc;
635 HANDLE hFile = birdOpenFileExW(hRoot, pwszPath,
636 FILE_READ_ATTRIBUTES,
637 FILE_ATTRIBUTE_NORMAL,
638 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
639 FILE_OPEN,
640 FILE_OPEN_FOR_BACKUP_INTENT | (fFollow ? 0 : FILE_OPEN_REPARSE_POINT),
641 OBJ_CASE_INSENSITIVE);
642 if (hFile != INVALID_HANDLE_VALUE)
643 {
644 rc = birdStatHandle2(hFile, pStat, NULL, pwszPath);
645 birdCloseFile(hFile);
646
647 if (rc || !pStat->st_ismountpoint)
648 { /* very likely */ }
649 else
650 {
651 /*
652 * If we hit a mount point (NTFS volume mounted under an empty NTFS directory),
653 * we should return information about what's mounted there rather than the
654 * directory it is mounted at as this is what UNIX does.
655 */
656 hFile = birdOpenFileExW(hRoot, pwszPath,
657 FILE_READ_ATTRIBUTES,
658 FILE_ATTRIBUTE_NORMAL,
659 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
660 FILE_OPEN,
661 FILE_OPEN_FOR_BACKUP_INTENT,
662 OBJ_CASE_INSENSITIVE);
663 if (hFile != INVALID_HANDLE_VALUE)
664 {
665 rc = birdStatHandle2(hFile, pStat, NULL, pwszPath);
666 pStat->st_ismountpoint = 2;
667 birdCloseFile(hFile);
668 }
669 }
670 }
671 else
672 {
673 /*
674 * On things like pagefile.sys we may get sharing violation. We fall
675 * back on directory enumeration for dealing with that.
676 */
677 if ( errno == ETXTBSY
678 && wcschr(pwszPath, '*') == NULL /* Serious paranoia... */
679 && wcschr(pwszPath, '?') == NULL)
680 {
681 MY_UNICODE_STRING NameUniStr;
682 hFile = birdOpenParentDirW(hRoot, pwszPath,
683 FILE_READ_DATA | SYNCHRONIZE,
684 FILE_ATTRIBUTE_NORMAL,
685 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
686 FILE_OPEN,
687 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
688 OBJ_CASE_INSENSITIVE,
689 &NameUniStr);
690 if (hFile != INVALID_HANDLE_VALUE)
691 {
692 MY_FILE_ID_FULL_DIR_INFORMATION *pBuf;
693 ULONG cbBuf = sizeof(*pBuf) + NameUniStr.MaximumLength + 1024;
694 MY_IO_STATUS_BLOCK Ios;
695 MY_NTSTATUS rcNt;
696
697 pBuf = (MY_FILE_ID_FULL_DIR_INFORMATION *)alloca(cbBuf);
698 Ios.u.Status = -1;
699 Ios.Information = -1;
700 rcNt = g_pfnNtQueryDirectoryFile(hFile, NULL, NULL, NULL, &Ios, pBuf, cbBuf,
701 MyFileIdFullDirectoryInformation, FALSE, &NameUniStr, TRUE);
702 if (MY_NT_SUCCESS(rcNt))
703 rcNt = Ios.u.Status;
704 if (MY_NT_SUCCESS(rcNt))
705 {
706 /*
707 * Convert the data.
708 */
709 birdStatFillFromFileIdFullDirInfo(pStat, pBuf);
710
711 /* Get the serial number, reusing the buffer from above. */
712 rcNt = birdQueryVolumeDeviceNumber(hFile, (MY_FILE_FS_VOLUME_INFORMATION *)pBuf, cbBuf, &pStat->st_dev);
713 if (MY_NT_SUCCESS(rcNt))
714 rc = 0;
715 else
716 rc = birdSetErrnoFromNt(rcNt);
717 }
718
719 birdFreeNtPath(&NameUniStr);
720 birdCloseFile(hFile);
721
722 if (MY_NT_SUCCESS(rcNt))
723 return 0;
724 birdSetErrnoFromNt(rcNt);
725 }
726 }
727 rc = -1;
728 }
729
730 return rc;
731}
732
733
734/**
735 * Implements UNIX fstat().
736 */
737int birdStatOnFd(int fd, BirdStat_T *pStat)
738{
739 int rc;
740 HANDLE hFile = (HANDLE)_get_osfhandle(fd);
741 if (hFile != INVALID_HANDLE_VALUE)
742 {
743 DWORD fFileType;
744
745 birdResolveImports();
746
747 SetLastError(NO_ERROR);
748 fFileType = GetFileType(hFile) & ~FILE_TYPE_REMOTE;
749 switch (fFileType)
750 {
751 case FILE_TYPE_DISK:
752 rc = birdStatHandle2(hFile, pStat, NULL, NULL);
753 break;
754
755 case FILE_TYPE_CHAR:
756 case FILE_TYPE_PIPE:
757 if (fFileType == FILE_TYPE_PIPE)
758 pStat->st_mode = S_IFIFO | 0666;
759 else
760 pStat->st_mode = S_IFCHR | 0666;
761 pStat->st_padding0[0] = 0;
762 pStat->st_padding0[1] = 0;
763 pStat->st_size = 0;
764 pStat->st_atim.tv_sec = 0;
765 pStat->st_atim.tv_nsec = 0;
766 pStat->st_mtim.tv_sec = 0;
767 pStat->st_mtim.tv_nsec = 0;
768 pStat->st_ctim.tv_sec = 0;
769 pStat->st_ctim.tv_nsec = 0;
770 pStat->st_birthtim.tv_sec = 0;
771 pStat->st_birthtim.tv_nsec = 0;
772 pStat->st_ino = 0;
773 pStat->st_dev = 0;
774 pStat->st_rdev = 0;
775 pStat->st_uid = 0;
776 pStat->st_gid = 0;
777 pStat->st_padding1 = 0;
778 pStat->st_attribs = fFileType == FILE_TYPE_PIPE ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_DEVICE;
779 pStat->st_blksize = 512;
780 pStat->st_blocks = 0;
781 if (fFileType == FILE_TYPE_PIPE)
782 {
783 DWORD cbAvail;
784 if (PeekNamedPipe(hFile, NULL, 0, NULL, &cbAvail, NULL))
785 pStat->st_size = cbAvail;
786 }
787 rc = 0;
788 break;
789
790 case FILE_TYPE_UNKNOWN:
791 default:
792 if (GetLastError() == NO_ERROR)
793 rc = birdSetErrnoToBadFileNo();
794 else
795 rc = birdSetErrnoFromWin32(GetLastError());
796 break;
797 }
798 }
799 else
800 rc = -1;
801 return rc;
802}
803
804
805/**
806 * Special case that only gets the file size and nothing else.
807 */
808int birdStatOnFdJustSize(int fd, __int64 *pcbFile)
809{
810 int rc;
811 HANDLE hFile = (HANDLE)_get_osfhandle(fd);
812 if (hFile != INVALID_HANDLE_VALUE)
813 {
814 LARGE_INTEGER cbLocal;
815 if (GetFileSizeEx(hFile, &cbLocal))
816 {
817 *pcbFile = cbLocal.QuadPart;
818 rc = 0;
819 }
820 else
821 {
822 BirdStat_T Stat;
823 rc = birdStatOnFd(fd, &Stat);
824 if (rc == 0)
825 *pcbFile = Stat.st_size;
826 }
827 }
828 else
829 rc = -1;
830 return rc;
831}
832
833
834/**
835 * Implements UNIX stat().
836 */
837int birdStatFollowLink(const char *pszPath, BirdStat_T *pStat)
838{
839 return birdStatInternal(NULL, pszPath, pStat, 1 /*fFollow*/);
840}
841
842
843/**
844 * Implements UNIX stat().
845 */
846int birdStatFollowLinkW(const wchar_t *pwszPath, BirdStat_T *pStat)
847{
848 return birdStatInternalW(NULL, pwszPath, pStat, 1 /*fFollow*/);
849}
850
851
852/**
853 * Implements UNIX lstat().
854 */
855int birdStatOnLink(const char *pszPath, BirdStat_T *pStat)
856{
857 return birdStatInternal(NULL, pszPath, pStat, 0 /*fFollow*/);
858}
859
860
861/**
862 * Implements UNIX lstat().
863 */
864int birdStatOnLinkW(const wchar_t *pwszPath, BirdStat_T *pStat)
865{
866 return birdStatInternalW(NULL, pwszPath, pStat, 0 /*fFollow*/);
867}
868
869
870/**
871 * Implements an API like UNIX fstatat().
872 *
873 * @returns 0 on success, -1 and errno on failure.
874 * @param hRoot NT handle pwszPath is relative to.
875 * @param pszPath The path.
876 * @param pStat Where to return stats.
877 * @param fFollowLink Whether to follow links.
878 */
879int birdStatAt(HANDLE hRoot, const char *pszPath, BirdStat_T *pStat, int fFollowLink)
880{
881 return birdStatInternal(hRoot, pszPath, pStat, fFollowLink != 0);
882}
883
884
885/**
886 * Implements an API like UNIX fstatat().
887 *
888 * @returns 0 on success, -1 and errno on failure.
889 * @param hRoot NT handle pwszPath is relative to.
890 * @param pwszPath The path.
891 * @param pStat Where to return stats.
892 * @param fFollowLink Whether to follow links.
893 */
894int birdStatAtW(HANDLE hRoot, const wchar_t *pwszPath, BirdStat_T *pStat, int fFollowLink)
895{
896 return birdStatInternalW(hRoot, pwszPath, pStat, fFollowLink != 0);
897}
898
899
900/**
901 * Internal worker for birdStatModTimeOnly.
902 */
903static int birdStatOnlyInternal(const char *pszPath, int fFollowLink, MY_FILE_BASIC_INFORMATION *pBasicInfo)
904{
905 int rc;
906 HANDLE hFile = birdOpenFile(pszPath,
907 FILE_READ_ATTRIBUTES,
908 FILE_ATTRIBUTE_NORMAL,
909 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
910 FILE_OPEN,
911 FILE_OPEN_FOR_BACKUP_INTENT | (fFollowLink ? 0 : FILE_OPEN_REPARSE_POINT),
912 OBJ_CASE_INSENSITIVE);
913 if (hFile != INVALID_HANDLE_VALUE)
914 {
915 MY_NTSTATUS rcNt = 0;
916 MY_IO_STATUS_BLOCK Ios;
917 Ios.Information = 0;
918 Ios.u.Status = -1;
919
920 if (pBasicInfo)
921 {
922 rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, pBasicInfo, sizeof(*pBasicInfo), MyFileBasicInformation);
923 if (MY_NT_SUCCESS(rcNt))
924 rcNt = Ios.u.Status;
925 }
926 birdCloseFile(hFile);
927
928 if (MY_NT_SUCCESS(rcNt))
929 rc = 0;
930 else
931 {
932 birdSetErrnoFromNt(rcNt);
933 rc = -1;
934 }
935 }
936 else
937 {
938 //fprintf(stderr, "stat: %s -> %u\n", pszPath, GetLastError());
939
940 /* On things like pagefile.sys we may get sharing violation. */
941 if (GetLastError() == ERROR_SHARING_VIOLATION)
942 {
943 /** @todo Fall back on the parent directory enum if we run into a sharing
944 * violation. */
945 }
946 rc = -1;
947 }
948 return rc;
949}
950
951
952/**
953 * Special function for getting the modification time.
954 */
955int birdStatModTimeOnly(const char *pszPath, BirdTimeSpec_T *pTimeSpec, int fFollowLink)
956{
957 /*
958 * Convert the path and call NtQueryFullAttributesFile.
959 *
960 * Note! NtQueryAttributesFile cannot be used as it only returns attributes.
961 */
962 MY_UNICODE_STRING NtPath;
963
964 birdResolveImports();
965 if (birdDosToNtPath(pszPath, &NtPath) == 0)
966 {
967 MY_OBJECT_ATTRIBUTES ObjAttr;
968 MY_FILE_NETWORK_OPEN_INFORMATION Info;
969 MY_NTSTATUS rcNt;
970
971 memset(&Info, 0xfe, sizeof(Info));
972
973 MyInitializeObjectAttributes(&ObjAttr, &NtPath, OBJ_CASE_INSENSITIVE, NULL /*hRoot*/, NULL /*pSecAttr*/);
974 rcNt = g_pfnNtQueryFullAttributesFile(&ObjAttr, &Info);
975
976 birdFreeNtPath(&NtPath);
977 if (MY_NT_SUCCESS(rcNt))
978 {
979 birdNtTimeToTimeSpec(Info.LastWriteTime.QuadPart, pTimeSpec);
980
981 /* Do the trailing slash check. */
982 if ( (Info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
983 || !birdIsPathDirSpec(pszPath))
984 {
985 MY_FILE_BASIC_INFORMATION BasicInfo;
986 if ( !(Info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
987 || !fFollowLink)
988 return 0;
989
990 /* Fallback on birdStatOnlyInternal to follow the reparse point. */
991 if (!birdStatOnlyInternal(pszPath, fFollowLink, &BasicInfo))
992 {
993 birdNtTimeToTimeSpec(BasicInfo.LastWriteTime.QuadPart, pTimeSpec);
994 return 0;
995 }
996 }
997 else
998 errno = ENOTDIR;
999 }
1000 else
1001 birdSetErrnoFromNt(rcNt);
1002 }
1003 return -1;
1004}
1005
1006/**
1007 * Special function for getting the file mode.
1008 */
1009int birdStatModeOnly(const char *pszPath, unsigned __int16 *pMode, int fFollowLink)
1010{
1011 /*
1012 * Convert the path and call NtQueryFullAttributesFile.
1013 */
1014 MY_UNICODE_STRING NtPath;
1015
1016 birdResolveImports();
1017 if (birdDosToNtPath(pszPath, &NtPath) == 0)
1018 {
1019 MY_OBJECT_ATTRIBUTES ObjAttr;
1020 MY_FILE_BASIC_INFORMATION Info;
1021 MY_NTSTATUS rcNt;
1022
1023 memset(&Info, 0xfe, sizeof(Info));
1024
1025 MyInitializeObjectAttributes(&ObjAttr, &NtPath, OBJ_CASE_INSENSITIVE, NULL /*hRoot*/, NULL /*pSecAttr*/);
1026 rcNt = g_pfnNtQueryAttributesFile(&ObjAttr, &Info);
1027
1028 if (MY_NT_SUCCESS(rcNt))
1029 {
1030 unsigned __int8 isdirsymlink = 0;
1031 unsigned __int8 ismountpoint = 0;
1032 *pMode = birdFileInfoToMode(Info.FileAttributes, 0, pszPath, NtPath.Buffer, NtPath.Length,
1033 &isdirsymlink, &ismountpoint);
1034
1035 /* Do the trailing slash check. */
1036 if ( (Info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1037 || !birdIsPathDirSpec(pszPath))
1038 {
1039 if ( !(Info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
1040 || !fFollowLink)
1041 {
1042 birdFreeNtPath(&NtPath);
1043 return 0;
1044 }
1045
1046 /* Fallback on birdStatOnlyInternal to follow the reparse point. */
1047 if (!birdStatOnlyInternal(pszPath, fFollowLink, &Info))
1048 {
1049 *pMode = birdFileInfoToMode(Info.FileAttributes, 0, pszPath, NtPath.Buffer, NtPath.Length,
1050 &isdirsymlink, &ismountpoint);
1051 birdFreeNtPath(&NtPath);
1052 return 0;
1053 }
1054 }
1055 else
1056 errno = ENOTDIR;
1057 }
1058 else
1059 birdSetErrnoFromNt(rcNt);
1060 birdFreeNtPath(&NtPath);
1061 }
1062 return -1;
1063}
1064
1065
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