summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-server/VFSExplorerImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/VBox/Main/src-server/VFSExplorerImpl.cpp542
1 files changed, 542 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/VFSExplorerImpl.cpp b/src/VBox/Main/src-server/VFSExplorerImpl.cpp
new file mode 100644
index 00000000..5c028158
--- /dev/null
+++ b/src/VBox/Main/src-server/VFSExplorerImpl.cpp
@@ -0,0 +1,542 @@
+/* $Id: VFSExplorerImpl.cpp $ */
+/** @file
+ * IVFSExplorer COM class implementations.
+ */
+
+/*
+ * Copyright (C) 2009-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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_GROUP LOG_GROUP_MAIN_VFSEXPLORER
+#include <iprt/dir.h>
+#include <iprt/path.h>
+#include <iprt/file.h>
+#include <iprt/cpp/utils.h>
+
+#include <VBox/com/array.h>
+
+#include <VBox/param.h>
+#include <VBox/version.h>
+
+#include "VFSExplorerImpl.h"
+#include "VirtualBoxImpl.h"
+#include "ProgressImpl.h"
+
+#include "AutoCaller.h"
+#include "LoggingNew.h"
+#include "ThreadTask.h"
+
+#include <memory>
+
+struct VFSExplorer::Data
+{
+ struct DirEntry
+ {
+ DirEntry(Utf8Str strName, FsObjType_T fileType, RTFOFF cbSize, uint32_t fMode)
+ : name(strName)
+ , type(fileType)
+ , size(cbSize)
+ , mode(fMode) {}
+
+ Utf8Str name;
+ FsObjType_T type;
+ RTFOFF size;
+ uint32_t mode;
+ };
+
+ VFSType_T storageType;
+ Utf8Str strUsername;
+ Utf8Str strPassword;
+ Utf8Str strHostname;
+ Utf8Str strPath;
+ Utf8Str strBucket;
+ std::list<DirEntry> entryList;
+};
+
+
+VFSExplorer::VFSExplorer()
+ : mVirtualBox(NULL)
+{
+}
+
+VFSExplorer::~VFSExplorer()
+{
+}
+
+
+/**
+ * VFSExplorer COM initializer.
+ * @param aType VFS type.
+ * @param aFilePath File path.
+ * @param aHostname Host name.
+ * @param aUsername User name.
+ * @param aPassword Password.
+ * @param aVirtualBox VirtualBox object.
+ * @return
+ */
+HRESULT VFSExplorer::init(VFSType_T aType, Utf8Str aFilePath, Utf8Str aHostname, Utf8Str aUsername,
+ Utf8Str aPassword, VirtualBox *aVirtualBox)
+{
+ /* Enclose the state transition NotReady->InInit->Ready */
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ /* Weak reference to a VirtualBox object */
+ unconst(mVirtualBox) = aVirtualBox;
+
+ /* initialize data */
+ m = new Data;
+
+ m->storageType = aType;
+ m->strPath = aFilePath;
+ m->strHostname = aHostname;
+ m->strUsername = aUsername;
+ m->strPassword = aPassword;
+
+ if (m->storageType == VFSType_S3)
+ {
+ size_t bpos = aFilePath.find("/", 1);
+ if (bpos != Utf8Str::npos)
+ {
+ m->strBucket = aFilePath.substr(1, bpos - 1); /* The bucket without any slashes */
+ aFilePath = aFilePath.substr(bpos); /* The rest of the file path */
+ }
+ }
+
+ /* Confirm a successful initialization */
+ autoInitSpan.setSucceeded();
+
+ return S_OK;
+}
+
+/**
+ * VFSExplorer COM uninitializer.
+ */
+void VFSExplorer::uninit()
+{
+ delete m;
+ m = NULL;
+}
+
+/**
+ * Public method implementation.
+ * @param aPath Where to store the path.
+ * @return S_OK
+ */
+HRESULT VFSExplorer::getPath(com::Utf8Str &aPath)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ aPath = m->strPath;
+
+ return S_OK;
+}
+
+
+HRESULT VFSExplorer::getType(VFSType_T *aType)
+{
+ if (!aType)
+ return E_POINTER;
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ *aType = m->storageType;
+
+ return S_OK;
+}
+
+class VFSExplorer::TaskVFSExplorer : public ThreadTask
+{
+public:
+ enum TaskType
+ {
+ Update,
+ Delete
+ };
+
+ TaskVFSExplorer(TaskType aTaskType, VFSExplorer *aThat, Progress *aProgress)
+ : m_taskType(aTaskType),
+ m_pVFSExplorer(aThat),
+ m_ptrProgress(aProgress),
+ m_rc(S_OK)
+ {
+ m_strTaskName = "Explorer::Task";
+ }
+ ~TaskVFSExplorer() {}
+
+private:
+ void handler();
+
+#if 0 /* unused */
+ static DECLCALLBACK(int) uploadProgress(unsigned uPercent, void *pvUser);
+#endif
+
+ TaskType m_taskType;
+ VFSExplorer *m_pVFSExplorer;
+
+ ComObjPtr<Progress> m_ptrProgress;
+ HRESULT m_rc;
+
+ /* task data */
+ std::list<Utf8Str> m_lstFilenames;
+
+ friend class VFSExplorer;
+};
+
+/* static */
+void VFSExplorer::TaskVFSExplorer::handler()
+{
+ VFSExplorer *pVFSExplorer = this->m_pVFSExplorer;
+
+ LogFlowFuncEnter();
+ LogFlowFunc(("VFSExplorer %p\n", pVFSExplorer));
+
+ HRESULT hrc = S_OK;
+
+ switch (this->m_taskType)
+ {
+ case TaskVFSExplorer::Update:
+ {
+ if (pVFSExplorer->m->storageType == VFSType_File)
+ hrc = pVFSExplorer->i_updateFS(this);
+ else if (pVFSExplorer->m->storageType == VFSType_S3)
+ hrc = E_NOTIMPL;
+ break;
+ }
+ case TaskVFSExplorer::Delete:
+ {
+ if (pVFSExplorer->m->storageType == VFSType_File)
+ hrc = pVFSExplorer->i_deleteFS(this);
+ else if (pVFSExplorer->m->storageType == VFSType_S3)
+ hrc = E_NOTIMPL;
+ break;
+ }
+ default:
+ AssertMsgFailed(("Invalid task type %u specified!\n", this->m_taskType));
+ break;
+ }
+
+ LogFlowFunc(("hrc=%Rhrc\n", hrc)); NOREF(hrc);
+ LogFlowFuncLeave();
+}
+
+#if 0 /* unused */
+/* static */
+DECLCALLBACK(int) VFSExplorer::TaskVFSExplorer::uploadProgress(unsigned uPercent, void *pvUser)
+{
+ VFSExplorer::TaskVFSExplorer* pTask = *(VFSExplorer::TaskVFSExplorer**)pvUser;
+
+ if (pTask &&
+ !pTask->m_ptrProgress.isNull())
+ {
+ BOOL fCanceled;
+ pTask->m_ptrProgress->COMGETTER(Canceled)(&fCanceled);
+ if (fCanceled)
+ return -1;
+ pTask->m_ptrProgress->SetCurrentOperationProgress(uPercent);
+ }
+ return VINF_SUCCESS;
+}
+#endif
+
+FsObjType_T VFSExplorer::i_iprtToVfsObjType(RTFMODE aType) const
+{
+ switch (aType & RTFS_TYPE_MASK)
+ {
+ case RTFS_TYPE_DIRECTORY: return FsObjType_Directory;
+ case RTFS_TYPE_FILE: return FsObjType_File;
+ case RTFS_TYPE_SYMLINK: return FsObjType_Symlink;
+ case RTFS_TYPE_FIFO: return FsObjType_Fifo;
+ case RTFS_TYPE_DEV_CHAR: return FsObjType_DevChar;
+ case RTFS_TYPE_DEV_BLOCK: return FsObjType_DevBlock;
+ case RTFS_TYPE_SOCKET: return FsObjType_Socket;
+ case RTFS_TYPE_WHITEOUT: return FsObjType_WhiteOut;
+ default: return FsObjType_Unknown;
+ }
+}
+
+HRESULT VFSExplorer::i_updateFS(TaskVFSExplorer *aTask)
+{
+ LogFlowFuncEnter();
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
+
+ AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
+
+ HRESULT hrc = S_OK;
+
+ std::list<VFSExplorer::Data::DirEntry> fileList;
+ RTDIR hDir;
+ int vrc = RTDirOpen(&hDir, m->strPath.c_str());
+ if (RT_SUCCESS(vrc))
+ {
+ try
+ {
+ if (aTask->m_ptrProgress)
+ aTask->m_ptrProgress->SetCurrentOperationProgress(33);
+ RTDIRENTRYEX entry;
+ while (RT_SUCCESS(vrc))
+ {
+ vrc = RTDirReadEx(hDir, &entry, NULL, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
+ if (RT_SUCCESS(vrc))
+ {
+ Utf8Str name(entry.szName);
+ if ( name != "."
+ && name != "..")
+ fileList.push_back(VFSExplorer::Data::DirEntry(name, i_iprtToVfsObjType(entry.Info.Attr.fMode),
+ entry.Info.cbObject,
+ entry.Info.Attr.fMode
+ & (RTFS_UNIX_IRWXU | RTFS_UNIX_IRWXG | RTFS_UNIX_IRWXO)));
+ }
+ }
+ if (aTask->m_ptrProgress)
+ aTask->m_ptrProgress->SetCurrentOperationProgress(66);
+ }
+ catch (HRESULT hrcXcpt)
+ {
+ hrc = hrcXcpt;
+ }
+
+ /* Clean up */
+ RTDirClose(hDir);
+ }
+ else
+ hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr ("Can't open directory '%s' (%Rrc)"), m->strPath.c_str(), vrc);
+
+ if (aTask->m_ptrProgress)
+ aTask->m_ptrProgress->SetCurrentOperationProgress(99);
+
+ /* Assign the result on success (this clears the old list) */
+ if (hrc == S_OK)
+ m->entryList.assign(fileList.begin(), fileList.end());
+
+ aTask->m_rc = hrc;
+
+ if (!aTask->m_ptrProgress.isNull())
+ aTask->m_ptrProgress->i_notifyComplete(hrc);
+
+ LogFlowFunc(("hrc=%Rhrc\n", hrc));
+ LogFlowFuncLeave();
+
+ return S_OK; /** @todo ??? */
+}
+
+HRESULT VFSExplorer::i_deleteFS(TaskVFSExplorer *aTask)
+{
+ LogFlowFuncEnter();
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
+
+ AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
+
+ HRESULT hrc = S_OK;
+
+ float fPercentStep = 100.0f / (float)aTask->m_lstFilenames.size();
+ try
+ {
+ char szPath[RTPATH_MAX];
+ std::list<Utf8Str>::const_iterator it;
+ size_t i = 0;
+ for (it = aTask->m_lstFilenames.begin();
+ it != aTask->m_lstFilenames.end();
+ ++it, ++i)
+ {
+ int vrc = RTPathJoin(szPath, sizeof(szPath), m->strPath.c_str(), (*it).c_str());
+ if (RT_FAILURE(vrc))
+ throw setErrorBoth(E_FAIL, vrc, tr("Internal Error (%Rrc)"), vrc);
+ vrc = RTFileDelete(szPath);
+ if (RT_FAILURE(vrc))
+ throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Can't delete file '%s' (%Rrc)"), szPath, vrc);
+ if (aTask->m_ptrProgress)
+ aTask->m_ptrProgress->SetCurrentOperationProgress((ULONG)(fPercentStep * (float)i));
+ }
+ }
+ catch (HRESULT hrcXcpt)
+ {
+ hrc = hrcXcpt;
+ }
+
+ aTask->m_rc = hrc;
+
+ if (aTask->m_ptrProgress.isNotNull())
+ aTask->m_ptrProgress->i_notifyComplete(hrc);
+
+ LogFlowFunc(("hrc=%Rhrc\n", hrc));
+ LogFlowFuncLeave();
+
+ return VINF_SUCCESS;
+}
+
+HRESULT VFSExplorer::update(ComPtr<IProgress> &aProgress)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ HRESULT hrc = S_OK;
+
+ ComObjPtr<Progress> progress;
+ try
+ {
+ Bstr progressDesc = BstrFmt(tr("Update directory info for '%s'"),
+ m->strPath.c_str());
+ /* Create the progress object */
+ progress.createObject();
+
+ hrc = progress->init(mVirtualBox, static_cast<IVFSExplorer*>(this), progressDesc.raw(), TRUE /* aCancelable */);
+ if (FAILED(hrc)) throw hrc;
+
+ /* Initialize our worker task */
+ TaskVFSExplorer* pTask = new TaskVFSExplorer(TaskVFSExplorer::Update, this, progress);
+
+ //this function delete task in case of exceptions, so there is no need in the call of delete operator
+ hrc = pTask->createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
+ }
+ catch (HRESULT hrcXcpt)
+ {
+ hrc = hrcXcpt;
+ }
+
+ if (SUCCEEDED(hrc))
+ /* Return progress to the caller */
+ progress.queryInterfaceTo(aProgress.asOutParam());
+
+ return hrc;
+}
+
+HRESULT VFSExplorer::cd(const com::Utf8Str &aDir, ComPtr<IProgress> &aProgress)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ m->strPath = aDir;
+ return update(aProgress);
+}
+
+HRESULT VFSExplorer::cdUp(ComPtr<IProgress> &aProgress)
+{
+ Utf8Str strUpPath;
+ {
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ /* Remove lowest dir entry in a platform neutral way. */
+ char *pszNewPath = RTStrDup(m->strPath.c_str());
+ RTPathStripTrailingSlash(pszNewPath);
+ RTPathStripFilename(pszNewPath);
+ strUpPath = pszNewPath;
+ RTStrFree(pszNewPath);
+ }
+
+ return cd(strUpPath, aProgress);
+}
+
+HRESULT VFSExplorer::entryList(std::vector<com::Utf8Str> &aNames,
+ std::vector<ULONG> &aTypes,
+ std::vector<LONG64> &aSizes,
+ std::vector<ULONG> &aModes)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aNames.resize(m->entryList.size());
+ aTypes.resize(m->entryList.size());
+ aSizes.resize(m->entryList.size());
+ aModes.resize(m->entryList.size());
+
+ std::list<VFSExplorer::Data::DirEntry>::const_iterator it;
+ size_t i = 0;
+ for (it = m->entryList.begin();
+ it != m->entryList.end();
+ ++it, ++i)
+ {
+ const VFSExplorer::Data::DirEntry &entry = (*it);
+ aNames[i] = entry.name;
+ aTypes[i] = entry.type;
+ aSizes[i] = entry.size;
+ aModes[i] = entry.mode;
+ }
+
+ return S_OK;
+}
+
+HRESULT VFSExplorer::exists(const std::vector<com::Utf8Str> &aNames,
+ std::vector<com::Utf8Str> &aExists)
+{
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aExists.resize(0);
+ for (size_t i=0; i < aNames.size(); ++i)
+ {
+ std::list<VFSExplorer::Data::DirEntry>::const_iterator it;
+ for (it = m->entryList.begin();
+ it != m->entryList.end();
+ ++it)
+ {
+ const VFSExplorer::Data::DirEntry &entry = (*it);
+ if (entry.name == RTPathFilename(aNames[i].c_str()))
+ aExists.push_back(aNames[i]);
+ }
+ }
+
+ return S_OK;
+}
+
+HRESULT VFSExplorer::remove(const std::vector<com::Utf8Str> &aNames,
+ ComPtr<IProgress> &aProgress)
+{
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ HRESULT hrc = S_OK;
+
+ ComObjPtr<Progress> progress;
+ try
+ {
+ /* Create the progress object */
+ progress.createObject();
+
+ hrc = progress->init(mVirtualBox, static_cast<IVFSExplorer*>(this),
+ Bstr(tr("Delete files")).raw(),
+ TRUE /* aCancelable */);
+ if (FAILED(hrc)) throw hrc;
+
+ /* Initialize our worker task */
+ TaskVFSExplorer* pTask = new TaskVFSExplorer(TaskVFSExplorer::Delete, this, progress);
+
+ /* Add all filenames to delete as task data */
+ for (size_t i = 0; i < aNames.size(); ++i)
+ pTask->m_lstFilenames.push_back(aNames[i]);
+
+ //this function delete task in case of exceptions, so there is no need in the call of delete operator
+ hrc = pTask->createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
+ }
+ catch (HRESULT hrcXcpt)
+ {
+ hrc = hrcXcpt;
+ }
+
+ if (SUCCEEDED(hrc))
+ /* Return progress to the caller */
+ progress.queryInterfaceTo(aProgress.asOutParam());
+
+ return hrc;
+}
+