1 /* $Id: ntstat.c 3019 2017-01-07 00:07:08Z 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 
birdIsExecutableExtension(const char * pszExt)46 static 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 
birdIsFileExecutable(const char * pszName)69 static 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  */
birdIsFileExecutableW(WCHAR const * pwcName,size_t cwcName)119 static 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 
birdFileInfoToMode(ULONG fAttribs,ULONG uReparseTag,const char * pszName,const wchar_t * pwszName,size_t cbNameW,unsigned __int8 * pfIsDirSymlink,unsigned __int8 * pfIsMountPoint)155 static 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  */
birdStatFillFromFileIdFullDirInfo(BirdStat_T * pStat,MY_FILE_ID_FULL_DIR_INFORMATION const * pBuf)212 void 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  */
birdStatFillFromFileIdBothDirInfo(BirdStat_T * pStat,MY_FILE_ID_BOTH_DIR_INFORMATION const * pBuf)243 void 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  */
birdStatFillFromFileBothDirInfo(BirdStat_T * pStat,MY_FILE_BOTH_DIR_INFORMATION const * pBuf)274 void 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 
birdStatHandle2(HANDLE hFile,BirdStat_T * pStat,const char * pszPath,const wchar_t * pwszPath)298 int 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 
birdStatHandle(HANDLE hFile,BirdStat_T * pStat,const char * pszPath)463 int 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  */
birdVolumeInfoToDeviceNumber(const MY_FILE_FS_VOLUME_INFORMATION * pVolInfo)475 unsigned __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  */
birdQueryVolumeDeviceNumber(HANDLE hFile,MY_FILE_FS_VOLUME_INFORMATION * pVolInfo,size_t cbVolInfo,unsigned __int64 * puDevNo)493 MY_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 
birdStatInternal(HANDLE hRoot,const char * pszPath,BirdStat_T * pStat,int fFollow)516 static 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 
birdStatInternalW(HANDLE hRoot,const wchar_t * pwszPath,BirdStat_T * pStat,int fFollow)632 static 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  */
birdStatOnFd(int fd,BirdStat_T * pStat)737 int 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  */
birdStatOnFdJustSize(int fd,__int64 * pcbFile)808 int 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  */
birdStatFollowLink(const char * pszPath,BirdStat_T * pStat)837 int 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  */
birdStatFollowLinkW(const wchar_t * pwszPath,BirdStat_T * pStat)846 int 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  */
birdStatOnLink(const char * pszPath,BirdStat_T * pStat)855 int 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  */
birdStatOnLinkW(const wchar_t * pwszPath,BirdStat_T * pStat)864 int 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  */
birdStatAt(HANDLE hRoot,const char * pszPath,BirdStat_T * pStat,int fFollowLink)879 int 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  */
birdStatAtW(HANDLE hRoot,const wchar_t * pwszPath,BirdStat_T * pStat,int fFollowLink)894 int 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  */
birdStatOnlyInternal(const char * pszPath,int fFollowLink,MY_FILE_BASIC_INFORMATION * pBasicInfo)903 static 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  */
birdStatModTimeOnly(const char * pszPath,BirdTimeSpec_T * pTimeSpec,int fFollowLink)955 int birdStatModTimeOnly(const char *pszPath, BirdTimeSpec_T *pTimeSpec, int fFollowLink)
956 {
957     MY_FILE_BASIC_INFORMATION BasicInfo;
958     int rc = birdStatOnlyInternal(pszPath, fFollowLink, &BasicInfo);
959     if (!rc)
960         birdNtTimeToTimeSpec(BasicInfo.LastWriteTime.QuadPart, pTimeSpec);
961     return rc;
962 }
963 
964 
965 
966