Continue download support
This commit is contained in:
@@ -56,12 +56,12 @@ private:
|
||||
private:
|
||||
CSiaDriveConfig* GetSiaDriveConfig() const { return _siaDriveConfig; }
|
||||
|
||||
bool HandleFileRemove(const CSiaCurl& siaCurl, const SString& siaPath, const SString& siaDriveFilePath = "");
|
||||
bool CreateSiaDriveFile(const SString& siaPath, const SString& filePath, const SString& tempSourcePath, const SString& siaDriveFilePath);
|
||||
bool HandleFileRemove(const CSiaCurl& siaCurl, const SString& siaPath);
|
||||
void DeleteFilesRemovedFromSia(const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriveConfig, const bool& isStartup = false);
|
||||
|
||||
protected:
|
||||
virtual void AutoThreadCallback(const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriveConfig) override;
|
||||
|
||||
public:
|
||||
static SString UploadStatusToString(const _UploadStatus& uploadStatus);
|
||||
|
||||
@@ -76,48 +76,13 @@ typedef CUploadManager::_UploadErrorCode UploadErrorCode;
|
||||
typedef CSiaError<CUploadManager::_UploadErrorCode> UploadError;
|
||||
|
||||
// Event Notifications
|
||||
class CreatingTemporarySiaDriveFile :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
CreatingTemporarySiaDriveFile(const SString& siaPath, const SString& filePath, const SString& tempSourcePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_tempSourcePath(tempSourcePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~CreatingTemporarySiaDriveFile()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const SString _tempSourcePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"CreatingTemporarySiaDriveFile|SP|" + _siaPath + L"|FP|" + _filePath + L"|TSP|" + _tempSourcePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new CreatingTemporarySiaDriveFile(_siaPath, _filePath, _tempSourcePath));
|
||||
}
|
||||
};
|
||||
|
||||
class FileAddedToQueue :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
FileAddedToQueue(const SString& siaPath, const SString& filePath, const SString& siaDriveFilePath) :
|
||||
FileAddedToQueue(const SString& siaPath, const SString& filePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_siaDriveFilePath(siaDriveFilePath)
|
||||
_filePath(filePath)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -130,17 +95,16 @@ public:
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const SString _siaDriveFilePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"FileAddedToQueue|SP|" + _siaPath + L"|FP|" + _filePath + "|SFP|" + _siaDriveFilePath;
|
||||
return L"FileAddedToQueue|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new FileAddedToQueue(_siaPath, _filePath, _siaDriveFilePath));
|
||||
return std::shared_ptr<CEvent>(new FileAddedToQueue(_siaPath, _filePath));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -176,38 +140,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class ModifiedUploadQueued :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
ModifiedUploadQueued(const SString& siaPath, const SString& filePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~ModifiedUploadQueued()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"ModifiedUploadQueued|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new ModifiedUploadQueued(_siaPath, _filePath));
|
||||
}
|
||||
};
|
||||
|
||||
class UploadToSiaStarted :
|
||||
public CEvent
|
||||
{
|
||||
@@ -240,11 +172,11 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class UploadComplete :
|
||||
class UploadToSiaComplete :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
UploadComplete(const SString& siaPath, const SString& filePath) :
|
||||
UploadToSiaComplete(const SString& siaPath, const SString& filePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath)
|
||||
{
|
||||
@@ -252,7 +184,7 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~UploadComplete()
|
||||
virtual ~UploadToSiaComplete()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -263,20 +195,20 @@ private:
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"UploadComplete|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
return L"UploadToSiaComplete|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new UploadComplete(_siaPath, _filePath));
|
||||
return std::shared_ptr<CEvent>(new UploadToSiaComplete(_siaPath, _filePath));
|
||||
}
|
||||
};
|
||||
|
||||
class FileRemoved :
|
||||
class FileRemovedFromSia :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
FileRemoved(const SString& siaPath, const SString& filePath) :
|
||||
FileRemovedFromSia(const SString& siaPath, const SString& filePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath)
|
||||
{
|
||||
@@ -284,7 +216,7 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~FileRemoved()
|
||||
virtual ~FileRemovedFromSia()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -295,84 +227,20 @@ private:
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"FileRemoved|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
return L"FileRemovedFromSia|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new FileRemoved(_siaPath, _filePath));
|
||||
return std::shared_ptr<CEvent>(new FileRemovedFromSia(_siaPath, _filePath));
|
||||
}
|
||||
};
|
||||
|
||||
class UploadStatusSetToModified :
|
||||
class FailedToRemoveFileFromSia :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
UploadStatusSetToModified(const SString& siaPath, const SString& filePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~UploadStatusSetToModified()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"UploadStatusSetToModified|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new UploadStatusSetToModified(_siaPath, _filePath));
|
||||
}
|
||||
};
|
||||
|
||||
class UploadStatusSetToRemoved :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
UploadStatusSetToRemoved(const SString& siaPath, const SString& filePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~UploadStatusSetToRemoved()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"UploadStatusSetToRemoved|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new UploadStatusSetToRemoved(_siaPath, _filePath));
|
||||
}
|
||||
};
|
||||
|
||||
class FailedToDeleteFromSia :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
FailedToDeleteFromSia(const SString& siaPath, const SString& filePath, const SiaCurlError& curlError) :
|
||||
FailedToRemoveFileFromSia(const SString& siaPath, const SString& filePath, const SiaCurlError& curlError) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_curlError(curlError)
|
||||
@@ -381,7 +249,7 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~FailedToDeleteFromSia()
|
||||
virtual ~FailedToRemoveFileFromSia()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -393,12 +261,12 @@ private:
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"FailedToDeleteFromSia|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
return L"FailedToRemoveFileFromSia|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new FailedToDeleteFromSia(_siaPath, _filePath, _curlError));
|
||||
return std::shared_ptr<CEvent>(new FailedToRemoveFileFromSia(_siaPath, _filePath, _curlError));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -438,214 +306,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class CreatingTemporarySiaDriveFileFailed :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
CreatingTemporarySiaDriveFileFailed(const SString& siaPath, const SString& filePath, const SString& tempSourcePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_tempSourcePath(tempSourcePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~CreatingTemporarySiaDriveFileFailed()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const SString _tempSourcePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"CreatingTemporarySiaDriveFileFailed|SP|" + _siaPath + L"|FP|" + _filePath + L"|TSP|" + _tempSourcePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new CreatingTemporarySiaDriveFileFailed(_siaPath, _filePath, _tempSourcePath));
|
||||
}
|
||||
};
|
||||
|
||||
class DeleteSiaDriveFileFailed :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
DeleteSiaDriveFileFailed(const SString& siaPath, const SString& filePath, const SString& siaDriveFilePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_siaDriveFilePath(siaDriveFilePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~DeleteSiaDriveFileFailed()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const SString _siaDriveFilePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"DeleteSiaDriveFileFailed|SP|" + _siaPath + L"|FP|" + _filePath + L"|SDP|" + _siaDriveFilePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new DeleteSiaDriveFileFailed(_siaPath, _filePath, _siaDriveFilePath));
|
||||
}
|
||||
};
|
||||
|
||||
class RenameModifiedFileFailed :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
RenameModifiedFileFailed(const SString& siaPath, const SString& filePath, const SString& tempSourcePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_tempSourcePath(tempSourcePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~RenameModifiedFileFailed()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const SString _tempSourcePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"RenameModifiedFileFailed|SP|" + _siaPath + L"|FP|" + _filePath + L"|TSP|" + _tempSourcePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new RenameModifiedFileFailed(_siaPath, _filePath, _tempSourcePath));
|
||||
}
|
||||
};
|
||||
|
||||
class DeleteTemporarySiaDriveFileFailed :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
DeleteTemporarySiaDriveFileFailed(const SString& siaPath, const SString& filePath, const SString& tempSourcePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_tempSourcePath(tempSourcePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~DeleteTemporarySiaDriveFileFailed()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const SString _tempSourcePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"DeleteTemporarySiaDriveFileFailed|SP|" + _siaPath + L"|FP|" + _filePath + L"|TSP|" + _tempSourcePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new DeleteTemporarySiaDriveFileFailed(_siaPath, _filePath, _tempSourcePath));
|
||||
}
|
||||
};
|
||||
|
||||
class RenamingTemporarySiaDriveFile :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
RenamingTemporarySiaDriveFile(const SString& siaPath, const SString& filePath, const SString& tempSourcePath, const SString& siaDriveFilePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_tempSourcePath(tempSourcePath),
|
||||
_siaDriveFilePath(siaDriveFilePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~RenamingTemporarySiaDriveFile()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const SString _tempSourcePath;
|
||||
const SString _siaDriveFilePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"RenamingTemporarySiaDriveFile|SP|" + _siaPath + L"|FP|" + _filePath + L"|TSP|" + _tempSourcePath + L"|SDP|" + _siaDriveFilePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new RenamingTemporarySiaDriveFile(_siaPath, _filePath, _tempSourcePath, _siaDriveFilePath));
|
||||
}
|
||||
};
|
||||
|
||||
class RenamingTemporarySiaDriveFileFailed :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
RenamingTemporarySiaDriveFileFailed(const SString& siaPath, const SString& filePath, const SString& tempSourcePath, const SString& siaDriveFilePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_tempSourcePath(tempSourcePath),
|
||||
_siaDriveFilePath(siaDriveFilePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~RenamingTemporarySiaDriveFileFailed()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const SString _tempSourcePath;
|
||||
const SString _siaDriveFilePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"RenamingTemporarySiaDriveFileFailed|SP|" + _siaPath + L"|FP|" + _filePath + L"|TSP|" + _tempSourcePath + L"|SDP|" + _siaDriveFilePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new RenamingTemporarySiaDriveFileFailed(_siaPath, _filePath, _tempSourcePath, _siaDriveFilePath));
|
||||
}
|
||||
};
|
||||
|
||||
class DatabaseInsertFailed :
|
||||
public CEvent
|
||||
{
|
||||
@@ -714,72 +374,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class RemoveFileFailed :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
RemoveFileFailed(const SString& siaPath, const SString& filePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~RemoveFileFailed()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"RemoveFileFailed|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new RemoveFileFailed(_siaPath, _filePath));
|
||||
}
|
||||
};
|
||||
|
||||
class ExistingUploadFound :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
ExistingUploadFound(const SString& siaPath, const SString& filePath, const UploadStatus& uploadStatus) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_uploadStatus(uploadStatus)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~ExistingUploadFound()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const UploadStatus _uploadStatus;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"ExistingUploadFound|SP|" + _siaPath + L"|FP|" + _filePath + L"|ST|" + CUploadManager::UploadStatusToString(_uploadStatus);
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new ExistingUploadFound(_siaPath, _filePath, _uploadStatus));
|
||||
}
|
||||
};
|
||||
|
||||
class SourceFileNotFound :
|
||||
public CEvent
|
||||
{
|
||||
|
@@ -393,6 +393,7 @@ void CSiaDriveApp::SiaApiRefreshCallback(CefRefPtr<CefV8Context> context, const
|
||||
#else
|
||||
a
|
||||
#endif
|
||||
|
||||
auto uiActions = global->GetValue("uiUpdate");
|
||||
auto renterActions = uiActions->GetValue("Renter");
|
||||
auto walletActions = uiActions->GetValue("Wallet");
|
||||
@@ -421,6 +422,7 @@ void CSiaDriveApp::SiaApiRefreshCallback(CefRefPtr<CefV8Context> context, const
|
||||
ExecuteSetter(context, renterActions, "setUsedFunds", SiaCurrencyToString(allocatedFunds - unspentFunds));
|
||||
ExecuteSetter(context, renterActions, "setAvailableFunds", SiaCurrencyToString(unspentFunds));
|
||||
ExecuteSetter(context, renterActions, "setHostCount", SString::FromUInt64(_siaApi->GetRenter()->GetHosts()));
|
||||
global->GetValue("uiState")->SetValue("allocatedRenterFunds", CefV8Value::CreateString(SiaCurrencyToString(allocatedFunds).str()), V8_PROPERTY_ATTRIBUTE_NONE);
|
||||
|
||||
// Space
|
||||
SiaCurrency totalUsedGb = _siaApi->GetRenter()->GetTotalUsedBytes() ? _siaApi->GetRenter()->GetTotalUsedBytes() / (1024.0 * 1024.0 * 1024.0) : 0.0;
|
||||
|
@@ -4,18 +4,18 @@
|
||||
#include <eventsystem.h>
|
||||
#include <siadriveconfig.h>
|
||||
#include <filepath.h>
|
||||
|
||||
using namespace Sia::Api;
|
||||
|
||||
#define TABLE_CREATE L"create table if not exists %s (%s);"
|
||||
#define UPLOAD_TABLE L"upload_table"
|
||||
#define UPLOAD_TABLE_COLUMNS L"id integer primary key autoincrement, sia_path text unique not null, file_path text unique not null, sd_file_path text unique not null, status integer not null"
|
||||
#define UPLOAD_TABLE_COLUMNS L"id integer primary key autoincrement, sia_path text unique not null, file_path text unique not null, status integer not null"
|
||||
#define QUERY_STATUS "select * from upload_table where sia_path=@sia_path order by id desc limit 1;"
|
||||
#define QUERY_UPLOADS_BY_STATUS "select * from upload_table where status=@status order by id desc limit 1;"
|
||||
#define QUERY_UPLOADS_BY_2_STATUS "select * from upload_table where (status=@status1 or status=@status2) order by id desc limit 1;"
|
||||
#define QUERY_UPLOADS_BY_SIA_PATH "select * from upload_table where sia_path=@sia_path order by id desc limit 1;"
|
||||
#define QUERY_UPLOADS_BY_SIA_PATH_AND_STATUS "select * from upload_table where sia_path=@sia_path and status=@status order by id desc limit 1;"
|
||||
#define UPDATE_STATUS "update upload_table set status=@status where sia_path=@sia_path;"
|
||||
#define INSERT_UPLOAD "insert into upload_table (sia_path, status, file_path, sd_file_path) values (@sia_path, @status, @file_path, @sd_file_path);"
|
||||
#define INSERT_UPLOAD "insert into upload_table (sia_path, status, file_path) values (@sia_path, @status, @file_path);"
|
||||
#define DELETE_UPLOAD "delete from upload_table where sia_path=@sia_path;"
|
||||
|
||||
#define SET_STATUS(status, success_event, fail_event)\
|
||||
@@ -89,17 +89,6 @@ CUploadManager::CUploadManager(const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriv
|
||||
{
|
||||
CreateTableIfNotFound(&_uploadDatabase, UPLOAD_TABLE, UPLOAD_TABLE_COLUMNS);
|
||||
|
||||
// Clean-up cache folder
|
||||
if (!RecurDeleteFilesByExtentsion(siaDriveConfig->GetCacheFolder(), L".siadrive"))
|
||||
{
|
||||
throw StartupException(L"Failed to remove '.siadrive' files");
|
||||
}
|
||||
|
||||
if (!RecurDeleteFilesByExtentsion(siaDriveConfig->GetCacheFolder(), L".siadrive.temp"))
|
||||
{
|
||||
throw StartupException(L"Failed to remove '.siadrive.temp' files");
|
||||
}
|
||||
|
||||
// Detect files that have been removed since last startup
|
||||
DeleteFilesRemovedFromSia(siaCurl, siaDriveConfig, true);
|
||||
|
||||
@@ -133,102 +122,31 @@ void CUploadManager::DeleteFilesRemovedFromSia(const CSiaCurl& siaCurl, CSiaDriv
|
||||
}
|
||||
}
|
||||
|
||||
bool CUploadManager::HandleFileRemove(const CSiaCurl& siaCurl, const SString& siaPath, const SString& siaDriveFilePath)
|
||||
bool CUploadManager::HandleFileRemove(const CSiaCurl& siaCurl, const SString& siaPath)
|
||||
{
|
||||
bool ret = false;
|
||||
FilePath removeFilePath(GetSiaDriveConfig()->GetCacheFolder(), siaPath);
|
||||
|
||||
bool deleteFromSia = true;
|
||||
if (removeFilePath.IsFile())
|
||||
{
|
||||
if (RetryDeleteFileIfExists(removeFilePath))
|
||||
{
|
||||
if (!siaDriveFilePath.IsNullOrEmpty() && !RetryDeleteFileIfExists(siaDriveFilePath))
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DeleteSiaDriveFileFailed(siaPath, removeFilePath, siaDriveFilePath)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(RemoveFileFailed(siaPath, removeFilePath)));
|
||||
deleteFromSia = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteFromSia)
|
||||
{
|
||||
json response;
|
||||
SiaCurlError cerror = siaCurl.Post(SString(L"/renter/delete/") + siaPath, {}, response);
|
||||
if (ApiSuccess(cerror))
|
||||
{
|
||||
SQLite::Statement del(_uploadDatabase, DELETE_UPLOAD);
|
||||
del.bind("@sia_path", SString::ToUtf8(siaPath).c_str());
|
||||
if (del.exec() >= 0)
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(FileRemoved(siaPath, removeFilePath)));
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DatabaseDeleteFailed(siaPath, removeFilePath, del.getErrorMsg())));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(FailedToDeleteFromSia(siaPath, removeFilePath, cerror)));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool CUploadManager::CreateSiaDriveFile(const SString& siaPath, const SString& filePath, const SString& tempSourcePath, const SString& siaDriveFilePath)
|
||||
{
|
||||
bool ret = FilePath(siaDriveFilePath).IsFile();
|
||||
if (!ret)
|
||||
|
||||
json response;
|
||||
SiaCurlError cerror = siaCurl.Post(SString(L"/renter/delete/") + siaPath, {}, response);
|
||||
if (ApiSuccess(cerror))
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(CreatingTemporarySiaDriveFile(siaPath, filePath, tempSourcePath)));
|
||||
if (RetryableAction(::CopyFile(filePath.str().c_str(), tempSourcePath.str().c_str(), FALSE), DEFAULT_RETRY_COUNT, DEFAULT_RETRY_DELAY_MS))
|
||||
SQLite::Statement del(_uploadDatabase, DELETE_UPLOAD);
|
||||
del.bind("@sia_path", SString::ToUtf8(siaPath).c_str());
|
||||
if (del.exec() >= 0)
|
||||
{
|
||||
// Rename '.siadrive.temp' to '.siadrive'
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(RenamingTemporarySiaDriveFile(siaPath, filePath, tempSourcePath, siaDriveFilePath)));
|
||||
if (RetryableAction(::MoveFile(tempSourcePath.str().c_str(), siaDriveFilePath.str().c_str()), DEFAULT_RETRY_COUNT, DEFAULT_RETRY_DELAY_MS))
|
||||
{
|
||||
if (RetryDeleteFileIfExists(tempSourcePath))
|
||||
{
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DeleteTemporarySiaDriveFileFailed(siaPath, filePath, tempSourcePath)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(RenamingTemporarySiaDriveFileFailed(siaPath, filePath, tempSourcePath, siaDriveFilePath)));
|
||||
if (!RetryDeleteFileIfExists(tempSourcePath))
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DeleteTemporarySiaDriveFileFailed(siaPath, filePath, tempSourcePath)));
|
||||
}
|
||||
|
||||
if (!RetryDeleteFileIfExists(siaDriveFilePath))
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DeleteSiaDriveFileFailed(siaPath, filePath, siaDriveFilePath)));
|
||||
}
|
||||
}
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(FileRemovedFromSia(siaPath, removeFilePath)));
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(CreatingTemporarySiaDriveFileFailed(siaPath, filePath, tempSourcePath)));
|
||||
|
||||
// If temp copy fails, try to delete
|
||||
// If partial copy and file is unable to be deleted, log warning
|
||||
if (!RetryDeleteFileIfExists(tempSourcePath))
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DeleteTemporarySiaDriveFileFailed(siaPath, filePath, tempSourcePath)));
|
||||
}
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DatabaseDeleteFailed(siaPath, removeFilePath, del.getErrorMsg())));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(FailedToRemoveFileFromSia(siaPath, removeFilePath, cerror)));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -246,15 +164,14 @@ void CUploadManager::AutoThreadCallback(const CSiaCurl& siaCurl, CSiaDriveConfig
|
||||
// Lock here - if file is modified again before previously queued upload is complete, delete it and
|
||||
// start again later
|
||||
std::lock_guard<std::mutex> l(_uploadMutex);
|
||||
SQLite::Statement query(_uploadDatabase, QUERY_UPLOADS_BY_2_STATUS);
|
||||
query.bind("@status1", static_cast<unsigned>(UploadStatus::Uploading));
|
||||
SQLite::Statement query(_uploadDatabase, QUERY_UPLOADS_BY_STATUS);
|
||||
query.bind("@status", static_cast<unsigned>(UploadStatus::Uploading));
|
||||
|
||||
fileTree->BuildTree(result);
|
||||
if (query.executeStep())
|
||||
{
|
||||
SString siaPath = static_cast<const char*>(query.getColumn(query.getColumnIndex("sia_path")));
|
||||
SString filePath = static_cast<const char*>(query.getColumn(query.getColumnIndex("file_path")));
|
||||
SString siaDriveFilePath = static_cast<const char*>(query.getColumn(query.getColumnIndex("sd_file_path")));
|
||||
UploadStatus uploadStatus = static_cast<UploadStatus>(query.getColumn(query.getColumnIndex("status")).getUInt());
|
||||
|
||||
auto fileList = fileTree->GetFileList();
|
||||
@@ -266,16 +183,12 @@ void CUploadManager::AutoThreadCallback(const CSiaCurl& siaCurl, CSiaDriveConfig
|
||||
// Removed by another client
|
||||
if (it == fileList.end())
|
||||
{
|
||||
HandleFileRemove(siaCurl, siaPath, siaDriveFilePath);
|
||||
HandleFileRemove(siaCurl, siaPath);
|
||||
}
|
||||
// Upload is complete
|
||||
else if ((*it)->GetAvailable())
|
||||
{
|
||||
SET_STATUS(UploadStatus::Complete, UploadComplete, ModifyUploadStatusFailed)
|
||||
if (!RetryDeleteFileIfExists(siaDriveFilePath))
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DeleteSiaDriveFileFailed(siaPath, filePath, siaDriveFilePath)));
|
||||
}
|
||||
SET_STATUS(UploadStatus::Complete, UploadToSiaComplete, ModifyUploadStatusFailed)
|
||||
}
|
||||
// Upload still active, don't process another file
|
||||
else
|
||||
@@ -304,28 +217,16 @@ void CUploadManager::AutoThreadCallback(const CSiaCurl& siaCurl, CSiaDriveConfig
|
||||
std::lock_guard<std::mutex> l(_uploadMutex);
|
||||
SQLite::Statement query(_uploadDatabase, QUERY_UPLOADS_BY_STATUS);
|
||||
query.bind("@status", static_cast<unsigned>(UploadStatus::Queued));
|
||||
|
||||
if (query.executeStep())
|
||||
{
|
||||
SString siaPath = static_cast<const char*>(query.getColumn(query.getColumnIndex("sia_path")));
|
||||
SString filePath = static_cast<const char*>(query.getColumn(query.getColumnIndex("file_path")));
|
||||
SString sdFilePath = static_cast<const char*>(query.getColumn(query.getColumnIndex("sd_file_path")));
|
||||
if (dokanDrive->LockFile(filePath))
|
||||
|
||||
json response;
|
||||
SiaCurlError cerror = siaCurl.Post(SString(L"/renter/upload/") + siaPath, { {L"source", filePath} }, response);
|
||||
if (ApiSuccess(cerror))
|
||||
{
|
||||
if (CreateSiaDriveFile(siaPath, filePath, sdFilePath + ".temp", sdFilePath))
|
||||
{
|
||||
dokanDrive->UnLockFile(filePath, sdFilePath);
|
||||
json response;
|
||||
SiaCurlError cerror = siaCurl.Post(SString(L"/renter/upload/") + siaPath, { {L"source", filePath} }, response);
|
||||
if (ApiSuccess(cerror))
|
||||
{
|
||||
SET_STATUS(UploadStatus::Uploading, UploadToSiaStarted, ModifyUploadStatusFailed)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dokanDrive->UnLockFile(filePath);
|
||||
}
|
||||
SET_STATUS(UploadStatus::Uploading, UploadToSiaStarted, ModifyUploadStatusFailed)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -371,12 +272,6 @@ UploadError CUploadManager::AddOrUpdate(const SString& siaPath, SString filePath
|
||||
query.bind("@sia_path", SString::ToUtf8(siaPath).c_str());
|
||||
query.bind("@status", static_cast<unsigned>(UploadStatus::Uploading));
|
||||
|
||||
// Strip drive specification (i.e. C:\)
|
||||
// TODO If mount to folder is ever enabled, this will need to change
|
||||
// TODO Case sensative file names? Going to be a bit of an issue.
|
||||
SString siaDriveFileName = GenerateSha256(&filePath[3]) + L".siadrive";
|
||||
FilePath siaDriveFilePath(rootPath, siaDriveFileName);
|
||||
|
||||
// Check uploading
|
||||
bool addToDatabase = true;
|
||||
if (query.executeStep())
|
||||
@@ -385,14 +280,6 @@ UploadError CUploadManager::AddOrUpdate(const SString& siaPath, SString filePath
|
||||
if (uploadStatus == UploadStatus::Uploading)
|
||||
{
|
||||
addToDatabase = HandleFileRemove(CSiaCurl(GetHostConfig()), siaPath);
|
||||
if (addToDatabase)
|
||||
{
|
||||
// Move modified file to real file
|
||||
if (!siaDriveFilePath.MoveFile(filePath))
|
||||
{
|
||||
// TODO Critical failure
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (uploadStatus == UploadStatus::Queued)
|
||||
{
|
||||
@@ -408,11 +295,10 @@ UploadError CUploadManager::AddOrUpdate(const SString& siaPath, SString filePath
|
||||
SQLite::Statement insert(_uploadDatabase, INSERT_UPLOAD);
|
||||
insert.bind("@sia_path", SString::ToUtf8(siaPath).c_str());
|
||||
insert.bind("@file_path", SString::ToUtf8(filePath).c_str());
|
||||
insert.bind("@sd_file_path", SString::ToUtf8(static_cast<SString>(siaDriveFilePath)).c_str());
|
||||
insert.bind("@status", static_cast<unsigned>(UploadStatus::Queued));
|
||||
if (insert.exec() == 1)
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(FileAddedToQueue(siaPath, filePath, siaDriveFilePath)));
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(FileAddedToQueue(siaPath, filePath)));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -448,8 +334,7 @@ UploadError CUploadManager::Remove(const SString& siaPath)
|
||||
try
|
||||
{
|
||||
std::lock_guard<std::mutex> l(_uploadMutex);
|
||||
FilePath siaDriveFilePath(GetSiaDriveConfig()->GetCacheFolder(), siaPath + ".siadrive");
|
||||
if (!HandleFileRemove(CSiaCurl(GetHostConfig()), siaPath, siaDriveFilePath))
|
||||
if (!HandleFileRemove(CSiaCurl(GetHostConfig()), siaPath))
|
||||
{
|
||||
ret = UploadErrorCode::SourceFileNotFound;
|
||||
}
|
||||
|
@@ -7,6 +7,15 @@
|
||||
using namespace Sia::Api;
|
||||
using namespace Sia::Api::Dokan;
|
||||
|
||||
static __int64 FileSize(const wchar_t* name)
|
||||
{
|
||||
struct _stat64 buf;
|
||||
if (_wstat64(name, &buf) != 0)
|
||||
return -1; // error, could use errno to find out more
|
||||
|
||||
return buf.st_size;
|
||||
}
|
||||
|
||||
// TODO Handle paths greater than MAX_PATH!!
|
||||
|
||||
// The general idea is that normal file I/O occurs in a local cache folder and once the file is closed, it is scheduled for upload into Sia.
|
||||
@@ -54,22 +63,35 @@ private:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool AddFileToCache(const SString& siaPath)
|
||||
static bool AddFileToCache(const SString& siaPath, PDOKAN_FILE_INFO dokanFileInfo)
|
||||
{
|
||||
FilePath tempFilePath = FilePath::GetTempDirectory();
|
||||
tempFilePath.Append(GenerateSha256(siaPath) + ".siatmp");
|
||||
bool active = true;
|
||||
bool ret = false;
|
||||
|
||||
// TODO Check cache size is large enough to hold new file
|
||||
bool ret = ApiSuccess(_siaApi->GetRenter()->DownloadFile(siaPath, tempFilePath));
|
||||
if (ret)
|
||||
{
|
||||
FilePath src(tempFilePath);
|
||||
FilePath dest(GetCacheLocation(), siaPath);
|
||||
ret = src.MoveFile(dest);
|
||||
if (!ret)
|
||||
std::thread th([&] {
|
||||
FilePath tempFilePath = FilePath::GetTempDirectory();
|
||||
tempFilePath.Append(GenerateSha256(siaPath) + ".siatmp");
|
||||
|
||||
// TODO Check cache size is large enough to hold new file
|
||||
ret = ApiSuccess(_siaApi->GetRenter()->DownloadFile(siaPath, tempFilePath));
|
||||
if (ret)
|
||||
{
|
||||
src.DeleteFile();
|
||||
FilePath src(tempFilePath);
|
||||
FilePath dest(GetCacheLocation(), siaPath);
|
||||
ret = src.MoveFile(dest);
|
||||
if (!ret)
|
||||
{
|
||||
src.DeleteFile();
|
||||
}
|
||||
}
|
||||
active = false;
|
||||
});
|
||||
th.detach();
|
||||
|
||||
while (active)
|
||||
{
|
||||
DokanResetTimeout(60000, dokanFileInfo);
|
||||
::Sleep(10);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -270,10 +292,10 @@ private:
|
||||
}
|
||||
else
|
||||
{
|
||||
bool exists;
|
||||
if (ApiSuccess(_siaApi->GetRenter()->FileExists(siaPath, exists)))
|
||||
bool siaExists;
|
||||
if (ApiSuccess(_siaApi->GetRenter()->FileExists(siaPath, siaExists)))
|
||||
{
|
||||
exists = exists || cacheFilePath.IsFile();
|
||||
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).
|
||||
@@ -359,11 +381,13 @@ private:
|
||||
bool isDummy = false;
|
||||
if (ret == STATUS_SUCCESS)
|
||||
{
|
||||
// If file must exist, then check for it in cache location. If not found,
|
||||
// it must be downloaded first and placed in cache
|
||||
if (!isCreateOp && !cacheFilePath.IsFile())
|
||||
if (!isCreateOp)
|
||||
{
|
||||
if (exists)
|
||||
if (cacheFilePath.IsFile())
|
||||
{
|
||||
isDummy = (siaExists && (FileSize(&cacheFilePath[0]) == 0));
|
||||
}
|
||||
else if (exists)
|
||||
{
|
||||
isDummy = AddDummyFileToCache(siaPath);
|
||||
if (!isDummy)
|
||||
@@ -441,7 +465,8 @@ private:
|
||||
auto siaFileTree = GetFileTree();
|
||||
if (siaFileTree)
|
||||
{
|
||||
SString siaFileQuery = CSiaApi::FormatToSiaPath(FilePath(fileName).SkipRoot());
|
||||
SString siaFileQuery = CSiaApi::FormatToSiaPath(FilePath(fileName).SkipRoot());
|
||||
SString siaRootPath = CSiaApi::FormatToSiaPath(FilePath(fileName).SkipRoot());
|
||||
FilePath siaDirQuery = siaFileQuery;
|
||||
FilePath findFile = GetCacheLocation();
|
||||
FilePath cachePath = GetCacheLocation();
|
||||
@@ -484,17 +509,22 @@ private:
|
||||
{
|
||||
if ((wcscmp(fileName, L"\\") != 0) || (wcscmp(findData.cFileName, L".") != 0) && (wcscmp(findData.cFileName, L"..") != 0))
|
||||
{
|
||||
if (!(SString(findData.cFileName).EndsWith(".siadrive") || SString(findData.cFileName).EndsWith(".siadrive.temp")))
|
||||
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
dirs.insert({ findData.cFileName, 0 });
|
||||
}
|
||||
|
||||
bool exists;
|
||||
if (!ApiSuccess(_siaApi->GetRenter()->FileExists(CSiaApi::FormatToSiaPath(FilePath(siaRootPath, findData.cFileName)), exists)))
|
||||
{
|
||||
::FindClose(findHandle);
|
||||
return STATUS_INVALID_DEVICE_STATE;
|
||||
}
|
||||
|
||||
if (findData.nFileSizeHigh || findData.nFileSizeLow || !exists)
|
||||
{
|
||||
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
dirs.insert({ findData.cFileName, 0 });
|
||||
}
|
||||
else
|
||||
{
|
||||
files.insert({ findData.cFileName, 1 });
|
||||
}
|
||||
fillFindData(&findData, dokanFileInfo);
|
||||
files.insert({ findData.cFileName, 1 });
|
||||
}
|
||||
}
|
||||
} while (::FindNextFile(findHandle, &findData) != 0);
|
||||
@@ -709,10 +739,30 @@ private:
|
||||
PDOKAN_FILE_INFO dokanFileInfo)
|
||||
{
|
||||
// TODO Check dummy and add to cache if not found
|
||||
|
||||
FilePath filePath(GetCacheLocation(), fileName);
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanReadFile(filePath)));
|
||||
|
||||
HANDLE handle = reinterpret_cast<HANDLE>(dokanFileInfo->Context);
|
||||
bool isDummy = false;
|
||||
SString siaPath;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(_fileTreeMutex);
|
||||
isDummy = _openFileMap.find(dokanFileInfo->Context) != _openFileMap.end();
|
||||
if (isDummy)
|
||||
{
|
||||
siaPath = _openFileMap[dokanFileInfo->Context].SiaPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDummy)
|
||||
{
|
||||
if (!AddFileToCache(siaPath, dokanFileInfo))
|
||||
{
|
||||
return STATUS_INVALID_DEVICE_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE handle = reinterpret_cast<HANDLE>(dokanFileInfo->Context);
|
||||
BOOL opened = FALSE;
|
||||
if (!handle || (handle == INVALID_HANDLE_VALUE))
|
||||
{
|
||||
|
Reference in New Issue
Block a user