// Windows/FileDir.cpp #include "StdAfx.h" #ifndef _UNICODE #include "../Common/StringConvert.h" #endif #include "FileDir.h" #include "FileFind.h" #include "FileName.h" #ifndef _UNICODE extern bool g_IsNT; #endif using namespace NWindows; using namespace NFile; using namespace NName; namespace NWindows { namespace NFile { namespace NDir { #ifndef UNDER_CE bool GetWindowsDir(FString &path) { UINT needLength; #ifndef _UNICODE if (!g_IsNT) { TCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetWindowsDirectory(s, MAX_PATH + 1); path = fas2fs(s); } else #endif { WCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetWindowsDirectoryW(s, MAX_PATH + 1); path = us2fs(s); } return (needLength > 0 && needLength <= MAX_PATH); } bool GetSystemDir(FString &path) { UINT needLength; #ifndef _UNICODE if (!g_IsNT) { TCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetSystemDirectory(s, MAX_PATH + 1); path = fas2fs(s); } else #endif { WCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetSystemDirectoryW(s, MAX_PATH + 1); path = us2fs(s); } return (needLength > 0 && needLength <= MAX_PATH); } #endif bool SetDirTime(CFSTR path, const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime) { #ifndef _UNICODE if (!g_IsNT) { ::SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return false; } #endif HANDLE hDir = INVALID_HANDLE_VALUE; IF_USE_MAIN_PATH hDir = ::CreateFileW(fs2us(path), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); #ifdef WIN_LONG_PATH if (hDir == INVALID_HANDLE_VALUE && USE_SUPER_PATH) { UString longPath; if (GetSuperPath(path, longPath, USE_MAIN_PATH)) hDir = ::CreateFileW(longPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); } #endif bool res = false; if (hDir != INVALID_HANDLE_VALUE) { res = BOOLToBool(::SetFileTime(hDir, cTime, aTime, mTime)); ::CloseHandle(hDir); } return res; } bool SetFileAttrib(CFSTR path, DWORD attrib) { #ifndef _UNICODE if (!g_IsNT) { if (::SetFileAttributes(fs2fas(path), attrib)) return true; } else #endif { IF_USE_MAIN_PATH if (::SetFileAttributesW(fs2us(path), attrib)) return true; #ifdef WIN_LONG_PATH if (USE_SUPER_PATH) { UString longPath; if (GetSuperPath(path, longPath, USE_MAIN_PATH)) return BOOLToBool(::SetFileAttributesW(longPath, attrib)); } #endif } return false; } bool RemoveDir(CFSTR path) { #ifndef _UNICODE if (!g_IsNT) { if (::RemoveDirectory(fs2fas(path))) return true; } else #endif { IF_USE_MAIN_PATH if (::RemoveDirectoryW(fs2us(path))) return true; #ifdef WIN_LONG_PATH if (USE_SUPER_PATH) { UString longPath; if (GetSuperPath(path, longPath, USE_MAIN_PATH)) return BOOLToBool(::RemoveDirectoryW(longPath)); } #endif } return false; } bool MyMoveFile(CFSTR oldFile, CFSTR newFile) { #ifndef _UNICODE if (!g_IsNT) { if (::MoveFile(fs2fas(oldFile), fs2fas(newFile))) return true; } else #endif { IF_USE_MAIN_PATH_2(oldFile, newFile) if (::MoveFileW(fs2us(oldFile), fs2us(newFile))) return true; #ifdef WIN_LONG_PATH if (USE_SUPER_PATH_2) { UString d1, d2; if (GetSuperPaths(oldFile, newFile, d1, d2, USE_MAIN_PATH_2)) return BOOLToBool(::MoveFileW(d1, d2)); } #endif } return false; } #ifndef UNDER_CE EXTERN_C_BEGIN typedef BOOL (WINAPI *Func_CreateHardLinkW)( LPCWSTR lpFileName, LPCWSTR lpExistingFileName, LPSECURITY_ATTRIBUTES lpSecurityAttributes ); EXTERN_C_END bool MyCreateHardLink(CFSTR newFileName, CFSTR existFileName) { #ifndef _UNICODE if (!g_IsNT) { SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return false; /* if (::CreateHardLink(fs2fas(newFileName), fs2fas(existFileName), NULL)) return true; */ } else #endif { Func_CreateHardLinkW my_CreateHardLinkW = (Func_CreateHardLinkW) ::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW"); if (!my_CreateHardLinkW) return false; IF_USE_MAIN_PATH_2(newFileName, existFileName) if (my_CreateHardLinkW(fs2us(newFileName), fs2us(existFileName), NULL)) return true; #ifdef WIN_LONG_PATH if (USE_SUPER_PATH_2) { UString d1, d2; if (GetSuperPaths(newFileName, existFileName, d1, d2, USE_MAIN_PATH_2)) return BOOLToBool(my_CreateHardLinkW(d1, d2, NULL)); } #endif } return false; } #endif bool CreateDir(CFSTR path) { #ifndef _UNICODE if (!g_IsNT) { if (::CreateDirectory(fs2fas(path), NULL)) return true; } else #endif { IF_USE_MAIN_PATH if (::CreateDirectoryW(fs2us(path), NULL)) return true; #ifdef WIN_LONG_PATH if ((!USE_MAIN_PATH || ::GetLastError() != ERROR_ALREADY_EXISTS) && USE_SUPER_PATH) { UString longPath; if (GetSuperPath(path, longPath, USE_MAIN_PATH)) return BOOLToBool(::CreateDirectoryW(longPath, NULL)); } #endif } return false; } bool CreateComplexDir(CFSTR _aPathName) { FString pathName = _aPathName; int pos = pathName.ReverseFind(FCHAR_PATH_SEPARATOR); if (pos > 0 && (unsigned)pos == pathName.Len() - 1) { if (pathName.Len() == 3 && pathName[1] == L':') return true; // Disk folder; pathName.Delete(pos); } const FString pathName2 = pathName; pos = pathName.Len(); for (;;) { if (CreateDir(pathName)) break; if (::GetLastError() == ERROR_ALREADY_EXISTS) { NFind::CFileInfo fileInfo; if (!fileInfo.Find(pathName)) // For network folders return true; if (!fileInfo.IsDir()) return false; break; } pos = pathName.ReverseFind(FCHAR_PATH_SEPARATOR); if (pos < 0 || pos == 0) return false; if (pathName[pos - 1] == L':') return false; pathName.DeleteFrom(pos); } while (pos < (int)pathName2.Len()) { pos = pathName2.Find(FCHAR_PATH_SEPARATOR, pos + 1); if (pos < 0) pos = pathName2.Len(); pathName.SetFrom(pathName2, pos); if (!CreateDir(pathName)) return false; } return true; } bool DeleteFileAlways(CFSTR path) { if (!SetFileAttrib(path, 0)) return false; #ifndef _UNICODE if (!g_IsNT) { if (::DeleteFile(fs2fas(path))) return true; } else #endif { IF_USE_MAIN_PATH if (::DeleteFileW(fs2us(path))) return true; #ifdef WIN_LONG_PATH if (USE_SUPER_PATH) { UString longPath; if (GetSuperPath(path, longPath, USE_MAIN_PATH)) return BOOLToBool(::DeleteFileW(longPath)); } #endif } return false; } bool RemoveDirWithSubItems(const FString &path) { bool needRemoveSubItems = true; { NFind::CFileInfo fi; if (!fi.Find(path)) return false; if (!fi.IsDir()) { ::SetLastError(ERROR_DIRECTORY); return false; } if (fi.HasReparsePoint()) needRemoveSubItems = false; } if (needRemoveSubItems) { FString s = path; s += FCHAR_PATH_SEPARATOR; unsigned prefixSize = s.Len(); s += FCHAR_ANY_MASK; NFind::CEnumerator enumerator(s); NFind::CFileInfo fi; while (enumerator.Next(fi)) { s.DeleteFrom(prefixSize); s += fi.Name; if (fi.IsDir()) { if (!RemoveDirWithSubItems(s)) return false; } else if (!DeleteFileAlways(s)) return false; } } if (!SetFileAttrib(path, 0)) return false; return RemoveDir(path); } #ifdef UNDER_CE bool MyGetFullPathName(CFSTR path, FString &resFullPath) { resFullPath = path; return true; } #else bool MyGetFullPathName(CFSTR path, FString &resFullPath) { return GetFullPath(path, resFullPath); } bool SetCurrentDir(CFSTR path) { // SetCurrentDirectory doesn't support \\?\ prefix #ifndef _UNICODE if (!g_IsNT) { return BOOLToBool(::SetCurrentDirectory(fs2fas(path))); } else #endif { return BOOLToBool(::SetCurrentDirectoryW(fs2us(path))); } } bool GetCurrentDir(FString &path) { path.Empty(); DWORD needLength; #ifndef _UNICODE if (!g_IsNT) { TCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetCurrentDirectory(MAX_PATH + 1, s); path = fas2fs(s); } else #endif { WCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetCurrentDirectoryW(MAX_PATH + 1, s); path = us2fs(s); } return (needLength > 0 && needLength <= MAX_PATH); } #endif bool GetFullPathAndSplit(CFSTR path, FString &resDirPrefix, FString &resFileName) { bool res = MyGetFullPathName(path, resDirPrefix); if (!res) resDirPrefix = path; int pos = resDirPrefix.ReverseFind(FCHAR_PATH_SEPARATOR); resFileName = resDirPrefix.Ptr(pos + 1); resDirPrefix.DeleteFrom(pos + 1); return res; } bool GetOnlyDirPrefix(CFSTR path, FString &resDirPrefix) { FString resFileName; return GetFullPathAndSplit(path, resDirPrefix, resFileName); } bool MyGetTempPath(FString &path) { path.Empty(); DWORD needLength; #ifndef _UNICODE if (!g_IsNT) { TCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetTempPath(MAX_PATH + 1, s); path = fas2fs(s); } else #endif { WCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetTempPathW(MAX_PATH + 1, s);; path = us2fs(s); } return (needLength > 0 && needLength <= MAX_PATH); } static bool CreateTempFile(CFSTR prefix, bool addRandom, FString &path, NIO::COutFile *outFile) { UInt32 d = (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId(); for (unsigned i = 0; i < 100; i++) { path = prefix; if (addRandom) { FChar s[16]; UInt32 value = d; unsigned k; for (k = 0; k < 8; k++) { unsigned t = value & 0xF; value >>= 4; s[k] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10))); } s[k] = '\0'; if (outFile) path += FChar('.'); path += s; UInt32 step = GetTickCount() + 2; if (step == 0) step = 1; d += step; } addRandom = true; if (outFile) path += FTEXT(".tmp"); if (NFind::DoesFileOrDirExist(path)) { SetLastError(ERROR_ALREADY_EXISTS); continue; } if (outFile) { if (outFile->Create(path, false)) return true; } else { if (CreateDir(path)) return true; } DWORD error = GetLastError(); if (error != ERROR_FILE_EXISTS && error != ERROR_ALREADY_EXISTS) break; } path.Empty(); return false; } bool CTempFile::Create(CFSTR prefix, NIO::COutFile *outFile) { if (!Remove()) return false; if (!CreateTempFile(prefix, false, _path, outFile)) return false; _mustBeDeleted = true; return true; } bool CTempFile::CreateRandomInTempFolder(CFSTR namePrefix, NIO::COutFile *outFile) { if (!Remove()) return false; FString tempPath; if (!MyGetTempPath(tempPath)) return false; if (!CreateTempFile(tempPath + namePrefix, true, _path, outFile)) return false; _mustBeDeleted = true; return true; } bool CTempFile::Remove() { if (!_mustBeDeleted) return true; _mustBeDeleted = !DeleteFileAlways(_path); return !_mustBeDeleted; } bool CTempFile::MoveTo(CFSTR name, bool deleteDestBefore) { if (deleteDestBefore) if (NFind::DoesFileExist(name)) if (!DeleteFileAlways(name)) return false; DisableDeleting(); return MyMoveFile(_path, name); } bool CTempDir::Create(CFSTR prefix) { if (!Remove()) return false; FString tempPath; if (!MyGetTempPath(tempPath)) return false; if (!CreateTempFile(tempPath + prefix, true, _path, NULL)) return false; _mustBeDeleted = true; return true; } bool CTempDir::Remove() { if (!_mustBeDeleted) return true; _mustBeDeleted = !RemoveDirWithSubItems(_path); return !_mustBeDeleted; } }}}