Home | History | Annotate | Download | only in Windows
      1 // Windows/FileDir.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #ifndef _UNICODE
      6 #include "../Common/StringConvert.h"
      7 #endif
      8 
      9 #include "FileDir.h"
     10 #include "FileFind.h"
     11 #include "FileName.h"
     12 
     13 #ifndef _UNICODE
     14 extern bool g_IsNT;
     15 #endif
     16 
     17 using namespace NWindows;
     18 using namespace NFile;
     19 using namespace NName;
     20 
     21 namespace NWindows {
     22 namespace NFile {
     23 namespace NDir {
     24 
     25 #ifndef UNDER_CE
     26 
     27 bool GetWindowsDir(FString &path)
     28 {
     29   UINT needLength;
     30   #ifndef _UNICODE
     31   if (!g_IsNT)
     32   {
     33     TCHAR s[MAX_PATH + 2];
     34     s[0] = 0;
     35     needLength = ::GetWindowsDirectory(s, MAX_PATH + 1);
     36     path = fas2fs(s);
     37   }
     38   else
     39   #endif
     40   {
     41     WCHAR s[MAX_PATH + 2];
     42     s[0] = 0;
     43     needLength = ::GetWindowsDirectoryW(s, MAX_PATH + 1);
     44     path = us2fs(s);
     45   }
     46   return (needLength > 0 && needLength <= MAX_PATH);
     47 }
     48 
     49 bool GetSystemDir(FString &path)
     50 {
     51   UINT needLength;
     52   #ifndef _UNICODE
     53   if (!g_IsNT)
     54   {
     55     TCHAR s[MAX_PATH + 2];
     56     s[0] = 0;
     57     needLength = ::GetSystemDirectory(s, MAX_PATH + 1);
     58     path = fas2fs(s);
     59   }
     60   else
     61   #endif
     62   {
     63     WCHAR s[MAX_PATH + 2];
     64     s[0] = 0;
     65     needLength = ::GetSystemDirectoryW(s, MAX_PATH + 1);
     66     path = us2fs(s);
     67   }
     68   return (needLength > 0 && needLength <= MAX_PATH);
     69 }
     70 #endif
     71 
     72 bool SetDirTime(CFSTR path, const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime)
     73 {
     74   #ifndef _UNICODE
     75   if (!g_IsNT)
     76   {
     77     ::SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
     78     return false;
     79   }
     80   #endif
     81 
     82   HANDLE hDir = INVALID_HANDLE_VALUE;
     83   IF_USE_MAIN_PATH
     84     hDir = ::CreateFileW(fs2us(path), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
     85         NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
     86   #ifdef WIN_LONG_PATH
     87   if (hDir == INVALID_HANDLE_VALUE && USE_SUPER_PATH)
     88   {
     89     UString longPath;
     90     if (GetSuperPath(path, longPath, USE_MAIN_PATH))
     91       hDir = ::CreateFileW(longPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
     92           NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
     93   }
     94   #endif
     95 
     96   bool res = false;
     97   if (hDir != INVALID_HANDLE_VALUE)
     98   {
     99     res = BOOLToBool(::SetFileTime(hDir, cTime, aTime, mTime));
    100     ::CloseHandle(hDir);
    101   }
    102   return res;
    103 }
    104 
    105 bool SetFileAttrib(CFSTR path, DWORD attrib)
    106 {
    107   #ifndef _UNICODE
    108   if (!g_IsNT)
    109   {
    110     if (::SetFileAttributes(fs2fas(path), attrib))
    111       return true;
    112   }
    113   else
    114   #endif
    115   {
    116     IF_USE_MAIN_PATH
    117       if (::SetFileAttributesW(fs2us(path), attrib))
    118         return true;
    119     #ifdef WIN_LONG_PATH
    120     if (USE_SUPER_PATH)
    121     {
    122       UString longPath;
    123       if (GetSuperPath(path, longPath, USE_MAIN_PATH))
    124         return BOOLToBool(::SetFileAttributesW(longPath, attrib));
    125     }
    126     #endif
    127   }
    128   return false;
    129 }
    130 
    131 bool RemoveDir(CFSTR path)
    132 {
    133   #ifndef _UNICODE
    134   if (!g_IsNT)
    135   {
    136     if (::RemoveDirectory(fs2fas(path)))
    137       return true;
    138   }
    139   else
    140   #endif
    141   {
    142     IF_USE_MAIN_PATH
    143       if (::RemoveDirectoryW(fs2us(path)))
    144         return true;
    145     #ifdef WIN_LONG_PATH
    146     if (USE_SUPER_PATH)
    147     {
    148       UString longPath;
    149       if (GetSuperPath(path, longPath, USE_MAIN_PATH))
    150         return BOOLToBool(::RemoveDirectoryW(longPath));
    151     }
    152     #endif
    153   }
    154   return false;
    155 }
    156 
    157 bool MyMoveFile(CFSTR oldFile, CFSTR newFile)
    158 {
    159   #ifndef _UNICODE
    160   if (!g_IsNT)
    161   {
    162     if (::MoveFile(fs2fas(oldFile), fs2fas(newFile)))
    163       return true;
    164   }
    165   else
    166   #endif
    167   {
    168     IF_USE_MAIN_PATH_2(oldFile, newFile)
    169       if (::MoveFileW(fs2us(oldFile), fs2us(newFile)))
    170         return true;
    171     #ifdef WIN_LONG_PATH
    172     if (USE_SUPER_PATH_2)
    173     {
    174       UString d1, d2;
    175       if (GetSuperPaths(oldFile, newFile, d1, d2, USE_MAIN_PATH_2))
    176         return BOOLToBool(::MoveFileW(d1, d2));
    177     }
    178     #endif
    179   }
    180   return false;
    181 }
    182 
    183 #ifndef UNDER_CE
    184 
    185 EXTERN_C_BEGIN
    186 typedef BOOL (WINAPI *Func_CreateHardLinkW)(
    187     LPCWSTR lpFileName,
    188     LPCWSTR lpExistingFileName,
    189     LPSECURITY_ATTRIBUTES lpSecurityAttributes
    190     );
    191 EXTERN_C_END
    192 
    193 bool MyCreateHardLink(CFSTR newFileName, CFSTR existFileName)
    194 {
    195   #ifndef _UNICODE
    196   if (!g_IsNT)
    197   {
    198     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    199     return false;
    200     /*
    201     if (::CreateHardLink(fs2fas(newFileName), fs2fas(existFileName), NULL))
    202       return true;
    203     */
    204   }
    205   else
    206   #endif
    207   {
    208     Func_CreateHardLinkW my_CreateHardLinkW = (Func_CreateHardLinkW)
    209         ::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW");
    210     if (!my_CreateHardLinkW)
    211       return false;
    212     IF_USE_MAIN_PATH_2(newFileName, existFileName)
    213       if (my_CreateHardLinkW(fs2us(newFileName), fs2us(existFileName), NULL))
    214         return true;
    215     #ifdef WIN_LONG_PATH
    216     if (USE_SUPER_PATH_2)
    217     {
    218       UString d1, d2;
    219       if (GetSuperPaths(newFileName, existFileName, d1, d2, USE_MAIN_PATH_2))
    220         return BOOLToBool(my_CreateHardLinkW(d1, d2, NULL));
    221     }
    222     #endif
    223   }
    224   return false;
    225 }
    226 
    227 #endif
    228 
    229 bool CreateDir(CFSTR path)
    230 {
    231   #ifndef _UNICODE
    232   if (!g_IsNT)
    233   {
    234     if (::CreateDirectory(fs2fas(path), NULL))
    235       return true;
    236   }
    237   else
    238   #endif
    239   {
    240     IF_USE_MAIN_PATH
    241       if (::CreateDirectoryW(fs2us(path), NULL))
    242         return true;
    243     #ifdef WIN_LONG_PATH
    244     if ((!USE_MAIN_PATH || ::GetLastError() != ERROR_ALREADY_EXISTS) && USE_SUPER_PATH)
    245     {
    246       UString longPath;
    247       if (GetSuperPath(path, longPath, USE_MAIN_PATH))
    248         return BOOLToBool(::CreateDirectoryW(longPath, NULL));
    249     }
    250     #endif
    251   }
    252   return false;
    253 }
    254 
    255 bool CreateComplexDir(CFSTR _aPathName)
    256 {
    257   FString pathName = _aPathName;
    258   int pos = pathName.ReverseFind(FCHAR_PATH_SEPARATOR);
    259   if (pos > 0 && (unsigned)pos == pathName.Len() - 1)
    260   {
    261     if (pathName.Len() == 3 && pathName[1] == L':')
    262       return true; // Disk folder;
    263     pathName.Delete(pos);
    264   }
    265   const FString pathName2 = pathName;
    266   pos = pathName.Len();
    267 
    268   for (;;)
    269   {
    270     if (CreateDir(pathName))
    271       break;
    272     if (::GetLastError() == ERROR_ALREADY_EXISTS)
    273     {
    274       NFind::CFileInfo fileInfo;
    275       if (!fileInfo.Find(pathName)) // For network folders
    276         return true;
    277       if (!fileInfo.IsDir())
    278         return false;
    279       break;
    280     }
    281     pos = pathName.ReverseFind(FCHAR_PATH_SEPARATOR);
    282     if (pos < 0 || pos == 0)
    283       return false;
    284     if (pathName[pos - 1] == L':')
    285       return false;
    286     pathName.DeleteFrom(pos);
    287   }
    288 
    289   while (pos < (int)pathName2.Len())
    290   {
    291     pos = pathName2.Find(FCHAR_PATH_SEPARATOR, pos + 1);
    292     if (pos < 0)
    293       pos = pathName2.Len();
    294     pathName.SetFrom(pathName2, pos);
    295     if (!CreateDir(pathName))
    296       return false;
    297   }
    298 
    299   return true;
    300 }
    301 
    302 bool DeleteFileAlways(CFSTR path)
    303 {
    304   if (!SetFileAttrib(path, 0))
    305     return false;
    306   #ifndef _UNICODE
    307   if (!g_IsNT)
    308   {
    309     if (::DeleteFile(fs2fas(path)))
    310       return true;
    311   }
    312   else
    313   #endif
    314   {
    315     IF_USE_MAIN_PATH
    316       if (::DeleteFileW(fs2us(path)))
    317         return true;
    318     #ifdef WIN_LONG_PATH
    319     if (USE_SUPER_PATH)
    320     {
    321       UString longPath;
    322       if (GetSuperPath(path, longPath, USE_MAIN_PATH))
    323         return BOOLToBool(::DeleteFileW(longPath));
    324     }
    325     #endif
    326   }
    327   return false;
    328 }
    329 
    330 bool RemoveDirWithSubItems(const FString &path)
    331 {
    332   bool needRemoveSubItems = true;
    333   {
    334     NFind::CFileInfo fi;
    335     if (!fi.Find(path))
    336       return false;
    337     if (!fi.IsDir())
    338     {
    339       ::SetLastError(ERROR_DIRECTORY);
    340       return false;
    341     }
    342     if (fi.HasReparsePoint())
    343       needRemoveSubItems = false;
    344   }
    345 
    346   if (needRemoveSubItems)
    347   {
    348     FString s = path;
    349     s += FCHAR_PATH_SEPARATOR;
    350     unsigned prefixSize = s.Len();
    351     s += FCHAR_ANY_MASK;
    352     NFind::CEnumerator enumerator(s);
    353     NFind::CFileInfo fi;
    354     while (enumerator.Next(fi))
    355     {
    356       s.DeleteFrom(prefixSize);
    357       s += fi.Name;
    358       if (fi.IsDir())
    359       {
    360         if (!RemoveDirWithSubItems(s))
    361           return false;
    362       }
    363       else if (!DeleteFileAlways(s))
    364         return false;
    365     }
    366   }
    367 
    368   if (!SetFileAttrib(path, 0))
    369     return false;
    370   return RemoveDir(path);
    371 }
    372 
    373 #ifdef UNDER_CE
    374 
    375 bool MyGetFullPathName(CFSTR path, FString &resFullPath)
    376 {
    377   resFullPath = path;
    378   return true;
    379 }
    380 
    381 #else
    382 
    383 bool MyGetFullPathName(CFSTR path, FString &resFullPath)
    384 {
    385   return GetFullPath(path, resFullPath);
    386 }
    387 
    388 bool SetCurrentDir(CFSTR path)
    389 {
    390   // SetCurrentDirectory doesn't support \\?\ prefix
    391   #ifndef _UNICODE
    392   if (!g_IsNT)
    393   {
    394     return BOOLToBool(::SetCurrentDirectory(fs2fas(path)));
    395   }
    396   else
    397   #endif
    398   {
    399     return BOOLToBool(::SetCurrentDirectoryW(fs2us(path)));
    400   }
    401 }
    402 
    403 bool GetCurrentDir(FString &path)
    404 {
    405   path.Empty();
    406   DWORD needLength;
    407   #ifndef _UNICODE
    408   if (!g_IsNT)
    409   {
    410     TCHAR s[MAX_PATH + 2];
    411     s[0] = 0;
    412     needLength = ::GetCurrentDirectory(MAX_PATH + 1, s);
    413     path = fas2fs(s);
    414   }
    415   else
    416   #endif
    417   {
    418     WCHAR s[MAX_PATH + 2];
    419     s[0] = 0;
    420     needLength = ::GetCurrentDirectoryW(MAX_PATH + 1, s);
    421     path = us2fs(s);
    422   }
    423   return (needLength > 0 && needLength <= MAX_PATH);
    424 }
    425 
    426 #endif
    427 
    428 bool GetFullPathAndSplit(CFSTR path, FString &resDirPrefix, FString &resFileName)
    429 {
    430   bool res = MyGetFullPathName(path, resDirPrefix);
    431   if (!res)
    432     resDirPrefix = path;
    433   int pos = resDirPrefix.ReverseFind(FCHAR_PATH_SEPARATOR);
    434   resFileName = resDirPrefix.Ptr(pos + 1);
    435   resDirPrefix.DeleteFrom(pos + 1);
    436   return res;
    437 }
    438 
    439 bool GetOnlyDirPrefix(CFSTR path, FString &resDirPrefix)
    440 {
    441   FString resFileName;
    442   return GetFullPathAndSplit(path, resDirPrefix, resFileName);
    443 }
    444 
    445 bool MyGetTempPath(FString &path)
    446 {
    447   path.Empty();
    448   DWORD needLength;
    449   #ifndef _UNICODE
    450   if (!g_IsNT)
    451   {
    452     TCHAR s[MAX_PATH + 2];
    453     s[0] = 0;
    454     needLength = ::GetTempPath(MAX_PATH + 1, s);
    455     path = fas2fs(s);
    456   }
    457   else
    458   #endif
    459   {
    460     WCHAR s[MAX_PATH + 2];
    461     s[0] = 0;
    462     needLength = ::GetTempPathW(MAX_PATH + 1, s);;
    463     path = us2fs(s);
    464   }
    465   return (needLength > 0 && needLength <= MAX_PATH);
    466 }
    467 
    468 static bool CreateTempFile(CFSTR prefix, bool addRandom, FString &path, NIO::COutFile *outFile)
    469 {
    470   UInt32 d = (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId();
    471   for (unsigned i = 0; i < 100; i++)
    472   {
    473     path = prefix;
    474     if (addRandom)
    475     {
    476       FChar s[16];
    477       UInt32 value = d;
    478       unsigned k;
    479       for (k = 0; k < 8; k++)
    480       {
    481         unsigned t = value & 0xF;
    482         value >>= 4;
    483         s[k] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10)));
    484       }
    485       s[k] = '\0';
    486       if (outFile)
    487         path += FChar('.');
    488       path += s;
    489       UInt32 step = GetTickCount() + 2;
    490       if (step == 0)
    491         step = 1;
    492       d += step;
    493     }
    494     addRandom = true;
    495     if (outFile)
    496       path += FTEXT(".tmp");
    497     if (NFind::DoesFileOrDirExist(path))
    498     {
    499       SetLastError(ERROR_ALREADY_EXISTS);
    500       continue;
    501     }
    502     if (outFile)
    503     {
    504       if (outFile->Create(path, false))
    505         return true;
    506     }
    507     else
    508     {
    509       if (CreateDir(path))
    510         return true;
    511     }
    512     DWORD error = GetLastError();
    513     if (error != ERROR_FILE_EXISTS &&
    514         error != ERROR_ALREADY_EXISTS)
    515       break;
    516   }
    517   path.Empty();
    518   return false;
    519 }
    520 
    521 bool CTempFile::Create(CFSTR prefix, NIO::COutFile *outFile)
    522 {
    523   if (!Remove())
    524     return false;
    525   if (!CreateTempFile(prefix, false, _path, outFile))
    526     return false;
    527   _mustBeDeleted = true;
    528   return true;
    529 }
    530 
    531 bool CTempFile::CreateRandomInTempFolder(CFSTR namePrefix, NIO::COutFile *outFile)
    532 {
    533   if (!Remove())
    534     return false;
    535   FString tempPath;
    536   if (!MyGetTempPath(tempPath))
    537     return false;
    538   if (!CreateTempFile(tempPath + namePrefix, true, _path, outFile))
    539     return false;
    540   _mustBeDeleted = true;
    541   return true;
    542 }
    543 
    544 bool CTempFile::Remove()
    545 {
    546   if (!_mustBeDeleted)
    547     return true;
    548   _mustBeDeleted = !DeleteFileAlways(_path);
    549   return !_mustBeDeleted;
    550 }
    551 
    552 bool CTempFile::MoveTo(CFSTR name, bool deleteDestBefore)
    553 {
    554   if (deleteDestBefore)
    555     if (NFind::DoesFileExist(name))
    556       if (!DeleteFileAlways(name))
    557         return false;
    558   DisableDeleting();
    559   return MyMoveFile(_path, name);
    560 }
    561 
    562 bool CTempDir::Create(CFSTR prefix)
    563 {
    564   if (!Remove())
    565     return false;
    566   FString tempPath;
    567   if (!MyGetTempPath(tempPath))
    568     return false;
    569   if (!CreateTempFile(tempPath + prefix, true, _path, NULL))
    570     return false;
    571   _mustBeDeleted = true;
    572   return true;
    573 }
    574 
    575 bool CTempDir::Remove()
    576 {
    577   if (!_mustBeDeleted)
    578     return true;
    579   _mustBeDeleted = !RemoveDirWithSubItems(_path);
    580   return !_mustBeDeleted;
    581 }
    582 
    583 }}}
    584