From 04a127053e909414fdc89e9fdc8e8fae6985ae25 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Thu, 6 Apr 2017 18:52:03 -0500 Subject: [PATCH] Try to fix download --- src/siadrive_dokan_api/siadokandrive.cpp | 350 ++++++++++++----------- 1 file changed, 180 insertions(+), 170 deletions(-) diff --git a/src/siadrive_dokan_api/siadokandrive.cpp b/src/siadrive_dokan_api/siadokandrive.cpp index 2c38109..fb5e395 100644 --- a/src/siadrive_dokan_api/siadokandrive.cpp +++ b/src/siadrive_dokan_api/siadokandrive.cpp @@ -49,6 +49,7 @@ private: static std::unique_ptr _mountThread; static NTSTATUS _mountStatus; static SString _mountPoint; + static std::vector _activeDownloads; private: inline static const FilePath& GetCacheLocation() @@ -64,47 +65,44 @@ private: static bool AddFileToCache(OpenFileInfo& openFileInfo, PDOKAN_FILE_INFO dokanFileInfo) { - bool active = true; - bool ret = false; + { + std::lock_guard l(_fileTreeMutex); + _activeDownloads.push_back(openFileInfo.SiaPath); + } - std::thread th([&] { - FilePath tempFilePath = FilePath::GetTempDirectory(); - tempFilePath.Append(GenerateSha256(openFileInfo.SiaPath) + ".siatmp"); + FilePath tempFilePath = FilePath::GetTempDirectory(); + tempFilePath.Append(GenerateSha256(openFileInfo.SiaPath) + ".siatmp"); + bool ret = ApiSuccess(_siaApi->GetRenter()->DownloadFile(openFileInfo.SiaPath, tempFilePath)); + if (ret) + { + ::CloseHandle(openFileInfo.FileHandle); - // TODO Check cache size is large enough to hold new file - ret = ApiSuccess(_siaApi->GetRenter()->DownloadFile(openFileInfo.SiaPath, tempFilePath)); + FilePath src(tempFilePath); + FilePath dest(GetCacheLocation(), openFileInfo.SiaPath); + ret = dest.DeleteFile(); + ret = ret && src.MoveFile(dest); if (ret) { - ::CloseHandle(openFileInfo.FileHandle); - - FilePath src(tempFilePath); - FilePath dest(GetCacheLocation(), openFileInfo.SiaPath); - 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) { - 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; - } + ret = false; } else { - src.DeleteFile(); + openFileInfo.Dummy = false; + openFileInfo.FileHandle = handle; } } - active = false; - }); - th.detach(); + else + { + src.DeleteFile(); + } + } - while (active) { - ::Sleep(10); + std::lock_guard l(_fileTreeMutex); + _activeDownloads.erase(std::remove(_activeDownloads.begin(), _activeDownloads.end(), openFileInfo.SiaPath), _activeDownloads.end()); } return ret; @@ -302,167 +300,178 @@ private: SString siaPath = CSiaApi::FormatToSiaPath(fileName); // Strip drive letter to get Sia path if (siaPath.Length()) { - // If cache file already exists and is a directory, requested file operation isn't valid - DWORD attribs = ::GetFileAttributes(&cacheFilePath[0]); - if ((attribs != INVALID_FILE_ATTRIBUTES) && (attribs & FILE_ATTRIBUTE_DIRECTORY)) - { - ret = STATUS_OBJECT_NAME_COLLISION; - } - else - { - bool siaExists; - if (ApiSuccess(_siaApi->GetRenter()->FileExists(siaPath, siaExists))) - { - bool exists = siaExists || cacheFilePath.IsFile(); - // Operations on existing files that are requested to be truncated, overwritten or re-created - // will first be deleted and then replaced if, after the file operation is done, the resulting file - // size is > 0. Sia doesn't support random access to files (upload/download/rename/delete). - bool isCreateOp = false; - bool isReplaceOp = false; - switch (creationDisposition) - { - case CREATE_ALWAYS: - { - isCreateOp = true; - isReplaceOp = exists; - } - break; + { + std::lock_guard l(_fileTreeMutex); + if (std::find(_activeDownloads.begin(), _activeDownloads.end(), siaPath) != _activeDownloads.end()) + { + ret = STATUS_ACCESS_DENIED; + } + } - case CREATE_NEW: - { - if (exists) - { - ret = STATUS_OBJECT_NAME_EXISTS; - } - else - { - isCreateOp = true; - } - } - break; + if (ret == STATUS_SUCCESS) + { + // If cache file already exists and is a directory, requested file operation isn't valid + DWORD attribs = ::GetFileAttributes(&cacheFilePath[0]); + if ((attribs != INVALID_FILE_ATTRIBUTES) && (attribs & FILE_ATTRIBUTE_DIRECTORY)) + { + ret = STATUS_OBJECT_NAME_COLLISION; + } + else + { + bool siaExists; + if (ApiSuccess(_siaApi->GetRenter()->FileExists(siaPath, siaExists))) + { + bool exists = siaExists || cacheFilePath.IsFile(); + // Operations on existing files that are requested to be truncated, overwritten or re-created + // will first be deleted and then replaced if, after the file operation is done, the resulting file + // size is > 0. Sia doesn't support random access to files (upload/download/rename/delete). + bool isCreateOp = false; + bool isReplaceOp = false; + switch (creationDisposition) + { + case CREATE_ALWAYS: + { + isCreateOp = true; + isReplaceOp = exists; + } + break; - case OPEN_ALWAYS: - { - if (!exists) - { - isCreateOp = true; - } - } - break; + case CREATE_NEW: + { + if (exists) + { + ret = STATUS_OBJECT_NAME_EXISTS; + } + else + { + isCreateOp = true; + } + } + break; - case OPEN_EXISTING: - { - if (!exists) - { - ret = STATUS_OBJECT_NAME_NOT_FOUND; - } - } - break; + case OPEN_ALWAYS: + { + if (!exists) + { + isCreateOp = true; + } + } + break; - case TRUNCATE_EXISTING: - { - if (exists) - { - isCreateOp = isReplaceOp = true; - } - else - { - ret = STATUS_OBJECT_NAME_NOT_FOUND; - } - } - break; + case OPEN_EXISTING: + { + if (!exists) + { + ret = STATUS_OBJECT_NAME_NOT_FOUND; + } + } + break; - default: - // Nothing to do - break; - } + case TRUNCATE_EXISTING: + { + if (exists) + { + isCreateOp = isReplaceOp = true; + } + else + { + ret = STATUS_OBJECT_NAME_NOT_FOUND; + } + } + break; - if (ret == STATUS_SUCCESS) - { - if (isReplaceOp) - { - // Since this is a request to replace an existing file, make sure cache is deleted first. - // If file isn't cached, delete from Sia only - if (!cacheFilePath.IsFile() || cacheFilePath.DeleteFile()) - { - if (!ApiSuccess(_uploadManager->Remove(siaPath))) - { - ret = STATUS_INVALID_SERVER_STATE; - } - } - else - { - ret = DokanNtStatusFromWin32(GetLastError()); - } - } + default: + // Nothing to do + break; + } - bool isDummy = false; - if (ret == STATUS_SUCCESS) - { - if (!isCreateOp) - { - if (cacheFilePath.IsFile()) + if (ret == STATUS_SUCCESS) + { + if (isReplaceOp) + { + // Since this is a request to replace an existing file, make sure cache is deleted first. + // If file isn't cached, delete from Sia only + if (!cacheFilePath.IsFile() || cacheFilePath.DeleteFile()) { - isDummy = (siaExists && (FileSize(&cacheFilePath[0]) == 0)); - } - else if (siaExists) - { - isDummy = AddDummyFileToCache(siaPath); - if (!isDummy) + if (!ApiSuccess(_uploadManager->Remove(siaPath))) { - ret = STATUS_ACCESS_DENIED; + ret = STATUS_INVALID_SERVER_STATE; } } else { - ret = STATUS_NOT_FOUND; + ret = DokanNtStatusFromWin32(GetLastError()); } - } + } - if (ret == STATUS_SUCCESS) - { - // Create file as specified - HANDLE handle = ::CreateFile( - &cacheFilePath[0], - genericDesiredAccess, - shareAccess, - &securityAttrib, - creationDisposition, - fileAttributesAndFlags, - nullptr); - if (handle == INVALID_HANDLE_VALUE) - { - ret = DokanNtStatusFromWin32(GetLastError()); - } - else - { - if ((creationDisposition == OPEN_ALWAYS) || (creationDisposition == CREATE_ALWAYS)) + bool isDummy = false; + if (ret == STATUS_SUCCESS) + { + if (!isCreateOp) + { + if (cacheFilePath.IsFile()) { - DWORD error = GetLastError(); - if (error == ERROR_ALREADY_EXISTS) + isDummy = (siaExists && (FileSize(&cacheFilePath[0]) == 0)); + } + else if (siaExists) + { + isDummy = AddDummyFileToCache(siaPath); + if (!isDummy) { - ret = STATUS_OBJECT_NAME_COLLISION; + ret = STATUS_ACCESS_DENIED; } } + else + { + ret = STATUS_NOT_FOUND; + } + } - 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; - dokanFileInfo->Context = reinterpret_cast(ofi); - } - } - } - } - } - else - { - ret = STATUS_INVALID_SERVER_STATE; - } - } + if (ret == STATUS_SUCCESS) + { + // Create file as specified + HANDLE handle = ::CreateFile( + &cacheFilePath[0], + genericDesiredAccess, + shareAccess, + &securityAttrib, + creationDisposition, + fileAttributesAndFlags, + nullptr); + if (handle == INVALID_HANDLE_VALUE) + { + ret = DokanNtStatusFromWin32(GetLastError()); + } + else + { + if ((creationDisposition == OPEN_ALWAYS) || (creationDisposition == CREATE_ALWAYS)) + { + DWORD error = GetLastError(); + if (error == ERROR_ALREADY_EXISTS) + { + ret = STATUS_OBJECT_NAME_COLLISION; + } + } + + 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; + dokanFileInfo->Context = reinterpret_cast(ofi); + } + } + } + } + } + else + { + ret = STATUS_INVALID_SERVER_STATE; + } + } + } } else { @@ -1492,6 +1501,7 @@ std::unique_ptr DokanImpl::_fileListThread; std::unique_ptr DokanImpl::_mountThread; NTSTATUS DokanImpl::_mountStatus = STATUS_SUCCESS; SString DokanImpl::_mountPoint; +std::vector DokanImpl::_activeDownloads; CSiaDokanDrive::CSiaDokanDrive(CSiaApi& siaApi, CSiaDriveConfig* siaDriveConfig) : _siaApi(siaApi),