From a70d22b568822b0fcdcfd9ce27bc86b830a825a9 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 11 Feb 2017 15:07:51 -0600 Subject: [PATCH] Dokan changes --- SiaDrive.Api/SiaApi.h | 20 +++ SiaDrive.Api/SiaDrive.Api.vcxproj | 1 + SiaDrive.Api/SiaDrive.Api.vcxproj.filters | 3 + SiaDrive.Api/SiaFileTree.cpp | 20 +++ SiaDrive.Api/SiaRenter.cpp | 19 +++ SiaDrive.Dokan.Api/SiaDokanDrive.cpp | 142 ++++++++++++++++++++-- 6 files changed, 194 insertions(+), 11 deletions(-) create mode 100644 SiaDrive.Api/SiaFileTree.cpp diff --git a/SiaDrive.Api/SiaApi.h b/SiaDrive.Api/SiaApi.h index c2149e8..1fa39ca 100644 --- a/SiaDrive.Api/SiaApi.h +++ b/SiaDrive.Api/SiaApi.h @@ -26,6 +26,22 @@ public: Japanese }; + class AFX_EXT_CLASS _CSiaFileTree + { + friend CSiaApi; + private: + _CSiaFileTree(CSiaCurl& siaCurl); + + public: + ~_CSiaFileTree(); + + private: + CSiaCurl& _siaCurl; + + public: + void BuildTree(const json& result); + }; + class AFX_EXT_CLASS _CSiaWallet { friend CSiaApi; @@ -69,6 +85,8 @@ public: _SiaApiError FileExists(const String& siaPath, bool& exists) const; _SiaApiError DeleteFile(const String& siaPath); _SiaApiError DownloadFile(const String& siaPath, const String& location) const; + _SiaApiError QueueUploadFile(const String& siaPath, const String& filePath); + _SiaApiError GetFileTree(std::shared_ptr<_CSiaFileTree>& siaFileTree) const; }; public: @@ -94,5 +112,7 @@ typedef CSiaApi::_CSiaWallet CSiaWallet; typedef CSiaApi::_CSiaRenter CSiaRenter; typedef std::shared_ptr CSiaWalletPtr; typedef std::shared_ptr CSiaRenterPtr; +typedef CSiaApi::_CSiaFileTree CSiaFileTree; +typedef std::shared_ptr CSiaFileTreePtr; NS_END(2) \ No newline at end of file diff --git a/SiaDrive.Api/SiaDrive.Api.vcxproj b/SiaDrive.Api/SiaDrive.Api.vcxproj index 1dcbb9c..a316ece 100644 --- a/SiaDrive.Api/SiaDrive.Api.vcxproj +++ b/SiaDrive.Api/SiaDrive.Api.vcxproj @@ -196,6 +196,7 @@ + diff --git a/SiaDrive.Api/SiaDrive.Api.vcxproj.filters b/SiaDrive.Api/SiaDrive.Api.vcxproj.filters index b75dcec..c0fe47b 100644 --- a/SiaDrive.Api/SiaDrive.Api.vcxproj.filters +++ b/SiaDrive.Api/SiaDrive.Api.vcxproj.filters @@ -42,6 +42,9 @@ Source Files + + Source Files + diff --git a/SiaDrive.Api/SiaFileTree.cpp b/SiaDrive.Api/SiaFileTree.cpp new file mode 100644 index 0000000..8d0db67 --- /dev/null +++ b/SiaDrive.Api/SiaFileTree.cpp @@ -0,0 +1,20 @@ +#include "StdAfx.h" +#include "SiaApi.h" + +using namespace Sia::Api; + +CSiaApi::_CSiaFileTree::_CSiaFileTree(CSiaCurl& siaCurl) : + _siaCurl(siaCurl) +{ + +} + +CSiaApi::_CSiaFileTree::~_CSiaFileTree() +{ + +} + +void CSiaApi::_CSiaFileTree::BuildTree(const json& result) +{ + +} \ No newline at end of file diff --git a/SiaDrive.Api/SiaRenter.cpp b/SiaDrive.Api/SiaRenter.cpp index 092ed2e..e25642b 100644 --- a/SiaDrive.Api/SiaRenter.cpp +++ b/SiaDrive.Api/SiaRenter.cpp @@ -26,4 +26,23 @@ SiaApiError CSiaApi::_CSiaRenter::DeleteFile(const String& siaPath) SiaApiError CSiaApi::_CSiaRenter::DownloadFile(const String& siaPath, const String& location) const { return SiaApiError::NotImplemented; +} + +SiaApiError CSiaApi::_CSiaRenter::QueueUploadFile(const String& siaPath, const String& filePath) +{ + return SiaApiError::NotImplemented; +} + +SiaApiError CSiaApi::_CSiaRenter::GetFileTree(CSiaFileTreePtr& siaFileTree) const +{ + SiaApiError ret = SiaApiError::RequestError; + siaFileTree.reset(new CSiaFileTree(_siaCurl)); + json result; + if (API_SUCCESS(SiaCurlError, _siaCurl.Get(L"/renter/files", result))) + { + siaFileTree->BuildTree(result); + ret = SiaApiError::Success; + } + + return ret; } \ No newline at end of file diff --git a/SiaDrive.Dokan.Api/SiaDokanDrive.cpp b/SiaDrive.Dokan.Api/SiaDokanDrive.cpp index 02346bb..737ea55 100644 --- a/SiaDrive.Dokan.Api/SiaDokanDrive.cpp +++ b/SiaDrive.Dokan.Api/SiaDokanDrive.cpp @@ -1,19 +1,30 @@ #include "stdafx.h" #include "SiaDokanDrive.h" #include -#include using namespace Sia::Api; using namespace Sia::Api::Dokan; - class DokanImpl { +private: + struct OpenFileInfo + { + String SiaPath; + String CacheFilePath; + bool ReadOnly; + }; + private: static std::mutex _dokanMutex; static CSiaApi* _siaApi; static DOKAN_OPERATIONS _dokanOps; static String _cacheLocation; + static std::unordered_map _openFileMap; + static std::unique_ptr _fileListThread; + static bool _fileListStopRequested; + static CSiaFileTreePtr _siaFileTree; + static std::mutex _fileTreeMutex; private: static bool AddFileToCache(const String& siaPath, const String& cachePath) @@ -33,6 +44,62 @@ private: return ret; } + static void QueueUploadIfChanged(const ULONG64& id, const std::uint64_t& size) + { + if (!_openFileMap[id].ReadOnly) + { + if (size > 0) + { + // TODO Always save for now - need to change to detect modifications + _siaApi->GetRenter()->QueueUploadFile(_openFileMap[id].SiaPath, _openFileMap[id].CacheFilePath); + } + else + { + // Treat 0 length files as deleted in Sia - cache retains 0-length + // TODO Retain 0 length in cache? + _siaApi->GetRenter()->DeleteFile(_openFileMap[id].SiaPath); + } + } + } + + static void StartFileListThread() + { + if (!_fileListThread) + { + _fileListStopRequested = false; + _fileListThread.reset(new std::thread([]() + { + while (!_fileListStopRequested) + { + CSiaFileTreePtr siaFileTree; + _siaApi->GetRenter()->GetFileTree(siaFileTree); + { + std::lock_guard l(_fileTreeMutex); + _siaFileTree = siaFileTree; + } + + if (!_fileListStopRequested) + { + // TODO Change to WaitForSingleObject() for immediate termination + Sleep(5000); + } + } + })); + } + } + + static void StopFileListThread() + { + if (_fileListThread) + { + _fileListStopRequested = true; + _fileListThread->join(); + _fileListThread.reset(nullptr); + } + } + + // Dokan callbacks +private: static NTSTATUS DOKAN_CALLBACK SiaDrive_ZwCreateFile( LPCWSTR FileName, PDOKAN_IO_SECURITY_CONTEXT SecurityContext, @@ -43,6 +110,7 @@ private: ULONG CreateOptions, PDOKAN_FILE_INFO DokanFileInfo) { + std::lock_guard l(_dokanMutex); SECURITY_ATTRIBUTES securityAttrib; securityAttrib.nLength = sizeof(securityAttrib); securityAttrib.lpSecurityDescriptor = SecurityContext->AccessState.SecurityDescriptor; @@ -142,7 +210,7 @@ private: // If file isn't cached, delete from Sia only if (!PathFileExists(cacheFilePath.c_str()) || ::DeleteFile(cacheFilePath.c_str())) { - // Delete from Sia + // Delete from Sia since this file will be replaced if (!API_SUCCESS(SiaApiError, _siaApi->GetRenter()->DeleteFile(siaPath))) { ret = STATUS_INVALID_SERVER_STATE; @@ -184,6 +252,17 @@ private: else { DokanFileInfo->Context = reinterpret_cast(handle); // save the file handle in Context + if (isFile) + { + OpenFileInfo ofi; + ofi.SiaPath = siaPath; + ofi.CacheFilePath = cacheFilePath; + // TODO Detect if file is read-only + // TODO Quick hash to detect changes + ofi.ReadOnly = false; + _openFileMap.insert({ DokanFileInfo->Context, ofi }); + } + /*if (creationDisposition == OPEN_ALWAYS || creationDisposition == CREATE_ALWAYS) { error = GetLastError(); @@ -218,20 +297,55 @@ private: PFillFindData FillFindData, PDOKAN_FILE_INFO DokanFileInfo) { - return 0; + std::lock_guard l(_dokanMutex); + return STATUS_NOT_IMPLEMENTED; } static void DOKAN_CALLBACK Sia_CloseFile(LPCWSTR FileName, PDOKAN_FILE_INFO DokanFileInfo) { - if (DokanFileInfo->Context) + std::lock_guard l(_dokanMutex); + ULONG64 id = DokanFileInfo->Context; + if (id) { - ::CloseHandle(reinterpret_cast(DokanFileInfo->Context)); - DokanFileInfo->Context = 0; + HANDLE handle = reinterpret_cast(DokanFileInfo->Context); + // Ignore directories in Sia - reside in cache only. + if (DokanFileInfo->IsDirectory) + { + ::CloseHandle(handle); + } + else + { + LARGE_INTEGER li = { 0 }; + BOOL sizeOk = GetFileSizeEx(handle, &li); - QueueUploadIfChanged(FileName); + ::CloseHandle(handle); + + // TODO If it's not ok, why and what to do? + if (sizeOk) + { + QueueUploadIfChanged(id, li.QuadPart); + } + _openFileMap.erase(id); + } + + DokanFileInfo->Context = 0; } } + static NTSTATUS DOKAN_CALLBACK Sia_Mounted(PDOKAN_FILE_INFO DokanFileInfo) + { + std::lock_guard l(_dokanMutex); + StartFileListThread(); + return STATUS_SUCCESS; + } + + static NTSTATUS DOKAN_CALLBACK Sia_Unmounted(PDOKAN_FILE_INFO DokanFileInfo) + { + std::lock_guard l(_dokanMutex); + StopFileListThread(); + return STATUS_SUCCESS; + } + public: static void Initialize(CSiaApi* siaApi) { @@ -249,7 +363,7 @@ public: _dokanOps.GetFileSecurityW = nullptr; _dokanOps.GetVolumeInformationW = nullptr; _dokanOps.LockFile = nullptr; - _dokanOps.Mounted = nullptr; + _dokanOps.Mounted = Sia_Mounted; _dokanOps.MoveFileW = nullptr; _dokanOps.ReadFile = nullptr; _dokanOps.SetAllocationSize = nullptr; @@ -258,7 +372,7 @@ public: _dokanOps.SetFileSecurityW = nullptr; _dokanOps.SetFileTime = nullptr; _dokanOps.UnlockFile = nullptr; - _dokanOps.Unmounted = nullptr; + _dokanOps.Unmounted = Sia_Unmounted; _dokanOps.WriteFile = nullptr; _dokanOps.ZwCreateFile = SiaDrive_ZwCreateFile; } @@ -279,10 +393,16 @@ public: return _siaApi != nullptr; } }; +// Static member variables std::mutex DokanImpl::_dokanMutex; CSiaApi* DokanImpl::_siaApi = nullptr; DOKAN_OPERATIONS DokanImpl::_dokanOps; String DokanImpl::_cacheLocation; +bool DokanImpl::_fileListStopRequested; +CSiaFileTreePtr DokanImpl::_siaFileTree; +std::mutex DokanImpl::_fileTreeMutex; +std::unique_ptr DokanImpl::_fileListThread; +std::unordered_map DokanImpl::_openFileMap; CSiaDokanDrive::CSiaDokanDrive(CSiaApi& siaApi) : @@ -315,5 +435,5 @@ void CSiaDokanDrive::Unmount(const bool& clearCache) void CSiaDokanDrive::ClearCache() { - + std::lock_guard l(DokanImpl::GetMutex()); } \ No newline at end of file