Source code
Revision control
Copy as Markdown
Other Tools
// 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 superPath;↩
if (GetSuperPath(path, superPath, USE_MAIN_PATH))↩
hDir = ::CreateFileW(superPath, 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 superPath;↩
if (GetSuperPath(path, superPath, USE_MAIN_PATH))↩
return BOOLToBool(::SetFileAttributesW(superPath, attrib));↩
}↩
#endif↩
}↩
return false;↩
}↩
↩
↩
bool SetFileAttrib_PosixHighDetect(CFSTR path, DWORD attrib)↩
{↩
if ((attrib & 0xF0000000) != 0)↩
attrib &= 0x3FFF;↩
return SetFileAttrib(path, attrib);↩
}↩
↩
↩
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 superPath;↩
if (GetSuperPath(path, superPath, USE_MAIN_PATH))↩
return BOOLToBool(::RemoveDirectoryW(superPath));↩
}↩
#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↩
↩
/*↩
WinXP-64 CreateDir():↩
"" - ERROR_PATH_NOT_FOUND↩
\ - ERROR_ACCESS_DENIED↩
C:\ - ERROR_ACCESS_DENIED, if there is such drive,↩
↩
D:\folder - ERROR_PATH_NOT_FOUND, if there is no such drive,↩
C:\nonExistent\folder - ERROR_PATH_NOT_FOUND↩
↩
C:\existFolder - ERROR_ALREADY_EXISTS↩
C:\existFolder\ - ERROR_ALREADY_EXISTS↩
↩
C:\folder - OK↩
C:\folder\ - OK↩
↩
\\Server\nonExistent - ERROR_BAD_NETPATH↩
\\Server\Share_Readonly - ERROR_ACCESS_DENIED↩
\\Server\Share - ERROR_ALREADY_EXISTS↩
↩
\\Server\Share_NTFS_drive - ERROR_ACCESS_DENIED↩
\\Server\Share_FAT_drive - ERROR_ALREADY_EXISTS↩
*/↩
↩
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 superPath;↩
if (GetSuperPath(path, superPath, USE_MAIN_PATH))↩
return BOOLToBool(::CreateDirectoryW(superPath, NULL));↩
}↩
#endif↩
}↩
return false;↩
}↩
↩
/*↩
CreateDir2 returns true, if directory can contain files after the call (two cases):↩
1) the directory already exists↩
2) the directory was created↩
path must be WITHOUT trailing path separator.↩
↩
We need CreateDir2, since fileInfo.Find() for reserved names like "com8"↩
returns FILE instead of DIRECTORY. And we need to use SuperPath */↩
↩
static bool CreateDir2(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 superPath;↩
if (GetSuperPath(path, superPath, USE_MAIN_PATH))↩
{↩
if (::CreateDirectoryW(superPath, NULL))↩
return true;↩
if (::GetLastError() != ERROR_ALREADY_EXISTS)↩
return false;↩
NFind::CFileInfo fi;↩
if (!fi.Find(us2fs(superPath)))↩
return false;↩
return fi.IsDir();↩
}↩
}↩
#endif↩
}↩
if (::GetLastError() != ERROR_ALREADY_EXISTS)↩
return false;↩
NFind::CFileInfo fi;↩
if (!fi.Find(path))↩
return false;↩
return fi.IsDir();↩
}↩
↩
bool CreateComplexDir(CFSTR _path)↩
{↩
#ifdef _WIN32↩
↩
{↩
DWORD attrib = NFind::GetFileAttrib(_path);↩
if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0)↩
return true;↩
}↩
↩
#ifndef UNDER_CE↩
↩
if (IsDriveRootPath_SuperAllowed(_path))↩
return false;↩
↩
unsigned prefixSize = GetRootPrefixSize(_path);↩
↩
#endif↩
↩
#endif↩
↩
FString path (_path);↩
↩
int pos = path.ReverseFind_PathSepar();↩
if (pos >= 0 && (unsigned)pos == path.Len() - 1)↩
{↩
if (path.Len() == 1)↩
return true;↩
path.DeleteBack();↩
}↩
↩
const FString path2 (path);↩
pos = path.Len();↩
↩
for (;;)↩
{↩
if (CreateDir2(path))↩
break;↩
if (::GetLastError() == ERROR_ALREADY_EXISTS)↩
return false;↩
pos = path.ReverseFind_PathSepar();↩
if (pos < 0 || pos == 0)↩
return false;↩
↩
#if defined(_WIN32) && !defined(UNDER_CE)↩
if (pos == 1 && IS_PATH_SEPAR(path[0]))↩
return false;↩
if (prefixSize >= (unsigned)pos + 1)↩
return false;↩
#endif↩
↩
path.DeleteFrom(pos);↩
}↩
↩
while (pos < (int)path2.Len())↩
{↩
int pos2 = NName::FindSepar(path2.Ptr(pos + 1));↩
if (pos2 < 0)↩
pos = path2.Len();↩
else↩
pos += 1 + pos2;↩
path.SetFrom(path2, pos);↩
if (!CreateDir(path))↩
return false;↩
}↩
↩
return true;↩
}↩
↩
bool DeleteFileAlways(CFSTR path)↩
{↩
/* If alt stream, we also need to clear READ-ONLY attribute of main file before delete.↩
SetFileAttrib("name:stream", ) changes attributes of main file. */↩
{↩
DWORD attrib = NFind::GetFileAttrib(path);↩
if (attrib != INVALID_FILE_ATTRIBUTES↩
&& (attrib & FILE_ATTRIBUTE_DIRECTORY) == 0↩
&& (attrib & FILE_ATTRIBUTE_READONLY) != 0)↩
{↩
if (!SetFileAttrib(path, attrib & ~FILE_ATTRIBUTE_READONLY))↩
return false;↩
}↩
}↩
↩
#ifndef _UNICODE↩
if (!g_IsNT)↩
{↩
if (::DeleteFile(fs2fas(path)))↩
return true;↩
}↩
else↩
#endif↩
{↩
/* DeleteFile("name::$DATA") deletes all alt streams (same as delete DeleteFile("name")).↩
Maybe it's better to open "name::$DATA" and clear data for unnamed stream? */↩
IF_USE_MAIN_PATH↩
if (::DeleteFileW(fs2us(path)))↩
return true;↩
#ifdef WIN_LONG_PATH↩
if (USE_SUPER_PATH)↩
{↩
UString superPath;↩
if (GetSuperPath(path, superPath, USE_MAIN_PATH))↩
return BOOLToBool(::DeleteFileW(superPath));↩
}↩
#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.Add_PathSepar();↩
const unsigned prefixSize = s.Len();↩
NFind::CEnumerator enumerator;↩
enumerator.SetDirPrefix(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_PathSepar();↩
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)↩
{↩
char s[16];↩
UInt32 val = d;↩
unsigned k;↩
for (k = 0; k < 8; k++)↩
{↩
unsigned t = val & 0xF;↩
val >>= 4;↩
s[k] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10)));↩
}↩
s[k] = '\0';↩
if (outFile)↩
path += '.';↩
path += s;↩
UInt32 step = GetTickCount() + 2;↩
if (step == 0)↩
step = 1;↩
d += step;↩
}↩
addRandom = true;↩
if (outFile)↩
path += ".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)↩
{↩
// DWORD attrib = 0;↩
if (deleteDestBefore)↩
{↩
if (NFind::DoesFileExist(name))↩
{↩
// attrib = NFind::GetFileAttrib(name);↩
if (!DeleteFileAlways(name))↩
return false;↩
}↩
}↩
DisableDeleting();↩
return MyMoveFile(_path, name);↩
↩
/*↩
if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_READONLY))↩
{↩
DWORD attrib2 = NFind::GetFileAttrib(name);↩
if (attrib2 != INVALID_FILE_ATTRIBUTES)↩
SetFileAttrib(name, attrib2 | FILE_ATTRIBUTE_READONLY);↩
}↩
*/↩
}↩
↩
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;↩
}↩
↩
}}}↩