diff --git a/SiaDrive.Api/SiaFileTree.cpp b/SiaDrive.Api/SiaFileTree.cpp index fca3322..e485771 100644 --- a/SiaDrive.Api/SiaFileTree.cpp +++ b/SiaDrive.Api/SiaFileTree.cpp @@ -64,24 +64,38 @@ CSiaFileCollection CSiaApi::_CSiaFileTree::Query(String query) const return std::move(ret); } -std::vector CSiaApi::_CSiaFileTree::QueryDirectories(String query) const +std::vector CSiaApi::_CSiaFileTree::QueryDirectories(String rootFolder) const { - query = CSiaApi::FormatToSiaPath(query); - ReplaceStringInPlace(ReplaceStringInPlace(ReplaceStringInPlace(query, L".", L"\\."), L"*", L"[^/]+"), L"?", L"[^/]?"); - std::wregex r(query); + CSiaFileCollection col; + ReplaceStringInPlace(rootFolder, L"/", L"\\"); + if (rootFolder[0] == '\\') + { + rootFolder = rootFolder.substr(1); + } std::vector ret; std::for_each(_fileList.begin(), _fileList.end(), [&](const CSiaFilePtr& v) { String dir = v->GetSiaPath(); - int pos = dir.find_last_of('/'); - if (pos > -1) + ReplaceStringInPlace(dir, L"/", L"\\"); + ::PathRemoveFileSpec(&dir[0]); + ::PathRemoveBackslash(&dir[0]); + ::PathRemoveBackslash(&rootFolder[0]); + dir = dir.c_str(); + rootFolder = rootFolder.c_str(); + if ((dir.length() > rootFolder.length()) && (dir.substr(0, rootFolder.length()) == rootFolder)) { - pos = dir.find_first_of('/'); - dir = dir.substr(0, pos); - if (std::find(ret.begin(), ret.end(), dir) == ret.end()) + String subFolder = dir.substr(rootFolder.length()); + int f = subFolder.find_first_of('\\'); + if (f > 0) { - ret.push_back(dir); + subFolder = subFolder.substr(0, f); + } + + auto it = std::find(ret.begin(), ret.end(), subFolder); + if (it == ret.end()) + { + ret.push_back(subFolder); } } }); diff --git a/SiaDrive.Dokan.Api/SiaDokanDrive.cpp b/SiaDrive.Dokan.Api/SiaDokanDrive.cpp index 15e59cf..814dfeb 100644 --- a/SiaDrive.Dokan.Api/SiaDokanDrive.cpp +++ b/SiaDrive.Dokan.Api/SiaDokanDrive.cpp @@ -148,20 +148,36 @@ private: } else { - bool isFile = (FileAttributes & FILE_NON_DIRECTORY_FILE) ? true : false; - DokanFileInfo->IsDirectory = !isFile; - if (isFile) + 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()); + + 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; + } + } + + if (!DokanFileInfo->IsDirectory) { // Formulate Sia path and cache path String siaPath = CSiaApi::FormatToSiaPath(PathSkipRoot(FileName)); // Strip drive letter to get Sia path if (siaPath.length()) - { - String cacheFilePath; - cacheFilePath.resize(MAX_PATH + 1); - PathCombine(&cacheFilePath[0], GetCacheLocation().c_str(), siaPath.c_str()); - + { // If cache file already exists and is a directory, requested file operation isn't valid - if (GetFileAttributes(cacheFilePath.c_str()) & FILE_ATTRIBUTE_DIRECTORY) + DWORD attribs = GetFileAttributes(cacheFilePath.c_str()); + if ((attribs != INVALID_FILE_ATTRIBUTES) && (attribs & FILE_ATTRIBUTE_DIRECTORY)) { ret = STATUS_OBJECT_NAME_COLLISION; } @@ -278,16 +294,14 @@ private: else { DokanFileInfo->Context = reinterpret_cast(handle); // save the file handle in Context - if (isFile) - { - OpenFileInfo ofi; - ofi.SiaPath = siaPath; - ofi.CacheFilePath = cacheFilePath; - // TODO Detect if file is read-only - // TODO Quick hash to detect changes - ofi.ReadOnly = false; - _openFileMap.insert({ DokanFileInfo->Context, ofi }); - } + + OpenFileInfo ofi; + ofi.SiaPath = siaPath; + ofi.CacheFilePath = cacheFilePath; + // TODO Detect if file is read-only + // TODO Quick hash to detect changes + ofi.ReadOnly = false; + _openFileMap.insert({ DokanFileInfo->Context, ofi }); /*if (creationDisposition == OPEN_ALWAYS || creationDisposition == CREATE_ALWAYS) { @@ -316,17 +330,7 @@ private: } } else // Folder Operation (cache operation only) - { - String cacheFilePath; - cacheFilePath.resize(MAX_PATH + 1); - if (String(L"\\") == FileName) - { - cacheFilePath = GetCacheLocation(); - } - else - { - PathCombine(&cacheFilePath[0], GetCacheLocation().c_str(), FileName); - } + { HANDLE handle = ::CreateFile(cacheFilePath.c_str(), genericDesiredAccess, ShareAccess, &securityAttrib, creationDisposition, fileAttributesAndFlags | FILE_FLAG_BACKUP_SEMANTICS, nullptr); if (handle == INVALID_HANDLE_VALUE) { @@ -352,27 +356,37 @@ private: if (siaFileTree) { String siaQuery = CSiaApi::FormatToSiaPath(PathSkipRoot(FileName)); - String dirQuery = siaQuery; + String rootPath = FileName; if (::PathIsDirectory(FileName)) { siaQuery += L"/*.*"; - dirQuery = siaQuery; } else { - String dir = FileName; - ::PathRemoveFileSpec(&dir[0]); - dirQuery = dir + L"/*.*"; + ::PathRemoveFileSpec(&rootPath[0]); + rootPath = rootPath.c_str(); } - auto dirList = siaFileTree->QueryDirectories(dirQuery); + auto dirList = siaFileTree->QueryDirectories(rootPath); for (auto& dir : dirList) { WIN32_FIND_DATA fd = { 0 }; wcscpy_s(fd.cFileName, dir.c_str()); fd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; - + String cachePath; + cachePath.resize(MAX_PATH + 1); + if (rootPath == L"\\") + { + ::PathCombine(&cachePath[0], GetCacheLocation().c_str(), dir.c_str()); + } + else + { + ::PathCombine(&cachePath[0], GetCacheLocation().c_str(), rootPath.c_str()); + ::PathAppend(&cachePath[0], dir.c_str()); + } + cachePath = cachePath.c_str(); + ::CreateDirectory(cachePath.c_str(), nullptr); FillFindData(&fd, DokanFileInfo); }