diff --git a/SiaDrive.Dokan.Api/SiaDokanDrive.cpp b/SiaDrive.Dokan.Api/SiaDokanDrive.cpp index 814dfeb..0e87074 100644 --- a/SiaDrive.Dokan.Api/SiaDokanDrive.cpp +++ b/SiaDrive.Dokan.Api/SiaDokanDrive.cpp @@ -6,6 +6,8 @@ using namespace Sia::Api; using namespace Sia::Api::Dokan; +// 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; @@ -48,8 +50,9 @@ private: bool ret = false; std::wstring tempPath; tempPath.resize(MAX_PATH + 1); - if (GetTempPath(MAX_PATH + 1, &tempPath[0])) + if (::GetTempPath(MAX_PATH + 1, &tempPath[0])) { + // Check cache size is large enough to hold new file ret = ApiSuccess(_siaApi->GetRenter()->DownloadFile(siaPath, tempPath)); if (ret) { @@ -148,14 +151,12 @@ private: } else { - OutputDebugString(FileName); - OutputDebugString(L"\n"); // When filePath is a directory, needs to change the flag so that the file can // be opened. String cacheFilePath; cacheFilePath.resize(MAX_PATH + 1); - PathCombine(&cacheFilePath[0], GetCacheLocation().c_str(), &FileName[1]); - DWORD fileAttr = GetFileAttributes(cacheFilePath.c_str()); + ::PathCombine(&cacheFilePath[0], GetCacheLocation().c_str(), &FileName[1]); + DWORD fileAttr = ::GetFileAttributes(cacheFilePath.c_str()); if (fileAttr != INVALID_FILE_ATTRIBUTES && (fileAttr & FILE_ATTRIBUTE_DIRECTORY) && @@ -176,7 +177,7 @@ private: if (siaPath.length()) { // If cache file already exists and is a directory, requested file operation isn't valid - DWORD attribs = GetFileAttributes(cacheFilePath.c_str()); + DWORD attribs = ::GetFileAttributes(cacheFilePath.c_str()); if ((attribs != INVALID_FILE_ATTRIBUTES) && (attribs & FILE_ATTRIBUTE_DIRECTORY)) { ret = STATUS_OBJECT_NAME_COLLISION; @@ -243,6 +244,10 @@ private: } } break; + + default: + // Nothing to do + break; } if (ret == STATUS_SUCCESS) @@ -251,7 +256,7 @@ private: { // 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 (!PathFileExists(cacheFilePath.c_str()) || ::DeleteFile(cacheFilePath.c_str())) + if (!::PathFileExists(cacheFilePath.c_str()) || ::DeleteFile(cacheFilePath.c_str())) { if (!ApiSuccess(_uploadManager->Remove(siaPath))) { @@ -279,7 +284,7 @@ private: if (ret == STATUS_SUCCESS) { // Create file as specified - HANDLE handle = CreateFile( + HANDLE handle = ::CreateFile( cacheFilePath.c_str(), genericDesiredAccess, ShareAccess, @@ -346,10 +351,7 @@ private: return ret; } - static NTSTATUS DOKAN_CALLBACK SiaDrive_FindFiles( - LPCWSTR FileName, - PFillFindData FillFindData, - PDOKAN_FILE_INFO DokanFileInfo) + static NTSTATUS DOKAN_CALLBACK SiaDrive_FindFiles(LPCWSTR FileName, PFillFindData FillFindData, PDOKAN_FILE_INFO DokanFileInfo) { std::lock_guard l(_dokanMutex); auto siaFileTree = _siaFileTree; @@ -394,7 +396,7 @@ private: for (auto& file : fileList) { WIN32_FIND_DATA fd = { 0 }; - wcscpy_s(fd.cFileName, PathFindFileName(file->GetSiaPath().c_str())); + wcscpy_s(fd.cFileName, ::PathFindFileName(file->GetSiaPath().c_str())); LARGE_INTEGER li = { 0 }; li.QuadPart = file->GetFileSize(); @@ -424,7 +426,7 @@ private: else { LARGE_INTEGER li = { 0 }; - BOOL sizeOk = GetFileSizeEx(handle, &li); + BOOL sizeOk = ::GetFileSizeEx(handle, &li); ::CloseHandle(handle); @@ -440,6 +442,75 @@ private: } } + static NTSTATUS DOKAN_CALLBACK Sia_GetFileInformation(LPCWSTR FileName, LPBY_HANDLE_FILE_INFORMATION HandleFileInformation, PDOKAN_FILE_INFO DokanFileInfo) + { + HANDLE handle = reinterpret_cast(DokanFileInfo->Context); + BOOL opened = FALSE; + NTSTATUS ret = STATUS_SUCCESS; + + String cachePath = GetCacheLocation().c_str(); + if (wcscmp(FileName, L"\\") != 0) + { + cachePath.resize(MAX_PATH + 1); + ::PathAppend(&cachePath[0], FileName); + cachePath = cachePath.c_str(); + } + + if (!handle || (handle == INVALID_HANDLE_VALUE)) + { + handle = ::CreateFile(&cachePath[0], GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); + if (handle == INVALID_HANDLE_VALUE) + { + ret = DokanNtStatusFromWin32(GetLastError()); + } + else + { + opened = TRUE; + } + } + + if (ret == STATUS_SUCCESS) + { + if (!::GetFileInformationByHandle(handle, HandleFileInformation)) + { + // FileName is a root directory + // in this case, FindFirstFile can't get directory information + if (wcslen(FileName) == 1) + { + HandleFileInformation->dwFileAttributes = ::GetFileAttributes(&cachePath[0]); + } + else + { + WIN32_FIND_DATAW find = { 0 }; + HANDLE findHandle = ::FindFirstFile(&cachePath[0], &find); + if (findHandle == INVALID_HANDLE_VALUE) + { + // TODO Not Cached, so manual attributes + ret = DokanNtStatusFromWin32(::GetLastError()); + } + else + { + HandleFileInformation->dwFileAttributes = find.dwFileAttributes; + HandleFileInformation->ftCreationTime = find.ftCreationTime; + HandleFileInformation->ftLastAccessTime = find.ftLastAccessTime; + HandleFileInformation->ftLastWriteTime = find.ftLastWriteTime; + HandleFileInformation->nFileSizeHigh = find.nFileSizeHigh; + HandleFileInformation->nFileSizeLow = find.nFileSizeLow; + + FindClose(findHandle); + } + } + } + + if (opened) + { + CloseHandle(handle); + } + } + + return ret; + } + static NTSTATUS DOKAN_CALLBACK Sia_Mounted(PDOKAN_FILE_INFO DokanFileInfo) { std::lock_guard l(_dokanMutex); @@ -505,7 +576,7 @@ public: _dokanOps.FindStreams = nullptr; _dokanOps.FlushFileBuffers = nullptr; _dokanOps.GetDiskFreeSpaceW = Sia_GetDiskFreeSpaceW; - _dokanOps.GetFileInformation = nullptr; + _dokanOps.GetFileInformation = Sia_GetFileInformation; _dokanOps.GetFileSecurityW = nullptr; _dokanOps.GetVolumeInformationW = Sia_GetVolumeInformation; _dokanOps.LockFile = nullptr;