Home | History | Annotate | Download | only in Common
      1 // FileStreams.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #ifndef _WIN32
      6 #include <fcntl.h>
      7 #include <unistd.h>
      8 #include <errno.h>
      9 #endif
     10 
     11 #ifdef SUPPORT_DEVICE_FILE
     12 #include "../../../C/Alloc.h"
     13 #include "../../Common/Defs.h"
     14 #endif
     15 
     16 #include "FileStreams.h"
     17 
     18 static inline HRESULT ConvertBoolToHRESULT(bool result)
     19 {
     20   #ifdef _WIN32
     21   if (result)
     22     return S_OK;
     23   DWORD lastError = ::GetLastError();
     24   if (lastError == 0)
     25     return E_FAIL;
     26   return HRESULT_FROM_WIN32(lastError);
     27   #else
     28   return result ? S_OK: E_FAIL;
     29   #endif
     30 }
     31 
     32 
     33 static const UInt32 kClusterSize = 1 << 18;
     34 CInFileStream::CInFileStream():
     35   #ifdef SUPPORT_DEVICE_FILE
     36   VirtPos(0),
     37   PhyPos(0),
     38   Buf(0),
     39   BufSize(0),
     40   #endif
     41   SupportHardLinks(false),
     42   Callback(NULL),
     43   CallbackRef(0)
     44 {
     45 }
     46 
     47 CInFileStream::~CInFileStream()
     48 {
     49   #ifdef SUPPORT_DEVICE_FILE
     50   MidFree(Buf);
     51   #endif
     52 
     53   if (Callback)
     54     Callback->InFileStream_On_Destroy(CallbackRef);
     55 }
     56 
     57 STDMETHODIMP CInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize)
     58 {
     59   #ifdef USE_WIN_FILE
     60 
     61   #ifdef SUPPORT_DEVICE_FILE
     62   if (processedSize)
     63     *processedSize = 0;
     64   if (size == 0)
     65     return S_OK;
     66   if (File.IsDeviceFile)
     67   {
     68     if (File.SizeDefined)
     69     {
     70       if (VirtPos >= File.Size)
     71         return VirtPos == File.Size ? S_OK : E_FAIL;
     72       UInt64 rem = File.Size - VirtPos;
     73       if (size > rem)
     74         size = (UInt32)rem;
     75     }
     76     for (;;)
     77     {
     78       const UInt32 mask = kClusterSize - 1;
     79       const UInt64 mask2 = ~(UInt64)mask;
     80       UInt64 alignedPos = VirtPos & mask2;
     81       if (BufSize > 0 && BufStartPos == alignedPos)
     82       {
     83         UInt32 pos = (UInt32)VirtPos & mask;
     84         if (pos >= BufSize)
     85           return S_OK;
     86         UInt32 rem = MyMin(BufSize - pos, size);
     87         memcpy(data, Buf + pos, rem);
     88         VirtPos += rem;
     89         if (processedSize)
     90           *processedSize += rem;
     91         return S_OK;
     92       }
     93 
     94       bool useBuf = false;
     95       if ((VirtPos & mask) != 0 || ((ptrdiff_t)data & mask) != 0 )
     96         useBuf = true;
     97       else
     98       {
     99         UInt64 end = VirtPos + size;
    100         if ((end & mask) != 0)
    101         {
    102           end &= mask2;
    103           if (end <= VirtPos)
    104             useBuf = true;
    105           else
    106             size = (UInt32)(end - VirtPos);
    107         }
    108       }
    109       if (!useBuf)
    110         break;
    111       if (alignedPos != PhyPos)
    112       {
    113         UInt64 realNewPosition;
    114         bool result = File.Seek(alignedPos, FILE_BEGIN, realNewPosition);
    115         if (!result)
    116           return ConvertBoolToHRESULT(result);
    117         PhyPos = realNewPosition;
    118       }
    119 
    120       BufStartPos = alignedPos;
    121       UInt32 readSize = kClusterSize;
    122       if (File.SizeDefined)
    123         readSize = (UInt32)MyMin(File.Size - PhyPos, (UInt64)kClusterSize);
    124 
    125       if (!Buf)
    126       {
    127         Buf = (Byte *)MidAlloc(kClusterSize);
    128         if (!Buf)
    129           return E_OUTOFMEMORY;
    130       }
    131       bool result = File.Read1(Buf, readSize, BufSize);
    132       if (!result)
    133         return ConvertBoolToHRESULT(result);
    134 
    135       if (BufSize == 0)
    136         return S_OK;
    137       PhyPos += BufSize;
    138     }
    139 
    140     if (VirtPos != PhyPos)
    141     {
    142       UInt64 realNewPosition;
    143       bool result = File.Seek(VirtPos, FILE_BEGIN, realNewPosition);
    144       if (!result)
    145         return ConvertBoolToHRESULT(result);
    146       PhyPos = VirtPos = realNewPosition;
    147     }
    148   }
    149   #endif
    150 
    151   UInt32 realProcessedSize;
    152   bool result = File.ReadPart(data, size, realProcessedSize);
    153   if (processedSize)
    154     *processedSize = realProcessedSize;
    155 
    156   #ifdef SUPPORT_DEVICE_FILE
    157   VirtPos += realProcessedSize;
    158   PhyPos += realProcessedSize;
    159   #endif
    160 
    161   if (result)
    162     return S_OK;
    163 
    164   {
    165     DWORD error = ::GetLastError();
    166 
    167     if (Callback)
    168       return Callback->InFileStream_On_Error(CallbackRef, error);
    169     if (error == 0)
    170       return E_FAIL;
    171 
    172     return HRESULT_FROM_WIN32(error);
    173   }
    174 
    175   #else
    176 
    177   if (processedSize)
    178     *processedSize = 0;
    179   ssize_t res = File.Read(data, (size_t)size);
    180   if (res == -1)
    181   {
    182     if (Callback)
    183       return Callback->InFileStream_On_Error(CallbackRef, E_FAIL);
    184     return E_FAIL;
    185   }
    186   if (processedSize)
    187     *processedSize = (UInt32)res;
    188   return S_OK;
    189 
    190   #endif
    191 }
    192 
    193 #ifdef UNDER_CE
    194 STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize)
    195 {
    196   size_t s2 = fread(data, 1, size, stdin);
    197   int error = ferror(stdin);
    198   if (processedSize)
    199     *processedSize = s2;
    200   if (s2 <= size && error == 0)
    201     return S_OK;
    202   return E_FAIL;
    203 }
    204 #else
    205 STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize)
    206 {
    207   #ifdef _WIN32
    208 
    209   DWORD realProcessedSize;
    210   UInt32 sizeTemp = (1 << 20);
    211   if (sizeTemp > size)
    212     sizeTemp = size;
    213   BOOL res = ::ReadFile(GetStdHandle(STD_INPUT_HANDLE), data, sizeTemp, &realProcessedSize, NULL);
    214   if (processedSize)
    215     *processedSize = realProcessedSize;
    216   if (res == FALSE && GetLastError() == ERROR_BROKEN_PIPE)
    217     return S_OK;
    218   return ConvertBoolToHRESULT(res != FALSE);
    219 
    220   #else
    221 
    222   if (processedSize)
    223     *processedSize = 0;
    224   ssize_t res;
    225   do
    226   {
    227     res = read(0, data, (size_t)size);
    228   }
    229   while (res < 0 && (errno == EINTR));
    230   if (res == -1)
    231     return E_FAIL;
    232   if (processedSize)
    233     *processedSize = (UInt32)res;
    234   return S_OK;
    235 
    236   #endif
    237 }
    238 
    239 #endif
    240 
    241 STDMETHODIMP CInFileStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
    242 {
    243   if (seekOrigin >= 3)
    244     return STG_E_INVALIDFUNCTION;
    245 
    246   #ifdef USE_WIN_FILE
    247 
    248   #ifdef SUPPORT_DEVICE_FILE
    249   if (File.IsDeviceFile && (File.SizeDefined || seekOrigin != STREAM_SEEK_END))
    250   {
    251     switch (seekOrigin)
    252     {
    253       case STREAM_SEEK_SET: break;
    254       case STREAM_SEEK_CUR: offset += VirtPos; break;
    255       case STREAM_SEEK_END: offset += File.Size; break;
    256       default: return STG_E_INVALIDFUNCTION;
    257     }
    258     if (offset < 0)
    259       return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
    260     VirtPos = offset;
    261     if (newPosition)
    262       *newPosition = offset;
    263     return S_OK;
    264   }
    265   #endif
    266 
    267   UInt64 realNewPosition;
    268   bool result = File.Seek(offset, seekOrigin, realNewPosition);
    269 
    270   #ifdef SUPPORT_DEVICE_FILE
    271   PhyPos = VirtPos = realNewPosition;
    272   #endif
    273 
    274   if (newPosition)
    275     *newPosition = realNewPosition;
    276   return ConvertBoolToHRESULT(result);
    277 
    278   #else
    279 
    280   off_t res = File.Seek((off_t)offset, seekOrigin);
    281   if (res == -1)
    282     return E_FAIL;
    283   if (newPosition)
    284     *newPosition = (UInt64)res;
    285   return S_OK;
    286 
    287   #endif
    288 }
    289 
    290 STDMETHODIMP CInFileStream::GetSize(UInt64 *size)
    291 {
    292   return ConvertBoolToHRESULT(File.GetLength(*size));
    293 }
    294 
    295 #ifdef USE_WIN_FILE
    296 
    297 STDMETHODIMP CInFileStream::GetProps(UInt64 *size, FILETIME *cTime, FILETIME *aTime, FILETIME *mTime, UInt32 *attrib)
    298 {
    299   BY_HANDLE_FILE_INFORMATION info;
    300   if (File.GetFileInformation(&info))
    301   {
    302     if (size) *size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow;
    303     if (cTime) *cTime = info.ftCreationTime;
    304     if (aTime) *aTime = info.ftLastAccessTime;
    305     if (mTime) *mTime = info.ftLastWriteTime;
    306     if (attrib) *attrib = info.dwFileAttributes;
    307     return S_OK;
    308   }
    309   return GetLastError();
    310 }
    311 
    312 STDMETHODIMP CInFileStream::GetProps2(CStreamFileProps *props)
    313 {
    314   BY_HANDLE_FILE_INFORMATION info;
    315   if (File.GetFileInformation(&info))
    316   {
    317     props->Size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow;
    318     props->VolID = info.dwVolumeSerialNumber;
    319     props->FileID_Low = (((UInt64)info.nFileIndexHigh) << 32) + info.nFileIndexLow;
    320     props->FileID_High = 0;
    321     props->NumLinks = SupportHardLinks ? info.nNumberOfLinks : 1;
    322     props->Attrib = info.dwFileAttributes;
    323     props->CTime = info.ftCreationTime;
    324     props->ATime = info.ftLastAccessTime;
    325     props->MTime = info.ftLastWriteTime;
    326     return S_OK;
    327   }
    328   return GetLastError();
    329 }
    330 
    331 #endif
    332 
    333 //////////////////////////
    334 // COutFileStream
    335 
    336 HRESULT COutFileStream::Close()
    337 {
    338   return ConvertBoolToHRESULT(File.Close());
    339 }
    340 
    341 STDMETHODIMP COutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
    342 {
    343   #ifdef USE_WIN_FILE
    344 
    345   UInt32 realProcessedSize;
    346   bool result = File.Write(data, size, realProcessedSize);
    347   ProcessedSize += realProcessedSize;
    348   if (processedSize)
    349     *processedSize = realProcessedSize;
    350   return ConvertBoolToHRESULT(result);
    351 
    352   #else
    353 
    354   if (processedSize)
    355     *processedSize = 0;
    356   ssize_t res = File.Write(data, (size_t)size);
    357   if (res == -1)
    358     return E_FAIL;
    359   if (processedSize)
    360     *processedSize = (UInt32)res;
    361   ProcessedSize += res;
    362   return S_OK;
    363 
    364   #endif
    365 }
    366 
    367 STDMETHODIMP COutFileStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
    368 {
    369   if (seekOrigin >= 3)
    370     return STG_E_INVALIDFUNCTION;
    371 
    372   #ifdef USE_WIN_FILE
    373 
    374   UInt64 realNewPosition;
    375   bool result = File.Seek(offset, seekOrigin, realNewPosition);
    376   if (newPosition)
    377     *newPosition = realNewPosition;
    378   return ConvertBoolToHRESULT(result);
    379 
    380   #else
    381 
    382   off_t res = File.Seek((off_t)offset, seekOrigin);
    383   if (res == -1)
    384     return E_FAIL;
    385   if (newPosition)
    386     *newPosition = (UInt64)res;
    387   return S_OK;
    388 
    389   #endif
    390 }
    391 
    392 STDMETHODIMP COutFileStream::SetSize(UInt64 newSize)
    393 {
    394   #ifdef USE_WIN_FILE
    395 
    396   UInt64 currentPos;
    397   if (!File.Seek(0, FILE_CURRENT, currentPos))
    398     return E_FAIL;
    399   bool result = File.SetLength(newSize);
    400   UInt64 currentPos2;
    401   result = result && File.Seek(currentPos, currentPos2);
    402   return result ? S_OK : E_FAIL;
    403 
    404   #else
    405 
    406   return E_FAIL;
    407 
    408   #endif
    409 }
    410 
    411 HRESULT COutFileStream::GetSize(UInt64 *size)
    412 {
    413   return ConvertBoolToHRESULT(File.GetLength(*size));
    414 }
    415 
    416 #ifdef UNDER_CE
    417 
    418 STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
    419 {
    420   size_t s2 = fwrite(data, 1, size, stdout);
    421   if (processedSize)
    422     *processedSize = s2;
    423   return (s2 == size) ? S_OK : E_FAIL;
    424 }
    425 
    426 #else
    427 
    428 STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
    429 {
    430   if (processedSize)
    431     *processedSize = 0;
    432 
    433   #ifdef _WIN32
    434 
    435   UInt32 realProcessedSize;
    436   BOOL res = TRUE;
    437   if (size > 0)
    438   {
    439     // Seems that Windows doesn't like big amounts writing to stdout.
    440     // So we limit portions by 32KB.
    441     UInt32 sizeTemp = (1 << 15);
    442     if (sizeTemp > size)
    443       sizeTemp = size;
    444     res = ::WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
    445         data, sizeTemp, (DWORD *)&realProcessedSize, NULL);
    446     _size += realProcessedSize;
    447     size -= realProcessedSize;
    448     data = (const void *)((const Byte *)data + realProcessedSize);
    449     if (processedSize)
    450       *processedSize += realProcessedSize;
    451   }
    452   return ConvertBoolToHRESULT(res != FALSE);
    453 
    454   #else
    455 
    456   ssize_t res;
    457 
    458   do
    459   {
    460     res = write(1, data, (size_t)size);
    461   }
    462   while (res < 0 && (errno == EINTR));
    463 
    464   if (res == -1)
    465     return E_FAIL;
    466 
    467   _size += (size_t)res;
    468   if (processedSize)
    469     *processedSize = (UInt32)res;
    470   return S_OK;
    471 
    472   #endif
    473 }
    474 
    475 #endif
    476