Home | History | Annotate | Download | only in Windows
      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)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 }}}
    433