diff options
Diffstat (limited to '')
-rw-r--r-- | src/lib/nt/ntdir.c | 673 |
1 files changed, 673 insertions, 0 deletions
diff --git a/src/lib/nt/ntdir.c b/src/lib/nt/ntdir.c new file mode 100644 index 0000000..61a58e3 --- /dev/null +++ b/src/lib/nt/ntdir.c @@ -0,0 +1,673 @@ +/* $Id: ntdir.c 3007 2016-11-06 16:46:43Z bird $ */ +/** @file + * MSC + NT opendir, readdir, telldir, seekdir, and closedir. + */ + +/* + * Copyright (c) 2005-2016 knut st. osmundsen <bird-kBuild-spamx@anduin.net> + * + * 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 <stdio.h> +#include <errno.h> +#include <malloc.h> +#include <assert.h> + +#include "ntstuff.h" +#include "nthlp.h" +#include "ntdir.h" + + +/** + * Implements opendir. + */ +BirdDir_T *birdDirOpen(const char *pszPath) +{ + HANDLE hDir = birdOpenFile(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); + if (hDir != INVALID_HANDLE_VALUE) + { + BirdDir_T *pDir = birdDirOpenFromHandle((void *)hDir, NULL, BIRDDIR_F_CLOSE_HANDLE); + if (pDir) + return pDir; + birdCloseFile(hDir); + } + return NULL; +} + + +/** + * Alternative opendir, with extra stat() info returned by readdir(). + */ +BirdDir_T *birdDirOpenExtraInfo(const char *pszPath) +{ + HANDLE hDir = birdOpenFile(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); + if (hDir != INVALID_HANDLE_VALUE) + { + BirdDir_T *pDir = birdDirOpenFromHandle((void *)hDir, NULL, BIRDDIR_F_CLOSE_HANDLE | BIRDDIR_F_EXTRA_INFO); + if (pDir) + return pDir; + birdCloseFile(hDir); + } + return NULL; +} + + +BirdDir_T *birdDirOpenExW(void *hRoot, const wchar_t *pwszPath, const wchar_t *pwszFilter, unsigned fFlags) +{ + HANDLE hDir = birdOpenFileExW((HANDLE)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); + if (hDir != INVALID_HANDLE_VALUE) + { + BirdDir_T *pDir = birdDirOpenFromHandle((void *)hDir, pwszFilter, fFlags | BIRDDIR_F_CLOSE_HANDLE); + if (pDir) + return pDir; + birdCloseFile(hDir); + } + return NULL; +} + + +/** + * Internal worker for birdStatModTimeOnly. + */ +BirdDir_T *birdDirOpenFromHandle(void *pvHandle, const void *pvReserved, unsigned fFlags) +{ + if (!pvReserved && !(fFlags & BIRDDIR_F_STATIC_ALLOC)) + { + /* + * Allocate and initialize the directory enum handle. + */ + BirdDir_T *pDir = (BirdDir_T *)birdMemAlloc(sizeof(*pDir)); + if (pDir) + { + pDir->uMagic = BIRD_DIR_MAGIC; + pDir->fFlags = fFlags; + pDir->pvHandle = pvHandle; + pDir->uDev = 0; + pDir->offPos = 0; + pDir->fHaveData = 0; + pDir->fFirst = 1; + pDir->iInfoClass = fFlags & BIRDDIR_F_EXTRA_INFO ? MyFileIdFullDirectoryInformation : MyFileNamesInformation; + pDir->offBuf = 0; + pDir->cbBuf = 0; + pDir->pabBuf = NULL; + return pDir; + } + } + else + { + assert(!(fFlags & BIRDDIR_F_STATIC_ALLOC)); + assert(pvReserved == NULL); + } + birdSetErrnoToInvalidArg(); + return NULL; +} + + +/** + * Special API that takes a preallocated BirdDir_T and can be called again + * without involving birdDirClose. + * + * + */ +BirdDir_T *birdDirOpenFromHandleWithReuse(BirdDir_T *pDir, void *pvHandle, const void *pvReserved, unsigned fFlags) +{ + if (!pvReserved) + { + /* + * Allocate and initialize the directory enum handle. + */ + if (pDir) + { + if (pDir->uMagic == BIRD_DIR_MAGIC) + { + if ( (pDir->fFlags & BIRDDIR_F_CLOSE_HANDLE) + && pDir->pvHandle != INVALID_HANDLE_VALUE) + birdCloseFile((HANDLE)pDir->pvHandle); + } + else + { + pDir->cbBuf = 0; + pDir->pabBuf = NULL; + pDir->uMagic = BIRD_DIR_MAGIC; + } + pDir->pvHandle = pvHandle; + pDir->fFlags = fFlags; + pDir->uDev = 0; + pDir->offPos = 0; + pDir->fHaveData = 0; + pDir->fFirst = 1; + pDir->iInfoClass = fFlags & BIRDDIR_F_EXTRA_INFO ? MyFileIdFullDirectoryInformation : MyFileNamesInformation; + pDir->offBuf = 0; + return pDir; + } + } + else + assert(pvReserved == NULL); + birdSetErrnoToInvalidArg(); + return NULL; +} + + +static int birdDirReadMore(BirdDir_T *pDir) +{ + MY_NTSTATUS rcNt; + MY_IO_STATUS_BLOCK Ios; + int fFirst; + + /* + * Retrieve the volume serial number + creation time and create the + * device number the first time around. Also allocate a buffer. + */ + fFirst = pDir->fFirst; + if (fFirst) + { + union + { + MY_FILE_FS_VOLUME_INFORMATION VolInfo; + unsigned char abBuf[1024]; + } uBuf; + + Ios.Information = 0; + Ios.u.Status = -1; + rcNt = g_pfnNtQueryVolumeInformationFile((HANDLE)pDir->pvHandle, &Ios, &uBuf, sizeof(uBuf), MyFileFsVolumeInformation); + if (MY_NT_SUCCESS(rcNt)) + rcNt = Ios.u.Status; + if (MY_NT_SUCCESS(rcNt)) + pDir->uDev = uBuf.VolInfo.VolumeSerialNumber + | (uBuf.VolInfo.VolumeCreationTime.QuadPart << 32); + else + pDir->uDev = 0; + + if (!pDir->pabBuf) + { + /* + * Allocate a buffer. + * + * Better not exceed 64KB or CIFS may throw a fit. Also, on win10/64 + * here there is a noticable speedup when going one byte below 64KB. + */ + pDir->cbBuf = 0xffe0; + pDir->pabBuf = birdMemAlloc(pDir->cbBuf); + if (!pDir->pabBuf) + return birdSetErrnoToNoMem(); + } + + pDir->fFirst = 0; + } + + /* + * Read another buffer full. + */ + Ios.Information = 0; + Ios.u.Status = -1; + + rcNt = g_pfnNtQueryDirectoryFile((HANDLE)pDir->pvHandle, + NULL, /* hEvent */ + NULL, /* pfnApcComplete */ + NULL, /* pvApcCompleteCtx */ + &Ios, + pDir->pabBuf, + pDir->cbBuf, + (MY_FILE_INFORMATION_CLASS)pDir->iInfoClass, + FALSE, /* fReturnSingleEntry */ + NULL, /* Filter / restart pos. */ + pDir->fFlags & BIRDDIR_F_RESTART_SCAN ? TRUE : FALSE); /* fRestartScan */ + if (!MY_NT_SUCCESS(rcNt)) + { + int rc; + if (rcNt == MY_STATUS_NO_MORE_FILES) + rc = 0; + else + rc = birdSetErrnoFromNt(rcNt); + pDir->fHaveData = 0; + pDir->offBuf = pDir->cbBuf; + return rc; + } + + pDir->offBuf = 0; + pDir->fHaveData = 1; + pDir->fFlags &= ~BIRDDIR_F_RESTART_SCAN; + + return 0; +} + + +static int birdDirCopyNameToEntry(WCHAR const *pwcName, ULONG cbName, BirdDirEntry_T *pEntry) +{ + int cchOut = WideCharToMultiByte(CP_ACP, 0, + pwcName, cbName / sizeof(WCHAR), + pEntry->d_name, sizeof(pEntry->d_name) - 1, + NULL, NULL); + if (cchOut > 0) + { + pEntry->d_name[cchOut] = '\0'; + pEntry->d_namlen = (unsigned __int16)cchOut; + pEntry->d_reclen = (unsigned __int16)((size_t)&pEntry->d_name[cchOut + 1] - (size_t)pEntry); + return 0; + } + return -1; +} + + +/** + * Deals with mount points. + * + * @param pDir The directory handle. + * @param pInfo The NT entry information. + * @param pEntryStat The stats for the mount point directory that needs + * updating (a d_stat member). + */ +static void birdDirUpdateMountPointInfo(BirdDir_T *pDir, MY_FILE_ID_FULL_DIR_INFORMATION *pInfo, + BirdStat_T *pEntryStat) +{ + /* + * Try open the root directory of the mount. + * (Can't use birdStatAtW here because the name isn't terminated.) + */ + HANDLE hRoot = INVALID_HANDLE_VALUE; + MY_NTSTATUS rcNt; + MY_UNICODE_STRING Name; + Name.Buffer = pInfo->FileName; + Name.Length = Name.MaximumLength = (USHORT)pInfo->FileNameLength; + + rcNt = birdOpenFileUniStr((HANDLE)pDir->pvHandle, &Name, + 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, + &hRoot); + if (MY_NT_SUCCESS(rcNt)) + { + int iSavedErrno = errno; + BirdStat_T RootStat; + if (birdStatHandle(hRoot, &RootStat, NULL) == 0) + { + RootStat.st_ismountpoint = 2; + *pEntryStat = RootStat; + } + birdCloseFile(hRoot); + errno = iSavedErrno; + } + /* else: don't mind failures, we've got some info. */ +} + + +/** + * Implements readdir_r(). + * + * @remarks birdDirReadReentrantW is a copy of this. Keep them in sync! + */ +int birdDirReadReentrant(BirdDir_T *pDir, BirdDirEntry_T *pEntry, BirdDirEntry_T **ppResult) +{ + int fSkipEntry; + + *ppResult = NULL; + + if (!pDir || pDir->uMagic != BIRD_DIR_MAGIC) + return birdSetErrnoToBadFileNo(); + + do + { + ULONG offNext; + ULONG cbMinCur; + + /* + * Read more? + */ + if (!pDir->fHaveData) + { + if (birdDirReadMore(pDir) != 0) + return -1; + if (!pDir->fHaveData) + return 0; + } + + /* + * Convert the NT data to the unixy output structure. + */ + fSkipEntry = 0; + switch (pDir->iInfoClass) + { + case MyFileNamesInformation: + { + MY_FILE_NAMES_INFORMATION *pInfo = (MY_FILE_NAMES_INFORMATION *)&pDir->pabBuf[pDir->offBuf]; + if ( pDir->offBuf >= pDir->cbBuf - MIN_SIZEOF_MY_FILE_NAMES_INFORMATION + || pInfo->FileNameLength >= pDir->cbBuf + || pDir->offBuf + pInfo->FileNameLength + MIN_SIZEOF_MY_FILE_NAMES_INFORMATION > pDir->cbBuf) + { + fSkipEntry = 1; + pDir->fHaveData = 0; + continue; + } + + memset(&pEntry->d_stat, 0, sizeof(pEntry->d_stat)); + pEntry->d_stat.st_mode = S_IFMT; + pEntry->d_type = DT_UNKNOWN; + pEntry->d_reclen = 0; + pEntry->d_namlen = 0; + if (birdDirCopyNameToEntry(pInfo->FileName, pInfo->FileNameLength, pEntry) != 0) + fSkipEntry = 1; + + cbMinCur = MIN_SIZEOF_MY_FILE_NAMES_INFORMATION + pInfo->FileNameLength; + offNext = pInfo->NextEntryOffset; + break; + } + + case MyFileIdFullDirectoryInformation: + { + MY_FILE_ID_FULL_DIR_INFORMATION *pInfo = (MY_FILE_ID_FULL_DIR_INFORMATION *)&pDir->pabBuf[pDir->offBuf]; + if ( pDir->offBuf >= pDir->cbBuf - MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION + || pInfo->FileNameLength >= pDir->cbBuf + || pDir->offBuf + pInfo->FileNameLength + MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION > pDir->cbBuf) + { + fSkipEntry = 1; + pDir->fHaveData = 0; + continue; + } + + pEntry->d_type = DT_UNKNOWN; + pEntry->d_reclen = 0; + pEntry->d_namlen = 0; + if (birdDirCopyNameToEntry(pInfo->FileName, pInfo->FileNameLength, pEntry) != 0) + fSkipEntry = 1; + birdStatFillFromFileIdFullDirInfo(&pEntry->d_stat, pInfo); + pEntry->d_stat.st_dev = pDir->uDev; + switch (pEntry->d_stat.st_mode & S_IFMT) + { + case S_IFREG: pEntry->d_type = DT_REG; break; + case S_IFDIR: pEntry->d_type = DT_DIR; break; + case S_IFLNK: pEntry->d_type = DT_LNK; break; + case S_IFIFO: pEntry->d_type = DT_FIFO; break; + case S_IFCHR: pEntry->d_type = DT_CHR; break; + default: +#ifndef NDEBUG + __debugbreak(); +#endif + pEntry->d_type = DT_UNKNOWN; + break; + } + + if (pEntry->d_stat.st_ismountpoint != 1) + { /* likely. */ } + else + birdDirUpdateMountPointInfo(pDir, pInfo, &pEntry->d_stat); + + cbMinCur = MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION + pInfo->FileNameLength; + offNext = pInfo->NextEntryOffset; + break; + } + + default: + return birdSetErrnoToBadFileNo(); + } + + /* + * Advance. + */ + if ( offNext >= cbMinCur + && offNext < pDir->cbBuf) + pDir->offBuf += offNext; + else + { + pDir->fHaveData = 0; + pDir->offBuf = pDir->cbBuf; + } + pDir->offPos++; + } while (fSkipEntry); + + + /* + * Successful return. + */ + *ppResult = pEntry; + return 0; +} + + +/** + * Implements readdir(). + */ +BirdDirEntry_T *birdDirRead(BirdDir_T *pDir) +{ + BirdDirEntry_T *pRet = NULL; + birdDirReadReentrant(pDir, &pDir->u.DirEntry, &pRet); + return pRet; +} + + +static int birdDirCopyNameToEntryW(WCHAR const *pwcName, ULONG cbName, BirdDirEntryW_T *pEntry) +{ + ULONG cwcName = cbName / sizeof(wchar_t); + if (cwcName < sizeof(pEntry->d_name)) + { + memcpy(pEntry->d_name, pwcName, cbName); + pEntry->d_name[cwcName] = '\0'; + pEntry->d_namlen = (unsigned __int16)cwcName; + pEntry->d_reclen = (unsigned __int16)((size_t)&pEntry->d_name[cwcName + 1] - (size_t)pEntry); + return 0; + } + return -1; +} + + +/** + * Implements readdir_r(), UTF-16 version. + * + * @remarks This is a copy of birdDirReadReentrant where only the name handling + * and entry type differs. Remember to keep them in sync! + */ +int birdDirReadReentrantW(BirdDir_T *pDir, BirdDirEntryW_T *pEntry, BirdDirEntryW_T **ppResult) +{ + int fSkipEntry; + + *ppResult = NULL; + + if (!pDir || pDir->uMagic != BIRD_DIR_MAGIC) + return birdSetErrnoToBadFileNo(); + + do + { + ULONG offNext; + ULONG cbMinCur; + + /* + * Read more? + */ + if (!pDir->fHaveData) + { + if (birdDirReadMore(pDir) != 0) + return -1; + if (!pDir->fHaveData) + return 0; + } + + /* + * Convert the NT data to the unixy output structure. + */ + fSkipEntry = 0; + switch (pDir->iInfoClass) + { + case MyFileNamesInformation: + { + MY_FILE_NAMES_INFORMATION *pInfo = (MY_FILE_NAMES_INFORMATION *)&pDir->pabBuf[pDir->offBuf]; + if ( pDir->offBuf >= pDir->cbBuf - MIN_SIZEOF_MY_FILE_NAMES_INFORMATION + || pInfo->FileNameLength >= pDir->cbBuf + || pDir->offBuf + pInfo->FileNameLength + MIN_SIZEOF_MY_FILE_NAMES_INFORMATION > pDir->cbBuf) + { + fSkipEntry = 1; + pDir->fHaveData = 0; + continue; + } + + memset(&pEntry->d_stat, 0, sizeof(pEntry->d_stat)); + pEntry->d_stat.st_mode = S_IFMT; + pEntry->d_type = DT_UNKNOWN; + pEntry->d_reclen = 0; + pEntry->d_namlen = 0; + if (birdDirCopyNameToEntryW(pInfo->FileName, pInfo->FileNameLength, pEntry) != 0) + fSkipEntry = 1; + + cbMinCur = MIN_SIZEOF_MY_FILE_NAMES_INFORMATION + pInfo->FileNameLength; + offNext = pInfo->NextEntryOffset; + break; + } + + case MyFileIdFullDirectoryInformation: + { + MY_FILE_ID_FULL_DIR_INFORMATION *pInfo = (MY_FILE_ID_FULL_DIR_INFORMATION *)&pDir->pabBuf[pDir->offBuf]; + if ( pDir->offBuf >= pDir->cbBuf - MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION + || pInfo->FileNameLength >= pDir->cbBuf + || pDir->offBuf + pInfo->FileNameLength + MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION > pDir->cbBuf) + { + fSkipEntry = 1; + pDir->fHaveData = 0; + continue; + } + + pEntry->d_type = DT_UNKNOWN; + pEntry->d_reclen = 0; + pEntry->d_namlen = 0; + if (birdDirCopyNameToEntryW(pInfo->FileName, pInfo->FileNameLength, pEntry) != 0) + fSkipEntry = 1; + birdStatFillFromFileIdFullDirInfo(&pEntry->d_stat, pInfo); + pEntry->d_stat.st_dev = pDir->uDev; + switch (pEntry->d_stat.st_mode & S_IFMT) + { + case S_IFREG: pEntry->d_type = DT_REG; break; + case S_IFDIR: pEntry->d_type = DT_DIR; break; + case S_IFLNK: pEntry->d_type = DT_LNK; break; + case S_IFIFO: pEntry->d_type = DT_FIFO; break; + case S_IFCHR: pEntry->d_type = DT_CHR; break; + default: +#ifndef NDEBUG + __debugbreak(); +#endif + pEntry->d_type = DT_UNKNOWN; + break; + } + + if (pEntry->d_stat.st_ismountpoint != 1) + { /* likely. */ } + else + birdDirUpdateMountPointInfo(pDir, pInfo, &pEntry->d_stat); + + cbMinCur = MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION + pInfo->FileNameLength; + offNext = pInfo->NextEntryOffset; + break; + } + + default: + return birdSetErrnoToBadFileNo(); + } + + /* + * Advance. + */ + if ( offNext >= cbMinCur + && offNext < pDir->cbBuf) + pDir->offBuf += offNext; + else + { + pDir->fHaveData = 0; + pDir->offBuf = pDir->cbBuf; + } + pDir->offPos++; + } while (fSkipEntry); + + + /* + * Successful return. + */ + *ppResult = pEntry; + return 0; +} + +/** + * Implements readdir(). + */ +BirdDirEntryW_T *birdDirReadW(BirdDir_T *pDir) +{ + BirdDirEntryW_T *pRet = NULL; + birdDirReadReentrantW(pDir, &pDir->u.DirEntryW, &pRet); + return pRet; +} + + +/** + * Implements telldir(). + */ +long birdDirTell(BirdDir_T *pDir) +{ + if (!pDir || pDir->uMagic != BIRD_DIR_MAGIC) + return birdSetErrnoToBadFileNo(); + return pDir->offPos; +} + + +void birdDirSeek(BirdDir_T *pDir, long offDir); + + +/** + * Implements closedir(). + */ +int birdDirClose(BirdDir_T *pDir) +{ + if (!pDir || pDir->uMagic != BIRD_DIR_MAGIC) + return birdSetErrnoToBadFileNo(); + + pDir->uMagic++; + if (pDir->fFlags & BIRDDIR_F_CLOSE_HANDLE) + birdCloseFile((HANDLE)pDir->pvHandle); + pDir->pvHandle = (void *)INVALID_HANDLE_VALUE; + birdMemFree(pDir->pabBuf); + pDir->pabBuf = NULL; + if (!(pDir->fFlags & BIRDDIR_F_STATIC_ALLOC)) + birdMemFree(pDir); + + return 0; +} |