summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/vfs/vfsstddir.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/vfs/vfsstddir.cpp')
-rw-r--r--src/VBox/Runtime/common/vfs/vfsstddir.cpp867
1 files changed, 867 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/vfs/vfsstddir.cpp b/src/VBox/Runtime/common/vfs/vfsstddir.cpp
new file mode 100644
index 00000000..38ebb8c3
--- /dev/null
+++ b/src/VBox/Runtime/common/vfs/vfsstddir.cpp
@@ -0,0 +1,867 @@
+/* $Id: vfsstddir.cpp $ */
+/** @file
+ * IPRT - Virtual File System, Standard Directory Implementation.
+ */
+
+/*
+ * Copyright (C) 2010-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_VFS
+#include <iprt/vfs.h>
+#include <iprt/vfslowlevel.h>
+
+#include <iprt/assert.h>
+#include <iprt/dir.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/log.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+
+#define RTDIR_AGNOSTIC
+#include "internal/dir.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Private data of a standard directory.
+ */
+typedef struct RTVFSSTDDIR
+{
+ /** The directory handle. */
+ RTDIR hDir;
+ /** Whether to leave the handle open when the VFS handle is closed. */
+ bool fLeaveOpen;
+ /** Open flags, RTDIR_F_XXX. */
+ uint32_t fFlags;
+ /** Handle to the director so we can make sure it sticks around for symbolic
+ * link objects. */
+ RTVFSDIR hSelf;
+} RTVFSSTDDIR;
+/** Pointer to the private data of a standard directory. */
+typedef RTVFSSTDDIR *PRTVFSSTDDIR;
+
+
+/**
+ * Private data of a standard symbolic link.
+ */
+typedef struct RTVFSSTDSYMLINK
+{
+ /** Pointer to the VFS directory where the symbolic link lives . */
+ PRTVFSSTDDIR pDir;
+ /** The symbolic link name. */
+ RT_FLEXIBLE_ARRAY_EXTENSION
+ char szSymlink[RT_FLEXIBLE_ARRAY];
+} RTVFSSTDSYMLINK;
+/** Pointer to the private data of a standard symbolic link. */
+typedef RTVFSSTDSYMLINK *PRTVFSSTDSYMLINK;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static DECLCALLBACK(int) rtVfsStdDir_OpenDir(void *pvThis, const char *pszSubDir, uint32_t fFlags, PRTVFSDIR phVfsDir);
+static DECLCALLBACK(int) rtVfsStdDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink);
+static DECLCALLBACK(int) rtVfsStdDir_QueryEntryInfo(void *pvThis, const char *pszEntry,
+ PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr);
+static int rtVfsDirFromRTDir(RTDIR hDir, uint32_t fFlags, bool fLeaveOpen, PRTVFSDIR phVfsDir);
+
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtVfsStdSym_Close(void *pvThis)
+{
+ PRTVFSSTDSYMLINK pThis = (PRTVFSSTDSYMLINK)pvThis;
+ RTVfsDirRelease(pThis->pDir->hSelf);
+ pThis->pDir = NULL;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtVfsStdSym_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTVFSSTDSYMLINK pThis = (PRTVFSSTDSYMLINK)pvThis;
+ return rtVfsStdDir_QueryEntryInfo(pThis->pDir, pThis->szSymlink, pObjInfo, enmAddAttr);
+}
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
+ */
+static DECLCALLBACK(int) rtVfsStdSym_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
+{
+ NOREF(pvThis); NOREF(fMode); NOREF(fMask);
+ return VERR_ACCESS_DENIED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
+ */
+static DECLCALLBACK(int) rtVfsStdSym_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ NOREF(pvThis); NOREF(pAccessTime); NOREF(pModificationTime); NOREF(pChangeTime); NOREF(pBirthTime);
+ return VERR_ACCESS_DENIED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
+ */
+static DECLCALLBACK(int) rtVfsStdSym_SetOwner(void *pvThis, RTUID uid, RTGID gid)
+{
+ NOREF(pvThis); NOREF(uid); NOREF(gid);
+ return VERR_ACCESS_DENIED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSSYMLINKOPS,pfnRead}
+ */
+static DECLCALLBACK(int) rtVfsStdSym_Read(void *pvThis, char *pszTarget, size_t cbTarget)
+{
+ PRTVFSSTDSYMLINK pThis = (PRTVFSSTDSYMLINK)pvThis;
+ return RTDirRelSymlinkRead(pThis->pDir->hDir, pThis->szSymlink, pszTarget, cbTarget, 0 /*fRead*/);
+}
+
+
+/**
+ * Symbolic operations for standard directory.
+ */
+static const RTVFSSYMLINKOPS g_rtVfsStdSymOps =
+{
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_SYMLINK,
+ "StdSymlink",
+ rtVfsStdSym_Close,
+ rtVfsStdSym_QueryInfo,
+ NULL,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSSYMLINKOPS_VERSION,
+ 0,
+ { /* ObjSet */
+ RTVFSOBJSETOPS_VERSION,
+ RT_UOFFSETOF(RTVFSSYMLINKOPS, ObjSet) - RT_UOFFSETOF(RTVFSSYMLINKOPS, Obj),
+ rtVfsStdSym_SetMode,
+ rtVfsStdSym_SetTimes,
+ rtVfsStdSym_SetOwner,
+ RTVFSOBJSETOPS_VERSION
+ },
+ rtVfsStdSym_Read,
+ RTVFSSYMLINKOPS_VERSION
+};
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtVfsStdDir_Close(void *pvThis)
+{
+ PRTVFSSTDDIR pThis = (PRTVFSSTDDIR)pvThis;
+
+ int rc;
+ if (!pThis->fLeaveOpen)
+ rc = RTDirClose(pThis->hDir);
+ else
+ rc = VINF_SUCCESS;
+ pThis->hDir = NULL;
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtVfsStdDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTVFSSTDDIR pThis = (PRTVFSSTDDIR)pvThis;
+ return RTDirQueryInfo(pThis->hDir, pObjInfo, enmAddAttr);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
+ */
+static DECLCALLBACK(int) rtVfsStdDir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
+{
+ PRTVFSSTDDIR pThis = (PRTVFSSTDDIR)pvThis;
+ if (fMask != ~RTFS_TYPE_MASK)
+ {
+ RTFSOBJINFO ObjInfo;
+ int rc = RTDirQueryInfo(pThis->hDir, &ObjInfo, RTFSOBJATTRADD_NOTHING);
+ if (RT_FAILURE(rc))
+ return rc;
+ fMode |= ~fMask & ObjInfo.Attr.fMode;
+ }
+ //RTPathSetMode
+ //return RTFileSetMode(pThis->hDir, fMode);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
+ */
+static DECLCALLBACK(int) rtVfsStdDir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ PRTVFSSTDDIR pThis = (PRTVFSSTDDIR)pvThis;
+ return RTDirSetTimes(pThis->hDir, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
+ */
+static DECLCALLBACK(int) rtVfsStdDir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
+{
+ PRTVFSSTDDIR pThis = (PRTVFSSTDDIR)pvThis;
+ return RTDirRelPathSetOwner(pThis->hDir, ".", uid, gid, RTPATH_F_FOLLOW_LINK);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnOpen}
+ */
+static DECLCALLBACK(int) rtVfsStdDir_Open(void *pvThis, const char *pszEntry, uint64_t fFileOpen,
+ uint32_t fObjFlags, PRTVFSOBJ phVfsObj)
+{
+ PRTVFSSTDDIR pThis = (PRTVFSSTDDIR)pvThis;
+
+ /*
+ * This is subject to race conditions, but we haven't too much of a choice
+ * without going platform specific here (we'll do that eventually).
+ */
+ RTFSOBJINFO ObjInfo;
+ int rc = RTDirRelPathQueryInfo(pThis->hDir, pszEntry, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
+ if (RT_SUCCESS(rc))
+ {
+ switch (ObjInfo.Attr.fMode & RTFS_TYPE_MASK)
+ {
+ case RTFS_TYPE_DIRECTORY:
+ if (fObjFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
+ {
+ if ( (fFileOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
+ || (fFileOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE
+ || (fFileOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
+ {
+ RTDIR hSubDir;
+ rc = RTDirRelDirOpenFiltered(pThis->hDir, pszEntry, RTDIRFILTER_NONE, 0 /*fFlags*/, &hSubDir);
+ if (RT_SUCCESS(rc))
+ {
+ RTVFSDIR hVfsDir;
+ rc = rtVfsDirFromRTDir(hSubDir, 0 /** @todo subdir open/inherit flags... */, false, &hVfsDir);
+ if (RT_SUCCESS(rc))
+ {
+ *phVfsObj = RTVfsObjFromDir(hVfsDir);
+ RTVfsDirRelease(hVfsDir);
+ AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
+ }
+ else
+ RTDirClose(hSubDir);
+ }
+ }
+ else
+ rc = VERR_ALREADY_EXISTS;
+ }
+ else
+ rc = VERR_IS_A_DIRECTORY;
+ break;
+
+ case RTFS_TYPE_FILE:
+ case RTFS_TYPE_DEV_BLOCK:
+ case RTFS_TYPE_DEV_CHAR:
+ case RTFS_TYPE_FIFO:
+ case RTFS_TYPE_SOCKET:
+ switch (ObjInfo.Attr.fMode & RTFS_TYPE_MASK)
+ {
+ case RTFS_TYPE_FILE:
+ rc = fObjFlags & RTVFSOBJ_F_OPEN_FILE ? VINF_SUCCESS : VERR_IS_A_FILE;
+ break;
+ case RTFS_TYPE_DEV_BLOCK:
+ rc = fObjFlags & RTVFSOBJ_F_OPEN_DEV_BLOCK ? VINF_SUCCESS : VERR_IS_A_BLOCK_DEVICE;
+ break;
+ case RTFS_TYPE_DEV_CHAR:
+ rc = fObjFlags & RTVFSOBJ_F_OPEN_DEV_CHAR ? VINF_SUCCESS : VERR_IS_A_CHAR_DEVICE;
+ break;
+ /** @todo These two types should not result in files, but pure I/O streams.
+ * possibly char device too. */
+ case RTFS_TYPE_FIFO:
+ rc = fObjFlags & RTVFSOBJ_F_OPEN_FIFO ? VINF_SUCCESS : VERR_IS_A_FIFO;
+ break;
+ case RTFS_TYPE_SOCKET:
+ rc = fObjFlags & RTVFSOBJ_F_OPEN_SOCKET ? VINF_SUCCESS : VERR_IS_A_SOCKET;
+ break;
+ default:
+ rc = VERR_INVALID_FLAGS;
+ break;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ if ( (fFileOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
+ || (fFileOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE
+ || (fFileOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
+ {
+ RTFILE hFile;
+ rc = RTDirRelFileOpen(pThis->hDir, pszEntry, fFileOpen, &hFile);
+ if (RT_SUCCESS(rc))
+ {
+ RTVFSFILE hVfsFile;
+ rc = RTVfsFileFromRTFile(hFile, fFileOpen, false /*fLeaveOpen*/, &hVfsFile);
+ if (RT_SUCCESS(rc))
+ {
+ *phVfsObj = RTVfsObjFromFile(hVfsFile);
+ RTVfsFileRelease(hVfsFile);
+ AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
+ }
+ else
+ RTFileClose(hFile);
+ }
+ }
+ else
+ rc = VERR_ALREADY_EXISTS;
+ }
+ break;
+
+ case RTFS_TYPE_SYMLINK:
+ if (fObjFlags & RTVFSOBJ_F_OPEN_SYMLINK)
+ {
+ uint32_t cRefs = RTVfsDirRetain(pThis->hSelf);
+ if (cRefs != UINT32_MAX)
+ {
+ RTVFSSYMLINK hVfsSymlink;
+ PRTVFSSTDSYMLINK pNewSymlink;
+ size_t cchSymlink = strlen(pszEntry);
+ rc = RTVfsNewSymlink(&g_rtVfsStdSymOps, RT_UOFFSETOF_DYN(RTVFSSTDSYMLINK, szSymlink[cchSymlink + 1]),
+ NIL_RTVFS, NIL_RTVFSLOCK, &hVfsSymlink, (void **)&pNewSymlink);
+ if (RT_SUCCESS(rc))
+ {
+ memcpy(pNewSymlink->szSymlink, pszEntry, cchSymlink);
+ pNewSymlink->szSymlink[cchSymlink] = '\0';
+ pNewSymlink->pDir = pThis;
+
+ *phVfsObj = RTVfsObjFromSymlink(hVfsSymlink);
+ RTVfsSymlinkRelease(hVfsSymlink);
+ AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
+ }
+ else
+ RTVfsDirRelease(pThis->hSelf);
+ }
+ else
+ rc = VERR_INTERNAL_ERROR_2;
+ }
+ else
+ rc = VERR_IS_A_SYMLINK;
+ break;
+
+ default:
+ break;
+ }
+ }
+ else if ( rc == VERR_FILE_NOT_FOUND
+ || rc == VERR_PATH_NOT_FOUND)
+ {
+ /*
+ * Consider file or directory creation.
+ */
+ if ( ( (fFileOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE
+ || (fFileOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE
+ || (fFileOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
+ && (fObjFlags & RTVFSOBJ_F_CREATE_MASK) != RTVFSOBJ_F_CREATE_NOTHING)
+ {
+
+ if ((fObjFlags & RTVFSOBJ_F_CREATE_MASK) == RTVFSOBJ_F_CREATE_FILE)
+ {
+ RTFILE hFile;
+ rc = RTDirRelFileOpen(pThis->hDir, pszEntry, fFileOpen, &hFile);
+ if (RT_SUCCESS(rc))
+ {
+ RTVFSFILE hVfsFile;
+ rc = RTVfsFileFromRTFile(hFile, fFileOpen, false /*fLeaveOpen*/, &hVfsFile);
+ if (RT_SUCCESS(rc))
+ {
+ *phVfsObj = RTVfsObjFromFile(hVfsFile);
+ RTVfsFileRelease(hVfsFile);
+ AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
+ }
+ else
+ RTFileClose(hFile);
+ }
+ }
+ else if ((fObjFlags & RTVFSOBJ_F_CREATE_MASK) == RTVFSOBJ_F_CREATE_DIRECTORY)
+ {
+ RTDIR hSubDir;
+ rc = RTDirRelDirCreate(pThis->hDir, pszEntry, (fFileOpen & RTFILE_O_CREATE_MODE_MASK) >> RTFILE_O_CREATE_MODE_SHIFT,
+ 0 /* fFlags */, &hSubDir);
+ if (RT_SUCCESS(rc))
+ {
+ RTVFSDIR hVfsDir;
+ rc = rtVfsDirFromRTDir(hSubDir, 0 /** @todo subdir open/inherit flags... */, false, &hVfsDir);
+ if (RT_SUCCESS(rc))
+ {
+ *phVfsObj = RTVfsObjFromDir(hVfsDir);
+ RTVfsDirRelease(hVfsDir);
+ AssertStmt(*phVfsObj == NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
+ }
+ else
+ RTDirClose(hSubDir);
+ }
+ }
+ else
+ rc = VERR_VFS_UNSUPPORTED_CREATE_TYPE;
+ }
+ else
+ rc = VERR_FILE_NOT_FOUND;
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnFollowAbsoluteSymlink}
+ */
+static DECLCALLBACK(int) rtVfsStdDir_FollowAbsoluteSymlink(void *pvThis, const char *pszRoot, PRTVFSDIR phVfsDir)
+{
+ //PRTVFSSTDDIR pThis = (PRTVFSSTDDIR)pvThis;
+ RT_NOREF(pvThis);
+ /** @todo walking restriction. */
+ return RTVfsDirOpenNormal(pszRoot, 0 /*fFlags*/, phVfsDir);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnOpenFile}
+ */
+static DECLCALLBACK(int) rtVfsStdDir_OpenFile(void *pvThis, const char *pszFilename, uint64_t fOpen, PRTVFSFILE phVfsFile)
+{
+ PRTVFSSTDDIR pThis = (PRTVFSSTDDIR)pvThis;
+ RTFILE hFile;
+ int rc = RTDirRelFileOpen(pThis->hDir, pszFilename, fOpen, &hFile);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, phVfsFile);
+ if (RT_FAILURE(rc))
+ RTFileClose(hFile);
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnOpenDir}
+ */
+static DECLCALLBACK(int) rtVfsStdDir_OpenDir(void *pvThis, const char *pszSubDir, uint32_t fFlags, PRTVFSDIR phVfsDir)
+{
+ PRTVFSSTDDIR pThis = (PRTVFSSTDDIR)pvThis;
+ /** @todo subdir open flags */
+ RTDIR hSubDir;
+ int rc = RTDirRelDirOpenFiltered(pThis->hDir, pszSubDir, RTDIRFILTER_NONE, fFlags, &hSubDir);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtVfsDirFromRTDir(hSubDir, fFlags, false, phVfsDir);
+ if (RT_FAILURE(rc))
+ RTDirClose(hSubDir);
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
+ */
+static DECLCALLBACK(int) rtVfsStdDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
+{
+ PRTVFSSTDDIR pThis = (PRTVFSSTDDIR)pvThis;
+ int rc;
+ if (!phVfsDir)
+ rc = RTDirRelDirCreate(pThis->hDir, pszSubDir, fMode, 0 /* fFlags */, NULL);
+ else
+ {
+ RTDIR hSubDir;
+ rc = RTDirRelDirCreate(pThis->hDir, pszSubDir, fMode, 0 /* fFlags */, &hSubDir);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo subdir open flags... */
+ rc = rtVfsDirFromRTDir(hSubDir, 0, false, phVfsDir);
+ if (RT_FAILURE(rc))
+ RTDirClose(hSubDir);
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
+ */
+static DECLCALLBACK(int) rtVfsStdDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
+{
+ RTFSOBJINFO ObjInfo;
+ int rc = rtVfsStdDir_QueryEntryInfo(pvThis, pszSymlink, &ObjInfo, RTFSOBJATTRADD_NOTHING);
+ if (RT_SUCCESS(rc))
+ {
+ if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
+ {
+ PRTVFSSTDDIR pThis = (PRTVFSSTDDIR)pvThis;
+ uint32_t cRefs = RTVfsDirRetain(pThis->hSelf);
+ if (cRefs != UINT32_MAX)
+ {
+ PRTVFSSTDSYMLINK pNewSymlink;
+ size_t cchSymlink = strlen(pszSymlink);
+ rc = RTVfsNewSymlink(&g_rtVfsStdSymOps, RT_UOFFSETOF_DYN(RTVFSSTDSYMLINK, szSymlink[cchSymlink + 1]),
+ NIL_RTVFS, NIL_RTVFSLOCK, phVfsSymlink, (void **)&pNewSymlink);
+ if (RT_SUCCESS(rc))
+ {
+ memcpy(pNewSymlink->szSymlink, pszSymlink, cchSymlink);
+ pNewSymlink->szSymlink[cchSymlink] = '\0';
+ pNewSymlink->pDir = pThis;
+ return VINF_SUCCESS;
+ }
+
+ RTVfsDirRelease(pThis->hSelf);
+ }
+ else
+ rc = VERR_INTERNAL_ERROR_2;
+ }
+ else
+ rc = VERR_NOT_SYMLINK;
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
+ */
+static DECLCALLBACK(int) rtVfsStdDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
+ RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
+{
+ PRTVFSSTDDIR pThis = (PRTVFSSTDDIR)pvThis;
+ int rc = RTDirRelSymlinkCreate(pThis->hDir, pszSymlink, pszTarget, enmType, 0 /*fCreate*/);
+ if (RT_SUCCESS(rc))
+ {
+ if (!phVfsSymlink)
+ return VINF_SUCCESS;
+ return rtVfsStdDir_OpenSymlink(pThis, pszSymlink, phVfsSymlink);
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnQueryEntryInfo}
+ */
+static DECLCALLBACK(int) rtVfsStdDir_QueryEntryInfo(void *pvThis, const char *pszEntry,
+ PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTVFSSTDDIR pThis = (PRTVFSSTDDIR)pvThis;
+ return RTDirRelPathQueryInfo(pThis->hDir, pszEntry, pObjInfo, enmAddAttr, RTPATH_F_ON_LINK);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
+ */
+static DECLCALLBACK(int) rtVfsStdDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
+{
+ PRTVFSSTDDIR pThis = (PRTVFSSTDDIR)pvThis;
+ if (fType != 0)
+ {
+ if (fType == RTFS_TYPE_DIRECTORY)
+ return RTDirRelDirRemove(pThis->hDir, pszEntry);
+
+ RTFSOBJINFO ObjInfo;
+ int rc = rtVfsStdDir_QueryEntryInfo(pThis, pszEntry, &ObjInfo, RTFSOBJATTRADD_NOTHING);
+ if (RT_FAILURE(rc))
+ return rc;
+ if ((fType & RTFS_TYPE_MASK) != (ObjInfo.Attr.fMode & RTFS_TYPE_MASK))
+ return VERR_WRONG_TYPE;
+ }
+ return RTDirRelPathUnlink(pThis->hDir, pszEntry, 0 /*fUnlink*/);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
+ */
+static DECLCALLBACK(int) rtVfsStdDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
+{
+ PRTVFSSTDDIR pThis = (PRTVFSSTDDIR)pvThis;
+ if (fType != 0)
+ {
+ RTFSOBJINFO ObjInfo;
+ int rc = rtVfsStdDir_QueryEntryInfo(pThis, pszEntry, &ObjInfo, RTFSOBJATTRADD_NOTHING);
+ if (RT_FAILURE(rc))
+ return rc;
+ if ((fType & RTFS_TYPE_MASK) != (ObjInfo.Attr.fMode & RTFS_TYPE_MASK))
+ return VERR_WRONG_TYPE;
+ }
+
+ /** @todo RTVFSDIROPS::pfnRenameEntry doesn't really work, this must move to
+ * file system level. */
+ return RTDirRelPathRename(pThis->hDir, pszEntry, pThis->hDir, pszNewName,
+ RTPATHRENAME_FLAGS_NO_SYMLINKS | RTPATHRENAME_FLAGS_NO_REPLACE);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
+ */
+static DECLCALLBACK(int) rtVfsStdDir_RewindDir(void *pvThis)
+{
+ NOREF(pvThis);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
+ */
+static DECLCALLBACK(int) rtVfsStdDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTVFSSTDDIR pThis = (PRTVFSSTDDIR)pvThis;
+ return RTDirReadEx(pThis->hDir, pDirEntry, pcbDirEntry, enmAddAttr, RTPATH_F_ON_LINK);
+}
+
+
+/**
+ * Standard file operations.
+ */
+DECL_HIDDEN_CONST(const RTVFSDIROPS) g_rtVfsStdDirOps =
+{
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_DIR,
+ "StdDir",
+ rtVfsStdDir_Close,
+ rtVfsStdDir_QueryInfo,
+ NULL,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSDIROPS_VERSION,
+ 0,
+ { /* ObjSet */
+ RTVFSOBJSETOPS_VERSION,
+ RT_UOFFSETOF(RTVFSDIROPS, ObjSet) - RT_UOFFSETOF(RTVFSDIROPS, Obj),
+ rtVfsStdDir_SetMode,
+ rtVfsStdDir_SetTimes,
+ rtVfsStdDir_SetOwner,
+ RTVFSOBJSETOPS_VERSION
+ },
+ rtVfsStdDir_Open,
+ rtVfsStdDir_FollowAbsoluteSymlink,
+ rtVfsStdDir_OpenFile,
+ rtVfsStdDir_OpenDir,
+ rtVfsStdDir_CreateDir,
+ rtVfsStdDir_OpenSymlink,
+ rtVfsStdDir_CreateSymlink,
+ rtVfsStdDir_QueryEntryInfo,
+ rtVfsStdDir_UnlinkEntry,
+ rtVfsStdDir_RenameEntry,
+ rtVfsStdDir_RewindDir,
+ rtVfsStdDir_ReadDir,
+ RTVFSDIROPS_VERSION
+};
+
+
+/**
+ * Internal worker for RTVfsDirFromRTDir and RTVfsDirOpenNormal.
+ *
+ * @returns IRPT status code.
+ * @param hDir The IPRT directory handle.
+ * @param fOpen Reserved for future.
+ * @param fLeaveOpen Whether to leave it open or close it.
+ * @param phVfsDir Where to return the handle.
+ */
+static int rtVfsDirFromRTDir(RTDIR hDir, uint32_t fFlags, bool fLeaveOpen, PRTVFSDIR phVfsDir)
+{
+ PRTVFSSTDDIR pThis;
+ RTVFSDIR hVfsDir;
+ int rc = RTVfsNewDir(&g_rtVfsStdDirOps, sizeof(RTVFSSTDDIR), 0 /*fFlags*/, NIL_RTVFS, NIL_RTVFSLOCK,
+ &hVfsDir, (void **)&pThis);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->hDir = hDir;
+ pThis->fLeaveOpen = fLeaveOpen;
+ pThis->fFlags = fFlags;
+ pThis->hSelf = hVfsDir;
+
+ *phVfsDir = hVfsDir;
+ return VINF_SUCCESS;
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTVfsDirFromRTDir(RTDIR hDir, bool fLeaveOpen, PRTVFSDIR phVfsDir)
+{
+ AssertReturn(RTDirIsValid(hDir), VERR_INVALID_HANDLE);
+ return rtVfsDirFromRTDir(hDir, hDir->fFlags, fLeaveOpen, phVfsDir);
+}
+
+
+RTDECL(int) RTVfsDirOpenNormal(const char *pszPath, uint32_t fFlags, PRTVFSDIR phVfsDir)
+{
+ /*
+ * Open the file the normal way and pass it to RTVfsFileFromRTFile.
+ */
+ RTDIR hDir;
+ int rc = RTDirOpenFiltered(&hDir, pszPath, RTDIRFILTER_NONE, fFlags);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create a VFS file handle.
+ */
+ rc = rtVfsDirFromRTDir(hDir, fFlags, false /*fLeaveOpen*/, phVfsDir);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ RTDirClose(hDir);
+ }
+ return rc;
+}
+
+
+RTDECL(bool) RTVfsDirIsStdDir(RTVFSDIR hVfsDir)
+{
+ return RTVfsDirToPrivate(hVfsDir, &g_rtVfsStdDirOps) != NULL;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
+ */
+static DECLCALLBACK(int) rtVfsChainStdDir_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
+ PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
+{
+ RT_NOREF(pProviderReg, pSpec);
+
+ /*
+ * Basic checks.
+ */
+ if (pElement->enmTypeIn != RTVFSOBJTYPE_INVALID)
+ return VERR_VFS_CHAIN_MUST_BE_FIRST_ELEMENT;
+ if (pElement->enmType != RTVFSOBJTYPE_DIR)
+ return VERR_VFS_CHAIN_ONLY_DIR;
+ if (pElement->cArgs < 1)
+ return VERR_VFS_CHAIN_AT_LEAST_ONE_ARG;
+
+ /*
+ * Parse flag arguments if any, storing them in the element.
+ */
+ uint32_t fFlags = 0;
+ for (uint32_t i = 1; i < pElement->cArgs; i++)
+ if (strcmp(pElement->paArgs[i].psz, "deny-ascent") == 0)
+ fFlags |= RTDIR_F_DENY_ASCENT;
+ else if (strcmp(pElement->paArgs[i].psz, "allow-ascent") == 0)
+ fFlags &= ~RTDIR_F_DENY_ASCENT;
+ else
+ {
+ *poffError = pElement->paArgs[i].offSpec;
+ return RTErrInfoSetF(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Unknown flag argument: %s", pElement->paArgs[i].psz);
+ }
+ pElement->uProvider = fFlags;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
+ */
+static DECLCALLBACK(int) rtVfsChainStdDir_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
+ PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
+ PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
+{
+ RT_NOREF(pProviderReg, pSpec, poffError, pErrInfo);
+ AssertReturn(hPrevVfsObj == NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE);
+
+ RTVFSDIR hVfsDir;
+ int rc = RTVfsDirOpenNormal(pElement->paArgs[0].psz, (uint32_t)pElement->uProvider, &hVfsDir);
+ if (RT_SUCCESS(rc))
+ {
+ *phVfsObj = RTVfsObjFromDir(hVfsDir);
+ RTVfsDirRelease(hVfsDir);
+ if (*phVfsObj != NIL_RTVFSOBJ)
+ return VINF_SUCCESS;
+ rc = VERR_VFS_CHAIN_CAST_FAILED;
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
+ */
+static DECLCALLBACK(bool) rtVfsChainStdDir_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
+ PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
+ PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
+{
+ RT_NOREF(pProviderReg, pSpec, pReuseSpec);
+ if (strcmp(pElement->paArgs[0].psz, pReuseElement->paArgs[0].psz) == 0)
+ if (pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider)
+ return true;
+ return false;
+}
+
+
+/** VFS chain element 'file'. */
+static RTVFSCHAINELEMENTREG g_rtVfsChainStdDirReg =
+{
+ /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
+ /* fReserved = */ 0,
+ /* pszName = */ "stddir",
+ /* ListEntry = */ { NULL, NULL },
+ /* pszHelp = */ "Open a real directory. Initial element.\n"
+ "Takes zero or more flag arguments: deny-ascent, allow-ascent",
+ /* pfnValidate = */ rtVfsChainStdDir_Validate,
+ /* pfnInstantiate = */ rtVfsChainStdDir_Instantiate,
+ /* pfnCanReuseElement = */ rtVfsChainStdDir_CanReuseElement,
+ /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
+};
+
+RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainStdDirReg, rtVfsChainStdDirReg);
+