summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/r3/win/direnum-win.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/r3/win/direnum-win.cpp')
-rw-r--r--src/VBox/Runtime/r3/win/direnum-win.cpp404
1 files changed, 404 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r3/win/direnum-win.cpp b/src/VBox/Runtime/r3/win/direnum-win.cpp
new file mode 100644
index 00000000..80d36864
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/direnum-win.cpp
@@ -0,0 +1,404 @@
+/* $Id: direnum-win.cpp $ */
+/** @file
+ * IPRT - Directory Enumeration, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+#include <iprt/win/windows.h>
+
+#include <iprt/dir.h>
+#include <iprt/path.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/log.h>
+#include "internal/fs.h"
+#include "internal/dir.h"
+
+
+size_t rtDirNativeGetStructSize(const char *pszPath)
+{
+ NOREF(pszPath);
+ return sizeof(RTDIRINTERNAL);
+}
+
+
+int rtDirNativeOpen(PRTDIRINTERNAL pDir, uintptr_t hRelativeDir, void *pvNativeRelative))
+{
+ RT_NOREF(hRelativeDir, pvNativeRelative);
+
+ /*
+ * Setup the search expression.
+ *
+ * pszPathBuf is pointing to the return 4K return buffer for the RTPathReal()
+ * call in rtDirOpenCommon(), so all we gota do is check that we don't overflow
+ * it when adding the wildcard expression.
+ */
+/** @todo the pszPathBuf argument was removed in order to support paths longer than RTPATH_MAX. Rewrite this code. */
+ size_t cbExpr;
+ const char *pszExpr;
+ if (pDir->enmFilter == RTDIRFILTER_WINNT)
+ {
+ pszExpr = pDir->pszFilter;
+ cbExpr = pDir->cchFilter + 1;
+ }
+ else
+ {
+ pszExpr = "*";
+ cbExpr = sizeof("*");
+ }
+ if (pDir->cchPath + cbExpr > RTPATH_MAX)
+ return VERR_FILENAME_TOO_LONG;
+ memcpy(pszPathBuf + pDir->cchPath, pszExpr, cbExpr);
+
+
+ /*
+ * Attempt opening the search.
+ */
+ PRTUTF16 pwszName;
+ int rc = RTPathWinFromUtf8(pwszPathBuf, &pwszName, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ pDir->hDir = FindFirstFileW((LPCWSTR)pwszName, &pDir->Data);
+ if (pDir->hDir != INVALID_HANDLE_VALUE)
+ pDir->fDataUnread = true;
+ else
+ {
+ DWORD dwErr = GetLastError();
+ /* Theoretical case of an empty directory or more normal case of no matches. */
+ if ( dwErr == ERROR_FILE_NOT_FOUND
+ || dwErr == ERROR_NO_MORE_FILES /* ???*/)
+ pDir->fDataUnread = false;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ RTPathWinFree(pwszName);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTDirClose(PRTDIRINTERNAL pDir)
+{
+ /*
+ * Validate input.
+ */
+ if (!pDir)
+ return VERR_INVALID_PARAMETER;
+ if (pDir->u32Magic != RTDIR_MAGIC)
+ {
+ AssertMsgFailed(("Invalid pDir=%p\n", pDir));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Close the handle.
+ */
+ pDir->u32Magic++;
+ if (pDir->hDir != INVALID_HANDLE_VALUE)
+ {
+ BOOL fRc = FindClose(pDir->hDir);
+ Assert(fRc);
+ pDir->hDir = INVALID_HANDLE_VALUE;
+ }
+ RTStrFree(pDir->pszName);
+ pDir->pszName = NULL;
+ RTMemFree(pDir);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTDirRead(RTDIR hDir, PRTDIRENTRY pDirEntry, size_t *pcbDirEntry)
+{
+ PPRTDIRINTERNAL pDir = hDir;
+
+ /*
+ * Validate input.
+ */
+ if (!pDir || pDir->u32Magic != RTDIR_MAGIC)
+ {
+ AssertMsgFailed(("Invalid pDir=%p\n", pDir));
+ return VERR_INVALID_PARAMETER;
+ }
+ if (!pDirEntry)
+ {
+ AssertMsgFailed(("Invalid pDirEntry=%p\n", pDirEntry));
+ return VERR_INVALID_PARAMETER;
+ }
+ size_t cbDirEntry = sizeof(*pDirEntry);
+ if (pcbDirEntry)
+ {
+ cbDirEntry = *pcbDirEntry;
+ if (cbDirEntry < RT_UOFFSETOF(RTDIRENTRY, szName[2]))
+ {
+ AssertMsgFailed(("Invalid *pcbDirEntry=%zu (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRY, szName[2])));
+ return VERR_INVALID_PARAMETER;
+ }
+ }
+
+ /*
+ * Fetch data?
+ */
+ if (!pDir->fDataUnread)
+ {
+ RTStrFree(pDir->pszName);
+ pDir->pszName = NULL;
+
+ BOOL fRc = FindNextFileW(pDir->hDir, &pDir->Data);
+ if (!fRc)
+ {
+ int iErr = GetLastError();
+ if (pDir->hDir == INVALID_HANDLE_VALUE || iErr == ERROR_NO_MORE_FILES)
+ return VERR_NO_MORE_FILES;
+ return RTErrConvertFromWin32(iErr);
+ }
+ }
+
+ /*
+ * Convert the filename to UTF-8.
+ */
+ if (!pDir->pszName)
+ {
+ int rc = RTUtf16ToUtf8((PCRTUTF16)pDir->Data.cFileName, &pDir->pszName);
+ if (RT_FAILURE(rc))
+ {
+ pDir->pszName = NULL;
+ return rc;
+ }
+ pDir->cchName = strlen(pDir->pszName);
+ }
+
+ /*
+ * Check if we've got enough space to return the data.
+ */
+ const char *pszName = pDir->pszName;
+ const size_t cchName = pDir->cchName;
+ const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRY, szName[1]) + cchName;
+ if (pcbDirEntry)
+ *pcbDirEntry = cbRequired;
+ if (cbRequired > cbDirEntry)
+ return VERR_BUFFER_OVERFLOW;
+
+ /*
+ * Setup the returned data.
+ */
+ pDir->fDataUnread = false;
+ pDirEntry->INodeId = 0; /** @todo we can use the fileid here if we must (see GetFileInformationByHandle). */
+ pDirEntry->enmType = pDir->Data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
+ ? RTDIRENTRYTYPE_DIRECTORY : RTDIRENTRYTYPE_FILE;
+ pDirEntry->cbName = (uint16_t)cchName;
+ Assert(pDirEntry->cbName == cchName);
+ memcpy(pDirEntry->szName, pszName, cchName + 1);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
+{
+ PPRTDIRINTERNAL pDir = hDir;
+ /** @todo Symlinks: Find[First|Next]FileW will return info about
+ the link, so RTPATH_F_FOLLOW_LINK is not handled correctly. */
+ /*
+ * Validate input.
+ */
+ if (!pDir || pDir->u32Magic != RTDIR_MAGIC)
+ {
+ AssertMsgFailed(("Invalid pDir=%p\n", pDir));
+ return VERR_INVALID_PARAMETER;
+ }
+ if (!pDirEntry)
+ {
+ AssertMsgFailed(("Invalid pDirEntry=%p\n", pDirEntry));
+ return VERR_INVALID_PARAMETER;
+ }
+ if ( enmAdditionalAttribs < RTFSOBJATTRADD_NOTHING
+ || enmAdditionalAttribs > RTFSOBJATTRADD_LAST)
+ {
+ AssertMsgFailed(("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs));
+ return VERR_INVALID_PARAMETER;
+ }
+ AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+ size_t cbDirEntry = sizeof(*pDirEntry);
+ if (pcbDirEntry)
+ {
+ cbDirEntry = *pcbDirEntry;
+ if (cbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName[2]))
+ {
+ AssertMsgFailed(("Invalid *pcbDirEntry=%zu (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRYEX, szName[2])));
+ return VERR_INVALID_PARAMETER;
+ }
+ }
+
+ /*
+ * Fetch data?
+ */
+ if (!pDir->fDataUnread)
+ {
+ RTStrFree(pDir->pszName);
+ pDir->pszName = NULL;
+
+ BOOL fRc = FindNextFileW(pDir->hDir, &pDir->Data);
+ if (!fRc)
+ {
+ int iErr = GetLastError();
+ if (pDir->hDir == INVALID_HANDLE_VALUE || iErr == ERROR_NO_MORE_FILES)
+ return VERR_NO_MORE_FILES;
+ return RTErrConvertFromWin32(iErr);
+ }
+ }
+
+ /*
+ * Convert the filename to UTF-8.
+ */
+ if (!pDir->pszName)
+ {
+ int rc = RTUtf16ToUtf8((PCRTUTF16)pDir->Data.cFileName, &pDir->pszName);
+ if (RT_FAILURE(rc))
+ {
+ pDir->pszName = NULL;
+ return rc;
+ }
+ pDir->cchName = strlen(pDir->pszName);
+ }
+
+ /*
+ * Check if we've got enough space to return the data.
+ */
+ const char *pszName = pDir->pszName;
+ const size_t cchName = pDir->cchName;
+ const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRYEX, szName[1]) + cchName;
+ if (pcbDirEntry)
+ *pcbDirEntry = cbRequired;
+ if (cbRequired > cbDirEntry)
+ return VERR_BUFFER_OVERFLOW;
+
+ /*
+ * Setup the returned data.
+ */
+ pDir->fDataUnread = false;
+ pDirEntry->cbName = (uint16_t)cchName;
+ Assert(pDirEntry->cbName == cchName);
+ memcpy(pDirEntry->szName, pszName, cchName + 1);
+ if (pDir->Data.cAlternateFileName[0])
+ {
+ /* copy and calc length */
+ PCRTUTF16 pwszSrc = (PCRTUTF16)pDir->Data.cAlternateFileName;
+ PRTUTF16 pwszDst = pDirEntry->wszShortName;
+ uint32_t off = 0;
+ while (off < RT_ELEMENTS(pDirEntry->wszShortName) - 1U && pwszSrc[off])
+ {
+ pwszDst[off] = pwszSrc[off];
+ off++;
+ }
+ pDirEntry->cwcShortName = (uint16_t)off;
+
+ /* zero the rest */
+ do
+ pwszDst[off++] = '\0';
+ while (off < RT_ELEMENTS(pDirEntry->wszShortName));
+ }
+ else
+ {
+ memset(pDirEntry->wszShortName, 0, sizeof(pDirEntry->wszShortName));
+ pDirEntry->cwcShortName = 0;
+ }
+
+ pDirEntry->Info.cbObject = ((uint64_t)pDir->Data.nFileSizeHigh << 32)
+ | (uint64_t)pDir->Data.nFileSizeLow;
+ pDirEntry->Info.cbAllocated = pDirEntry->Info.cbObject;
+
+ Assert(sizeof(uint64_t) == sizeof(pDir->Data.ftCreationTime));
+ RTTimeSpecSetNtTime(&pDirEntry->Info.BirthTime, *(uint64_t *)&pDir->Data.ftCreationTime);
+ RTTimeSpecSetNtTime(&pDirEntry->Info.AccessTime, *(uint64_t *)&pDir->Data.ftLastAccessTime);
+ RTTimeSpecSetNtTime(&pDirEntry->Info.ModificationTime, *(uint64_t *)&pDir->Data.ftLastWriteTime);
+ pDirEntry->Info.ChangeTime = pDirEntry->Info.ModificationTime;
+
+ pDirEntry->Info.Attr.fMode = rtFsModeFromDos((pDir->Data.dwFileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
+ pszName, cchName, pDir->Data.dwReserved0, 0);
+
+ /*
+ * Requested attributes (we cannot provide anything actually).
+ */
+ switch (enmAdditionalAttribs)
+ {
+ case RTFSOBJATTRADD_EASIZE:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
+ pDirEntry->Info.Attr.u.EASize.cb = 0;
+ break;
+
+ case RTFSOBJATTRADD_UNIX:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
+ pDirEntry->Info.Attr.u.Unix.uid = ~0U;
+ pDirEntry->Info.Attr.u.Unix.gid = ~0U;
+ pDirEntry->Info.Attr.u.Unix.cHardlinks = 1;
+ pDirEntry->Info.Attr.u.Unix.INodeIdDevice = 0; /** @todo Use the volume serial number (see GetFileInformationByHandle). */
+ pDirEntry->Info.Attr.u.Unix.INodeId = 0; /** @todo Use the fileid (see GetFileInformationByHandle). */
+ pDirEntry->Info.Attr.u.Unix.fFlags = 0;
+ pDirEntry->Info.Attr.u.Unix.GenerationId = 0;
+ pDirEntry->Info.Attr.u.Unix.Device = 0;
+ break;
+
+ case RTFSOBJATTRADD_NOTHING:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
+ break;
+
+ case RTFSOBJATTRADD_UNIX_OWNER:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
+ pDirEntry->Info.Attr.u.UnixOwner.uid = ~0U;
+ pDirEntry->Info.Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
+ break;
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
+ pDirEntry->Info.Attr.u.UnixGroup.gid = ~0U;
+ pDirEntry->Info.Attr.u.UnixGroup.szName[0] = '\0';
+ break;
+
+ default:
+ AssertMsgFailed(("Impossible!\n"));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ return VINF_SUCCESS;
+}
+