#include "stdafx.h" #include "SiaDokanDrive.h" #include #include using namespace Sia::Api; using namespace Sia::Api::Dokan; class DokanImpl { private: static std::mutex _dokanMutex; static CSiaApi* _siaApi; static DOKAN_OPERATIONS _dokanOps; static String _cacheLocation; private: static NTSTATUS DOKAN_CALLBACK SiaDrive_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; bool isFile = (fileAttributesAndFlags & FILE_NON_DIRECTORY_FILE); if (isFile) { // Formulate Sia path and cache path String siaPath = PathSkipRoot(FileName); // Strip drive letter to get Sia path String cacheFilePath; cacheFilePath.resize(MAX_PATH + 1); PathCombine(&cacheFilePath[0], _cacheLocation.c_str(), siaPath.c_str()); // If cache file already exists and is a directory, file operation should fail if (GetFileAttributes(cacheFilePath.c_str()) & FILE_ATTRIBUTE_DIRECTORY) { ret = STATUS_OBJECT_NAME_COLLISION; } else { bool exists; if (API_SUCCESS(SiaApiError, _siaApi->GetRenter()->FileExists(siaPath, exists))) { 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_NOT_FOUND; } } break; case TRUNCATE_EXISTING: { if (exists) { isCreateOp = isReplaceOp = true; } else { ret = STATUS_NOT_FOUND; } } break; } if (ret == STATUS_SUCCESS) { if (isReplaceOp) { if (!PathFileExists(cacheFilePath.c_str()) || ::DeleteFile(cacheFilePath.c_str())) { // Delete from Sia if (!API_SUCCESS(SiaApiError, _siaApi->GetRenter()->DeleteFile(siaPath))) { ret = STATUS_INVALID_SERVER_STATE; } } else { ret = DokanNtStatusFromWin32(GetLastError()); } } if (ret == STATUS_SUCCESS) { // If file should exist, then check for it in cache location. If not found, // it must be downloaded first and placed in cache if (!isCreateOp && !PathFileExists(cacheFilePath.c_str())) { // Download to temp location // Move to cache location } // Create file as specified HANDLE handle = CreateFile( cacheFilePath.c_str(), genericDesiredAccess, ShareAccess, &securityAttrib, creationDisposition, fileAttributesAndFlags, nullptr); if (handle == INVALID_HANDLE_VALUE) { ret = DokanNtStatusFromWin32(GetLastError()); } else { DokanFileInfo->Context = reinterpret_cast(handle); // save the file handle in Context /*if (creationDisposition == OPEN_ALWAYS || creationDisposition == CREATE_ALWAYS) { error = GetLastError(); if (error == ERROR_ALREADY_EXISTS) { DbgPrint(L"\tOpen an already existing file\n"); // Open succeed but we need to inform the driver // that the file open and not created by returning STATUS_OBJECT_NAME_COLLISION return STATUS_OBJECT_NAME_COLLISION; } }*/ } } } } else { ret = STATUS_INVALID_SERVER_STATE; } } } else // Folder Operation (cache operation only) { ret = STATUS_NOT_IMPLEMENTED; } return ret; } static NTSTATUS DOKAN_CALLBACK SiaDrive_FindFiles( LPCWSTR FileName, PFillFindData FillFindData, PDOKAN_FILE_INFO DokanFileInfo) { return 0; } public: static void Initialize(CSiaApi* siaApi) { _siaApi = siaApi; _dokanOps.Cleanup = nullptr; _dokanOps.CloseFile = nullptr; _dokanOps.DeleteDirectory = nullptr; _dokanOps.DeleteFileW = nullptr; _dokanOps.FindFiles = SiaDrive_FindFiles; _dokanOps.FindFilesWithPattern = nullptr; _dokanOps.FindStreams = nullptr; _dokanOps.FlushFileBuffers = nullptr; _dokanOps.GetDiskFreeSpaceW = nullptr; _dokanOps.GetFileInformation = nullptr; _dokanOps.GetFileSecurityW = nullptr; _dokanOps.GetVolumeInformationW = nullptr; _dokanOps.LockFile = nullptr; _dokanOps.Mounted = nullptr; _dokanOps.MoveFileW = nullptr; _dokanOps.ReadFile = nullptr; _dokanOps.SetAllocationSize = nullptr; _dokanOps.SetEndOfFile = nullptr; _dokanOps.SetFileAttributesW = nullptr; _dokanOps.SetFileSecurityW = nullptr; _dokanOps.SetFileTime = nullptr; _dokanOps.UnlockFile = nullptr; _dokanOps.Unmounted = nullptr; _dokanOps.WriteFile = nullptr; _dokanOps.ZwCreateFile = SiaDrive_ZwCreateFile; } static void Shutdown() { _siaApi = nullptr; ZeroMemory(&_dokanOps, sizeof(_dokanOps)); } static std::mutex& GetMutex() { return _dokanMutex; } static bool IsInitialized() { return _siaApi != nullptr; } }; std::mutex DokanImpl::_dokanMutex; CSiaApi* DokanImpl::_siaApi = nullptr; DOKAN_OPERATIONS DokanImpl::_dokanOps; String DokanImpl::_cacheLocation; CSiaDokanDrive::CSiaDokanDrive(CSiaApi& siaApi) : _siaApi(siaApi), _Mounted(false) { std::lock_guard l(DokanImpl::GetMutex()); if (DokanImpl::IsInitialized()) throw SiaDokanDriveException("Sia drive has already been activated"); DokanImpl::Initialize(&_siaApi); } CSiaDokanDrive::~CSiaDokanDrive() { std::lock_guard l(DokanImpl::GetMutex()); Unmount(); DokanImpl::Shutdown(); } void CSiaDokanDrive::Mount(const wchar_t& driveLetter, const String& cacheLocation, const std::uint64_t& maxCacheSizeBytes) { } void CSiaDokanDrive::Unmount(const bool& clearCache) { }