/* $Id: vfsstddir.cpp 99730 2023-05-10 16:14:23Z vboxsync $ */ /** @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 . * * 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 #include #include #include #include #include #include #include #include #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; } #if 0 RTPathSetMode return RTFileSetMode(pThis->hDir, fMode); #else RT_NOREF(fMode); return VERR_NOT_IMPLEMENTED; #endif } /** * @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);