1
0

Try to fix download

This commit is contained in:
Scott E. Graves
2017-04-06 18:52:03 -05:00
parent 9118fe6b86
commit 04a127053e

View File

@@ -49,6 +49,7 @@ private:
static std::unique_ptr<std::thread> _mountThread; static std::unique_ptr<std::thread> _mountThread;
static NTSTATUS _mountStatus; static NTSTATUS _mountStatus;
static SString _mountPoint; static SString _mountPoint;
static std::vector<SString> _activeDownloads;
private: private:
inline static const FilePath& GetCacheLocation() inline static const FilePath& GetCacheLocation()
@@ -64,47 +65,44 @@ private:
static bool AddFileToCache(OpenFileInfo& openFileInfo, PDOKAN_FILE_INFO dokanFileInfo) static bool AddFileToCache(OpenFileInfo& openFileInfo, PDOKAN_FILE_INFO dokanFileInfo)
{ {
bool active = true; {
bool ret = false; std::lock_guard<std::mutex> l(_fileTreeMutex);
_activeDownloads.push_back(openFileInfo.SiaPath);
}
std::thread th([&] { FilePath tempFilePath = FilePath::GetTempDirectory();
FilePath tempFilePath = FilePath::GetTempDirectory(); tempFilePath.Append(GenerateSha256(openFileInfo.SiaPath) + ".siatmp");
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 FilePath src(tempFilePath);
ret = ApiSuccess(_siaApi->GetRenter()->DownloadFile(openFileInfo.SiaPath, tempFilePath)); FilePath dest(GetCacheLocation(), openFileInfo.SiaPath);
ret = dest.DeleteFile();
ret = ret && src.MoveFile(dest);
if (ret) if (ret)
{ {
::CloseHandle(openFileInfo.FileHandle); HANDLE handle = ::CreateFile(&dest[0], GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if (handle == INVALID_HANDLE_VALUE)
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); ret = false;
if (handle == INVALID_HANDLE_VALUE)
{
ret = false;
}
else
{
openFileInfo.Dummy = false;
openFileInfo.FileHandle = handle;
}
} }
else else
{ {
src.DeleteFile(); openFileInfo.Dummy = false;
openFileInfo.FileHandle = handle;
} }
} }
active = false; else
}); {
th.detach(); src.DeleteFile();
}
}
while (active)
{ {
::Sleep(10); std::lock_guard<std::mutex> l(_fileTreeMutex);
_activeDownloads.erase(std::remove(_activeDownloads.begin(), _activeDownloads.end(), openFileInfo.SiaPath), _activeDownloads.end());
} }
return ret; return ret;
@@ -302,167 +300,178 @@ private:
SString siaPath = CSiaApi::FormatToSiaPath(fileName); // Strip drive letter to get Sia path SString siaPath = CSiaApi::FormatToSiaPath(fileName); // Strip drive letter to get Sia path
if (siaPath.Length()) if (siaPath.Length())
{ {
// If cache file already exists and is a directory, requested file operation isn't valid {
DWORD attribs = ::GetFileAttributes(&cacheFilePath[0]); std::lock_guard<std::mutex> l(_fileTreeMutex);
if ((attribs != INVALID_FILE_ATTRIBUTES) && (attribs & FILE_ATTRIBUTE_DIRECTORY)) if (std::find(_activeDownloads.begin(), _activeDownloads.end(), siaPath) != _activeDownloads.end())
{ {
ret = STATUS_OBJECT_NAME_COLLISION; ret = STATUS_ACCESS_DENIED;
} }
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 CREATE_NEW: if (ret == STATUS_SUCCESS)
{ {
if (exists) // If cache file already exists and is a directory, requested file operation isn't valid
{ DWORD attribs = ::GetFileAttributes(&cacheFilePath[0]);
ret = STATUS_OBJECT_NAME_EXISTS; if ((attribs != INVALID_FILE_ATTRIBUTES) && (attribs & FILE_ATTRIBUTE_DIRECTORY))
} {
else ret = STATUS_OBJECT_NAME_COLLISION;
{ }
isCreateOp = true; else
} {
} bool siaExists;
break; 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: case CREATE_NEW:
{ {
if (!exists) if (exists)
{ {
isCreateOp = true; ret = STATUS_OBJECT_NAME_EXISTS;
} }
} else
break; {
isCreateOp = true;
}
}
break;
case OPEN_EXISTING: case OPEN_ALWAYS:
{ {
if (!exists) if (!exists)
{ {
ret = STATUS_OBJECT_NAME_NOT_FOUND; isCreateOp = true;
} }
} }
break; break;
case TRUNCATE_EXISTING: case OPEN_EXISTING:
{ {
if (exists) if (!exists)
{ {
isCreateOp = isReplaceOp = true; ret = STATUS_OBJECT_NAME_NOT_FOUND;
} }
else }
{ break;
ret = STATUS_OBJECT_NAME_NOT_FOUND;
}
}
break;
default: case TRUNCATE_EXISTING:
// Nothing to do {
break; if (exists)
} {
isCreateOp = isReplaceOp = true;
}
else
{
ret = STATUS_OBJECT_NAME_NOT_FOUND;
}
}
break;
if (ret == STATUS_SUCCESS) default:
{ // Nothing to do
if (isReplaceOp) break;
{ }
// 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());
}
}
bool isDummy = false; if (ret == STATUS_SUCCESS)
if (ret == STATUS_SUCCESS) {
{ if (isReplaceOp)
if (!isCreateOp) {
{ // Since this is a request to replace an existing file, make sure cache is deleted first.
if (cacheFilePath.IsFile()) // If file isn't cached, delete from Sia only
if (!cacheFilePath.IsFile() || cacheFilePath.DeleteFile())
{ {
isDummy = (siaExists && (FileSize(&cacheFilePath[0]) == 0)); if (!ApiSuccess(_uploadManager->Remove(siaPath)))
}
else if (siaExists)
{
isDummy = AddDummyFileToCache(siaPath);
if (!isDummy)
{ {
ret = STATUS_ACCESS_DENIED; ret = STATUS_INVALID_SERVER_STATE;
} }
} }
else else
{ {
ret = STATUS_NOT_FOUND; ret = DokanNtStatusFromWin32(GetLastError());
} }
} }
if (ret == STATUS_SUCCESS) bool isDummy = false;
{ if (ret == STATUS_SUCCESS)
// Create file as specified {
HANDLE handle = ::CreateFile( if (!isCreateOp)
&cacheFilePath[0], {
genericDesiredAccess, if (cacheFilePath.IsFile())
shareAccess,
&securityAttrib,
creationDisposition,
fileAttributesAndFlags,
nullptr);
if (handle == INVALID_HANDLE_VALUE)
{
ret = DokanNtStatusFromWin32(GetLastError());
}
else
{
if ((creationDisposition == OPEN_ALWAYS) || (creationDisposition == CREATE_ALWAYS))
{ {
DWORD error = GetLastError(); isDummy = (siaExists && (FileSize(&cacheFilePath[0]) == 0));
if (error == ERROR_ALREADY_EXISTS) }
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(); if (ret == STATUS_SUCCESS)
ofi->SiaPath = siaPath; {
ofi->CacheFilePath = cacheFilePath; // Create file as specified
// TODO Detect if file is read-only HANDLE handle = ::CreateFile(
ofi->Dummy = isDummy; &cacheFilePath[0],
ofi->Changed = false; genericDesiredAccess,
ofi->FileHandle = handle; shareAccess,
dokanFileInfo->Context = reinterpret_cast<ULONG64>(ofi); &securityAttrib,
} creationDisposition,
} fileAttributesAndFlags,
} nullptr);
} if (handle == INVALID_HANDLE_VALUE)
} {
else ret = DokanNtStatusFromWin32(GetLastError());
{ }
ret = STATUS_INVALID_SERVER_STATE; 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<ULONG64>(ofi);
}
}
}
}
}
else
{
ret = STATUS_INVALID_SERVER_STATE;
}
}
}
} }
else else
{ {
@@ -1492,6 +1501,7 @@ std::unique_ptr<std::thread> DokanImpl::_fileListThread;
std::unique_ptr<std::thread> DokanImpl::_mountThread; std::unique_ptr<std::thread> DokanImpl::_mountThread;
NTSTATUS DokanImpl::_mountStatus = STATUS_SUCCESS; NTSTATUS DokanImpl::_mountStatus = STATUS_SUCCESS;
SString DokanImpl::_mountPoint; SString DokanImpl::_mountPoint;
std::vector<SString> DokanImpl::_activeDownloads;
CSiaDokanDrive::CSiaDokanDrive(CSiaApi& siaApi, CSiaDriveConfig* siaDriveConfig) : CSiaDokanDrive::CSiaDokanDrive(CSiaApi& siaApi, CSiaDriveConfig* siaDriveConfig) :
_siaApi(siaApi), _siaApi(siaApi),