diff --git a/SiaDrive.Dokan.Api/SiaDokanDrive.cpp b/SiaDrive.Dokan.Api/SiaDokanDrive.cpp index c84f3a5..da3631f 100644 --- a/SiaDrive.Dokan.Api/SiaDokanDrive.cpp +++ b/SiaDrive.Dokan.Api/SiaDokanDrive.cpp @@ -130,7 +130,7 @@ private: // Dokan callbacks private: - static NTSTATUS DOKAN_CALLBACK SiaDrive_ZwCreateFile( + static NTSTATUS DOKAN_CALLBACK Sia_ZwCreateFile( LPCWSTR FileName, PDOKAN_IO_SECURITY_CONTEXT SecurityContext, ACCESS_MASK DesiredAccess, @@ -180,14 +180,48 @@ private: // Folder (cache operation only) if (DokanFileInfo->IsDirectory) { - HANDLE handle = ::CreateFile(cacheFilePath.c_str(), genericDesiredAccess, ShareAccess, &securityAttrib, creationDisposition, fileAttributesAndFlags | FILE_FLAG_BACKUP_SEMANTICS, nullptr); - if (handle == INVALID_HANDLE_VALUE) + if (creationDisposition == CREATE_NEW) { - ret = DokanNtStatusFromWin32(GetLastError()); + if (!::CreateDirectory(cacheFilePath.c_str(), &securityAttrib)) + { + DWORD error = GetLastError(); + ret = DokanNtStatusFromWin32(error); + } } - else + else if (creationDisposition == OPEN_ALWAYS) { - DokanFileInfo->Context = reinterpret_cast(handle); // save the file handle in Context + if (!CreateDirectory(cacheFilePath.c_str(), &securityAttrib)) + { + DWORD error = GetLastError(); + + if (error != ERROR_ALREADY_EXISTS) + { + ret = DokanNtStatusFromWin32(error); + } + } + } + + 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)) + { + return STATUS_NOT_A_DIRECTORY; + } + + // FILE_FLAG_BACKUP_SEMANTICS is required for opening directory handles + HANDLE handle = CreateFile(cacheFilePath.c_str(), genericDesiredAccess, ShareAccess, &securityAttrib, OPEN_EXISTING, fileAttributesAndFlags | FILE_FLAG_BACKUP_SEMANTICS, nullptr); + if (handle == INVALID_HANDLE_VALUE) + { + DWORD error = GetLastError(); + ret = DokanNtStatusFromWin32(error); + } + else + { + DokanFileInfo->Context = reinterpret_cast(handle); // save the file handle in Context + } } } else // File (cache and/or Sia operation) @@ -359,7 +393,7 @@ private: return ret; } - static NTSTATUS DOKAN_CALLBACK SiaDrive_FindFiles(LPCWSTR FileName, PFillFindData FillFindData, PDOKAN_FILE_INFO DokanFileInfo) + static NTSTATUS DOKAN_CALLBACK Sia_FindFiles(LPCWSTR FileName, PFillFindData FillFindData, PDOKAN_FILE_INFO DokanFileInfo) { std::lock_guard l(_dokanMutex); auto siaFileTree = _siaFileTree; @@ -573,6 +607,236 @@ private: return STATUS_SUCCESS; } + + static NTSTATUS DOKAN_CALLBACK Sia_ReadFile(LPCWSTR FileName, LPVOID Buffer, + DWORD BufferLength, + LPDWORD ReadLength, + LONGLONG Offset, + PDOKAN_FILE_INFO DokanFileInfo) + { + String filePath = StdConstructPath(GetCacheLocation(), FileName); + + HANDLE handle = reinterpret_cast(DokanFileInfo->Context); + ULONG offset = static_cast(Offset); + BOOL opened = FALSE; + + if (!handle || (handle == INVALID_HANDLE_VALUE)) + { + handle = ::CreateFile(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); + if (handle == INVALID_HANDLE_VALUE) + { + DWORD error = GetLastError(); + return DokanNtStatusFromWin32(error); + } + opened = TRUE; + } + + LARGE_INTEGER distanceToMove; + distanceToMove.QuadPart = Offset; + if (!::SetFilePointerEx(handle, distanceToMove, nullptr, FILE_BEGIN)) + { + DWORD error = GetLastError(); + if (opened) + CloseHandle(handle); + return DokanNtStatusFromWin32(error); + } + + if (!ReadFile(handle, Buffer, BufferLength, ReadLength, nullptr)) + { + DWORD error = GetLastError(); + if (opened) + CloseHandle(handle); + return DokanNtStatusFromWin32(error); + + } + else { + } + + if (opened) + CloseHandle(handle); + + return STATUS_SUCCESS; + } + + static NTSTATUS DOKAN_CALLBACK Sia_WriteFile(LPCWSTR FileName, LPCVOID Buffer, + DWORD NumberOfBytesToWrite, + LPDWORD NumberOfBytesWritten, + LONGLONG Offset, + PDOKAN_FILE_INFO DokanFileInfo) + { + String filePath = StdConstructPath(GetCacheLocation(), FileName); + HANDLE handle = reinterpret_cast(DokanFileInfo->Context); + BOOL opened = FALSE; + + // reopen the file + if (!handle || (handle == INVALID_HANDLE_VALUE)) + { + // TODO Get from cache if not found + handle = ::CreateFile(filePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr); + if (handle == INVALID_HANDLE_VALUE) + { + DWORD error = GetLastError(); + return DokanNtStatusFromWin32(error); + } + opened = TRUE; + } + + UINT64 fileSize = 0; + DWORD fileSizeLow = 0; + DWORD fileSizeHigh = 0; + fileSizeLow = ::GetFileSize(handle, &fileSizeHigh); + if (fileSizeLow == INVALID_FILE_SIZE) + { + DWORD error = GetLastError(); + if (opened) + CloseHandle(handle); + return DokanNtStatusFromWin32(error); + } + + fileSize = (static_cast(fileSizeHigh) << 32) | fileSizeLow; + + LARGE_INTEGER distanceToMove; + if (DokanFileInfo->WriteToEndOfFile) + { + LARGE_INTEGER z; + z.QuadPart = 0; + if (!::SetFilePointerEx(handle, z, nullptr, FILE_END)) + { + DWORD error = GetLastError(); + if (opened) + CloseHandle(handle); + return DokanNtStatusFromWin32(error); + } + } + else + { + // Paging IO cannot write after allocate file size. + if (DokanFileInfo->PagingIo) + { + if (static_cast(Offset) >= fileSize) + { + *NumberOfBytesWritten = 0; + if (opened) + CloseHandle(handle); + return STATUS_SUCCESS; + } + + if ((static_cast(Offset) + NumberOfBytesToWrite) > fileSize) + { + UINT64 bytes = fileSize - Offset; + if (bytes >> 32) + { + NumberOfBytesToWrite = static_cast(bytes & 0xFFFFFFFFUL); + } + else + { + NumberOfBytesToWrite = static_cast(bytes); + } + } + } + + if (static_cast(Offset) > fileSize) + { + // 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(handle, distanceToMove, nullptr, FILE_BEGIN)) + { + DWORD error = GetLastError(); + if (opened) + CloseHandle(handle); + return DokanNtStatusFromWin32(error); + } + } + + if (!::WriteFile(handle, Buffer, NumberOfBytesToWrite, NumberOfBytesWritten, nullptr)) + { + DWORD error = GetLastError(); + if (opened) + CloseHandle(handle); + return DokanNtStatusFromWin32(error); + + } + else { + } + + // close the file when it is reopened + if (opened) + CloseHandle(handle); + + return STATUS_SUCCESS; + } + + static NTSTATUS DOKAN_CALLBACK Sia_SetEndOfFile(LPCWSTR FileName, LONGLONG ByteOffset, PDOKAN_FILE_INFO DokanFileInfo) + { + String filePath = StdConstructPath(GetCacheLocation(), FileName); + HANDLE handle; + LARGE_INTEGER offset; + + handle = reinterpret_cast(DokanFileInfo->Context); + if (!handle || handle == INVALID_HANDLE_VALUE) + { + return STATUS_INVALID_HANDLE; + } + + offset.QuadPart = ByteOffset; + if (!::SetFilePointerEx(handle, offset, nullptr, FILE_BEGIN)) + { + DWORD error = GetLastError(); + return DokanNtStatusFromWin32(error); + } + + if (!::SetEndOfFile(handle)) + { + DWORD error = GetLastError(); + return DokanNtStatusFromWin32(error); + } + + return STATUS_SUCCESS; + } + + static void DOKAN_CALLBACK Sia_Cleanup(LPCWSTR FileName, PDOKAN_FILE_INFO DokanFileInfo) + { + String filePath = StdConstructPath(GetCacheLocation(), FileName); + + if (DokanFileInfo->Context) + { + ::CloseHandle(reinterpret_cast(DokanFileInfo->Context)); + _openFileMap.erase(DokanFileInfo->Context); + DokanFileInfo->Context = 0; + } + else { + } + + if (DokanFileInfo->DeleteOnClose) + { + // Should already be deleted by CloseHandle + // if open with FILE_FLAG_DELETE_ON_CLOSE + if (DokanFileInfo->IsDirectory) + { + if (::RemoveDirectory(filePath.c_str())) + { + } + else + { + } + } + else + { + if (::DeleteFile(filePath.c_str()) == 0) + { + } + else + { + } + } + } + } + public: static void Initialize(CSiaApi* siaApi, CSiaDriveConfig* siaDriveConfig) { @@ -580,11 +844,11 @@ public: _siaDriveConfig = siaDriveConfig; // 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)); - _dokanOps.Cleanup = nullptr; + _dokanOps.Cleanup = Sia_Cleanup; _dokanOps.CloseFile = Sia_CloseFile; _dokanOps.DeleteDirectory = nullptr; _dokanOps.DeleteFileW = nullptr; - _dokanOps.FindFiles = SiaDrive_FindFiles; + _dokanOps.FindFiles = Sia_FindFiles; _dokanOps.FindFilesWithPattern = nullptr; _dokanOps.FindStreams = nullptr; _dokanOps.FlushFileBuffers = nullptr; @@ -595,16 +859,16 @@ public: _dokanOps.LockFile = nullptr; _dokanOps.Mounted = Sia_Mounted; _dokanOps.MoveFileW = nullptr; - _dokanOps.ReadFile = nullptr; + _dokanOps.ReadFile = Sia_ReadFile; _dokanOps.SetAllocationSize = nullptr; - _dokanOps.SetEndOfFile = nullptr; + _dokanOps.SetEndOfFile = Sia_SetEndOfFile; _dokanOps.SetFileAttributesW = nullptr; _dokanOps.SetFileSecurityW = nullptr; _dokanOps.SetFileTime = nullptr; _dokanOps.UnlockFile = nullptr; _dokanOps.Unmounted = Sia_Unmounted; - _dokanOps.WriteFile = nullptr; - _dokanOps.ZwCreateFile = SiaDrive_ZwCreateFile; + _dokanOps.WriteFile = Sia_WriteFile; + _dokanOps.ZwCreateFile = Sia_ZwCreateFile; ZeroMemory(&_dokanOptions, sizeof(DOKAN_OPTIONS)); _dokanOptions.Version = DOKAN_VERSION; diff --git a/SiaDrive/SiaDriveDlg.cpp b/SiaDrive/SiaDriveDlg.cpp index c452277..845b96c 100644 --- a/SiaDrive/SiaDriveDlg.cpp +++ b/SiaDrive/SiaDriveDlg.cpp @@ -226,11 +226,6 @@ void CSiaDriveDlg::OnDocumentComplete(LPDISPATCH, LPCTSTR) } ClearDisplay(); CallClientScript(L"setAvailableDrives", json(GetAvailableDrives()), nullptr); - if (!_dokan) - { - _dokan.reset(new Dokan::CSiaDokanDrive(*_siaApi, &_siaConfig)); - _dokan->Mount('Y', CA2W(_siaConfig.GetCacheFolder().c_str()).m_psz, 10); - } SetTimer(IDT_UPDATE, 2000, nullptr); SetTimer(IDT_UI_ACTION_QUEUE, 100, nullptr); @@ -491,6 +486,11 @@ void CSiaDriveDlg::ConfigureWallet() } else { + if (!_dokan) + { + _dokan.reset(new Dokan::CSiaDokanDrive(*_siaApi, &_siaConfig)); + _dokan->Mount('Y', CA2W(_siaConfig.GetCacheFolder().c_str()).m_psz, 10); + } SetMainWindow(L"ID_TabWindow"); switch (_siaConfig.GetUI_Main_TabIndex())