From 29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:21:29 +0200 Subject: Adding upstream version 1:0.1.9998svn3589+dfsg. Signed-off-by: Daniel Baumann --- src/lib/nt/ntstat.c | 1065 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1065 insertions(+) create mode 100644 src/lib/nt/ntstat.c (limited to 'src/lib/nt/ntstat.c') diff --git a/src/lib/nt/ntstat.c b/src/lib/nt/ntstat.c new file mode 100644 index 0000000..0aa30ab --- /dev/null +++ b/src/lib/nt/ntstat.c @@ -0,0 +1,1065 @@ +/* $Id: ntstat.c 3485 2020-09-21 12:25:08Z bird $ */ +/** @file + * MSC + NT stat, lstat and fstat. + */ + +/* + * Copyright (c) 2005-2013 knut st. osmundsen + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Alternatively, the content of this file may be used under the terms of the + * GPL version 2 or later, or LGPL version 2.1 or later. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include +#include +#include + +#include "ntstuff.h" +#include "nthlp.h" +#include "ntstat.h" + + +#undef stat + +static int birdIsExecutableExtension(const char *pszExt) +{ + switch (pszExt[0]) + { + default: + return 0; + + case 'e': /* exe */ + return pszExt[1] == 'x' && pszExt[2] == 'e' && pszExt[3] == '\0'; + + case 'b': /* bat */ + return pszExt[1] == 'a' && pszExt[2] == 't' && pszExt[3] == '\0'; + + case 'v': /* vbs */ + return pszExt[1] == 'b' && pszExt[2] == 's' && pszExt[3] == '\0'; + + case 'c': /* com and cmd */ + return (pszExt[1] == 'o' && pszExt[2] == 'm' && pszExt[3] == '\0') + || (pszExt[1] == 'm' && pszExt[2] == 'd' && pszExt[3] == '\0'); + } +} + + +static int birdIsFileExecutable(const char *pszName) +{ + if (pszName) + { + const char *pszExt = NULL; + char szExt[8]; + size_t cchExt; + unsigned i; + char ch; + + /* Look for a 3 char extension. */ + ch = *pszName++; + if (!ch) + return 0; + + while ((ch = *pszName++) != '\0') + if (ch == '.') + pszExt = pszName; + + if (!pszExt) + return 0; + pszExt++; + cchExt = pszName - pszExt; + if (cchExt != 3) + return 0; + + /* Copy the extension out and lower case it. Fail immediately on non-alpha chars. */ + for (i = 0; i < cchExt; i++, pszExt++) + { + ch = *pszExt; + if (ch >= 'a' && ch <= 'z') + { /* likely */ } + else if (ch >= 'A' && ch <= 'Z') + ch += 'a' - 'A'; + else + return 0; + szExt[i] = ch; + } + szExt[i] = '\0'; + + return birdIsExecutableExtension(szExt); + } + + return 0; +} + + +/** + * @a pwcName could be the full path. + */ +static int birdIsFileExecutableW(WCHAR const *pwcName, size_t cwcName) +{ + char szExt[8]; + unsigned cchExt; + unsigned i; + WCHAR const *pwc; + + /* Look for a 3 char extension. */ + if (cwcName > 2 && pwcName[cwcName - 2] == '.') + return 0; + else if (cwcName > 3 && pwcName[cwcName - 3] == '.') + return 0; + else if (cwcName > 4 && pwcName[cwcName - 4] == '.') + cchExt = 3; + else + return 0; + + /* Copy the extension out and lower case it. Fail immediately on non-alpha chars. */ + pwc = &pwcName[cwcName - cchExt]; + for (i = 0; i < cchExt; i++, pwc++) + { + WCHAR wc = *pwc; + if (wc >= 'a' && wc <= 'z') + { /* likely */ } + else if (wc >= 'A' && wc <= 'Z') + wc += 'a' - 'A'; + else + return 0; + szExt[i] = (char)wc; + } + szExt[i] = '\0'; + + return birdIsExecutableExtension(szExt); +} + + +static unsigned short birdFileInfoToMode(ULONG fAttribs, ULONG uReparseTag, + const char *pszName, const wchar_t *pwszName, size_t cbNameW, + unsigned __int8 *pfIsDirSymlink, unsigned __int8 *pfIsMountPoint) +{ + unsigned short fMode; + + /* File type. */ + *pfIsDirSymlink = 0; + *pfIsMountPoint = 0; + if (!(fAttribs & FILE_ATTRIBUTE_REPARSE_POINT)) + { + if (fAttribs & FILE_ATTRIBUTE_DIRECTORY) + fMode = S_IFDIR; + else + fMode = S_IFREG; + } + else + { + switch (uReparseTag) + { + case IO_REPARSE_TAG_SYMLINK: + *pfIsDirSymlink = !!(fAttribs & FILE_ATTRIBUTE_DIRECTORY); + fMode = S_IFLNK; + break; + + case IO_REPARSE_TAG_MOUNT_POINT: + *pfIsMountPoint = 1; + default: + if (fAttribs & FILE_ATTRIBUTE_DIRECTORY) + fMode = S_IFDIR; + else + fMode = S_IFREG; + break; + } + } + + /* Access mask. */ + fMode |= S_IROTH | S_IRGRP | S_IRUSR; + if (!(fAttribs & FILE_ATTRIBUTE_READONLY)) + fMode |= S_IWOTH | S_IWGRP | S_IWUSR; + if ( (fAttribs & FILE_ATTRIBUTE_DIRECTORY) + || (pwszName + ? birdIsFileExecutableW(pwszName, cbNameW / sizeof(wchar_t)) + : birdIsFileExecutable(pszName)) ) + fMode |= S_IXOTH | S_IXGRP | S_IXUSR; + + return fMode; +} + + +/** + * Fills in a stat structure from an MY_FILE_ID_FULL_DIR_INFORMATION entry. + * + * @param pStat The stat structure. + * @param pBuf The MY_FILE_ID_FULL_DIR_INFORMATION entry. + * @remarks Caller sets st_dev. + */ +void birdStatFillFromFileIdFullDirInfo(BirdStat_T *pStat, MY_FILE_ID_FULL_DIR_INFORMATION const *pBuf) +{ + pStat->st_mode = birdFileInfoToMode(pBuf->FileAttributes, pBuf->EaSize, NULL /*pszPath*/, pBuf->FileName, + pBuf->FileNameLength, &pStat->st_isdirsymlink, &pStat->st_ismountpoint); + pStat->st_padding0[0] = 0; + pStat->st_padding0[1] = 0; + pStat->st_size = pBuf->EndOfFile.QuadPart; + birdNtTimeToTimeSpec(pBuf->CreationTime.QuadPart, &pStat->st_birthtim); + birdNtTimeToTimeSpec(pBuf->ChangeTime.QuadPart, &pStat->st_ctim); + birdNtTimeToTimeSpec(pBuf->LastWriteTime.QuadPart, &pStat->st_mtim); + birdNtTimeToTimeSpec(pBuf->LastAccessTime.QuadPart, &pStat->st_atim); + pStat->st_ino = pBuf->FileId.QuadPart; + pStat->st_nlink = 1; + pStat->st_rdev = 0; + pStat->st_uid = 0; + pStat->st_gid = 0; + pStat->st_padding1 = 0; + pStat->st_attribs = pBuf->FileAttributes; + pStat->st_blksize = 65536; + pStat->st_blocks = (pBuf->AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1) + / BIRD_STAT_BLOCK_SIZE; +} + + +/** + * Fills in a stat structure from an MY_FILE_ID_BOTH_DIR_INFORMATION entry. + * + * @param pStat The stat structure. + * @param pBuf The MY_FILE_ID_BOTH_DIR_INFORMATION entry. + * @remarks Caller sets st_dev. + */ +void birdStatFillFromFileIdBothDirInfo(BirdStat_T *pStat, MY_FILE_ID_BOTH_DIR_INFORMATION const *pBuf) +{ + pStat->st_mode = birdFileInfoToMode(pBuf->FileAttributes, pBuf->EaSize, NULL /*pszPath*/, pBuf->FileName, + pBuf->FileNameLength, &pStat->st_isdirsymlink, &pStat->st_ismountpoint); + pStat->st_padding0[0] = 0; + pStat->st_padding0[1] = 0; + pStat->st_size = pBuf->EndOfFile.QuadPart; + birdNtTimeToTimeSpec(pBuf->CreationTime.QuadPart, &pStat->st_birthtim); + birdNtTimeToTimeSpec(pBuf->ChangeTime.QuadPart, &pStat->st_ctim); + birdNtTimeToTimeSpec(pBuf->LastWriteTime.QuadPart, &pStat->st_mtim); + birdNtTimeToTimeSpec(pBuf->LastAccessTime.QuadPart, &pStat->st_atim); + pStat->st_ino = pBuf->FileId.QuadPart; + pStat->st_nlink = 1; + pStat->st_rdev = 0; + pStat->st_uid = 0; + pStat->st_gid = 0; + pStat->st_padding1 = 0; + pStat->st_attribs = pBuf->FileAttributes; + pStat->st_blksize = 65536; + pStat->st_blocks = (pBuf->AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1) + / BIRD_STAT_BLOCK_SIZE; +} + + +/** + * Fills in a stat structure from an MY_FILE_BOTH_DIR_INFORMATION entry. + * + * @param pStat The stat structure. + * @param pBuf The MY_FILE_BOTH_DIR_INFORMATION entry. + * @remarks Caller sets st_dev. + */ +void birdStatFillFromFileBothDirInfo(BirdStat_T *pStat, MY_FILE_BOTH_DIR_INFORMATION const *pBuf) +{ + pStat->st_mode = birdFileInfoToMode(pBuf->FileAttributes, pBuf->EaSize, NULL /*pszPath*/, pBuf->FileName, + pBuf->FileNameLength, &pStat->st_isdirsymlink, &pStat->st_ismountpoint); + pStat->st_padding0[0] = 0; + pStat->st_padding0[1] = 0; + pStat->st_size = pBuf->EndOfFile.QuadPart; + birdNtTimeToTimeSpec(pBuf->CreationTime.QuadPart, &pStat->st_birthtim); + birdNtTimeToTimeSpec(pBuf->ChangeTime.QuadPart, &pStat->st_ctim); + birdNtTimeToTimeSpec(pBuf->LastWriteTime.QuadPart, &pStat->st_mtim); + birdNtTimeToTimeSpec(pBuf->LastAccessTime.QuadPart, &pStat->st_atim); + pStat->st_ino = 0; + pStat->st_nlink = 1; + pStat->st_rdev = 0; + pStat->st_uid = 0; + pStat->st_gid = 0; + pStat->st_padding1 = 0; + pStat->st_attribs = pBuf->FileAttributes; + pStat->st_blksize = 65536; + pStat->st_blocks = (pBuf->AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1) + / BIRD_STAT_BLOCK_SIZE; +} + + +int birdStatHandle2(HANDLE hFile, BirdStat_T *pStat, const char *pszPath, const wchar_t *pwszPath) +{ + int rc; + MY_NTSTATUS rcNt; +#if 0 + ULONG cbAll = sizeof(MY_FILE_ALL_INFORMATION) + 0x10000; + MY_FILE_ALL_INFORMATION *pAll = (MY_FILE_ALL_INFORMATION *)birdTmpAlloc(cbAll); + if (pAll) + { + MY_IO_STATUS_BLOCK Ios; + Ios.Information = 0; + Ios.u.Status = -1; + rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, pAll, cbAll, MyFileAllInformation); + if (MY_NT_SUCCESS(rcNt)) + rcNt = Ios.u.Status; + if (MY_NT_SUCCESS(rcNt)) + { + pStat->st_mode = birdFileInfoToMode(pAll->BasicInformation.FileAttributes, pszPath, + pAll->NameInformation.FileNamepAll->NameInformation.FileNameLength, + hFile, &pStat->st_isdirsymlink, &pStat->st_ismountpoint); + pStat->st_padding0[0] = 0; + pStat->st_padding0[1] = 0; + pStat->st_size = pAll->StandardInformation.EndOfFile.QuadPart; + birdNtTimeToTimeSpec(pAll->BasicInformation.CreationTime.QuadPart, &pStat->st_birthtim); + birdNtTimeToTimeSpec(pAll->BasicInformation.ChangeTime.QuadPart, &pStat->st_ctim); + birdNtTimeToTimeSpec(pAll->BasicInformation.LastWriteTime.QuadPart, &pStat->st_mtim); + birdNtTimeToTimeSpec(pAll->BasicInformation.LastAccessTime.QuadPart, &pStat->st_atim); + pStat->st_ino = pAll->InternalInformation.IndexNumber.QuadPart; + pStat->st_nlink = pAll->StandardInformation.NumberOfLinks; + pStat->st_rdev = 0; + pStat->st_uid = 0; + pStat->st_gid = 0; + pStat->st_padding1 = 0; + pStat->st_attribs = pAll->StandardInformation.FileAttributes; + pStat->st_blksize = 65536; + pStat->st_blocks = (pAll->StandardInformation.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1) + / BIRD_STAT_BLOCK_SIZE; + + /* Get the serial number, reusing the buffer from above. */ + rcNt = g_pfnNtQueryVolumeInformationFile(hFile, &Ios, pAll, cbAll, MyFileFsVolumeInformation); + if (MY_NT_SUCCESS(rcNt)) + rcNt = Ios.u.Status; + if (MY_NT_SUCCESS(rcNt)) + { + MY_FILE_FS_VOLUME_INFORMATION const *pVolInfo = (MY_FILE_FS_VOLUME_INFORMATION const *)pAll; + pStat->st_dev = pVolInfo->VolumeSerialNumber + | (pVolInfo->VolumeCreationTime.QuadPart << 32); + rc = 0; + } + else + { + pStat->st_dev = 0; + rc = birdSetErrnoFromNt(rcNt); + } + } + else + rc = birdSetErrnoFromNt(rcNt); + } + else + rc = birdSetErrnoToNoMem(); +#else + ULONG cbNameInfo = 0; + MY_FILE_NAME_INFORMATION *pNameInfo = NULL; + MY_FILE_STANDARD_INFORMATION StdInfo; + MY_FILE_BASIC_INFORMATION BasicInfo; + MY_FILE_INTERNAL_INFORMATION InternalInfo; + MY_FILE_ATTRIBUTE_TAG_INFORMATION TagInfo; + MY_IO_STATUS_BLOCK Ios; + + Ios.Information = 0; + Ios.u.Status = -1; + rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &StdInfo, sizeof(StdInfo), MyFileStandardInformation); + if (MY_NT_SUCCESS(rcNt)) + rcNt = Ios.u.Status; + + if (MY_NT_SUCCESS(rcNt)) + rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &BasicInfo, sizeof(BasicInfo), MyFileBasicInformation); + if (MY_NT_SUCCESS(rcNt)) + rcNt = Ios.u.Status; + + if (MY_NT_SUCCESS(rcNt)) + rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &InternalInfo, sizeof(InternalInfo), MyFileInternalInformation); + if (MY_NT_SUCCESS(rcNt)) + rcNt = Ios.u.Status; + + if (MY_NT_SUCCESS(rcNt)) + { + if (!(BasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) + TagInfo.ReparseTag = 0; + else + { + MY_NTSTATUS rcNt2 = g_pfnNtQueryInformationFile(hFile, &Ios, &TagInfo, sizeof(TagInfo), MyFileAttributeTagInformation); + if ( !MY_NT_SUCCESS(rcNt2) + || !MY_NT_SUCCESS(Ios.u.Status)) + TagInfo.ReparseTag = 0; + } + } + + if ( MY_NT_SUCCESS(rcNt) + && !pszPath + && !pwszPath + && !(BasicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + cbNameInfo = 0x10020; + pNameInfo = (MY_FILE_NAME_INFORMATION *)alloca(cbNameInfo); + rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, pNameInfo, cbNameInfo, MyFileNameInformation); + if (MY_NT_SUCCESS(rcNt)) + rcNt = Ios.u.Status; + } + + if (MY_NT_SUCCESS(rcNt)) + { + pStat->st_mode = birdFileInfoToMode(BasicInfo.FileAttributes, TagInfo.ReparseTag, pszPath, + pNameInfo ? pNameInfo->FileName : pwszPath, + pNameInfo ? pNameInfo->FileNameLength + : pwszPath ? wcslen(pwszPath) * sizeof(wchar_t) : 0, + &pStat->st_isdirsymlink, &pStat->st_ismountpoint); + pStat->st_padding0[0] = 0; + pStat->st_padding0[1] = 0; + pStat->st_size = StdInfo.EndOfFile.QuadPart; + birdNtTimeToTimeSpec(BasicInfo.CreationTime.QuadPart, &pStat->st_birthtim); + birdNtTimeToTimeSpec(BasicInfo.ChangeTime.QuadPart, &pStat->st_ctim); + birdNtTimeToTimeSpec(BasicInfo.LastWriteTime.QuadPart, &pStat->st_mtim); + birdNtTimeToTimeSpec(BasicInfo.LastAccessTime.QuadPart, &pStat->st_atim); + pStat->st_ino = InternalInfo.IndexNumber.QuadPart; + pStat->st_nlink = StdInfo.NumberOfLinks; + pStat->st_rdev = 0; + pStat->st_uid = 0; + pStat->st_gid = 0; + pStat->st_padding1 = 0; + pStat->st_attribs = BasicInfo.FileAttributes; + pStat->st_blksize = 65536; + pStat->st_blocks = (StdInfo.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1) + / BIRD_STAT_BLOCK_SIZE; + + /* Get the serial number, reusing the buffer from above. */ + if (!cbNameInfo) + { + cbNameInfo = sizeof(MY_FILE_FS_VOLUME_INFORMATION) + 1024; + pNameInfo = (MY_FILE_NAME_INFORMATION *)alloca(cbNameInfo); + } + rcNt = g_pfnNtQueryVolumeInformationFile(hFile, &Ios, pNameInfo, cbNameInfo, MyFileFsVolumeInformation); + if (MY_NT_SUCCESS(rcNt)) + rcNt = Ios.u.Status; + if (MY_NT_SUCCESS(rcNt)) + { + MY_FILE_FS_VOLUME_INFORMATION const *pVolInfo = (MY_FILE_FS_VOLUME_INFORMATION const *)pNameInfo; + pStat->st_dev = pVolInfo->VolumeSerialNumber + | (pVolInfo->VolumeCreationTime.QuadPart << 32); + rc = 0; + } + else + { + pStat->st_dev = 0; + rc = birdSetErrnoFromNt(rcNt); + } + } + else + rc = birdSetErrnoFromNt(rcNt); + +#endif + return rc; +} + + +int birdStatHandle(HANDLE hFile, BirdStat_T *pStat, const char *pszPath) +{ + return birdStatHandle2(hFile, pStat, pszPath, NULL); +} + + +/** + * Generates a device number from the volume information. + * + * @returns Device number. + * @param pVolInfo Volume information. + */ +unsigned __int64 birdVolumeInfoToDeviceNumber(const MY_FILE_FS_VOLUME_INFORMATION *pVolInfo) +{ + return pVolInfo->VolumeSerialNumber + | (pVolInfo->VolumeCreationTime.QuadPart << 32); +} + + +/** + * Quries the volume information and generates a device number from it. + * + * @returns NT status code. + * @param hFile The file/dir/whatever to query the volume info + * and device number for. + * @param pVolInfo User provided buffer for volume information. + * @param cbVolInfo The size of the buffer. + * @param puDevNo Where to return the device number. This is set + * to zero on failure. + */ +MY_NTSTATUS birdQueryVolumeDeviceNumber(HANDLE hFile, MY_FILE_FS_VOLUME_INFORMATION *pVolInfo, size_t cbVolInfo, + unsigned __int64 *puDevNo) +{ + MY_IO_STATUS_BLOCK Ios; + MY_NTSTATUS rcNt; + + Ios.u.Status = -1; + Ios.Information = -1; + + pVolInfo->VolumeSerialNumber = 0; + pVolInfo->VolumeCreationTime.QuadPart = 0; + + rcNt = g_pfnNtQueryVolumeInformationFile(hFile, &Ios, pVolInfo, (LONG)cbVolInfo, MyFileFsVolumeInformation); + if (MY_NT_SUCCESS(rcNt)) + { + *puDevNo = birdVolumeInfoToDeviceNumber(pVolInfo); + return Ios.u.Status; + } + *puDevNo = 0; + return rcNt; +} + + +static int birdStatInternal(HANDLE hRoot, const char *pszPath, BirdStat_T *pStat, int fFollow) +{ + int rc; + HANDLE hFile = birdOpenFileEx(hRoot, pszPath, + FILE_READ_ATTRIBUTES, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_OPEN_FOR_BACKUP_INTENT | (fFollow ? 0 : FILE_OPEN_REPARSE_POINT), + OBJ_CASE_INSENSITIVE); + if (hFile != INVALID_HANDLE_VALUE) + { + rc = birdStatHandle2(hFile, pStat, pszPath, NULL); + birdCloseFile(hFile); + + if (rc || !pStat->st_ismountpoint) + { /* very likely */ } + else + { + /* + * If we hit a mount point (NTFS volume mounted under an empty NTFS directory), + * we should return information about what's mounted there rather than the + * directory it is mounted at as this is what UNIX does. + */ + hFile = birdOpenFileEx(hRoot, pszPath, + FILE_READ_ATTRIBUTES, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_OPEN_FOR_BACKUP_INTENT, + OBJ_CASE_INSENSITIVE); + if (hFile != INVALID_HANDLE_VALUE) + { + rc = birdStatHandle2(hFile, pStat, pszPath, NULL); + pStat->st_ismountpoint = 2; + birdCloseFile(hFile); + } + } + +#if 0 + { + static char s_szPrev[256]; + size_t cchPath = strlen(pszPath); + if (memcmp(s_szPrev, pszPath, cchPath >= 255 ? 255 : cchPath + 1) == 0) + fprintf(stderr, "stat: %s -> rc/errno=%d/%u\n", pszPath, rc, errno); + else + memcpy(s_szPrev, pszPath, cchPath + 1); + } +#endif + //fprintf(stderr, "stat: %s -> rc/errno=%d/%u\n", pszPath, rc, errno); + } + else + { + //fprintf(stderr, "stat: %s -> %u\n", pszPath, GetLastError()); + + /* + * On things like pagefile.sys we may get sharing violation. We fall + * back on directory enumeration for dealing with that. + */ + if ( errno == ETXTBSY + && strchr(pszPath, '*') == NULL /* Serious paranoia... */ + && strchr(pszPath, '?') == NULL) + { + MY_UNICODE_STRING NameUniStr; + hFile = birdOpenParentDir(hRoot, pszPath, + FILE_READ_DATA | SYNCHRONIZE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT, + OBJ_CASE_INSENSITIVE, + &NameUniStr); + if (hFile != INVALID_HANDLE_VALUE) + { + MY_FILE_ID_FULL_DIR_INFORMATION *pBuf; + ULONG cbBuf = sizeof(*pBuf) + NameUniStr.MaximumLength + 1024; + MY_IO_STATUS_BLOCK Ios; + MY_NTSTATUS rcNt; + + pBuf = (MY_FILE_ID_FULL_DIR_INFORMATION *)alloca(cbBuf); + Ios.u.Status = -1; + Ios.Information = -1; + rcNt = g_pfnNtQueryDirectoryFile(hFile, NULL, NULL, NULL, &Ios, pBuf, cbBuf, + MyFileIdFullDirectoryInformation, FALSE, &NameUniStr, TRUE); + if (MY_NT_SUCCESS(rcNt)) + rcNt = Ios.u.Status; + if (MY_NT_SUCCESS(rcNt)) + { + /* + * Convert the data. + */ + birdStatFillFromFileIdFullDirInfo(pStat, pBuf); + + /* Get the serial number, reusing the buffer from above. */ + rcNt = birdQueryVolumeDeviceNumber(hFile, (MY_FILE_FS_VOLUME_INFORMATION *)pBuf, cbBuf, &pStat->st_dev); + if (MY_NT_SUCCESS(rcNt)) + rc = 0; + else + rc = birdSetErrnoFromNt(rcNt); + } + + birdFreeNtPath(&NameUniStr); + birdCloseFile(hFile); + + if (MY_NT_SUCCESS(rcNt)) + return 0; + birdSetErrnoFromNt(rcNt); + } + } + rc = -1; + } + + return rc; +} + + +static int birdStatInternalW(HANDLE hRoot, const wchar_t *pwszPath, BirdStat_T *pStat, int fFollow) +{ + int rc; + HANDLE hFile = birdOpenFileExW(hRoot, pwszPath, + FILE_READ_ATTRIBUTES, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_OPEN_FOR_BACKUP_INTENT | (fFollow ? 0 : FILE_OPEN_REPARSE_POINT), + OBJ_CASE_INSENSITIVE); + if (hFile != INVALID_HANDLE_VALUE) + { + rc = birdStatHandle2(hFile, pStat, NULL, pwszPath); + birdCloseFile(hFile); + + if (rc || !pStat->st_ismountpoint) + { /* very likely */ } + else + { + /* + * If we hit a mount point (NTFS volume mounted under an empty NTFS directory), + * we should return information about what's mounted there rather than the + * directory it is mounted at as this is what UNIX does. + */ + hFile = birdOpenFileExW(hRoot, pwszPath, + FILE_READ_ATTRIBUTES, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_OPEN_FOR_BACKUP_INTENT, + OBJ_CASE_INSENSITIVE); + if (hFile != INVALID_HANDLE_VALUE) + { + rc = birdStatHandle2(hFile, pStat, NULL, pwszPath); + pStat->st_ismountpoint = 2; + birdCloseFile(hFile); + } + } + } + else + { + /* + * On things like pagefile.sys we may get sharing violation. We fall + * back on directory enumeration for dealing with that. + */ + if ( errno == ETXTBSY + && wcschr(pwszPath, '*') == NULL /* Serious paranoia... */ + && wcschr(pwszPath, '?') == NULL) + { + MY_UNICODE_STRING NameUniStr; + hFile = birdOpenParentDirW(hRoot, pwszPath, + FILE_READ_DATA | SYNCHRONIZE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT, + OBJ_CASE_INSENSITIVE, + &NameUniStr); + if (hFile != INVALID_HANDLE_VALUE) + { + MY_FILE_ID_FULL_DIR_INFORMATION *pBuf; + ULONG cbBuf = sizeof(*pBuf) + NameUniStr.MaximumLength + 1024; + MY_IO_STATUS_BLOCK Ios; + MY_NTSTATUS rcNt; + + pBuf = (MY_FILE_ID_FULL_DIR_INFORMATION *)alloca(cbBuf); + Ios.u.Status = -1; + Ios.Information = -1; + rcNt = g_pfnNtQueryDirectoryFile(hFile, NULL, NULL, NULL, &Ios, pBuf, cbBuf, + MyFileIdFullDirectoryInformation, FALSE, &NameUniStr, TRUE); + if (MY_NT_SUCCESS(rcNt)) + rcNt = Ios.u.Status; + if (MY_NT_SUCCESS(rcNt)) + { + /* + * Convert the data. + */ + birdStatFillFromFileIdFullDirInfo(pStat, pBuf); + + /* Get the serial number, reusing the buffer from above. */ + rcNt = birdQueryVolumeDeviceNumber(hFile, (MY_FILE_FS_VOLUME_INFORMATION *)pBuf, cbBuf, &pStat->st_dev); + if (MY_NT_SUCCESS(rcNt)) + rc = 0; + else + rc = birdSetErrnoFromNt(rcNt); + } + + birdFreeNtPath(&NameUniStr); + birdCloseFile(hFile); + + if (MY_NT_SUCCESS(rcNt)) + return 0; + birdSetErrnoFromNt(rcNt); + } + } + rc = -1; + } + + return rc; +} + + +/** + * Implements UNIX fstat(). + */ +int birdStatOnFd(int fd, BirdStat_T *pStat) +{ + int rc; + HANDLE hFile = (HANDLE)_get_osfhandle(fd); + if (hFile != INVALID_HANDLE_VALUE) + { + DWORD fFileType; + + birdResolveImports(); + + SetLastError(NO_ERROR); + fFileType = GetFileType(hFile) & ~FILE_TYPE_REMOTE; + switch (fFileType) + { + case FILE_TYPE_DISK: + rc = birdStatHandle2(hFile, pStat, NULL, NULL); + break; + + case FILE_TYPE_CHAR: + case FILE_TYPE_PIPE: + if (fFileType == FILE_TYPE_PIPE) + pStat->st_mode = S_IFIFO | 0666; + else + pStat->st_mode = S_IFCHR | 0666; + pStat->st_padding0[0] = 0; + pStat->st_padding0[1] = 0; + pStat->st_size = 0; + pStat->st_atim.tv_sec = 0; + pStat->st_atim.tv_nsec = 0; + pStat->st_mtim.tv_sec = 0; + pStat->st_mtim.tv_nsec = 0; + pStat->st_ctim.tv_sec = 0; + pStat->st_ctim.tv_nsec = 0; + pStat->st_birthtim.tv_sec = 0; + pStat->st_birthtim.tv_nsec = 0; + pStat->st_ino = 0; + pStat->st_dev = 0; + pStat->st_rdev = 0; + pStat->st_uid = 0; + pStat->st_gid = 0; + pStat->st_padding1 = 0; + pStat->st_attribs = fFileType == FILE_TYPE_PIPE ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_DEVICE; + pStat->st_blksize = 512; + pStat->st_blocks = 0; + if (fFileType == FILE_TYPE_PIPE) + { + DWORD cbAvail; + if (PeekNamedPipe(hFile, NULL, 0, NULL, &cbAvail, NULL)) + pStat->st_size = cbAvail; + } + rc = 0; + break; + + case FILE_TYPE_UNKNOWN: + default: + if (GetLastError() == NO_ERROR) + rc = birdSetErrnoToBadFileNo(); + else + rc = birdSetErrnoFromWin32(GetLastError()); + break; + } + } + else + rc = -1; + return rc; +} + + +/** + * Special case that only gets the file size and nothing else. + */ +int birdStatOnFdJustSize(int fd, __int64 *pcbFile) +{ + int rc; + HANDLE hFile = (HANDLE)_get_osfhandle(fd); + if (hFile != INVALID_HANDLE_VALUE) + { + LARGE_INTEGER cbLocal; + if (GetFileSizeEx(hFile, &cbLocal)) + { + *pcbFile = cbLocal.QuadPart; + rc = 0; + } + else + { + BirdStat_T Stat; + rc = birdStatOnFd(fd, &Stat); + if (rc == 0) + *pcbFile = Stat.st_size; + } + } + else + rc = -1; + return rc; +} + + +/** + * Implements UNIX stat(). + */ +int birdStatFollowLink(const char *pszPath, BirdStat_T *pStat) +{ + return birdStatInternal(NULL, pszPath, pStat, 1 /*fFollow*/); +} + + +/** + * Implements UNIX stat(). + */ +int birdStatFollowLinkW(const wchar_t *pwszPath, BirdStat_T *pStat) +{ + return birdStatInternalW(NULL, pwszPath, pStat, 1 /*fFollow*/); +} + + +/** + * Implements UNIX lstat(). + */ +int birdStatOnLink(const char *pszPath, BirdStat_T *pStat) +{ + return birdStatInternal(NULL, pszPath, pStat, 0 /*fFollow*/); +} + + +/** + * Implements UNIX lstat(). + */ +int birdStatOnLinkW(const wchar_t *pwszPath, BirdStat_T *pStat) +{ + return birdStatInternalW(NULL, pwszPath, pStat, 0 /*fFollow*/); +} + + +/** + * Implements an API like UNIX fstatat(). + * + * @returns 0 on success, -1 and errno on failure. + * @param hRoot NT handle pwszPath is relative to. + * @param pszPath The path. + * @param pStat Where to return stats. + * @param fFollowLink Whether to follow links. + */ +int birdStatAt(HANDLE hRoot, const char *pszPath, BirdStat_T *pStat, int fFollowLink) +{ + return birdStatInternal(hRoot, pszPath, pStat, fFollowLink != 0); +} + + +/** + * Implements an API like UNIX fstatat(). + * + * @returns 0 on success, -1 and errno on failure. + * @param hRoot NT handle pwszPath is relative to. + * @param pwszPath The path. + * @param pStat Where to return stats. + * @param fFollowLink Whether to follow links. + */ +int birdStatAtW(HANDLE hRoot, const wchar_t *pwszPath, BirdStat_T *pStat, int fFollowLink) +{ + return birdStatInternalW(hRoot, pwszPath, pStat, fFollowLink != 0); +} + + +/** + * Internal worker for birdStatModTimeOnly. + */ +static int birdStatOnlyInternal(const char *pszPath, int fFollowLink, MY_FILE_BASIC_INFORMATION *pBasicInfo) +{ + int rc; + HANDLE hFile = birdOpenFile(pszPath, + FILE_READ_ATTRIBUTES, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_OPEN_FOR_BACKUP_INTENT | (fFollowLink ? 0 : FILE_OPEN_REPARSE_POINT), + OBJ_CASE_INSENSITIVE); + if (hFile != INVALID_HANDLE_VALUE) + { + MY_NTSTATUS rcNt = 0; + MY_IO_STATUS_BLOCK Ios; + Ios.Information = 0; + Ios.u.Status = -1; + + if (pBasicInfo) + { + rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, pBasicInfo, sizeof(*pBasicInfo), MyFileBasicInformation); + if (MY_NT_SUCCESS(rcNt)) + rcNt = Ios.u.Status; + } + birdCloseFile(hFile); + + if (MY_NT_SUCCESS(rcNt)) + rc = 0; + else + { + birdSetErrnoFromNt(rcNt); + rc = -1; + } + } + else + { + //fprintf(stderr, "stat: %s -> %u\n", pszPath, GetLastError()); + + /* On things like pagefile.sys we may get sharing violation. */ + if (GetLastError() == ERROR_SHARING_VIOLATION) + { + /** @todo Fall back on the parent directory enum if we run into a sharing + * violation. */ + } + rc = -1; + } + return rc; +} + + +/** + * Special function for getting the modification time. + */ +int birdStatModTimeOnly(const char *pszPath, BirdTimeSpec_T *pTimeSpec, int fFollowLink) +{ + /* + * Convert the path and call NtQueryFullAttributesFile. + * + * Note! NtQueryAttributesFile cannot be used as it only returns attributes. + */ + MY_UNICODE_STRING NtPath; + + birdResolveImports(); + if (birdDosToNtPath(pszPath, &NtPath) == 0) + { + MY_OBJECT_ATTRIBUTES ObjAttr; + MY_FILE_NETWORK_OPEN_INFORMATION Info; + MY_NTSTATUS rcNt; + + memset(&Info, 0xfe, sizeof(Info)); + + MyInitializeObjectAttributes(&ObjAttr, &NtPath, OBJ_CASE_INSENSITIVE, NULL /*hRoot*/, NULL /*pSecAttr*/); + rcNt = g_pfnNtQueryFullAttributesFile(&ObjAttr, &Info); + + birdFreeNtPath(&NtPath); + if (MY_NT_SUCCESS(rcNt)) + { + birdNtTimeToTimeSpec(Info.LastWriteTime.QuadPart, pTimeSpec); + + /* Do the trailing slash check. */ + if ( (Info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) + || !birdIsPathDirSpec(pszPath)) + { + MY_FILE_BASIC_INFORMATION BasicInfo; + if ( !(Info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) + || !fFollowLink) + return 0; + + /* Fallback on birdStatOnlyInternal to follow the reparse point. */ + if (!birdStatOnlyInternal(pszPath, fFollowLink, &BasicInfo)) + { + birdNtTimeToTimeSpec(BasicInfo.LastWriteTime.QuadPart, pTimeSpec); + return 0; + } + } + else + errno = ENOTDIR; + } + else + birdSetErrnoFromNt(rcNt); + } + return -1; +} + +/** + * Special function for getting the file mode. + */ +int birdStatModeOnly(const char *pszPath, unsigned __int16 *pMode, int fFollowLink) +{ + /* + * Convert the path and call NtQueryFullAttributesFile. + */ + MY_UNICODE_STRING NtPath; + + birdResolveImports(); + if (birdDosToNtPath(pszPath, &NtPath) == 0) + { + MY_OBJECT_ATTRIBUTES ObjAttr; + MY_FILE_BASIC_INFORMATION Info; + MY_NTSTATUS rcNt; + + memset(&Info, 0xfe, sizeof(Info)); + + MyInitializeObjectAttributes(&ObjAttr, &NtPath, OBJ_CASE_INSENSITIVE, NULL /*hRoot*/, NULL /*pSecAttr*/); + rcNt = g_pfnNtQueryAttributesFile(&ObjAttr, &Info); + + if (MY_NT_SUCCESS(rcNt)) + { + unsigned __int8 isdirsymlink = 0; + unsigned __int8 ismountpoint = 0; + *pMode = birdFileInfoToMode(Info.FileAttributes, 0, pszPath, NtPath.Buffer, NtPath.Length, + &isdirsymlink, &ismountpoint); + + /* Do the trailing slash check. */ + if ( (Info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) + || !birdIsPathDirSpec(pszPath)) + { + if ( !(Info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) + || !fFollowLink) + { + birdFreeNtPath(&NtPath); + return 0; + } + + /* Fallback on birdStatOnlyInternal to follow the reparse point. */ + if (!birdStatOnlyInternal(pszPath, fFollowLink, &Info)) + { + *pMode = birdFileInfoToMode(Info.FileAttributes, 0, pszPath, NtPath.Buffer, NtPath.Length, + &isdirsymlink, &ismountpoint); + birdFreeNtPath(&NtPath); + return 0; + } + } + else + errno = ENOTDIR; + } + else + birdSetErrnoFromNt(rcNt); + birdFreeNtPath(&NtPath); + } + return -1; +} + + -- cgit v1.2.3