diff --git a/src/siadrive_dokan_api/siadokandrive.cpp b/src/siadrive_dokan_api/siadokandrive.cpp index fb5e395..eb10347 100644 --- a/src/siadrive_dokan_api/siadokandrive.cpp +++ b/src/siadrive_dokan_api/siadokandrive.cpp @@ -32,6 +32,11 @@ private: FilePath CacheFilePath; bool Dummy; bool Changed; + ACCESS_MASK DesiredAccess; + ULONG ShareMode; + SECURITY_ATTRIBUTES SecurityAttrib; + ULONG CreateDisp; + DWORD AttributesAndFlags; } OpenFileInfo; private: @@ -49,7 +54,7 @@ private: static std::unique_ptr _mountThread; static NTSTATUS _mountStatus; static SString _mountPoint; - static std::vector _activeDownloads; + static std::vector _openFiles; private: inline static const FilePath& GetCacheLocation() @@ -64,45 +69,49 @@ private: } static bool AddFileToCache(OpenFileInfo& openFileInfo, PDOKAN_FILE_INFO dokanFileInfo) - { - { - std::lock_guard l(_fileTreeMutex); - _activeDownloads.push_back(openFileInfo.SiaPath); - } - + { FilePath tempFilePath = FilePath::GetTempDirectory(); tempFilePath.Append(GenerateSha256(openFileInfo.SiaPath) + ".siatmp"); bool ret = ApiSuccess(_siaApi->GetRenter()->DownloadFile(openFileInfo.SiaPath, tempFilePath)); if (ret) { - ::CloseHandle(openFileInfo.FileHandle); + // Find all open handles for requested file + std::vector matched; + matched.push_back(&openFileInfo); + { + std::lock_guard l(_fileTreeMutex); + std::for_each(_openFiles.begin(), _openFiles.end(), [&](OpenFileInfo* ofi) + { + if ((ofi->FileHandle != openFileInfo.FileHandle) && (ofi->SiaPath == openFileInfo.SiaPath)) + { + matched.push_back(ofi); + } + }); + } + + // Close all to allow move to complete + for (auto* ofi : matched) + { + ::CloseHandle(ofi->FileHandle); + } FilePath src(tempFilePath); - FilePath dest(GetCacheLocation(), openFileInfo.SiaPath); - ret = dest.DeleteFile(); - ret = ret && src.MoveFile(dest); + FilePath dest(openFileInfo.CacheFilePath); + ret = dest.DeleteFile() && src.MoveFile(dest); if (ret) { - HANDLE handle = ::CreateFile(&dest[0], GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); - if (handle == INVALID_HANDLE_VALUE) - { - ret = false; - } - else - { - openFileInfo.Dummy = false; - openFileInfo.FileHandle = handle; - } + openFileInfo.Dummy = false; } else { src.DeleteFile(); } - } - { - std::lock_guard l(_fileTreeMutex); - _activeDownloads.erase(std::remove(_activeDownloads.begin(), _activeDownloads.end(), openFileInfo.SiaPath), _activeDownloads.end()); + // Re-open all files + for (auto& ofi : matched) + { + ofi->FileHandle = ::CreateFile(&ofi->CacheFilePath[0], ofi->DesiredAccess, ofi->ShareMode, ofi->SecurityAttrib.nLength ? &ofi->SecurityAttrib : nullptr, ofi->CreateDisp, ofi->AttributesAndFlags, nullptr); + } } return ret; @@ -152,6 +161,9 @@ private: { ::CloseHandle(openFileInfo.FileHandle); } + + std::lock_guard l(_fileTreeMutex); + _openFiles.erase(std::remove(_openFiles.begin(), _openFiles.end(), &openFileInfo), _openFiles.end()); } static void StartFileListThread() @@ -300,14 +312,6 @@ private: SString siaPath = CSiaApi::FormatToSiaPath(fileName); // Strip drive letter to get Sia path if (siaPath.Length()) { - { - std::lock_guard l(_fileTreeMutex); - if (std::find(_activeDownloads.begin(), _activeDownloads.end(), siaPath) != _activeDownloads.end()) - { - ret = STATUS_ACCESS_DENIED; - } - } - if (ret == STATUS_SUCCESS) { // If cache file already exists and is a directory, requested file operation isn't valid @@ -456,11 +460,17 @@ private: OpenFileInfo* ofi = new OpenFileInfo(); ofi->SiaPath = siaPath; ofi->CacheFilePath = cacheFilePath; - // TODO Detect if file is read-only ofi->Dummy = isDummy; ofi->Changed = false; ofi->FileHandle = handle; + ofi->DesiredAccess = genericDesiredAccess; + ofi->ShareMode = shareAccess; + ofi->SecurityAttrib = securityAttrib; + ofi->CreateDisp = createDisposition; + ofi->AttributesAndFlags = fileAttributesAndFlags; dokanFileInfo->Context = reinterpret_cast(ofi); + std::lock_guard l(_fileTreeMutex); + _openFiles.push_back(ofi); } } } @@ -674,7 +684,6 @@ private: handleFileInfo->dwFileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_ARCHIVE; handleFileInfo->nFileSizeHigh = li.HighPart; handleFileInfo->nFileSizeLow = li.LowPart; - ret = STATUS_SUCCESS; } else if (!::GetFileInformationByHandle(tempHandle, handleFileInfo)) { @@ -986,7 +995,12 @@ private: ofi.CacheFilePath = filePath; ofi.Changed = true; ofi.Dummy = false; - ofi.SiaPath = CSiaApi::FormatToSiaPath(fileName);; + ofi.SiaPath = CSiaApi::FormatToSiaPath(fileName); + ofi.AttributesAndFlags = 0; + ofi.CreateDisp = OPEN_EXISTING; + ofi.DesiredAccess = GENERIC_WRITE; + ofi.SecurityAttrib = { 0 }; + ofi.ShareMode = FILE_SHARE_WRITE; HandleSiaFileClose(ofi, li.QuadPart, false); } @@ -1424,9 +1438,9 @@ public: ZeroMemory(&_dokanOptions, sizeof(DOKAN_OPTIONS)); _dokanOptions.Version = DOKAN_VERSION; _dokanOptions.ThreadCount = 0; // use default + _dokanOptions.Timeout = (60 * 1000) * 60; #ifdef _DEBUG _dokanOptions.Options = DOKAN_OPTION_DEBUG | DOKAN_OPTION_DEBUG_LOG_FILE; - _dokanOptions.Timeout = (60 * 1000) * 60; #else _dokanOptions.Options = 0; #endif @@ -1501,7 +1515,7 @@ std::unique_ptr DokanImpl::_fileListThread; std::unique_ptr DokanImpl::_mountThread; NTSTATUS DokanImpl::_mountStatus = STATUS_SUCCESS; SString DokanImpl::_mountPoint; -std::vector DokanImpl::_activeDownloads; +std::vector DokanImpl::_openFiles; CSiaDokanDrive::CSiaDokanDrive(CSiaApi& siaApi, CSiaDriveConfig* siaDriveConfig) : _siaApi(siaApi),