tor-browser

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

FileIO.cpp (12076B)


      1 // Windows/FileIO.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #ifdef SUPPORT_DEVICE_FILE
      6 #include "../../C/Alloc.h"
      7 #endif
      8 
      9 #include "FileIO.h"
     10 #include "FileName.h"
     11 
     12 #ifndef _UNICODE
     13 extern bool g_IsNT;
     14 #endif
     15 
     16 using namespace NWindows;
     17 using namespace NFile;
     18 using namespace NName;
     19 
     20 namespace NWindows {
     21 namespace NFile {
     22 
     23 #ifdef SUPPORT_DEVICE_FILE
     24 
     25 namespace NSystem
     26 {
     27 bool MyGetDiskFreeSpace(CFSTR rootPath, UInt64 &clusterSize, UInt64 &totalSize, UInt64 &freeSize);
     28 }
     29 #endif
     30 
     31 namespace NIO {
     32 
     33 /*
     34 WinXP-64 CreateFile():
     35  ""             -  ERROR_PATH_NOT_FOUND
     36  :stream        -  OK
     37  .:stream       -  ERROR_PATH_NOT_FOUND
     38  .\:stream      -  OK
     39  
     40  folder\:stream -  ERROR_INVALID_NAME
     41  folder:stream  -  OK
     42 
     43  c:\:stream     -  OK
     44 
     45  c::stream      -  ERROR_INVALID_NAME, if current dir is NOT ROOT ( c:\dir1 )
     46  c::stream      -  OK,                 if current dir is ROOT     ( c:\ )
     47 */
     48 
     49 bool CFileBase::Create(CFSTR path, DWORD desiredAccess,
     50    DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
     51 {
     52  if (!Close())
     53    return false;
     54 
     55  #ifdef SUPPORT_DEVICE_FILE
     56  IsDeviceFile = false;
     57  #endif
     58 
     59  #ifndef _UNICODE
     60  if (!g_IsNT)
     61  {
     62    _handle = ::CreateFile(fs2fas(path), desiredAccess, shareMode,
     63        (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, flagsAndAttributes, (HANDLE)NULL);
     64  }
     65  else
     66  #endif
     67  {
     68    IF_USE_MAIN_PATH
     69      _handle = ::CreateFileW(fs2us(path), desiredAccess, shareMode,
     70        (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, flagsAndAttributes, (HANDLE)NULL);
     71    #ifdef WIN_LONG_PATH
     72    if (_handle == INVALID_HANDLE_VALUE && USE_SUPER_PATH)
     73    {
     74      UString superPath;
     75      if (GetSuperPath(path, superPath, USE_MAIN_PATH))
     76        _handle = ::CreateFileW(superPath, desiredAccess, shareMode,
     77            (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, flagsAndAttributes, (HANDLE)NULL);
     78    }
     79    #endif
     80  }
     81  return (_handle != INVALID_HANDLE_VALUE);
     82 }
     83 
     84 bool CFileBase::Close() throw()
     85 {
     86  if (_handle == INVALID_HANDLE_VALUE)
     87    return true;
     88  if (!::CloseHandle(_handle))
     89    return false;
     90  _handle = INVALID_HANDLE_VALUE;
     91  return true;
     92 }
     93 
     94 bool CFileBase::GetPosition(UInt64 &position) const throw()
     95 {
     96  return Seek(0, FILE_CURRENT, position);
     97 }
     98 
     99 bool CFileBase::GetLength(UInt64 &length) const throw()
    100 {
    101  #ifdef SUPPORT_DEVICE_FILE
    102  if (IsDeviceFile && SizeDefined)
    103  {
    104    length = Size;
    105    return true;
    106  }
    107  #endif
    108 
    109  DWORD sizeHigh;
    110  DWORD sizeLow = ::GetFileSize(_handle, &sizeHigh);
    111  if (sizeLow == 0xFFFFFFFF)
    112    if (::GetLastError() != NO_ERROR)
    113      return false;
    114  length = (((UInt64)sizeHigh) << 32) + sizeLow;
    115  return true;
    116 }
    117 
    118 bool CFileBase::Seek(Int64 distanceToMove, DWORD moveMethod, UInt64 &newPosition) const throw()
    119 {
    120  #ifdef SUPPORT_DEVICE_FILE
    121  if (IsDeviceFile && SizeDefined && moveMethod == FILE_END)
    122  {
    123    distanceToMove += Size;
    124    moveMethod = FILE_BEGIN;
    125  }
    126  #endif
    127 
    128  LONG high = (LONG)(distanceToMove >> 32);
    129  DWORD low = ::SetFilePointer(_handle, (LONG)(distanceToMove & 0xFFFFFFFF), &high, moveMethod);
    130  if (low == 0xFFFFFFFF)
    131    if (::GetLastError() != NO_ERROR)
    132      return false;
    133  newPosition = (((UInt64)(UInt32)high) << 32) + low;
    134  return true;
    135 }
    136 
    137 bool CFileBase::Seek(UInt64 position, UInt64 &newPosition) const throw()
    138 {
    139  return Seek(position, FILE_BEGIN, newPosition);
    140 }
    141 
    142 bool CFileBase::SeekToBegin() const throw()
    143 {
    144  UInt64 newPosition;
    145  return Seek(0, newPosition);
    146 }
    147 
    148 bool CFileBase::SeekToEnd(UInt64 &newPosition) const throw()
    149 {
    150  return Seek(0, FILE_END, newPosition);
    151 }
    152 
    153 // ---------- CInFile ---------
    154 
    155 #ifdef SUPPORT_DEVICE_FILE
    156 
    157 void CInFile::CorrectDeviceSize()
    158 {
    159  // maybe we must decrease kClusterSize to 1 << 12, if we want correct size at tail
    160  static const UInt32 kClusterSize = 1 << 14;
    161  UInt64 pos = Size & ~(UInt64)(kClusterSize - 1);
    162  UInt64 realNewPosition;
    163  if (!Seek(pos, realNewPosition))
    164    return;
    165  Byte *buf = (Byte *)MidAlloc(kClusterSize);
    166 
    167  bool needbackward = true;
    168 
    169  for (;;)
    170  {
    171    UInt32 processed = 0;
    172    // up test is slow for "PhysicalDrive".
    173    // processed size for latest block for "PhysicalDrive0" is 0.
    174    if (!Read1(buf, kClusterSize, processed))
    175      break;
    176    if (processed == 0)
    177      break;
    178    needbackward = false;
    179    Size = pos + processed;
    180    if (processed != kClusterSize)
    181      break;
    182    pos += kClusterSize;
    183  }
    184 
    185  if (needbackward && pos != 0)
    186  {
    187    pos -= kClusterSize;
    188    for (;;)
    189    {
    190      // break;
    191      if (!Seek(pos, realNewPosition))
    192        break;
    193      if (!buf)
    194      {
    195        buf = (Byte *)MidAlloc(kClusterSize);
    196        if (!buf)
    197          break;
    198      }
    199      UInt32 processed = 0;
    200      // that code doesn't work for "PhysicalDrive0"
    201      if (!Read1(buf, kClusterSize, processed))
    202        break;
    203      if (processed != 0)
    204      {
    205        Size = pos + processed;
    206        break;
    207      }
    208      if (pos == 0)
    209        break;
    210      pos -= kClusterSize;
    211    }
    212  }
    213  MidFree(buf);
    214 }
    215 
    216 
    217 void CInFile::CalcDeviceSize(CFSTR s)
    218 {
    219  SizeDefined = false;
    220  Size = 0;
    221  if (_handle == INVALID_HANDLE_VALUE || !IsDeviceFile)
    222    return;
    223  #ifdef UNDER_CE
    224 
    225  SizeDefined = true;
    226  Size = 128 << 20;
    227  
    228  #else
    229  
    230  PARTITION_INFORMATION partInfo;
    231  bool needCorrectSize = true;
    232 
    233  /*
    234    WinXP 64-bit:
    235 
    236    HDD \\.\PhysicalDrive0 (MBR):
    237      GetPartitionInfo == GeometryEx :  corrrect size? (includes tail)
    238      Geometry   :  smaller than GeometryEx (no tail, maybe correct too?)
    239      MyGetDiskFreeSpace : FAIL
    240      Size correction is slow and block size (kClusterSize) must be small?
    241 
    242    HDD partition \\.\N: (NTFS):
    243      MyGetDiskFreeSpace   :  Size of NTFS clusters. Same size can be calculated after correction
    244      GetPartitionInfo     :  size of partition data: NTFS clusters + TAIL; TAIL contains extra empty sectors and copy of first sector of NTFS
    245      Geometry / CdRomGeometry / GeometryEx :  size of HDD (not that partition)
    246 
    247    CD-ROM drive (ISO):
    248      MyGetDiskFreeSpace   :  correct size. Same size can be calculated after correction
    249      Geometry == CdRomGeometry  :  smaller than corrrect size
    250      GetPartitionInfo == GeometryEx :  larger than corrrect size
    251 
    252    Floppy \\.\a: (FAT):
    253      Geometry :  correct size.
    254      CdRomGeometry / GeometryEx / GetPartitionInfo / MyGetDiskFreeSpace - FAIL
    255      correction works OK for FAT.
    256      correction works OK for non-FAT, if kClusterSize = 512.
    257  */
    258 
    259  if (GetPartitionInfo(&partInfo))
    260  {
    261    Size = partInfo.PartitionLength.QuadPart;
    262    SizeDefined = true;
    263    needCorrectSize = false;
    264    if ((s)[0] == '\\' && (s)[1] == '\\' && (s)[2] == '.' && (s)[3] == '\\' && (s)[5] == ':' && (s)[6] == 0)
    265    {
    266      FChar path[4] = { s[4], ':', '\\', 0 };
    267      UInt64 clusterSize, totalSize, freeSize;
    268      if (NSystem::MyGetDiskFreeSpace(path, clusterSize, totalSize, freeSize))
    269        Size = totalSize;
    270      else
    271        needCorrectSize = true;
    272    }
    273  }
    274  
    275  if (!SizeDefined)
    276  {
    277    my_DISK_GEOMETRY_EX geomEx;
    278    SizeDefined = GetGeometryEx(&geomEx);
    279    if (SizeDefined)
    280      Size = geomEx.DiskSize.QuadPart;
    281    else
    282    {
    283      DISK_GEOMETRY geom;
    284      SizeDefined = GetGeometry(&geom);
    285      if (!SizeDefined)
    286        SizeDefined = GetCdRomGeometry(&geom);
    287      if (SizeDefined)
    288        Size = geom.Cylinders.QuadPart * geom.TracksPerCylinder * geom.SectorsPerTrack * geom.BytesPerSector;
    289    }
    290  }
    291  
    292  if (needCorrectSize && SizeDefined && Size != 0)
    293  {
    294    CorrectDeviceSize();
    295    SeekToBegin();
    296  }
    297 
    298  // SeekToBegin();
    299  #endif
    300 }
    301 
    302 // ((desiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE)) == 0 &&
    303 
    304 #define MY_DEVICE_EXTRA_CODE \
    305  IsDeviceFile = IsDevicePath(fileName); \
    306  CalcDeviceSize(fileName);
    307 #else
    308 #define MY_DEVICE_EXTRA_CODE
    309 #endif
    310 
    311 bool CInFile::Open(CFSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
    312 {
    313  bool res = Create(fileName, GENERIC_READ, shareMode, creationDisposition, flagsAndAttributes);
    314  MY_DEVICE_EXTRA_CODE
    315  return res;
    316 }
    317 
    318 bool CInFile::OpenShared(CFSTR fileName, bool shareForWrite)
    319 { return Open(fileName, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); }
    320 
    321 bool CInFile::Open(CFSTR fileName)
    322  { return OpenShared(fileName, false); }
    323 
    324 // ReadFile and WriteFile functions in Windows have BUG:
    325 // If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1)
    326 // from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES
    327 // (Insufficient system resources exist to complete the requested service).
    328 
    329 // Probably in some version of Windows there are problems with other sizes:
    330 // for 32 MB (maybe also for 16 MB).
    331 // And message can be "Network connection was lost"
    332 
    333 static UInt32 kChunkSizeMax = (1 << 22);
    334 
    335 bool CInFile::Read1(void *data, UInt32 size, UInt32 &processedSize) throw()
    336 {
    337  DWORD processedLoc = 0;
    338  bool res = BOOLToBool(::ReadFile(_handle, data, size, &processedLoc, NULL));
    339  processedSize = (UInt32)processedLoc;
    340  return res;
    341 }
    342 
    343 bool CInFile::ReadPart(void *data, UInt32 size, UInt32 &processedSize) throw()
    344 {
    345  if (size > kChunkSizeMax)
    346    size = kChunkSizeMax;
    347  return Read1(data, size, processedSize);
    348 }
    349 
    350 bool CInFile::Read(void *data, UInt32 size, UInt32 &processedSize) throw()
    351 {
    352  processedSize = 0;
    353  do
    354  {
    355    UInt32 processedLoc = 0;
    356    bool res = ReadPart(data, size, processedLoc);
    357    processedSize += processedLoc;
    358    if (!res)
    359      return false;
    360    if (processedLoc == 0)
    361      return true;
    362    data = (void *)((unsigned char *)data + processedLoc);
    363    size -= processedLoc;
    364  }
    365  while (size > 0);
    366  return true;
    367 }
    368 
    369 // ---------- COutFile ---------
    370 
    371 static inline DWORD GetCreationDisposition(bool createAlways)
    372  { return createAlways? CREATE_ALWAYS: CREATE_NEW; }
    373 
    374 bool COutFile::Open(CFSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
    375  { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); }
    376 
    377 bool COutFile::Open(CFSTR fileName, DWORD creationDisposition)
    378  { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); }
    379 
    380 bool COutFile::Create(CFSTR fileName, bool createAlways)
    381  { return Open(fileName, GetCreationDisposition(createAlways)); }
    382 
    383 bool COutFile::CreateAlways(CFSTR fileName, DWORD flagsAndAttributes)
    384  { return Open(fileName, FILE_SHARE_READ, GetCreationDisposition(true), flagsAndAttributes); }
    385 
    386 bool COutFile::SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime) throw()
    387  { return BOOLToBool(::SetFileTime(_handle, cTime, aTime, mTime)); }
    388 
    389 bool COutFile::SetMTime(const FILETIME *mTime) throw() {  return SetTime(NULL, NULL, mTime); }
    390 
    391 bool COutFile::WritePart(const void *data, UInt32 size, UInt32 &processedSize) throw()
    392 {
    393  if (size > kChunkSizeMax)
    394    size = kChunkSizeMax;
    395  DWORD processedLoc = 0;
    396  bool res = BOOLToBool(::WriteFile(_handle, data, size, &processedLoc, NULL));
    397  processedSize = (UInt32)processedLoc;
    398  return res;
    399 }
    400 
    401 bool COutFile::Write(const void *data, UInt32 size, UInt32 &processedSize) throw()
    402 {
    403  processedSize = 0;
    404  do
    405  {
    406    UInt32 processedLoc = 0;
    407    bool res = WritePart(data, size, processedLoc);
    408    processedSize += processedLoc;
    409    if (!res)
    410      return false;
    411    if (processedLoc == 0)
    412      return true;
    413    data = (const void *)((const unsigned char *)data + processedLoc);
    414    size -= processedLoc;
    415  }
    416  while (size > 0);
    417  return true;
    418 }
    419 
    420 bool COutFile::SetEndOfFile() throw() { return BOOLToBool(::SetEndOfFile(_handle)); }
    421 
    422 bool COutFile::SetLength(UInt64 length) throw()
    423 {
    424  UInt64 newPosition;
    425  if (!Seek(length, newPosition))
    426    return false;
    427  if (newPosition != length)
    428    return false;
    429  return SetEndOfFile();
    430 }
    431 
    432 }}}