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