diff options
Diffstat (limited to 'src/VBox/Runtime/common/vfs/vfsstddir.cpp')
-rw-r--r-- | src/VBox/Runtime/common/vfs/vfsstddir.cpp | 867 |
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); + |