summaryrefslogtreecommitdiffstats
path: root/src/lib/nt/ntdir.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/nt/ntdir.c')
-rw-r--r--src/lib/nt/ntdir.c673
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;
+}