tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

FileDir.cpp (16122B)


      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 superPath;
     90    if (GetSuperPath(path, superPath, USE_MAIN_PATH))
     91      hDir = ::CreateFileW(superPath, 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 superPath;
    123      if (GetSuperPath(path, superPath, USE_MAIN_PATH))
    124        return BOOLToBool(::SetFileAttributesW(superPath, attrib));
    125    }
    126    #endif
    127  }
    128  return false;
    129 }
    130 
    131 
    132 bool SetFileAttrib_PosixHighDetect(CFSTR path, DWORD attrib)
    133 {
    134  if ((attrib & 0xF0000000) != 0)
    135    attrib &= 0x3FFF;
    136  return SetFileAttrib(path, attrib);
    137 }
    138 
    139 
    140 bool RemoveDir(CFSTR path)
    141 {
    142  #ifndef _UNICODE
    143  if (!g_IsNT)
    144  {
    145    if (::RemoveDirectory(fs2fas(path)))
    146      return true;
    147  }
    148  else
    149  #endif
    150  {
    151    IF_USE_MAIN_PATH
    152      if (::RemoveDirectoryW(fs2us(path)))
    153        return true;
    154    #ifdef WIN_LONG_PATH
    155    if (USE_SUPER_PATH)
    156    {
    157      UString superPath;
    158      if (GetSuperPath(path, superPath, USE_MAIN_PATH))
    159        return BOOLToBool(::RemoveDirectoryW(superPath));
    160    }
    161    #endif
    162  }
    163  return false;
    164 }
    165 
    166 bool MyMoveFile(CFSTR oldFile, CFSTR newFile)
    167 {
    168  #ifndef _UNICODE
    169  if (!g_IsNT)
    170  {
    171    if (::MoveFile(fs2fas(oldFile), fs2fas(newFile)))
    172      return true;
    173  }
    174  else
    175  #endif
    176  {
    177    IF_USE_MAIN_PATH_2(oldFile, newFile)
    178      if (::MoveFileW(fs2us(oldFile), fs2us(newFile)))
    179        return true;
    180    #ifdef WIN_LONG_PATH
    181    if (USE_SUPER_PATH_2)
    182    {
    183      UString d1, d2;
    184      if (GetSuperPaths(oldFile, newFile, d1, d2, USE_MAIN_PATH_2))
    185        return BOOLToBool(::MoveFileW(d1, d2));
    186    }
    187    #endif
    188  }
    189  return false;
    190 }
    191 
    192 #ifndef UNDER_CE
    193 
    194 EXTERN_C_BEGIN
    195 typedef BOOL (WINAPI *Func_CreateHardLinkW)(
    196    LPCWSTR lpFileName,
    197    LPCWSTR lpExistingFileName,
    198    LPSECURITY_ATTRIBUTES lpSecurityAttributes
    199    );
    200 EXTERN_C_END
    201 
    202 bool MyCreateHardLink(CFSTR newFileName, CFSTR existFileName)
    203 {
    204  #ifndef _UNICODE
    205  if (!g_IsNT)
    206  {
    207    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    208    return false;
    209    /*
    210    if (::CreateHardLink(fs2fas(newFileName), fs2fas(existFileName), NULL))
    211      return true;
    212    */
    213  }
    214  else
    215  #endif
    216  {
    217    Func_CreateHardLinkW my_CreateHardLinkW = (Func_CreateHardLinkW)
    218        ::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW");
    219    if (!my_CreateHardLinkW)
    220      return false;
    221    IF_USE_MAIN_PATH_2(newFileName, existFileName)
    222      if (my_CreateHardLinkW(fs2us(newFileName), fs2us(existFileName), NULL))
    223        return true;
    224    #ifdef WIN_LONG_PATH
    225    if (USE_SUPER_PATH_2)
    226    {
    227      UString d1, d2;
    228      if (GetSuperPaths(newFileName, existFileName, d1, d2, USE_MAIN_PATH_2))
    229        return BOOLToBool(my_CreateHardLinkW(d1, d2, NULL));
    230    }
    231    #endif
    232  }
    233  return false;
    234 }
    235 
    236 #endif
    237 
    238 /*
    239 WinXP-64 CreateDir():
    240  ""                  - ERROR_PATH_NOT_FOUND
    241  \                   - ERROR_ACCESS_DENIED
    242  C:\                 - ERROR_ACCESS_DENIED, if there is such drive,
    243  
    244  D:\folder             - ERROR_PATH_NOT_FOUND, if there is no such drive,
    245  C:\nonExistent\folder - ERROR_PATH_NOT_FOUND
    246  
    247  C:\existFolder      - ERROR_ALREADY_EXISTS
    248  C:\existFolder\     - ERROR_ALREADY_EXISTS
    249 
    250  C:\folder   - OK
    251  C:\folder\  - OK
    252 
    253  \\Server\nonExistent    - ERROR_BAD_NETPATH
    254  \\Server\Share_Readonly - ERROR_ACCESS_DENIED
    255  \\Server\Share          - ERROR_ALREADY_EXISTS
    256 
    257  \\Server\Share_NTFS_drive - ERROR_ACCESS_DENIED
    258  \\Server\Share_FAT_drive  - ERROR_ALREADY_EXISTS
    259 */
    260 
    261 bool CreateDir(CFSTR path)
    262 {
    263  #ifndef _UNICODE
    264  if (!g_IsNT)
    265  {
    266    if (::CreateDirectory(fs2fas(path), NULL))
    267      return true;
    268  }
    269  else
    270  #endif
    271  {
    272    IF_USE_MAIN_PATH
    273      if (::CreateDirectoryW(fs2us(path), NULL))
    274        return true;
    275    #ifdef WIN_LONG_PATH
    276    if ((!USE_MAIN_PATH || ::GetLastError() != ERROR_ALREADY_EXISTS) && USE_SUPER_PATH)
    277    {
    278      UString superPath;
    279      if (GetSuperPath(path, superPath, USE_MAIN_PATH))
    280        return BOOLToBool(::CreateDirectoryW(superPath, NULL));
    281    }
    282    #endif
    283  }
    284  return false;
    285 }
    286 
    287 /*
    288  CreateDir2 returns true, if directory can contain files after the call (two cases):
    289    1) the directory already exists
    290    2) the directory was created
    291  path must be WITHOUT trailing path separator.
    292 
    293  We need CreateDir2, since fileInfo.Find() for reserved names like "com8"
    294   returns FILE instead of DIRECTORY. And we need to use SuperPath */
    295 
    296 static bool CreateDir2(CFSTR path)
    297 {
    298  #ifndef _UNICODE
    299  if (!g_IsNT)
    300  {
    301    if (::CreateDirectory(fs2fas(path), NULL))
    302      return true;
    303  }
    304  else
    305  #endif
    306  {
    307    IF_USE_MAIN_PATH
    308      if (::CreateDirectoryW(fs2us(path), NULL))
    309        return true;
    310    #ifdef WIN_LONG_PATH
    311    if ((!USE_MAIN_PATH || ::GetLastError() != ERROR_ALREADY_EXISTS) && USE_SUPER_PATH)
    312    {
    313      UString superPath;
    314      if (GetSuperPath(path, superPath, USE_MAIN_PATH))
    315      {
    316        if (::CreateDirectoryW(superPath, NULL))
    317          return true;
    318        if (::GetLastError() != ERROR_ALREADY_EXISTS)
    319          return false;
    320        NFind::CFileInfo fi;
    321        if (!fi.Find(us2fs(superPath)))
    322          return false;
    323        return fi.IsDir();
    324      }
    325    }
    326    #endif
    327  }
    328  if (::GetLastError() != ERROR_ALREADY_EXISTS)
    329    return false;
    330  NFind::CFileInfo fi;
    331  if (!fi.Find(path))
    332    return false;
    333  return fi.IsDir();
    334 }
    335 
    336 bool CreateComplexDir(CFSTR _path)
    337 {
    338  #ifdef _WIN32
    339  
    340  {
    341    DWORD attrib = NFind::GetFileAttrib(_path);
    342    if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0)
    343      return true;
    344  }
    345 
    346  #ifndef UNDER_CE
    347  
    348  if (IsDriveRootPath_SuperAllowed(_path))
    349    return false;
    350  
    351  unsigned prefixSize = GetRootPrefixSize(_path);
    352  
    353  #endif
    354  
    355  #endif
    356 
    357  FString path (_path);
    358 
    359  int pos = path.ReverseFind_PathSepar();
    360  if (pos >= 0 && (unsigned)pos == path.Len() - 1)
    361  {
    362    if (path.Len() == 1)
    363      return true;
    364    path.DeleteBack();
    365  }
    366 
    367  const FString path2 (path);
    368  pos = path.Len();
    369  
    370  for (;;)
    371  {
    372    if (CreateDir2(path))
    373      break;
    374    if (::GetLastError() == ERROR_ALREADY_EXISTS)
    375      return false;
    376    pos = path.ReverseFind_PathSepar();
    377    if (pos < 0 || pos == 0)
    378      return false;
    379    
    380    #if defined(_WIN32) && !defined(UNDER_CE)
    381    if (pos == 1 && IS_PATH_SEPAR(path[0]))
    382      return false;
    383    if (prefixSize >= (unsigned)pos + 1)
    384      return false;
    385    #endif
    386    
    387    path.DeleteFrom(pos);
    388  }
    389  
    390  while (pos < (int)path2.Len())
    391  {
    392    int pos2 = NName::FindSepar(path2.Ptr(pos + 1));
    393    if (pos2 < 0)
    394      pos = path2.Len();
    395    else
    396      pos += 1 + pos2;
    397    path.SetFrom(path2, pos);
    398    if (!CreateDir(path))
    399      return false;
    400  }
    401  
    402  return true;
    403 }
    404 
    405 bool DeleteFileAlways(CFSTR path)
    406 {
    407  /* If alt stream, we also need to clear READ-ONLY attribute of main file before delete.
    408     SetFileAttrib("name:stream", ) changes attributes of main file. */
    409  {
    410    DWORD attrib = NFind::GetFileAttrib(path);
    411    if (attrib != INVALID_FILE_ATTRIBUTES
    412        && (attrib & FILE_ATTRIBUTE_DIRECTORY) == 0
    413        && (attrib & FILE_ATTRIBUTE_READONLY) != 0)
    414    {
    415      if (!SetFileAttrib(path, attrib & ~FILE_ATTRIBUTE_READONLY))
    416        return false;
    417    }
    418  }
    419 
    420  #ifndef _UNICODE
    421  if (!g_IsNT)
    422  {
    423    if (::DeleteFile(fs2fas(path)))
    424      return true;
    425  }
    426  else
    427  #endif
    428  {
    429    /* DeleteFile("name::$DATA") deletes all alt streams (same as delete DeleteFile("name")).
    430       Maybe it's better to open "name::$DATA" and clear data for unnamed stream? */
    431    IF_USE_MAIN_PATH
    432      if (::DeleteFileW(fs2us(path)))
    433        return true;
    434    #ifdef WIN_LONG_PATH
    435    if (USE_SUPER_PATH)
    436    {
    437      UString superPath;
    438      if (GetSuperPath(path, superPath, USE_MAIN_PATH))
    439        return BOOLToBool(::DeleteFileW(superPath));
    440    }
    441    #endif
    442  }
    443  return false;
    444 }
    445 
    446 bool RemoveDirWithSubItems(const FString &path)
    447 {
    448  bool needRemoveSubItems = true;
    449  {
    450    NFind::CFileInfo fi;
    451    if (!fi.Find(path))
    452      return false;
    453    if (!fi.IsDir())
    454    {
    455      ::SetLastError(ERROR_DIRECTORY);
    456      return false;
    457    }
    458    if (fi.HasReparsePoint())
    459      needRemoveSubItems = false;
    460  }
    461 
    462  if (needRemoveSubItems)
    463  {
    464    FString s (path);
    465    s.Add_PathSepar();
    466    const unsigned prefixSize = s.Len();
    467    NFind::CEnumerator enumerator;
    468    enumerator.SetDirPrefix(s);
    469    NFind::CFileInfo fi;
    470    while (enumerator.Next(fi))
    471    {
    472      s.DeleteFrom(prefixSize);
    473      s += fi.Name;
    474      if (fi.IsDir())
    475      {
    476        if (!RemoveDirWithSubItems(s))
    477          return false;
    478      }
    479      else if (!DeleteFileAlways(s))
    480        return false;
    481    }
    482  }
    483  
    484  if (!SetFileAttrib(path, 0))
    485    return false;
    486  return RemoveDir(path);
    487 }
    488 
    489 #ifdef UNDER_CE
    490 
    491 bool MyGetFullPathName(CFSTR path, FString &resFullPath)
    492 {
    493  resFullPath = path;
    494  return true;
    495 }
    496 
    497 #else
    498 
    499 bool MyGetFullPathName(CFSTR path, FString &resFullPath)
    500 {
    501  return GetFullPath(path, resFullPath);
    502 }
    503 
    504 bool SetCurrentDir(CFSTR path)
    505 {
    506  // SetCurrentDirectory doesn't support \\?\ prefix
    507  #ifndef _UNICODE
    508  if (!g_IsNT)
    509  {
    510    return BOOLToBool(::SetCurrentDirectory(fs2fas(path)));
    511  }
    512  else
    513  #endif
    514  {
    515    return BOOLToBool(::SetCurrentDirectoryW(fs2us(path)));
    516  }
    517 }
    518 
    519 bool GetCurrentDir(FString &path)
    520 {
    521  path.Empty();
    522  DWORD needLength;
    523  #ifndef _UNICODE
    524  if (!g_IsNT)
    525  {
    526    TCHAR s[MAX_PATH + 2];
    527    s[0] = 0;
    528    needLength = ::GetCurrentDirectory(MAX_PATH + 1, s);
    529    path = fas2fs(s);
    530  }
    531  else
    532  #endif
    533  {
    534    WCHAR s[MAX_PATH + 2];
    535    s[0] = 0;
    536    needLength = ::GetCurrentDirectoryW(MAX_PATH + 1, s);
    537    path = us2fs(s);
    538  }
    539  return (needLength > 0 && needLength <= MAX_PATH);
    540 }
    541 
    542 #endif
    543 
    544 bool GetFullPathAndSplit(CFSTR path, FString &resDirPrefix, FString &resFileName)
    545 {
    546  bool res = MyGetFullPathName(path, resDirPrefix);
    547  if (!res)
    548    resDirPrefix = path;
    549  int pos = resDirPrefix.ReverseFind_PathSepar();
    550  resFileName = resDirPrefix.Ptr(pos + 1);
    551  resDirPrefix.DeleteFrom(pos + 1);
    552  return res;
    553 }
    554 
    555 bool GetOnlyDirPrefix(CFSTR path, FString &resDirPrefix)
    556 {
    557  FString resFileName;
    558  return GetFullPathAndSplit(path, resDirPrefix, resFileName);
    559 }
    560 
    561 bool MyGetTempPath(FString &path)
    562 {
    563  path.Empty();
    564  DWORD needLength;
    565  #ifndef _UNICODE
    566  if (!g_IsNT)
    567  {
    568    TCHAR s[MAX_PATH + 2];
    569    s[0] = 0;
    570    needLength = ::GetTempPath(MAX_PATH + 1, s);
    571    path = fas2fs(s);
    572  }
    573  else
    574  #endif
    575  {
    576    WCHAR s[MAX_PATH + 2];
    577    s[0] = 0;
    578    needLength = ::GetTempPathW(MAX_PATH + 1, s);;
    579    path = us2fs(s);
    580  }
    581  return (needLength > 0 && needLength <= MAX_PATH);
    582 }
    583 
    584 static bool CreateTempFile(CFSTR prefix, bool addRandom, FString &path, NIO::COutFile *outFile)
    585 {
    586  UInt32 d = (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId();
    587  for (unsigned i = 0; i < 100; i++)
    588  {
    589    path = prefix;
    590    if (addRandom)
    591    {
    592      char s[16];
    593      UInt32 val = d;
    594      unsigned k;
    595      for (k = 0; k < 8; k++)
    596      {
    597        unsigned t = val & 0xF;
    598        val >>= 4;
    599        s[k] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10)));
    600      }
    601      s[k] = '\0';
    602      if (outFile)
    603        path += '.';
    604      path += s;
    605      UInt32 step = GetTickCount() + 2;
    606      if (step == 0)
    607        step = 1;
    608      d += step;
    609    }
    610    addRandom = true;
    611    if (outFile)
    612      path += ".tmp";
    613    if (NFind::DoesFileOrDirExist(path))
    614    {
    615      SetLastError(ERROR_ALREADY_EXISTS);
    616      continue;
    617    }
    618    if (outFile)
    619    {
    620      if (outFile->Create(path, false))
    621        return true;
    622    }
    623    else
    624    {
    625      if (CreateDir(path))
    626        return true;
    627    }
    628    DWORD error = GetLastError();
    629    if (error != ERROR_FILE_EXISTS &&
    630        error != ERROR_ALREADY_EXISTS)
    631      break;
    632  }
    633  path.Empty();
    634  return false;
    635 }
    636 
    637 bool CTempFile::Create(CFSTR prefix, NIO::COutFile *outFile)
    638 {
    639  if (!Remove())
    640    return false;
    641  if (!CreateTempFile(prefix, false, _path, outFile))
    642    return false;
    643  _mustBeDeleted = true;
    644  return true;
    645 }
    646 
    647 bool CTempFile::CreateRandomInTempFolder(CFSTR namePrefix, NIO::COutFile *outFile)
    648 {
    649  if (!Remove())
    650    return false;
    651  FString tempPath;
    652  if (!MyGetTempPath(tempPath))
    653    return false;
    654  if (!CreateTempFile(tempPath + namePrefix, true, _path, outFile))
    655    return false;
    656  _mustBeDeleted = true;
    657  return true;
    658 }
    659 
    660 bool CTempFile::Remove()
    661 {
    662  if (!_mustBeDeleted)
    663    return true;
    664  _mustBeDeleted = !DeleteFileAlways(_path);
    665  return !_mustBeDeleted;
    666 }
    667 
    668 bool CTempFile::MoveTo(CFSTR name, bool deleteDestBefore)
    669 {
    670  // DWORD attrib = 0;
    671  if (deleteDestBefore)
    672  {
    673    if (NFind::DoesFileExist(name))
    674    {
    675      // attrib = NFind::GetFileAttrib(name);
    676      if (!DeleteFileAlways(name))
    677        return false;
    678    }
    679  }
    680  DisableDeleting();
    681  return MyMoveFile(_path, name);
    682  
    683  /*
    684  if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_READONLY))
    685  {
    686    DWORD attrib2 = NFind::GetFileAttrib(name);
    687    if (attrib2 != INVALID_FILE_ATTRIBUTES)
    688      SetFileAttrib(name, attrib2 | FILE_ATTRIBUTE_READONLY);
    689  }
    690  */
    691 }
    692 
    693 bool CTempDir::Create(CFSTR prefix)
    694 {
    695  if (!Remove())
    696    return false;
    697  FString tempPath;
    698  if (!MyGetTempPath(tempPath))
    699    return false;
    700  if (!CreateTempFile(tempPath + prefix, true, _path, NULL))
    701    return false;
    702  _mustBeDeleted = true;
    703  return true;
    704 }
    705 
    706 bool CTempDir::Remove()
    707 {
    708  if (!_mustBeDeleted)
    709    return true;
    710  _mustBeDeleted = !RemoveDirWithSubItems(_path);
    711  return !_mustBeDeleted;
    712 }
    713 
    714 }}}