Home | History | Annotate | Download | only in Windows
      1 // Windows/FileIO.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include "FileIO.h"
      6 
      7 #if defined(WIN_LONG_PATH) || defined(SUPPORT_DEVICE_FILE)
      8 #include "../Common/MyString.h"
      9 #endif
     10 #ifndef _UNICODE
     11 #include "../Common/StringConvert.h"
     12 #endif
     13 
     14 #ifndef _UNICODE
     15 extern bool g_IsNT;
     16 #endif
     17 
     18 namespace NWindows {
     19 namespace NFile {
     20 
     21 #ifdef SUPPORT_DEVICE_FILE
     22 bool IsDeviceName(LPCTSTR n)
     23 {
     24   #ifdef UNDER_CE
     25   int len = (int)MyStringLen(n);
     26   if (len < 5 || len > 5 || memcmp(n, TEXT("DSK"), 3 * sizeof(TCHAR)) != 0)
     27     return false;
     28   if (n[4] != ':')
     29     return false;
     30   // for reading use SG_REQ sg; if (DeviceIoControl(dsk, IOCTL_DISK_READ));
     31   #else
     32   if (n[0] != '\\' || n[1] != '\\' || n[2] != '.' ||  n[3] != '\\')
     33     return false;
     34   int len = (int)MyStringLen(n);
     35   if (len == 6 && n[5] == ':')
     36     return true;
     37   if (len < 18 || len > 22 || memcmp(n + 4, TEXT("PhysicalDrive"), 13 * sizeof(TCHAR)) != 0)
     38     return false;
     39   for (int i = 17; i < len; i++)
     40     if (n[i] < '0' || n[i] > '9')
     41       return false;
     42   #endif
     43   return true;
     44 }
     45 
     46 #ifndef _UNICODE
     47 bool IsDeviceName(LPCWSTR n)
     48 {
     49   if (n[0] != '\\' || n[1] != '\\' || n[2] != '.' ||  n[3] != '\\')
     50     return false;
     51   int len = (int)wcslen(n);
     52   if (len == 6 && n[5] == ':')
     53     return true;
     54   if (len < 18 || len > 22 || wcsncmp(n + 4, L"PhysicalDrive", 13) != 0)
     55     return false;
     56   for (int i = 17; i < len; i++)
     57     if (n[i] < '0' || n[i] > '9')
     58       return false;
     59   return true;
     60 }
     61 #endif
     62 #endif
     63 
     64 #if defined(WIN_LONG_PATH) && defined(_UNICODE)
     65 #define WIN_LONG_PATH2
     66 #endif
     67 
     68 #ifdef WIN_LONG_PATH
     69 bool GetLongPathBase(LPCWSTR s, UString &res)
     70 {
     71   res.Empty();
     72   int len = MyStringLen(s);
     73   wchar_t c = s[0];
     74   if (len < 1 || c == L'\\' || c == L'.' && (len == 1 || len == 2 && s[1] == L'.'))
     75     return true;
     76   UString curDir;
     77   bool isAbs = false;
     78   if (len > 3)
     79     isAbs = (s[1] == L':' && s[2] == L'\\' && (c >= L'a' && c <= L'z' || c >= L'A' && c <= L'Z'));
     80 
     81   if (!isAbs)
     82     {
     83       DWORD needLength = ::GetCurrentDirectoryW(MAX_PATH + 1, curDir.GetBuffer(MAX_PATH + 1));
     84       curDir.ReleaseBuffer();
     85       if (needLength == 0 || needLength > MAX_PATH)
     86         return false;
     87       if (curDir[curDir.Length() - 1] != L'\\')
     88         curDir += L'\\';
     89     }
     90   res = UString(L"\\\\?\\") + curDir + s;
     91   return true;
     92 }
     93 
     94 bool GetLongPath(LPCWSTR path, UString &longPath)
     95 {
     96   if (GetLongPathBase(path, longPath))
     97     return !longPath.IsEmpty();
     98   return false;
     99 }
    100 #endif
    101 
    102 namespace NIO {
    103 
    104 CFileBase::~CFileBase() { Close(); }
    105 
    106 bool CFileBase::Create(LPCTSTR fileName, DWORD desiredAccess,
    107     DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
    108 {
    109   if (!Close())
    110     return false;
    111   _handle = ::CreateFile(fileName, desiredAccess, shareMode,
    112       (LPSECURITY_ATTRIBUTES)NULL, creationDisposition,
    113       flagsAndAttributes, (HANDLE)NULL);
    114   #ifdef WIN_LONG_PATH2
    115   if (_handle == INVALID_HANDLE_VALUE)
    116   {
    117     UString longPath;
    118     if (GetLongPath(fileName, longPath))
    119       _handle = ::CreateFileW(longPath, desiredAccess, shareMode,
    120         (LPSECURITY_ATTRIBUTES)NULL, creationDisposition,
    121         flagsAndAttributes, (HANDLE)NULL);
    122   }
    123   #endif
    124   #ifdef SUPPORT_DEVICE_FILE
    125   IsDeviceFile = false;
    126   #endif
    127   return (_handle != INVALID_HANDLE_VALUE);
    128 }
    129 
    130 #ifndef _UNICODE
    131 bool CFileBase::Create(LPCWSTR fileName, DWORD desiredAccess,
    132     DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
    133 {
    134   if (!g_IsNT)
    135     return Create(UnicodeStringToMultiByte(fileName, ::AreFileApisANSI() ? CP_ACP : CP_OEMCP),
    136       desiredAccess, shareMode, creationDisposition, flagsAndAttributes);
    137   if (!Close())
    138     return false;
    139   _handle = ::CreateFileW(fileName, desiredAccess, shareMode,
    140     (LPSECURITY_ATTRIBUTES)NULL, creationDisposition,
    141     flagsAndAttributes, (HANDLE)NULL);
    142   #ifdef WIN_LONG_PATH
    143   if (_handle == INVALID_HANDLE_VALUE)
    144   {
    145     UString longPath;
    146     if (GetLongPath(fileName, longPath))
    147       _handle = ::CreateFileW(longPath, desiredAccess, shareMode,
    148         (LPSECURITY_ATTRIBUTES)NULL, creationDisposition,
    149         flagsAndAttributes, (HANDLE)NULL);
    150   }
    151   #endif
    152   #ifdef SUPPORT_DEVICE_FILE
    153   IsDeviceFile = false;
    154   #endif
    155   return (_handle != INVALID_HANDLE_VALUE);
    156 }
    157 #endif
    158 
    159 bool CFileBase::Close()
    160 {
    161   if (_handle == INVALID_HANDLE_VALUE)
    162     return true;
    163   if (!::CloseHandle(_handle))
    164     return false;
    165   _handle = INVALID_HANDLE_VALUE;
    166   return true;
    167 }
    168 
    169 bool CFileBase::GetPosition(UInt64 &position) const
    170 {
    171   return Seek(0, FILE_CURRENT, position);
    172 }
    173 
    174 bool CFileBase::GetLength(UInt64 &length) const
    175 {
    176   #ifdef SUPPORT_DEVICE_FILE
    177   if (IsDeviceFile && LengthDefined)
    178   {
    179     length = Length;
    180     return true;
    181   }
    182   #endif
    183 
    184   DWORD sizeHigh;
    185   DWORD sizeLow = ::GetFileSize(_handle, &sizeHigh);
    186   if (sizeLow == 0xFFFFFFFF)
    187     if (::GetLastError() != NO_ERROR)
    188       return false;
    189   length = (((UInt64)sizeHigh) << 32) + sizeLow;
    190   return true;
    191 }
    192 
    193 bool CFileBase::Seek(Int64 distanceToMove, DWORD moveMethod, UInt64 &newPosition) const
    194 {
    195   #ifdef SUPPORT_DEVICE_FILE
    196   if (IsDeviceFile && LengthDefined && moveMethod == FILE_END)
    197   {
    198     distanceToMove += Length;
    199     moveMethod = FILE_BEGIN;
    200   }
    201   #endif
    202 
    203   LARGE_INTEGER value;
    204   value.QuadPart = distanceToMove;
    205   value.LowPart = ::SetFilePointer(_handle, value.LowPart, &value.HighPart, moveMethod);
    206   if (value.LowPart == 0xFFFFFFFF)
    207     if (::GetLastError() != NO_ERROR)
    208       return false;
    209   newPosition = value.QuadPart;
    210   return true;
    211 }
    212 
    213 bool CFileBase::Seek(UInt64 position, UInt64 &newPosition)
    214 {
    215   return Seek(position, FILE_BEGIN, newPosition);
    216 }
    217 
    218 bool CFileBase::SeekToBegin()
    219 {
    220   UInt64 newPosition;
    221   return Seek(0, newPosition);
    222 }
    223 
    224 bool CFileBase::SeekToEnd(UInt64 &newPosition)
    225 {
    226   return Seek(0, FILE_END, newPosition);
    227 }
    228 
    229 bool CFileBase::GetFileInformation(CByHandleFileInfo &fileInfo) const
    230 {
    231   BY_HANDLE_FILE_INFORMATION winFileInfo;
    232   if (!::GetFileInformationByHandle(_handle, &winFileInfo))
    233     return false;
    234   fileInfo.Attrib = winFileInfo.dwFileAttributes;
    235   fileInfo.CTime = winFileInfo.ftCreationTime;
    236   fileInfo.ATime = winFileInfo.ftLastAccessTime;
    237   fileInfo.MTime = winFileInfo.ftLastWriteTime;
    238   fileInfo.VolumeSerialNumber = winFileInfo.dwFileAttributes;
    239   fileInfo.Size = (((UInt64)winFileInfo.nFileSizeHigh) << 32) +  winFileInfo.nFileSizeLow;
    240   fileInfo.NumberOfLinks = winFileInfo.nNumberOfLinks;
    241   fileInfo.FileIndex = (((UInt64)winFileInfo.nFileIndexHigh) << 32) + winFileInfo.nFileIndexLow;
    242   return true;
    243 }
    244 
    245 /////////////////////////
    246 // CInFile
    247 
    248 #ifdef SUPPORT_DEVICE_FILE
    249 void CInFile::GetDeviceLength()
    250 {
    251   if (_handle != INVALID_HANDLE_VALUE && IsDeviceFile)
    252   {
    253     #ifdef UNDER_CE
    254     LengthDefined = true;
    255     Length = 128 << 20;
    256 
    257     #else
    258     PARTITION_INFORMATION partInfo;
    259     LengthDefined = true;
    260     Length = 0;
    261 
    262     if (GetPartitionInfo(&partInfo))
    263       Length = partInfo.PartitionLength.QuadPart;
    264     else
    265     {
    266       DISK_GEOMETRY geom;
    267       if (!GetGeometry(&geom))
    268         if (!GetCdRomGeometry(&geom))
    269           LengthDefined = false;
    270       if (LengthDefined)
    271         Length = geom.Cylinders.QuadPart * geom.TracksPerCylinder * geom.SectorsPerTrack * geom.BytesPerSector;
    272     }
    273     // SeekToBegin();
    274     #endif
    275   }
    276 }
    277 
    278 // ((desiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE)) == 0 &&
    279 
    280 #define MY_DEVICE_EXTRA_CODE \
    281   IsDeviceFile = IsDeviceName(fileName); \
    282   GetDeviceLength();
    283 #else
    284 #define MY_DEVICE_EXTRA_CODE
    285 #endif
    286 
    287 bool CInFile::Open(LPCTSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
    288 {
    289   bool res = Create(fileName, GENERIC_READ, shareMode, creationDisposition, flagsAndAttributes);
    290   MY_DEVICE_EXTRA_CODE
    291   return res;
    292 }
    293 
    294 bool CInFile::OpenShared(LPCTSTR fileName, bool shareForWrite)
    295 { return Open(fileName, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); }
    296 
    297 bool CInFile::Open(LPCTSTR fileName)
    298   { return OpenShared(fileName, false); }
    299 
    300 #ifndef _UNICODE
    301 bool CInFile::Open(LPCWSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
    302 {
    303   bool res = Create(fileName, GENERIC_READ, shareMode, creationDisposition, flagsAndAttributes);
    304   MY_DEVICE_EXTRA_CODE
    305   return res;
    306 }
    307 
    308 bool CInFile::OpenShared(LPCWSTR fileName, bool shareForWrite)
    309 { return Open(fileName, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); }
    310 
    311 bool CInFile::Open(LPCWSTR fileName)
    312   { return OpenShared(fileName, false); }
    313 #endif
    314 
    315 // ReadFile and WriteFile functions in Windows have BUG:
    316 // If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1)
    317 // from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES
    318 // (Insufficient system resources exist to complete the requested service).
    319 
    320 // Probably in some version of Windows there are problems with other sizes:
    321 // for 32 MB (maybe also for 16 MB).
    322 // And message can be "Network connection was lost"
    323 
    324 static UInt32 kChunkSizeMax = (1 << 22);
    325 
    326 bool CInFile::Read1(void *data, UInt32 size, UInt32 &processedSize)
    327 {
    328   DWORD processedLoc = 0;
    329   bool res = BOOLToBool(::ReadFile(_handle, data, size, &processedLoc, NULL));
    330   processedSize = (UInt32)processedLoc;
    331   return res;
    332 }
    333 
    334 bool CInFile::ReadPart(void *data, UInt32 size, UInt32 &processedSize)
    335 {
    336   if (size > kChunkSizeMax)
    337     size = kChunkSizeMax;
    338   return Read1(data, size, processedSize);
    339 }
    340 
    341 bool CInFile::Read(void *data, UInt32 size, UInt32 &processedSize)
    342 {
    343   processedSize = 0;
    344   do
    345   {
    346     UInt32 processedLoc = 0;
    347     bool res = ReadPart(data, size, processedLoc);
    348     processedSize += processedLoc;
    349     if (!res)
    350       return false;
    351     if (processedLoc == 0)
    352       return true;
    353     data = (void *)((unsigned char *)data + processedLoc);
    354     size -= processedLoc;
    355   }
    356   while (size > 0);
    357   return true;
    358 }
    359 
    360 /////////////////////////
    361 // COutFile
    362 
    363 bool COutFile::Open(LPCTSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
    364   { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); }
    365 
    366 static inline DWORD GetCreationDisposition(bool createAlways)
    367   { return createAlways? CREATE_ALWAYS: CREATE_NEW; }
    368 
    369 bool COutFile::Open(LPCTSTR fileName, DWORD creationDisposition)
    370   { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); }
    371 
    372 bool COutFile::Create(LPCTSTR fileName, bool createAlways)
    373   { return Open(fileName, GetCreationDisposition(createAlways)); }
    374 
    375 #ifndef _UNICODE
    376 
    377 bool COutFile::Open(LPCWSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
    378   { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); }
    379 
    380 bool COutFile::Open(LPCWSTR fileName, DWORD creationDisposition)
    381   { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); }
    382 
    383 bool COutFile::Create(LPCWSTR fileName, bool createAlways)
    384   { return Open(fileName, GetCreationDisposition(createAlways)); }
    385 
    386 #endif
    387 
    388 bool COutFile::SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime)
    389   { return BOOLToBool(::SetFileTime(_handle, cTime, aTime, mTime)); }
    390 
    391 bool COutFile::SetMTime(const FILETIME *mTime) {  return SetTime(NULL, NULL, mTime); }
    392 
    393 bool COutFile::WritePart(const void *data, UInt32 size, UInt32 &processedSize)
    394 {
    395   if (size > kChunkSizeMax)
    396     size = kChunkSizeMax;
    397   DWORD processedLoc = 0;
    398   bool res = BOOLToBool(::WriteFile(_handle, data, size, &processedLoc, NULL));
    399   processedSize = (UInt32)processedLoc;
    400   return res;
    401 }
    402 
    403 bool COutFile::Write(const void *data, UInt32 size, UInt32 &processedSize)
    404 {
    405   processedSize = 0;
    406   do
    407   {
    408     UInt32 processedLoc = 0;
    409     bool res = WritePart(data, size, processedLoc);
    410     processedSize += processedLoc;
    411     if (!res)
    412       return false;
    413     if (processedLoc == 0)
    414       return true;
    415     data = (const void *)((const unsigned char *)data + processedLoc);
    416     size -= processedLoc;
    417   }
    418   while (size > 0);
    419   return true;
    420 }
    421 
    422 bool COutFile::SetEndOfFile() { return BOOLToBool(::SetEndOfFile(_handle)); }
    423 
    424 bool COutFile::SetLength(UInt64 length)
    425 {
    426   UInt64 newPosition;
    427   if (!Seek(length, newPosition))
    428     return false;
    429   if (newPosition != length)
    430     return false;
    431   return SetEndOfFile();
    432 }
    433 
    434 }}}
    435