2734 lines
75 KiB
C++
2734 lines
75 KiB
C++
#include <siadokandrive.h>
|
|
#include <filesystem>
|
|
#include <uploadmanager.h>
|
|
#include <dokan.h>
|
|
#include <filepath.h>
|
|
|
|
using namespace Sia::Api;
|
|
using namespace Sia::Api::Dokan;
|
|
|
|
static __int64 FileSize(const SString name)
|
|
{
|
|
struct _stat64 buf;
|
|
if (_wstat64(&name[0], &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.
|
|
// Files requested to be openned that are not cached will be downloaded first. If the file is not found in Sia, it will be treated as new.
|
|
// Keeping cache and Sia in synch will be a bit of a hastle, so it's strongly suggested to treat the cache folder as if it doesn't exist;
|
|
// however, simply deleting files in the cache folder should not be an issue as long as the drive is not mounted.
|
|
class SIADRIVE_DOKAN_EXPORTABLE DokanImpl
|
|
{
|
|
private:
|
|
typedef struct
|
|
{
|
|
HANDLE FileHandle;
|
|
SString SiaPath;
|
|
FilePath CacheFilePath;
|
|
bool Dummy;
|
|
bool Changed;
|
|
ACCESS_MASK DesiredAccess;
|
|
ULONG ShareMode;
|
|
SECURITY_ATTRIBUTES SecurityAttrib;
|
|
ULONG CreateDisp;
|
|
DWORD AttributesAndFlags;
|
|
PDOKAN_FILE_INFO DokanFileInfo;
|
|
} OpenFileInfo;
|
|
|
|
class DownloadToCacheBegin :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DownloadToCacheBegin(const OpenFileInfo& openFileInfo, const SString& tempFilePath) :
|
|
_openFileInfo(openFileInfo),
|
|
_tempFilePath(tempFilePath)
|
|
{
|
|
|
|
}
|
|
|
|
public:
|
|
virtual ~DownloadToCacheBegin() {}
|
|
|
|
private:
|
|
const OpenFileInfo _openFileInfo;
|
|
const SString _tempFilePath;
|
|
|
|
public:
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|FP|" + _openFileInfo.CacheFilePath +
|
|
"|SP|" + _openFileInfo.SiaPath +
|
|
"|TP|" + _tempFilePath;
|
|
}
|
|
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DownloadToCacheBegin(_openFileInfo, _tempFilePath));
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DownloadToCacheBegin";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "file_path", static_cast<SString>(_openFileInfo.CacheFilePath) },
|
|
{ "sia_path", _openFileInfo.SiaPath},
|
|
{ "temp_file_path", _tempFilePath}
|
|
};
|
|
}
|
|
};
|
|
|
|
class DownloadToCacheEnd :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DownloadToCacheEnd(const OpenFileInfo& openFileInfo, const SString& tempFilePath, const bool& result) :
|
|
_openFileInfo(openFileInfo),
|
|
_tempFilePath(tempFilePath),
|
|
_result(result)
|
|
{
|
|
|
|
}
|
|
|
|
public:
|
|
virtual ~DownloadToCacheEnd() {}
|
|
|
|
private:
|
|
const OpenFileInfo _openFileInfo;
|
|
const SString _tempFilePath;
|
|
const bool _result;
|
|
|
|
public:
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|FP|" + static_cast<SString>(_openFileInfo.CacheFilePath) +
|
|
"|SP|" + _openFileInfo.SiaPath +
|
|
"|TP|" + _tempFilePath +
|
|
"|RES|" + SString::FromBool(_result);
|
|
}
|
|
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DownloadToCacheEnd(_openFileInfo, _tempFilePath, _result));
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DownloadToCacheEnd";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "file_path", static_cast<SString>(_openFileInfo.CacheFilePath) },
|
|
{ "sia_path", _openFileInfo.SiaPath },
|
|
{ "temp_file_path", _tempFilePath },
|
|
{ "result", _result}
|
|
};
|
|
}
|
|
};
|
|
|
|
class MoveTempToCacheResult :
|
|
public CEvent
|
|
{
|
|
public:
|
|
MoveTempToCacheResult(const OpenFileInfo& openFileInfo, const SString& tempFilePath, const bool& result) :
|
|
_openFileInfo(openFileInfo),
|
|
_tempFilePath(tempFilePath),
|
|
_result(result)
|
|
{
|
|
|
|
}
|
|
|
|
public:
|
|
virtual ~MoveTempToCacheResult() {}
|
|
|
|
private:
|
|
const OpenFileInfo _openFileInfo;
|
|
const SString _tempFilePath;
|
|
const bool _result;
|
|
|
|
public:
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|FP|" + static_cast<SString>(_openFileInfo.CacheFilePath) +
|
|
"|SP|" + _openFileInfo.SiaPath +
|
|
"|TP|" + _tempFilePath +
|
|
"|RES|" + SString::FromBool(_result);
|
|
}
|
|
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new MoveTempToCacheResult(_openFileInfo, _tempFilePath, _result));
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "MoveTempToCacheResult";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "file_path", static_cast<SString>(_openFileInfo.CacheFilePath) },
|
|
{ "sia_path", _openFileInfo.SiaPath },
|
|
{ "temp_file_path", _tempFilePath },
|
|
{ "result", _result }
|
|
};
|
|
}
|
|
};
|
|
|
|
class AddToCacheComplete :
|
|
public CEvent
|
|
{
|
|
public:
|
|
AddToCacheComplete(const OpenFileInfo& openFileInfo, const bool& result) :
|
|
_openFileInfo(openFileInfo),
|
|
_result(result)
|
|
{
|
|
|
|
}
|
|
|
|
public:
|
|
virtual ~AddToCacheComplete() {}
|
|
|
|
private:
|
|
const OpenFileInfo _openFileInfo;
|
|
const bool _result;
|
|
|
|
public:
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|FP|" + static_cast<SString>(_openFileInfo.CacheFilePath) +
|
|
"|SP|" + _openFileInfo.SiaPath +
|
|
"|RES|" + SString::FromBool(_result);
|
|
}
|
|
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new AddToCacheComplete(_openFileInfo, _result));
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "AddToCacheComplete";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "file_path", static_cast<SString>(_openFileInfo.CacheFilePath) },
|
|
{ "sia_path", _openFileInfo.SiaPath },
|
|
{ "result", _result }
|
|
};
|
|
}
|
|
};
|
|
|
|
class DriveMountEnded :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DriveMountEnded(const SString mountLocation, const NTSTATUS& result) :
|
|
_mountLocation(mountLocation),
|
|
_result(result)
|
|
{
|
|
}
|
|
|
|
public:
|
|
virtual ~DriveMountEnded()
|
|
{
|
|
}
|
|
|
|
private:
|
|
const SString _mountLocation;
|
|
const NTSTATUS _result;
|
|
|
|
public:
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DriveMountEnded(_mountLocation, _result));
|
|
}
|
|
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|LOC|" + _mountLocation +
|
|
"|RES|" + SString::FromInt32(_result);
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DriveMountEnded";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "mount_location", _mountLocation },
|
|
{ "result", _result }
|
|
};
|
|
}
|
|
};
|
|
|
|
class DriveUnMounted :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DriveUnMounted(const SString& mountLocation) :
|
|
_mountLocation(mountLocation)
|
|
{
|
|
|
|
}
|
|
|
|
public:
|
|
virtual ~DriveUnMounted()
|
|
{
|
|
}
|
|
|
|
private:
|
|
const SString _mountLocation;
|
|
|
|
public:
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DriveUnMounted(_mountLocation));
|
|
}
|
|
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|LOC|" + _mountLocation;
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DriveUnMounted";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "mount_location", _mountLocation }
|
|
};
|
|
}
|
|
};
|
|
|
|
class DriveMounted :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DriveMounted(const SString& mountLocation) :
|
|
_mountLocation(mountLocation)
|
|
{
|
|
|
|
}
|
|
|
|
public:
|
|
virtual ~DriveMounted()
|
|
{
|
|
}
|
|
|
|
private:
|
|
const SString _mountLocation;
|
|
|
|
public:
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DriveMounted(_mountLocation));
|
|
}
|
|
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|LOC|" + _mountLocation;
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DriveMounted";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "mount_location", _mountLocation }
|
|
};
|
|
}
|
|
};
|
|
|
|
class DokanCreateFile :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DokanCreateFile(const SString& filePath, const DWORD& fileAttributesAndFlags, const DWORD& creationDisposition, const ACCESS_MASK& genericDesiredAccess, const NTSTATUS& result) :
|
|
CEvent(EventLevel::Debug),
|
|
_filePath(filePath),
|
|
_fileAttributesAndFlags(fileAttributesAndFlags),
|
|
_creationDisposition(creationDisposition),
|
|
_genericDesiredAccess(genericDesiredAccess),
|
|
_result(result)
|
|
{
|
|
|
|
}
|
|
|
|
public:
|
|
virtual ~DokanCreateFile()
|
|
{
|
|
}
|
|
|
|
private:
|
|
const SString _filePath;
|
|
const DWORD _fileAttributesAndFlags;
|
|
const DWORD _creationDisposition;
|
|
const ACCESS_MASK _genericDesiredAccess;
|
|
const NTSTATUS _result;
|
|
|
|
public:
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DokanCreateFile(_filePath, _fileAttributesAndFlags, _creationDisposition, _genericDesiredAccess, _result));
|
|
}
|
|
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|FILE|" + _filePath +
|
|
"|ATTR|" + SString::FromUInt32(_fileAttributesAndFlags) +
|
|
"|DISP|" + SString::FromUInt32(_creationDisposition) +
|
|
"|MASK|" + SString::FromUInt32(_genericDesiredAccess) +
|
|
"|RES|" + SString::FromUInt32(_result);
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DokanCreateFile";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "file_path", _filePath },
|
|
{ "attributes", _fileAttributesAndFlags},
|
|
{ "create_disposition", _creationDisposition},
|
|
{ "access_mask", _genericDesiredAccess},
|
|
{ "result", _result }
|
|
};
|
|
}
|
|
};
|
|
|
|
class DokanFindFiles :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DokanFindFiles(const SString& cachePath, const SString& rootPath, const SString& siaQuery, const SString& findFile, const SString& fileName, const NTSTATUS& result) :
|
|
CEvent(EventLevel::Debug),
|
|
_cachePath(cachePath),
|
|
_rootPath(rootPath),
|
|
_siaQuery(siaQuery),
|
|
_findFile(findFile),
|
|
_fileName(fileName),
|
|
_result(result)
|
|
{
|
|
}
|
|
|
|
public:
|
|
virtual ~DokanFindFiles()
|
|
{
|
|
}
|
|
|
|
private:
|
|
const SString _cachePath;
|
|
const SString _rootPath;
|
|
const SString _siaQuery;
|
|
const SString _findFile;
|
|
const SString _fileName;
|
|
const NTSTATUS _result;
|
|
|
|
public:
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DokanFindFiles(_cachePath, _rootPath, _siaQuery, _findFile, _fileName, _result));
|
|
}
|
|
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|PATH|" + _cachePath +
|
|
"|ROOT|" + _rootPath +
|
|
"|QUERY|" + _siaQuery +
|
|
"|FIND|" + _findFile +
|
|
"|FN|" + _fileName +
|
|
"|RES|" + SString::FromUInt32(_result);
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DokanFindFiles";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "file_path", _cachePath },
|
|
{ "root_path", _rootPath },
|
|
{ "sia_query", _siaQuery },
|
|
{ "find", _findFile },
|
|
{ "file_name", _fileName },
|
|
{ "result", _result }
|
|
};
|
|
}
|
|
};
|
|
|
|
class DokanCloseFile :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DokanCloseFile(const SString& cachePath) :
|
|
CEvent(EventLevel::Debug),
|
|
_cachePath(cachePath)
|
|
{
|
|
}
|
|
|
|
public:
|
|
virtual ~DokanCloseFile()
|
|
{
|
|
}
|
|
|
|
private:
|
|
const SString _cachePath;
|
|
|
|
public:
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DokanCloseFile(_cachePath));
|
|
}
|
|
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|PATH|" + _cachePath;
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DokanCloseFile";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "file_path", _cachePath }
|
|
};
|
|
}
|
|
};
|
|
|
|
class DokanGetFileInformation :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DokanGetFileInformation(const SString& cachePath, const SString& fileName, const bool& opened, const NTSTATUS& result) :
|
|
CEvent(EventLevel::Debug),
|
|
_cachePath(cachePath),
|
|
_fileName(fileName),
|
|
_opened(opened),
|
|
_result(result)
|
|
{
|
|
}
|
|
|
|
public:
|
|
virtual ~DokanGetFileInformation()
|
|
{
|
|
}
|
|
|
|
private:
|
|
const SString _cachePath;
|
|
const SString _fileName;
|
|
const bool _opened;
|
|
const NTSTATUS _result;
|
|
|
|
public:
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DokanGetFileInformation(_cachePath, _fileName, _opened, _result));
|
|
}
|
|
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|PATH|" + _cachePath +
|
|
"|FN|" + _fileName +
|
|
"|OPN|" + SString::FromBool(_opened) +
|
|
"|RES|" + SString::FromUInt32(_result);
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DokanGetFileInformation";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "file_path", _cachePath },
|
|
{ "file_name", _fileName },
|
|
{ "was_opened", _opened },
|
|
{ "result", _result }
|
|
};
|
|
}
|
|
};
|
|
|
|
class DokanReadFile :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DokanReadFile(const SString& cachePath, const bool& opened, const NTSTATUS& result) :
|
|
CEvent(EventLevel::Debug),
|
|
_cachePath(cachePath),
|
|
_opened(opened),
|
|
_result(result)
|
|
{
|
|
}
|
|
|
|
public:
|
|
virtual ~DokanReadFile()
|
|
{
|
|
}
|
|
|
|
private:
|
|
const SString _cachePath;
|
|
const bool _opened;
|
|
const NTSTATUS _result;
|
|
|
|
public:
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DokanReadFile(_cachePath, _opened, _result));
|
|
}
|
|
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|PATH|" + _cachePath +
|
|
"|OPN|" + SString::FromBool(_opened) +
|
|
"|RES|" + SString::FromUInt32(_result);
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DokanReadFile";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "file_path", _cachePath },
|
|
{ "opened", _opened },
|
|
{ "result", _result }
|
|
};
|
|
}
|
|
};
|
|
|
|
class DokanWriteFile :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DokanWriteFile(const SString& cachePath, const bool& opened, const NTSTATUS& result) :
|
|
CEvent(EventLevel::Debug),
|
|
_cachePath(cachePath),
|
|
_opened(opened),
|
|
_result(result)
|
|
{
|
|
}
|
|
|
|
public:
|
|
virtual ~DokanWriteFile()
|
|
{
|
|
}
|
|
|
|
private:
|
|
const SString _cachePath;
|
|
const bool _opened;
|
|
const NTSTATUS _result;
|
|
|
|
public:
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DokanWriteFile(_cachePath, _opened, _result));
|
|
}
|
|
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|PATH|" + _cachePath +
|
|
"|OPN|" + SString::FromBool(_opened) +
|
|
"|RES|" + SString::FromUInt32(_result);
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DokanWriteFile";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "file_path", _cachePath },
|
|
{ "opened", _opened },
|
|
{ "result", _result }
|
|
};
|
|
}
|
|
};
|
|
|
|
class DokanSetEndOfFile :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DokanSetEndOfFile(const SString& cachePath, const NTSTATUS& result) :
|
|
CEvent(EventLevel::Debug),
|
|
_cachePath(cachePath),
|
|
_result(result)
|
|
{
|
|
}
|
|
|
|
public:
|
|
virtual ~DokanSetEndOfFile()
|
|
{
|
|
}
|
|
|
|
private:
|
|
const SString _cachePath;
|
|
const NTSTATUS _result;
|
|
|
|
public:
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DokanSetEndOfFile(_cachePath, _result));
|
|
}
|
|
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|PATH|" + _cachePath +
|
|
"|RES|" + SString::FromUInt32(_result);
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DokanSetEndOfFile";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "file_path", _cachePath },
|
|
{ "result", _result }
|
|
};
|
|
}
|
|
};
|
|
|
|
class DokanFlushFileBuffers :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DokanFlushFileBuffers(const SString& cachePath, const NTSTATUS& result) :
|
|
CEvent(EventLevel::Debug),
|
|
_cachePath(cachePath),
|
|
_result(result)
|
|
{
|
|
}
|
|
|
|
public:
|
|
virtual ~DokanFlushFileBuffers()
|
|
{
|
|
}
|
|
|
|
private:
|
|
const SString _cachePath;
|
|
const NTSTATUS _result;
|
|
|
|
public:
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DokanFlushFileBuffers(_cachePath, _result));
|
|
}
|
|
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|PATH|" + _cachePath +
|
|
"|RES|" + SString::FromUInt32(_result);
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DokanFlushFileBuffers";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "file_path", _cachePath },
|
|
{ "result", _result }
|
|
};
|
|
}
|
|
};
|
|
|
|
class DokanDeleteDirectory :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DokanDeleteDirectory(const SString& cachePath, const NTSTATUS& result) :
|
|
_cachePath(cachePath),
|
|
_result(result)
|
|
{
|
|
}
|
|
|
|
public:
|
|
virtual ~DokanDeleteDirectory()
|
|
{
|
|
}
|
|
|
|
private:
|
|
const SString _cachePath;
|
|
const NTSTATUS _result;
|
|
|
|
public:
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DokanDeleteDirectory(_cachePath, _result));
|
|
}
|
|
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|PATH|" + _cachePath +
|
|
"|RES|" + SString::FromUInt32(_result);
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DokanDeleteDirectory";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "file_path", _cachePath },
|
|
{ "result", _result }
|
|
};
|
|
}
|
|
};
|
|
|
|
class DokanDeleteFileW :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DokanDeleteFileW(const SString& cachePath, const NTSTATUS& result) :
|
|
CEvent(EventLevel::Debug),
|
|
_cachePath(cachePath),
|
|
_result(result)
|
|
{
|
|
}
|
|
|
|
public:
|
|
virtual ~DokanDeleteFileW()
|
|
{
|
|
}
|
|
|
|
private:
|
|
const SString _cachePath;
|
|
const NTSTATUS _result;
|
|
|
|
public:
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DokanDeleteFileW(_cachePath, _result));
|
|
}
|
|
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|PATH|" + _cachePath +
|
|
"|RES|" + SString::FromUInt32(_result);
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DokanDeleteFileW";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "file_path", _cachePath },
|
|
{ "result", _result }
|
|
};
|
|
}
|
|
};
|
|
|
|
class DokanSetFileAttributesW :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DokanSetFileAttributesW(const SString& cachePath, const NTSTATUS& result) :
|
|
CEvent(EventLevel::Debug),
|
|
_cachePath(cachePath),
|
|
_result(result)
|
|
{
|
|
}
|
|
|
|
public:
|
|
virtual ~DokanSetFileAttributesW()
|
|
{
|
|
}
|
|
|
|
private:
|
|
const SString _cachePath;
|
|
const NTSTATUS _result;
|
|
|
|
public:
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DokanSetFileAttributesW(_cachePath, _result));
|
|
}
|
|
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|PATH|" + _cachePath +
|
|
"|RES|" + SString::FromUInt64(_result);
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DokanSetFileAttributesW";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "file_path", _cachePath },
|
|
{ "result", _result }
|
|
};
|
|
}
|
|
};
|
|
|
|
class DokanGetFileAttributesW :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DokanGetFileAttributesW(const SString& cachePath) :
|
|
CEvent(EventLevel::Debug),
|
|
_cachePath(cachePath)
|
|
{
|
|
}
|
|
|
|
public:
|
|
virtual ~DokanGetFileAttributesW()
|
|
{
|
|
}
|
|
|
|
private:
|
|
const SString _cachePath;
|
|
|
|
public:
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DokanGetFileAttributesW(_cachePath));
|
|
}
|
|
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|PATH|" + _cachePath;
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DokanGetFileAttributesW";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "file_path", _cachePath }
|
|
};
|
|
}
|
|
};
|
|
|
|
class DokanSetFileSecurityW :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DokanSetFileSecurityW(const SString& cachePath) :
|
|
CEvent(EventLevel::Debug),
|
|
_cachePath(cachePath)
|
|
{
|
|
}
|
|
|
|
public:
|
|
virtual ~DokanSetFileSecurityW()
|
|
{
|
|
}
|
|
|
|
private:
|
|
const SString _cachePath;
|
|
|
|
public:
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DokanSetFileSecurityW(_cachePath));
|
|
}
|
|
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|PATH|" + _cachePath;
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DokanSetFileSecurityW";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "file_path", _cachePath }
|
|
};
|
|
}
|
|
};
|
|
|
|
class DokanSetFileTime :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DokanSetFileTime(const SString& cachePath, const NTSTATUS& result) :
|
|
CEvent(EventLevel::Debug),
|
|
_cachePath(cachePath),
|
|
_result(result)
|
|
{
|
|
}
|
|
|
|
public:
|
|
virtual ~DokanSetFileTime()
|
|
{
|
|
}
|
|
|
|
private:
|
|
const SString _cachePath;
|
|
const NTSTATUS _result;
|
|
|
|
public:
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DokanSetFileTime(_cachePath, _result));
|
|
}
|
|
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|PATH|" + _cachePath +
|
|
"|RES|" + SString::FromUInt64(_result);
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DokanSetFileTime";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "file_path", _cachePath },
|
|
{ "result", _result }
|
|
};
|
|
}
|
|
};
|
|
|
|
class DokanSetAllocationSize :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DokanSetAllocationSize(const SString& cachePath, const NTSTATUS& result) :
|
|
CEvent(EventLevel::Debug),
|
|
_cachePath(cachePath),
|
|
_result(result)
|
|
{
|
|
}
|
|
|
|
public:
|
|
virtual ~DokanSetAllocationSize()
|
|
{
|
|
}
|
|
|
|
private:
|
|
const SString _cachePath;
|
|
const NTSTATUS _result;
|
|
|
|
public:
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DokanSetAllocationSize(_cachePath, _result));
|
|
}
|
|
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|PATH|" + _cachePath +
|
|
"|RES|" + SString::FromUInt64(_result);
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DokanSetAllocationSize";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "file_path", _cachePath },
|
|
{ "result", _result }
|
|
};
|
|
}
|
|
};
|
|
|
|
class DokanMoveFileW :
|
|
public CEvent
|
|
{
|
|
public:
|
|
DokanMoveFileW(const SString& srcPath, const SString& destPath, const NTSTATUS& result) :
|
|
CEvent(EventLevel::Debug),
|
|
_srcPath(srcPath),
|
|
_destPath(destPath),
|
|
_result(result)
|
|
{
|
|
}
|
|
|
|
public:
|
|
virtual ~DokanMoveFileW()
|
|
{
|
|
}
|
|
|
|
private:
|
|
const SString _srcPath;
|
|
const SString _destPath;
|
|
const NTSTATUS _result;
|
|
|
|
public:
|
|
virtual std::shared_ptr<CEvent> Clone() const override
|
|
{
|
|
return std::shared_ptr<CEvent>(new DokanMoveFileW(_srcPath, _destPath, _result));
|
|
}
|
|
|
|
virtual SString GetSingleLineMessage() const override
|
|
{
|
|
return GetEventName() +
|
|
"|SRC|" + _srcPath +
|
|
"|DEST|" + _destPath +
|
|
"|RES|" + SString::FromUInt64(_result);
|
|
}
|
|
|
|
virtual SString GetEventName() const override
|
|
{
|
|
return "DokanMoveFileW";
|
|
}
|
|
|
|
virtual json GetEventJson() const override
|
|
{
|
|
return
|
|
{
|
|
{ "event", GetEventName() },
|
|
{ "source_path", _srcPath },
|
|
{ "dest_path", _destPath },
|
|
{ "result", _result }
|
|
};
|
|
}
|
|
};
|
|
|
|
private:
|
|
static CSiaApi* _siaApi;
|
|
static CSiaDriveConfig* _siaDriveConfig;
|
|
static std::unique_ptr<CUploadManager> _uploadManager;
|
|
static DOKAN_OPERATIONS _dokanOps;
|
|
static DOKAN_OPTIONS _dokanOptions;
|
|
static FilePath _cacheLocation;
|
|
static std::unique_ptr<std::thread> _fileListThread;
|
|
static HANDLE _fileListStopEvent;
|
|
static CSiaFileTreePtr _siaFileTree;
|
|
static std::mutex _openFileMutex;
|
|
static std::unique_ptr<std::thread> _mountThread;
|
|
static NTSTATUS _mountStatus;
|
|
static SString _mountPoint;
|
|
static std::vector<OpenFileInfo*> _openFiles;
|
|
|
|
private:
|
|
inline static const FilePath& GetCacheLocation()
|
|
{
|
|
return _cacheLocation;
|
|
}
|
|
|
|
inline static CSiaFileTreePtr GetFileTree()
|
|
{
|
|
return _siaFileTree;
|
|
}
|
|
|
|
template<typename T>
|
|
static void SetCachedFileTime(const SString& filePath, T* fd)
|
|
{
|
|
WIN32_FIND_DATA find = { 0 };
|
|
HANDLE findHandle = ::FindFirstFile(&filePath[0], &find);
|
|
if (findHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
fd->ftCreationTime = find.ftCreationTime;
|
|
fd->ftLastAccessTime = find.ftLastAccessTime;
|
|
fd->ftLastWriteTime = find.ftLastWriteTime;
|
|
|
|
::FindClose(findHandle);
|
|
}
|
|
}
|
|
|
|
static bool AddFileToCache(OpenFileInfo& openFileInfo, PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
// TODO taking too long
|
|
bool ret = true;
|
|
if (openFileInfo.Dummy)
|
|
{
|
|
FilePath tempFilePath = FilePath::GetTempDirectory();
|
|
tempFilePath.Append(GenerateSha256(openFileInfo.SiaPath) + ".siatmp");
|
|
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DownloadToCacheBegin(openFileInfo, tempFilePath)));
|
|
ret = ApiSuccess(_siaApi->GetRenter()->DownloadFile(openFileInfo.SiaPath, tempFilePath));
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DownloadToCacheEnd(openFileInfo, tempFilePath, ret)));
|
|
if (ret)
|
|
{
|
|
// Find all open handles for requested file
|
|
std::vector<OpenFileInfo*> matched;
|
|
matched.push_back(&openFileInfo);
|
|
{
|
|
std::lock_guard<std::mutex> l(_openFileMutex);
|
|
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)
|
|
{
|
|
std::lock_guard<std::mutex> l(_openFileMutex);
|
|
if (std::find(_openFiles.begin(), _openFiles.end(), ofi) != _openFiles.end())
|
|
{
|
|
::CloseHandle(ofi->FileHandle);
|
|
}
|
|
}
|
|
|
|
FilePath src(tempFilePath);
|
|
FilePath dest(openFileInfo.CacheFilePath);
|
|
ret = dest.DeleteFile() && src.MoveFile(dest);
|
|
if (!ret)
|
|
{
|
|
src.DeleteFile();
|
|
}
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(MoveTempToCacheResult(openFileInfo, tempFilePath, ret)));
|
|
|
|
// Re-open all files
|
|
{
|
|
std::lock_guard<std::mutex> l(_openFileMutex);
|
|
for (auto& ofi : matched)
|
|
{
|
|
if (std::find(_openFiles.begin(), _openFiles.end(), ofi) != _openFiles.end())
|
|
{
|
|
ofi->FileHandle = ::CreateFile(&ofi->CacheFilePath[0], ofi->DesiredAccess, ofi->ShareMode, ofi->SecurityAttrib.nLength ? &ofi->SecurityAttrib : nullptr, ofi->CreateDisp, ofi->AttributesAndFlags, nullptr);
|
|
ofi->Dummy = !ret;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(AddToCacheComplete(openFileInfo, ret)));
|
|
return ret;
|
|
}
|
|
|
|
inline static bool AddDummyFileToCache(const SString& siaPath)
|
|
{
|
|
FilePath dest(GetCacheLocation(), siaPath);
|
|
return dest.CreateEmptyFile();
|
|
}
|
|
|
|
static void HandleSiaFileClose(const OpenFileInfo& openFileInfo, const std::uint64_t& fileSize, const bool& deleteOnClose)
|
|
{
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanCloseFile(openFileInfo.CacheFilePath)));
|
|
if (deleteOnClose)
|
|
{
|
|
::CloseHandle(openFileInfo.FileHandle);
|
|
auto result = _uploadManager->Remove(openFileInfo.SiaPath);
|
|
if (!ApiSuccess(result))
|
|
{
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemCriticalEvent(result.GetReason()));
|
|
}
|
|
}
|
|
else if (openFileInfo.Changed)
|
|
{
|
|
if (fileSize > 0)
|
|
{
|
|
// Retrieve the file times for the file.
|
|
FILETIME fileTimes[3];
|
|
::GetFileTime(openFileInfo.FileHandle, &fileTimes[0], &fileTimes[1], &fileTimes[2]);
|
|
::CloseHandle(openFileInfo.FileHandle);
|
|
auto result = _uploadManager->AddOrUpdate(openFileInfo.SiaPath, openFileInfo.CacheFilePath, *reinterpret_cast<std::uint64_t*>(&fileTimes[2]));
|
|
if (!ApiSuccess(result))
|
|
{
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemCriticalEvent(result.GetReason()));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
::CloseHandle(openFileInfo.FileHandle);
|
|
|
|
// Treat 0 length files as deleted in Sia - cache retains 0-length
|
|
auto result = _uploadManager->Remove(openFileInfo.SiaPath);
|
|
if (!ApiSuccess(result))
|
|
{
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemCriticalEvent(result.GetReason()));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
::CloseHandle(openFileInfo.FileHandle);
|
|
}
|
|
|
|
std::lock_guard<std::mutex> l(_openFileMutex);
|
|
_openFiles.erase(std::remove(_openFiles.begin(), _openFiles.end(), &openFileInfo), _openFiles.end());
|
|
}
|
|
|
|
static void StartFileListThread()
|
|
{
|
|
if (!_fileListThread)
|
|
{
|
|
_fileListStopEvent = ::CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
|
_fileListThread.reset(new std::thread([]()
|
|
{
|
|
do
|
|
{
|
|
RefreshActiveFileTree();
|
|
} while (::WaitForSingleObject(_fileListStopEvent, 1000) == WAIT_TIMEOUT);
|
|
}));
|
|
}
|
|
}
|
|
|
|
static void StopFileListThread()
|
|
{
|
|
if (_fileListThread)
|
|
{
|
|
::SetEvent(_fileListStopEvent);
|
|
_fileListThread->join();
|
|
_fileListThread.reset(nullptr);
|
|
::CloseHandle(_fileListStopEvent);
|
|
}
|
|
}
|
|
|
|
static void RefreshActiveFileTree(const bool& force = false)
|
|
{
|
|
if (force)
|
|
{
|
|
_siaApi->GetRenter()->RefreshFileTree();
|
|
}
|
|
|
|
CSiaFileTreePtr siaFileTree;
|
|
_siaApi->GetRenter()->GetFileTree(siaFileTree);
|
|
_siaFileTree = siaFileTree;
|
|
}
|
|
|
|
// Dokan callbacks
|
|
private:
|
|
static NTSTATUS DOKAN_CALLBACK Sia_ZwCreateFile(
|
|
LPCWSTR fileName,
|
|
PDOKAN_IO_SECURITY_CONTEXT securityContext,
|
|
ACCESS_MASK desiredAccess,
|
|
ULONG fileAttributes,
|
|
ULONG shareAccess,
|
|
ULONG createDisposition,
|
|
ULONG createOptions,
|
|
PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
SECURITY_ATTRIBUTES securityAttrib;
|
|
securityAttrib.nLength = sizeof(securityAttrib);
|
|
securityAttrib.lpSecurityDescriptor = securityContext->AccessState.SecurityDescriptor;
|
|
securityAttrib.bInheritHandle = FALSE;
|
|
|
|
DWORD fileAttributesAndFlags;
|
|
DWORD creationDisposition;
|
|
DokanMapKernelToUserCreateFileFlags(fileAttributes, createOptions, createDisposition, &fileAttributesAndFlags, &creationDisposition);
|
|
|
|
ACCESS_MASK genericDesiredAccess = DokanMapStandardToGenericAccess(desiredAccess);
|
|
|
|
NTSTATUS ret = STATUS_SUCCESS;
|
|
// Probably not going to happen, but just in case
|
|
if (FilePath(fileName).IsUNC())
|
|
{
|
|
ret = STATUS_ILLEGAL_ELEMENT_ADDRESS;
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanCreateFile(fileName, fileAttributesAndFlags, creationDisposition, genericDesiredAccess, ret)));
|
|
}
|
|
else
|
|
{
|
|
// When filePath is a directory, needs to change the flag so that the file can
|
|
// be opened.
|
|
FilePath cacheFilePath(GetCacheLocation(), fileName);
|
|
DWORD fileAttr = ::GetFileAttributes(&cacheFilePath[0]);
|
|
|
|
if ((fileAttr != INVALID_FILE_ATTRIBUTES) &&
|
|
(fileAttr & FILE_ATTRIBUTE_DIRECTORY) &&
|
|
!(createOptions & FILE_NON_DIRECTORY_FILE))
|
|
{
|
|
dokanFileInfo->IsDirectory = TRUE;
|
|
if (desiredAccess & DELETE)
|
|
{
|
|
// Needed by FindFirstFile to see if directory is empty or not
|
|
shareAccess |= FILE_SHARE_READ;
|
|
}
|
|
}
|
|
|
|
// Folder (cache operation only)
|
|
if (dokanFileInfo->IsDirectory)
|
|
{
|
|
if (creationDisposition == CREATE_NEW)
|
|
{
|
|
if (!cacheFilePath.CreateDirectory())
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
}
|
|
else if (creationDisposition == OPEN_ALWAYS)
|
|
{
|
|
if (!cacheFilePath.CreateDirectory())
|
|
{
|
|
DWORD error = ::GetLastError();
|
|
if (error != ERROR_ALREADY_EXISTS)
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
}
|
|
}
|
|
else if (createDisposition == OPEN_EXISTING)
|
|
{
|
|
if (!cacheFilePath.IsDirectory())
|
|
{
|
|
ret = STATUS_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
if (ret == STATUS_SUCCESS)
|
|
{
|
|
//Check first if we're trying to open a file as a directory.
|
|
if (fileAttr != INVALID_FILE_ATTRIBUTES &&
|
|
!(fileAttr & FILE_ATTRIBUTE_DIRECTORY) &&
|
|
(createOptions & FILE_DIRECTORY_FILE))
|
|
{
|
|
ret = STATUS_NOT_A_DIRECTORY;
|
|
}
|
|
else
|
|
{
|
|
// FILE_FLAG_BACKUP_SEMANTICS is required for opening directory handles
|
|
HANDLE handle = ::CreateFile(&cacheFilePath[0], genericDesiredAccess, shareAccess, &securityAttrib, OPEN_EXISTING, fileAttributesAndFlags | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr);
|
|
if (handle == INVALID_HANDLE_VALUE)
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
else
|
|
{
|
|
OpenFileInfo* ofi = new OpenFileInfo();
|
|
ofi->CacheFilePath = cacheFilePath;
|
|
ofi->Dummy = false;
|
|
ofi->Changed = false;
|
|
ofi->FileHandle = handle;
|
|
ofi->DokanFileInfo = dokanFileInfo;
|
|
dokanFileInfo->Context = reinterpret_cast<ULONG64>(ofi);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else // File (cache and/or Sia operation)
|
|
{
|
|
// Formulate Sia path
|
|
SString siaPath = CSiaApi::FormatToSiaPath(fileName); // Strip drive letter to get Sia path
|
|
if (siaPath.Length())
|
|
{
|
|
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 CREATE_NEW:
|
|
{
|
|
if (exists)
|
|
{
|
|
ret = STATUS_OBJECT_NAME_EXISTS;
|
|
}
|
|
else
|
|
{
|
|
isCreateOp = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OPEN_ALWAYS:
|
|
{
|
|
if (!exists)
|
|
{
|
|
isCreateOp = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OPEN_EXISTING:
|
|
{
|
|
if (!exists)
|
|
{
|
|
ret = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TRUNCATE_EXISTING:
|
|
{
|
|
if (exists)
|
|
{
|
|
isCreateOp = isReplaceOp = true;
|
|
}
|
|
else
|
|
{
|
|
ret = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// Nothing to do
|
|
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());
|
|
}
|
|
}
|
|
|
|
bool isDummy = false;
|
|
if (ret == STATUS_SUCCESS)
|
|
{
|
|
if (!isCreateOp)
|
|
{
|
|
if (cacheFilePath.IsFile())
|
|
{
|
|
isDummy = (siaExists && (FileSize(&cacheFilePath[0]) == 0));
|
|
}
|
|
else if (siaExists)
|
|
{
|
|
isDummy = AddDummyFileToCache(siaPath);
|
|
if (!isDummy)
|
|
{
|
|
ret = STATUS_ACCESS_DENIED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = STATUS_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
if (ret == STATUS_SUCCESS)
|
|
{
|
|
std::lock_guard<std::mutex> l(_openFileMutex);
|
|
// 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;
|
|
ofi->Dummy = isDummy;
|
|
ofi->Changed = false;
|
|
ofi->FileHandle = handle;
|
|
ofi->DesiredAccess = genericDesiredAccess;
|
|
ofi->ShareMode = shareAccess;
|
|
ofi->SecurityAttrib = securityAttrib;
|
|
ofi->CreateDisp = createDisposition;
|
|
ofi->AttributesAndFlags = fileAttributesAndFlags;
|
|
ofi->DokanFileInfo = dokanFileInfo;
|
|
dokanFileInfo->Context = reinterpret_cast<ULONG64>(ofi);
|
|
_openFiles.push_back(ofi);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = STATUS_INVALID_SERVER_STATE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
}
|
|
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanCreateFile(cacheFilePath, fileAttributesAndFlags, creationDisposition, genericDesiredAccess, ret)));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK Sia_FindFiles(LPCWSTR fileName, PFillFindData fillFindData, PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
NTSTATUS ret = STATUS_INVALID_SERVER_STATE;
|
|
auto siaFileTree = GetFileTree();
|
|
if (siaFileTree)
|
|
{
|
|
SString siaFileQuery = CSiaApi::FormatToSiaPath(fileName);
|
|
SString siaRootPath = CSiaApi::FormatToSiaPath(fileName);
|
|
SString siaDirQuery;
|
|
FilePath findFile = GetCacheLocation();
|
|
FilePath cachePath = GetCacheLocation();
|
|
if (FilePath::DirSep == fileName)
|
|
{
|
|
siaFileQuery += L"/*.*";
|
|
siaDirQuery = "/";
|
|
findFile.Append("*");
|
|
}
|
|
else
|
|
{
|
|
cachePath.Append(fileName);
|
|
findFile.Append(fileName);
|
|
if (dokanFileInfo->IsDirectory)
|
|
{
|
|
siaDirQuery = siaFileQuery;
|
|
siaFileQuery += L"/*.*";
|
|
findFile.Append("*");
|
|
}
|
|
}
|
|
|
|
WIN32_FIND_DATA findData = { 0 };
|
|
HANDLE findHandle = ::FindFirstFile(&findFile[0], &findData);
|
|
if (findHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
else
|
|
{
|
|
// Find local files
|
|
std::unordered_map<SString, std::uint8_t> dirs;
|
|
std::unordered_map<SString, std::uint8_t> files;
|
|
do
|
|
{
|
|
if ((wcscmp(fileName, L"\\") != 0) || (wcscmp(findData.cFileName, L".") != 0) && (wcscmp(findData.cFileName, L"..") != 0))
|
|
{
|
|
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
dirs.insert({ findData.cFileName, 0 });
|
|
fillFindData(&findData, dokanFileInfo);
|
|
}
|
|
else
|
|
{
|
|
bool exists;
|
|
if (!ApiSuccess(_siaApi->GetRenter()->FileExists(CSiaApi::FormatToSiaPath(FilePath(fileName, findData.cFileName)), exists)))
|
|
{
|
|
::FindClose(findHandle);
|
|
return STATUS_INVALID_DEVICE_STATE;
|
|
}
|
|
|
|
if (findData.nFileSizeHigh || findData.nFileSizeLow || !exists)
|
|
{
|
|
fillFindData(&findData, dokanFileInfo);
|
|
files.insert({ findData.cFileName, 1 });
|
|
}
|
|
}
|
|
}
|
|
} while (::FindNextFile(findHandle, &findData) != 0);
|
|
|
|
DWORD error = ::GetLastError();
|
|
::FindClose(findHandle);
|
|
|
|
if (error == ERROR_NO_MORE_FILES)
|
|
{
|
|
// Find Sia directories
|
|
if (!static_cast<SString>(siaDirQuery).IsNullOrEmpty())
|
|
{
|
|
auto dirList = siaFileTree->QueryDirectories(siaDirQuery);
|
|
for (auto& dir : dirList)
|
|
{
|
|
if (dirs.find(dir) == dirs.end())
|
|
{
|
|
// Create cache sub-folder
|
|
FilePath subCachePath(cachePath, dir);
|
|
if (!subCachePath.IsDirectory())
|
|
{
|
|
subCachePath.CreateDirectory();
|
|
}
|
|
|
|
WIN32_FIND_DATA fd = { 0 };
|
|
wcscpy_s(fd.cFileName, dir.str().c_str());
|
|
fd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
|
|
fillFindData(&fd, dokanFileInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find Sia files
|
|
auto fileList = siaFileTree->Query(siaFileQuery);
|
|
for (auto& file : fileList)
|
|
{
|
|
FilePath fp = file->GetSiaPath();
|
|
fp.StripToFileName();
|
|
if (files.find(fp) == files.end())
|
|
{
|
|
WIN32_FIND_DATA fd = { 0 };
|
|
wcscpy_s(fd.cFileName, &fp[0]);
|
|
|
|
LARGE_INTEGER li = { 0 };
|
|
li.QuadPart = file->GetFileSize();
|
|
fd.nFileSizeHigh = li.HighPart;
|
|
fd.nFileSizeLow = li.LowPart;
|
|
SetCachedFileTime(FilePath(GetCacheLocation(), file->GetSiaPath()), &fd);
|
|
fillFindData(&fd, dokanFileInfo);
|
|
}
|
|
}
|
|
|
|
ret = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
}
|
|
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanFindFiles(cachePath, siaDirQuery, siaFileQuery, findFile, fileName, ret)));
|
|
}
|
|
else
|
|
{
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanFindFiles("", "", "", "", fileName, ret)));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void DOKAN_CALLBACK Sia_CloseFile(LPCWSTR fileName, PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
FilePath filePath(GetCacheLocation(), fileName);
|
|
if (dokanFileInfo->Context)
|
|
{
|
|
auto openFileInfo = reinterpret_cast<OpenFileInfo*>(dokanFileInfo->Context);
|
|
if (dokanFileInfo->IsDirectory)
|
|
{
|
|
::CloseHandle(openFileInfo->FileHandle);
|
|
}
|
|
else
|
|
{
|
|
LARGE_INTEGER li = { 0 };
|
|
::GetFileSizeEx(openFileInfo->FileHandle, &li);
|
|
HandleSiaFileClose(*openFileInfo, li.QuadPart, dokanFileInfo->DeleteOnClose ? true : false);
|
|
}
|
|
|
|
delete openFileInfo;
|
|
dokanFileInfo->Context = 0;
|
|
}
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK Sia_GetFileInformation(LPCWSTR fileName, LPBY_HANDLE_FILE_INFORMATION handleFileInfo, PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
auto openFileInfo = reinterpret_cast<OpenFileInfo*>(dokanFileInfo->Context);
|
|
bool opened = false;
|
|
NTSTATUS ret = STATUS_SUCCESS;
|
|
|
|
auto siaFileTree = GetFileTree();
|
|
auto siaFile = siaFileTree && openFileInfo ? siaFileTree->GetFile(openFileInfo->SiaPath) : nullptr;
|
|
SString cachFileLocation = openFileInfo ? openFileInfo->CacheFilePath : FilePath(GetCacheLocation(), fileName);
|
|
|
|
HANDLE tempHandle = openFileInfo ? openFileInfo->FileHandle : nullptr;
|
|
if (FilePath(cachFileLocation).IsFile() && (!tempHandle || (tempHandle == INVALID_HANDLE_VALUE)))
|
|
{
|
|
tempHandle = ::CreateFile(&cachFileLocation[0], GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
|
|
if (tempHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
else
|
|
{
|
|
opened = true;
|
|
}
|
|
}
|
|
|
|
if (ret == STATUS_SUCCESS)
|
|
{
|
|
if ((openFileInfo && openFileInfo->Dummy) || (siaFile && !FilePath(cachFileLocation).IsFile()))
|
|
{
|
|
LARGE_INTEGER li = { 0 };
|
|
li.QuadPart = siaFile->GetFileSize();
|
|
GetFileInformationByHandle(tempHandle, handleFileInfo);
|
|
handleFileInfo->dwFileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_ARCHIVE;
|
|
handleFileInfo->nFileSizeHigh = li.HighPart;
|
|
handleFileInfo->nFileSizeLow = li.LowPart;
|
|
SetCachedFileTime(cachFileLocation, handleFileInfo);
|
|
}
|
|
else if (!::GetFileInformationByHandle(tempHandle, handleFileInfo))
|
|
{
|
|
// fileName is a root directory
|
|
// in this case, FindFirstFile can't get directory information
|
|
if (wcscmp(fileName, L"\\") == 0)
|
|
{
|
|
handleFileInfo->dwFileAttributes = ::GetFileAttributes(&cachFileLocation[0]);
|
|
}
|
|
else
|
|
{
|
|
WIN32_FIND_DATA find = { 0 };
|
|
HANDLE findHandle = ::FindFirstFile(&cachFileLocation[0], &find);
|
|
if (findHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
else
|
|
{
|
|
handleFileInfo->dwFileAttributes = find.dwFileAttributes;
|
|
handleFileInfo->ftCreationTime = find.ftCreationTime;
|
|
handleFileInfo->ftLastAccessTime = find.ftLastAccessTime;
|
|
handleFileInfo->ftLastWriteTime = find.ftLastWriteTime;
|
|
handleFileInfo->nFileSizeHigh = find.nFileSizeHigh;
|
|
handleFileInfo->nFileSizeLow = find.nFileSizeLow;
|
|
|
|
::FindClose(findHandle);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (opened)
|
|
{
|
|
::CloseHandle(tempHandle);
|
|
}
|
|
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanGetFileInformation(cachFileLocation, fileName, opened, ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK Sia_Mounted(PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
// May spend a little wait time here while files are cleaned-up and re-added to queue
|
|
_uploadManager.reset(new CUploadManager(CSiaCurl(_siaApi->GetHostConfig()), _siaDriveConfig));
|
|
StartFileListThread();
|
|
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DriveMounted(_mountPoint)));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK Sia_Unmounted(PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
_uploadManager.reset(nullptr);
|
|
StopFileListThread();
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DriveUnMounted(_mountPoint)));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK Sia_GetDiskFreeSpaceW(
|
|
PULONGLONG FreeBytesAvailable, PULONGLONG TotalNumberOfBytes,
|
|
PULONGLONG TotalNumberOfFreeBytes, PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
UNREFERENCED_PARAMETER(dokanFileInfo);
|
|
SiaCurrency allocatedFunds = _siaApi->GetRenter()->GetFunds();
|
|
SiaCurrency unspentFunds = _siaApi->GetRenter()->GetUnspent();
|
|
SiaCurrency totalUsedGb = _siaApi->GetRenter()->GetTotalUsedBytes() ? _siaApi->GetRenter()->GetTotalUsedBytes() : 0.0;
|
|
auto totalAvailable = (totalUsedGb / (allocatedFunds - unspentFunds)) * allocatedFunds;
|
|
auto totalRemainGb = totalAvailable - totalUsedGb;
|
|
|
|
*FreeBytesAvailable = totalRemainGb.ToUInt();
|
|
*TotalNumberOfBytes = totalAvailable.ToUInt();
|
|
*TotalNumberOfFreeBytes = totalAvailable.ToUInt();
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK Sia_GetVolumeInformationW(
|
|
LPWSTR VolumeNameBuffer, DWORD VolumeNameSize, LPDWORD VolumeSerialNumber,
|
|
LPDWORD MaximumComponentLength, LPDWORD FileSystemFlags,
|
|
LPWSTR FileSystemNameBuffer, DWORD FileSystemNameSize,
|
|
PDOKAN_FILE_INFO dokanFileInfo) {
|
|
UNREFERENCED_PARAMETER(dokanFileInfo);
|
|
|
|
wcscpy_s(VolumeNameBuffer, VolumeNameSize, L"SiaDrive");
|
|
*VolumeSerialNumber = 0x5E05140D;
|
|
*MaximumComponentLength = MAX_PATH;
|
|
*FileSystemFlags = FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES |
|
|
FILE_SUPPORTS_REMOTE_STORAGE | FILE_UNICODE_ON_DISK |
|
|
FILE_PERSISTENT_ACLS;
|
|
|
|
// File system name could be anything up to 10 characters.
|
|
// But Windows check few feature availability based on file system name.
|
|
// For this, it is recommended to set NTFS or FAT here.
|
|
wcscpy_s(FileSystemNameBuffer, FileSystemNameSize, L"NTFS");
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
static NTSTATUS DOKAN_CALLBACK Sia_ReadFile(LPCWSTR fileName, LPVOID buffer,
|
|
DWORD bufferLen,
|
|
LPDWORD readLength,
|
|
LONGLONG offset,
|
|
PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
NTSTATUS ret = STATUS_SUCCESS;
|
|
bool opened = false;
|
|
|
|
FilePath filePath(GetCacheLocation(), fileName);
|
|
auto openFileInfo = reinterpret_cast<OpenFileInfo*>(dokanFileInfo->Context);
|
|
if (openFileInfo && openFileInfo->Dummy)
|
|
{
|
|
if (!AddFileToCache(*openFileInfo, dokanFileInfo))
|
|
{
|
|
ret = STATUS_INVALID_DEVICE_STATE;
|
|
}
|
|
}
|
|
|
|
HANDLE tempHandle = openFileInfo ? openFileInfo->FileHandle : 0;
|
|
if (ret == STATUS_SUCCESS)
|
|
{
|
|
if (!tempHandle || (tempHandle == INVALID_HANDLE_VALUE))
|
|
{
|
|
if (!filePath.IsFile())
|
|
{
|
|
if (!AddFileToCache(*openFileInfo, dokanFileInfo))
|
|
{
|
|
ret = STATUS_INVALID_DEVICE_STATE;
|
|
}
|
|
}
|
|
|
|
if (ret == STATUS_SUCCESS)
|
|
{
|
|
tempHandle = ::CreateFile(&filePath[0], GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
|
|
if (tempHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
else
|
|
{
|
|
opened = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ret == STATUS_SUCCESS)
|
|
{
|
|
LARGE_INTEGER distanceToMove;
|
|
distanceToMove.QuadPart = offset;
|
|
if (!::SetFilePointerEx(tempHandle, distanceToMove, nullptr, FILE_BEGIN))
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
else if (!::ReadFile(tempHandle, buffer, bufferLen, readLength, nullptr))
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (opened)
|
|
{
|
|
::CloseHandle(tempHandle);
|
|
}
|
|
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanReadFile(filePath, opened, ret)));
|
|
return ret;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK Sia_WriteFile(LPCWSTR fileName, LPCVOID buffer,
|
|
DWORD bytesToWrite,
|
|
LPDWORD bytesWritten,
|
|
LONGLONG offset,
|
|
PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
NTSTATUS ret = STATUS_SUCCESS;
|
|
FilePath filePath(GetCacheLocation(), fileName);
|
|
auto openFileInfo = reinterpret_cast<OpenFileInfo*>(dokanFileInfo->Context);
|
|
if (openFileInfo && openFileInfo->Dummy)
|
|
{
|
|
if (!AddFileToCache(*openFileInfo, dokanFileInfo))
|
|
{
|
|
ret = STATUS_INVALID_DEVICE_STATE;
|
|
}
|
|
}
|
|
|
|
bool opened = false;
|
|
HANDLE tempHandle = openFileInfo ? openFileInfo->FileHandle : 0;
|
|
if (!tempHandle || (tempHandle == INVALID_HANDLE_VALUE))
|
|
{
|
|
if (!filePath.IsFile())
|
|
{
|
|
if (!AddFileToCache(*openFileInfo, dokanFileInfo))
|
|
{
|
|
ret = STATUS_INVALID_DEVICE_STATE;
|
|
}
|
|
}
|
|
|
|
if (ret == STATUS_SUCCESS)
|
|
{
|
|
tempHandle = ::CreateFile(&filePath[0], GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
|
|
if (tempHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
else
|
|
{
|
|
opened = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
LARGE_INTEGER li = { 0 };
|
|
if (ret == STATUS_SUCCESS)
|
|
{
|
|
if (!::GetFileSizeEx(tempHandle, &li))
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
else
|
|
{
|
|
LARGE_INTEGER distanceToMove;
|
|
if (dokanFileInfo->WriteToEndOfFile)
|
|
{
|
|
LARGE_INTEGER z = { 0 };
|
|
if (!::SetFilePointerEx(tempHandle, z, nullptr, FILE_END))
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Paging IO cannot write after allocate file size.
|
|
if (dokanFileInfo->PagingIo)
|
|
{
|
|
if (offset >= li.QuadPart)
|
|
{
|
|
*bytesWritten = 0;
|
|
if (opened)
|
|
{
|
|
::CloseHandle(tempHandle);
|
|
}
|
|
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanWriteFile(filePath, opened, ret)));
|
|
return ret;
|
|
}
|
|
|
|
if ((offset + bytesToWrite) > li.QuadPart)
|
|
{
|
|
UINT64 bytes = li.QuadPart - offset;
|
|
if (bytes >> 32)
|
|
{
|
|
bytesToWrite = static_cast<DWORD>(bytes & 0xFFFFFFFFUL);
|
|
}
|
|
else
|
|
{
|
|
bytesToWrite = static_cast<DWORD>(bytes);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (offset > li.QuadPart)
|
|
{
|
|
// In the mirror sample helperZeroFileData is not necessary. NTFS will
|
|
// zero a hole.
|
|
// But if user's file system is different from NTFS( or other Windows's
|
|
// file systems ) then users will have to zero the hole themselves.
|
|
}
|
|
|
|
distanceToMove.QuadPart = offset;
|
|
if (!::SetFilePointerEx(tempHandle, distanceToMove, nullptr, FILE_BEGIN))
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
}
|
|
|
|
if (ret == STATUS_SUCCESS)
|
|
{
|
|
if (::WriteFile(tempHandle, buffer, bytesToWrite, bytesWritten, nullptr))
|
|
{
|
|
if (openFileInfo)
|
|
{
|
|
openFileInfo->Changed = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (opened)
|
|
{
|
|
OpenFileInfo ofi;
|
|
ofi.FileHandle = tempHandle;
|
|
ofi.CacheFilePath = filePath;
|
|
ofi.Changed = true;
|
|
ofi.Dummy = false;
|
|
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);
|
|
}
|
|
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanWriteFile(filePath, opened, ret)));
|
|
return ret;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK Sia_SetEndOfFile(LPCWSTR fileName, LONGLONG byteOffset, PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
NTSTATUS ret = STATUS_SUCCESS;
|
|
FilePath filePath(GetCacheLocation(), fileName);
|
|
|
|
auto openFileInfo = reinterpret_cast<OpenFileInfo*>(dokanFileInfo->Context);
|
|
if (openFileInfo && openFileInfo->Dummy)
|
|
{
|
|
if (!AddFileToCache(*openFileInfo, dokanFileInfo))
|
|
{
|
|
ret = STATUS_INVALID_DEVICE_STATE;
|
|
}
|
|
}
|
|
|
|
if (ret == STATUS_SUCCESS)
|
|
{
|
|
if (!openFileInfo || !openFileInfo->FileHandle || (openFileInfo->FileHandle == INVALID_HANDLE_VALUE))
|
|
{
|
|
ret = STATUS_INVALID_HANDLE;
|
|
}
|
|
else
|
|
{
|
|
LARGE_INTEGER sz = { 0 };
|
|
::GetFileSizeEx(openFileInfo->FileHandle, &sz);
|
|
|
|
LARGE_INTEGER offset;
|
|
offset.QuadPart = byteOffset;
|
|
if (!::SetFilePointerEx(openFileInfo->FileHandle, offset, nullptr, FILE_BEGIN))
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
else if (!::SetEndOfFile(openFileInfo->FileHandle))
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
|
|
if (ret == STATUS_SUCCESS)
|
|
{
|
|
openFileInfo->Changed = (offset.QuadPart != (sz.QuadPart - 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanSetEndOfFile(filePath, ret)));
|
|
return ret;
|
|
}
|
|
|
|
static void DOKAN_CALLBACK Sia_Cleanup(LPCWSTR fileName, PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
FilePath filePath(GetCacheLocation(), fileName);
|
|
auto openFileInfo = reinterpret_cast<OpenFileInfo*>(dokanFileInfo->Context);
|
|
if (dokanFileInfo->Context)
|
|
{
|
|
if (dokanFileInfo->IsDirectory)
|
|
{
|
|
::CloseHandle(openFileInfo->FileHandle);
|
|
}
|
|
else
|
|
{
|
|
LARGE_INTEGER li = { 0 };
|
|
::GetFileSizeEx(openFileInfo->FileHandle, &li);
|
|
// !!! File handle will be closed in this method !!!
|
|
HandleSiaFileClose(*openFileInfo, li.QuadPart, dokanFileInfo->DeleteOnClose ? true : false);
|
|
}
|
|
|
|
delete openFileInfo;
|
|
dokanFileInfo->Context = 0;
|
|
}
|
|
|
|
if (dokanFileInfo->DeleteOnClose)
|
|
{
|
|
// Should already be deleted by CloseHandle
|
|
// if open with FILE_FLAG_DELETE_ON_CLOSE
|
|
if (dokanFileInfo->IsDirectory)
|
|
{
|
|
while (!filePath.RemoveDirectory());
|
|
}
|
|
else
|
|
{
|
|
filePath.DeleteFile();
|
|
|
|
int count = 0;
|
|
{
|
|
std::lock_guard<std::mutex> l(_openFileMutex);
|
|
std::count_if(_openFiles.begin(), _openFiles.end(), [&](const OpenFileInfo* inf) -> bool
|
|
{
|
|
return (inf->DokanFileInfo != dokanFileInfo) && (inf->DokanFileInfo->DeleteOnClose ? true : false);
|
|
});
|
|
}
|
|
|
|
if (count <= 1)
|
|
{
|
|
RefreshActiveFileTree(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK Sia_FlushFileBuffers(LPCWSTR fileName, PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
NTSTATUS ret = STATUS_SUCCESS;
|
|
FilePath filePath(GetCacheLocation(), fileName);
|
|
|
|
auto openFileInfo = reinterpret_cast<OpenFileInfo*>(dokanFileInfo->Context);
|
|
if (openFileInfo && openFileInfo->FileHandle && (openFileInfo->FileHandle != INVALID_HANDLE_VALUE))
|
|
{
|
|
if (!::FlushFileBuffers(openFileInfo->FileHandle))
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
}
|
|
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanFlushFileBuffers(filePath, ret)));
|
|
return ret;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK Sia_DeleteDirectory(LPCWSTR fileName, PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
FilePath filePath = FilePath(GetCacheLocation(), fileName);
|
|
NTSTATUS ret = STATUS_SUCCESS;
|
|
if (dokanFileInfo->DeleteOnClose)
|
|
{
|
|
filePath.Append("*");
|
|
|
|
WIN32_FIND_DATA findData;
|
|
HANDLE findHandle = ::FindFirstFile(&filePath[0], &findData);
|
|
if (findHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
if ((wcscmp(findData.cFileName, L"..") != 0) && (wcscmp(findData.cFileName, L".") != 0))
|
|
{
|
|
ret = STATUS_DIRECTORY_NOT_EMPTY;
|
|
}
|
|
}
|
|
while ((ret == STATUS_SUCCESS) && (::FindNextFile(findHandle, &findData) != 0));
|
|
DWORD error = ::GetLastError();
|
|
if ((ret != STATUS_DIRECTORY_NOT_EMPTY) && (error != ERROR_NO_MORE_FILES))
|
|
{
|
|
ret = DokanNtStatusFromWin32(error);
|
|
}
|
|
|
|
::FindClose(findHandle);
|
|
}
|
|
}
|
|
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanDeleteDirectory(filePath, ret)));
|
|
return ret;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK Sia_DeleteFileW(LPCWSTR fileName, PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
NTSTATUS ret = STATUS_SUCCESS;
|
|
|
|
auto openFileInfo = reinterpret_cast<OpenFileInfo*>(dokanFileInfo->Context);
|
|
FilePath filePath(GetCacheLocation(), fileName);
|
|
|
|
DWORD dwAttrib = ::GetFileAttributes(&filePath[0]);
|
|
if ((dwAttrib != INVALID_FILE_ATTRIBUTES) && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
ret = STATUS_ACCESS_DENIED;
|
|
}
|
|
else if (openFileInfo && openFileInfo->FileHandle && (openFileInfo->FileHandle != INVALID_HANDLE_VALUE))
|
|
{
|
|
FILE_DISPOSITION_INFO fdi;
|
|
fdi.DeleteFile = dokanFileInfo->DeleteOnClose;
|
|
if (!::SetFileInformationByHandle(openFileInfo->FileHandle, FileDispositionInfo, &fdi, sizeof(FILE_DISPOSITION_INFO)))
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
}
|
|
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanDeleteFileW(filePath, ret)));
|
|
return ret;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK Sia_MoveFileW(LPCWSTR fileName, LPCWSTR NewFileName, BOOL ReplaceIfExisting, PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
NTSTATUS ret = STATUS_SUCCESS;
|
|
|
|
FilePath filePath(GetCacheLocation(), fileName);
|
|
FilePath newFilePath(GetCacheLocation(), NewFileName);
|
|
|
|
auto openFileInfo = reinterpret_cast<OpenFileInfo*>(dokanFileInfo->Context);
|
|
if (!openFileInfo || !openFileInfo->FileHandle || (openFileInfo->FileHandle == INVALID_HANDLE_VALUE))
|
|
{
|
|
ret = STATUS_INVALID_HANDLE;
|
|
}
|
|
else
|
|
{
|
|
size_t len = wcslen(&newFilePath[0]);
|
|
DWORD bufferSize = static_cast<DWORD>(sizeof(FILE_RENAME_INFO) + (len * sizeof(newFilePath[0])));
|
|
|
|
PFILE_RENAME_INFO renameInfo = static_cast<PFILE_RENAME_INFO>(malloc(bufferSize));
|
|
if (renameInfo)
|
|
{
|
|
::ZeroMemory(renameInfo, bufferSize);
|
|
|
|
renameInfo->ReplaceIfExists = ReplaceIfExisting ? TRUE : FALSE; // some warning about converting BOOL to BOOLEAN
|
|
renameInfo->RootDirectory = nullptr; // hope it is never needed, shouldn't be
|
|
renameInfo->FileNameLength = static_cast<DWORD>(len) * sizeof(newFilePath[0]); // they want length in bytes
|
|
|
|
wcscpy_s(renameInfo->FileName, len + 1, &newFilePath[0]);
|
|
|
|
BOOL result = ::SetFileInformationByHandle(openFileInfo->FileHandle, FileRenameInfo, renameInfo, bufferSize);
|
|
free(renameInfo);
|
|
|
|
if (result)
|
|
{
|
|
if (!dokanFileInfo->IsDirectory && !ApiSuccess(_siaApi->GetRenter()->RenameFile(openFileInfo->SiaPath, CSiaApi::FormatToSiaPath(NewFileName))))
|
|
{
|
|
ret = STATUS_INTERNAL_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
}
|
|
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanMoveFileW(filePath, newFilePath, ret)));
|
|
return ret;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK Sia_SetFileAttributesW(LPCWSTR fileName, DWORD fileAttributes, PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
UNREFERENCED_PARAMETER(dokanFileInfo);
|
|
NTSTATUS ret = STATUS_SUCCESS;
|
|
|
|
FilePath filePath(GetCacheLocation(), fileName);
|
|
|
|
if (!::SetFileAttributes(&filePath[0], fileAttributes))
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanSetFileAttributesW(filePath, ret)));
|
|
return ret;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK Sia_GetFileSecurityW(
|
|
LPCWSTR fileName, PSECURITY_INFORMATION securityInfo,
|
|
PSECURITY_DESCRIPTOR securityDescriptor, ULONG bufferLen,
|
|
PULONG lengthNeeded, PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
UNREFERENCED_PARAMETER(dokanFileInfo);
|
|
FilePath filePath(GetCacheLocation(), fileName);
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanGetFileAttributesW(filePath)));
|
|
SECURITY_INFORMATION requestingSaclInfo = ((*securityInfo & SACL_SECURITY_INFORMATION) || (*securityInfo & BACKUP_SECURITY_INFORMATION));
|
|
|
|
// TODO Implement this properly
|
|
//if (!g_HasSeSecurityPrivilege) {
|
|
if (true)
|
|
{
|
|
*securityInfo &= ~SACL_SECURITY_INFORMATION;
|
|
*securityInfo &= ~BACKUP_SECURITY_INFORMATION;
|
|
}
|
|
|
|
HANDLE handle = ::CreateFile(&filePath[0], READ_CONTROL | ((requestingSaclInfo && false) ? ACCESS_SYSTEM_SECURITY : 0),
|
|
FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
|
|
nullptr, // security attribute
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS, // |FILE_FLAG_NO_BUFFERING,
|
|
nullptr);
|
|
|
|
if (!handle || (handle == INVALID_HANDLE_VALUE))
|
|
{
|
|
return DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
|
|
if (!::GetUserObjectSecurity(handle, securityInfo, securityDescriptor, bufferLen, lengthNeeded))
|
|
{
|
|
int error = ::GetLastError();
|
|
if (error == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
::CloseHandle(handle);
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
else
|
|
{
|
|
::CloseHandle(handle);
|
|
return DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
}
|
|
::CloseHandle(handle);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
static NTSTATUS DOKAN_CALLBACK Sia_SetFileSecurityW(
|
|
LPCWSTR fileName, PSECURITY_INFORMATION securityInfo,
|
|
PSECURITY_DESCRIPTOR securityDescriptor, ULONG securityDescriptorLength,
|
|
PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
UNREFERENCED_PARAMETER(securityDescriptorLength);
|
|
|
|
FilePath filePath(GetCacheLocation(), fileName);
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanSetFileSecurityW(filePath)));
|
|
|
|
auto openFileInfo = reinterpret_cast<OpenFileInfo*>(dokanFileInfo->Context);
|
|
if (!openFileInfo || !openFileInfo->FileHandle || (openFileInfo->FileHandle == INVALID_HANDLE_VALUE))
|
|
{
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
if (!::SetUserObjectSecurity(openFileInfo->FileHandle, securityInfo, securityDescriptor))
|
|
{
|
|
return DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK Sia_SetFileTime(LPCWSTR fileName, CONST FILETIME *creationTime,
|
|
CONST FILETIME *lastAccessTime, CONST FILETIME *lastWriteTime,
|
|
PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
NTSTATUS ret = STATUS_SUCCESS;
|
|
FilePath filePath(GetCacheLocation(), fileName);
|
|
|
|
auto openFileInfo = reinterpret_cast<OpenFileInfo*>(dokanFileInfo->Context);
|
|
if (!openFileInfo || !openFileInfo->FileHandle || (openFileInfo->FileHandle == INVALID_HANDLE_VALUE))
|
|
{
|
|
ret = STATUS_INVALID_HANDLE;
|
|
}
|
|
else if (!::SetFileTime(openFileInfo->FileHandle, creationTime, lastAccessTime, lastWriteTime))
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanSetFileTime(filePath, ret)));
|
|
return ret;
|
|
}
|
|
|
|
static NTSTATUS DOKAN_CALLBACK Sia_SetAllocationSize(LPCWSTR fileName, LONGLONG allocSize, PDOKAN_FILE_INFO dokanFileInfo)
|
|
{
|
|
// TODO Check dummy and add to cache if not found
|
|
NTSTATUS ret = STATUS_SUCCESS;
|
|
FilePath filePath(GetCacheLocation(), fileName);
|
|
|
|
auto openFileInfo = reinterpret_cast<OpenFileInfo*>(dokanFileInfo->Context);
|
|
if (!openFileInfo || !openFileInfo->FileHandle || (openFileInfo->FileHandle == INVALID_HANDLE_VALUE))
|
|
{
|
|
ret = STATUS_INVALID_HANDLE;
|
|
}
|
|
else
|
|
{
|
|
LARGE_INTEGER fileSize = { 0 };
|
|
if (::GetFileSizeEx(openFileInfo->FileHandle, &fileSize))
|
|
{
|
|
if (allocSize < fileSize.QuadPart)
|
|
{
|
|
fileSize.QuadPart = allocSize;
|
|
if (!::SetFilePointerEx(openFileInfo->FileHandle, fileSize, nullptr, FILE_BEGIN))
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
else if (!::SetEndOfFile(openFileInfo->FileHandle))
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = DokanNtStatusFromWin32(::GetLastError());
|
|
}
|
|
}
|
|
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DokanSetAllocationSize(filePath, ret)));
|
|
return ret;
|
|
}
|
|
|
|
public:
|
|
static void Initialize(CSiaApi* siaApi, CSiaDriveConfig* siaDriveConfig)
|
|
{
|
|
_siaApi = siaApi;
|
|
_siaDriveConfig = siaDriveConfig;
|
|
_dokanOps.Cleanup = Sia_Cleanup;
|
|
_dokanOps.CloseFile = Sia_CloseFile;
|
|
_dokanOps.DeleteDirectory = Sia_DeleteDirectory;
|
|
_dokanOps.DeleteFileW = Sia_DeleteFileW;
|
|
_dokanOps.FindFiles = Sia_FindFiles;
|
|
_dokanOps.FindFilesWithPattern = nullptr;
|
|
_dokanOps.FindStreams = nullptr;
|
|
_dokanOps.FlushFileBuffers = Sia_FlushFileBuffers;
|
|
_dokanOps.GetDiskFreeSpaceW = Sia_GetDiskFreeSpaceW;
|
|
_dokanOps.GetFileInformation = Sia_GetFileInformation;
|
|
_dokanOps.GetFileSecurityW = Sia_GetFileSecurityW;
|
|
_dokanOps.GetVolumeInformationW = Sia_GetVolumeInformationW;
|
|
_dokanOps.LockFile = nullptr;
|
|
_dokanOps.Mounted = Sia_Mounted;
|
|
_dokanOps.MoveFileW = Sia_MoveFileW;
|
|
_dokanOps.ReadFile = Sia_ReadFile;
|
|
_dokanOps.SetAllocationSize = Sia_SetAllocationSize;
|
|
_dokanOps.SetEndOfFile = Sia_SetEndOfFile;
|
|
_dokanOps.SetFileAttributesW = Sia_SetFileAttributesW;
|
|
_dokanOps.SetFileSecurityW = Sia_SetFileSecurityW;
|
|
_dokanOps.SetFileTime = Sia_SetFileTime;
|
|
_dokanOps.UnlockFile = nullptr;
|
|
_dokanOps.Unmounted = Sia_Unmounted;
|
|
_dokanOps.WriteFile = Sia_WriteFile;
|
|
_dokanOps.ZwCreateFile = Sia_ZwCreateFile;
|
|
|
|
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;
|
|
#else
|
|
_dokanOptions.Options = 0;
|
|
#endif
|
|
}
|
|
|
|
static void Mount(const wchar_t& driveLetter, const SString& cacheLocation)
|
|
{
|
|
if (_siaApi && !_mountThread)
|
|
{
|
|
_cacheLocation = cacheLocation;
|
|
wchar_t tmp[] = { driveLetter, ':', '\\', 0 };
|
|
_mountPoint = tmp;
|
|
_mountThread.reset(new std::thread([&]()
|
|
{
|
|
_dokanOptions.MountPoint = _mountPoint.ToUpper().str().c_str();
|
|
_mountStatus = DokanMain(&_dokanOptions, &_dokanOps);
|
|
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DriveMountEnded(_mountPoint, _mountStatus)));
|
|
}));
|
|
}
|
|
}
|
|
|
|
static void Unmount()
|
|
{
|
|
if (_mountThread)
|
|
{
|
|
while (!DokanRemoveMountPoint(&_mountPoint[0]))
|
|
::Sleep(1000);
|
|
|
|
_mountThread->join();
|
|
_mountThread.reset(nullptr);
|
|
_mountPoint = "";
|
|
}
|
|
}
|
|
|
|
static void Shutdown()
|
|
{
|
|
StopFileListThread();
|
|
Unmount();
|
|
_siaApi = nullptr;
|
|
_siaDriveConfig = nullptr;
|
|
ZeroMemory(&_dokanOps, sizeof(_dokanOps));
|
|
ZeroMemory(&_dokanOptions, sizeof(_dokanOptions));
|
|
}
|
|
|
|
static bool IsMounted()
|
|
{
|
|
return (_mountThread != nullptr);
|
|
}
|
|
|
|
static bool IsInitialized()
|
|
{
|
|
return (_siaApi != nullptr);
|
|
}
|
|
};
|
|
// Static member variables
|
|
CSiaApi* DokanImpl::_siaApi = nullptr;
|
|
CSiaDriveConfig* DokanImpl::_siaDriveConfig = nullptr;
|
|
std::unique_ptr<CUploadManager> DokanImpl::_uploadManager;
|
|
DOKAN_OPERATIONS DokanImpl::_dokanOps;
|
|
DOKAN_OPTIONS DokanImpl::_dokanOptions;
|
|
FilePath DokanImpl::_cacheLocation;
|
|
HANDLE DokanImpl::_fileListStopEvent;
|
|
CSiaFileTreePtr DokanImpl::_siaFileTree;
|
|
std::mutex DokanImpl::_openFileMutex;
|
|
std::unique_ptr<std::thread> DokanImpl::_fileListThread;
|
|
std::unique_ptr<std::thread> DokanImpl::_mountThread;
|
|
NTSTATUS DokanImpl::_mountStatus = STATUS_SUCCESS;
|
|
SString DokanImpl::_mountPoint;
|
|
std::vector<DokanImpl::OpenFileInfo*> DokanImpl::_openFiles;
|
|
|
|
CSiaDokanDrive::CSiaDokanDrive(CSiaApi& siaApi, CSiaDriveConfig* siaDriveConfig) :
|
|
_siaApi(siaApi),
|
|
_siaDriveConfig(siaDriveConfig)
|
|
{
|
|
std::lock_guard<std::mutex> l(_startStopMutex);
|
|
if (DokanImpl::IsInitialized())
|
|
throw SiaDokanDriveException("Sia drive has already been activated");
|
|
|
|
DokanImpl::Initialize(&_siaApi, _siaDriveConfig);
|
|
}
|
|
|
|
CSiaDokanDrive::~CSiaDokanDrive()
|
|
{
|
|
std::lock_guard<std::mutex> l(_startStopMutex);
|
|
DokanImpl::Shutdown();
|
|
}
|
|
|
|
bool CSiaDokanDrive::IsMounted() const
|
|
{
|
|
return DokanImpl::IsMounted();
|
|
}
|
|
|
|
void CSiaDokanDrive::Mount(const wchar_t& driveLetter, const SString& cacheLocation, const std::uint64_t& maxCacheSizeBytes)
|
|
{
|
|
std::lock_guard<std::mutex> l(_startStopMutex);
|
|
DokanImpl::Mount(driveLetter, cacheLocation);
|
|
}
|
|
|
|
void CSiaDokanDrive::Unmount(const bool& clearCache)
|
|
{
|
|
std::lock_guard<std::mutex> l(_startStopMutex);
|
|
DokanImpl::Unmount();
|
|
}
|
|
|
|
void CSiaDokanDrive::ClearCache()
|
|
{
|
|
std::lock_guard<std::mutex> l(_startStopMutex);
|
|
} |