// FileStreams.cpp #include "StdAfx.h" #ifndef _WIN32 #include <fcntl.h> #include <unistd.h> #include <errno.h> #endif #ifdef SUPPORT_DEVICE_FILE #include "../../../C/Alloc.h" #include "../../Common/Defs.h" #endif #include "FileStreams.h" static inline HRESULT ConvertBoolToHRESULT(bool result) { #ifdef _WIN32 if (result) return S_OK; DWORD lastError = ::GetLastError(); if (lastError == 0) return E_FAIL; return HRESULT_FROM_WIN32(lastError); #else return result ? S_OK: E_FAIL; #endif } #ifdef SUPPORT_DEVICE_FILE static const UInt32 kClusterSize = 1 << 18; CInFileStream::CInFileStream(): VirtPos(0), PhyPos(0), Buf(0), BufSize(0), SupportHardLinks(false) { } #endif CInFileStream::~CInFileStream() { #ifdef SUPPORT_DEVICE_FILE MidFree(Buf); #endif } STDMETHODIMP CInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize) { #ifdef USE_WIN_FILE #ifdef SUPPORT_DEVICE_FILE if (processedSize) *processedSize = 0; if (size == 0) return S_OK; if (File.IsDeviceFile) { if (File.SizeDefined) { if (VirtPos >= File.Size) return VirtPos == File.Size ? S_OK : E_FAIL; UInt64 rem = File.Size - VirtPos; if (size > rem) size = (UInt32)rem; } for (;;) { const UInt32 mask = kClusterSize - 1; const UInt64 mask2 = ~(UInt64)mask; UInt64 alignedPos = VirtPos & mask2; if (BufSize > 0 && BufStartPos == alignedPos) { UInt32 pos = (UInt32)VirtPos & mask; if (pos >= BufSize) return S_OK; UInt32 rem = MyMin(BufSize - pos, size); memcpy(data, Buf + pos, rem); VirtPos += rem; if (processedSize) *processedSize += rem; return S_OK; } bool useBuf = false; if ((VirtPos & mask) != 0 || ((ptrdiff_t)data & mask) != 0 ) useBuf = true; else { UInt64 end = VirtPos + size; if ((end & mask) != 0) { end &= mask2; if (end <= VirtPos) useBuf = true; else size = (UInt32)(end - VirtPos); } } if (!useBuf) break; if (alignedPos != PhyPos) { UInt64 realNewPosition; bool result = File.Seek(alignedPos, FILE_BEGIN, realNewPosition); if (!result) return ConvertBoolToHRESULT(result); PhyPos = realNewPosition; } BufStartPos = alignedPos; UInt32 readSize = kClusterSize; if (File.SizeDefined) readSize = (UInt32)MyMin(File.Size - PhyPos, (UInt64)kClusterSize); if (!Buf) { Buf = (Byte *)MidAlloc(kClusterSize); if (!Buf) return E_OUTOFMEMORY; } bool result = File.Read1(Buf, readSize, BufSize); if (!result) return ConvertBoolToHRESULT(result); if (BufSize == 0) return S_OK; PhyPos += BufSize; } if (VirtPos != PhyPos) { UInt64 realNewPosition; bool result = File.Seek(VirtPos, FILE_BEGIN, realNewPosition); if (!result) return ConvertBoolToHRESULT(result); PhyPos = VirtPos = realNewPosition; } } #endif UInt32 realProcessedSize; bool result = File.ReadPart(data, size, realProcessedSize); if (processedSize) *processedSize = realProcessedSize; #ifdef SUPPORT_DEVICE_FILE VirtPos += realProcessedSize; PhyPos += realProcessedSize; #endif return ConvertBoolToHRESULT(result); #else if (processedSize) *processedSize = 0; ssize_t res = File.Read(data, (size_t)size); if (res == -1) return E_FAIL; if (processedSize) *processedSize = (UInt32)res; return S_OK; #endif } #ifdef UNDER_CE STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize) { size_t s2 = fread(data, 1, size, stdin); int error = ferror(stdin); if (processedSize) *processedSize = s2; if (s2 <= size && error == 0) return S_OK; return E_FAIL; } #else STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize) { #ifdef _WIN32 DWORD realProcessedSize; UInt32 sizeTemp = (1 << 20); if (sizeTemp > size) sizeTemp = size; BOOL res = ::ReadFile(GetStdHandle(STD_INPUT_HANDLE), data, sizeTemp, &realProcessedSize, NULL); if (processedSize) *processedSize = realProcessedSize; if (res == FALSE && GetLastError() == ERROR_BROKEN_PIPE) return S_OK; return ConvertBoolToHRESULT(res != FALSE); #else if (processedSize) *processedSize = 0; ssize_t res; do { res = read(0, data, (size_t)size); } while (res < 0 && (errno == EINTR)); if (res == -1) return E_FAIL; if (processedSize) *processedSize = (UInt32)res; return S_OK; #endif } #endif STDMETHODIMP CInFileStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { if (seekOrigin >= 3) return STG_E_INVALIDFUNCTION; #ifdef USE_WIN_FILE #ifdef SUPPORT_DEVICE_FILE if (File.IsDeviceFile && (File.SizeDefined || seekOrigin != STREAM_SEEK_END)) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += VirtPos; break; case STREAM_SEEK_END: offset += File.Size; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; VirtPos = offset; if (newPosition) *newPosition = offset; return S_OK; } #endif UInt64 realNewPosition; bool result = File.Seek(offset, seekOrigin, realNewPosition); #ifdef SUPPORT_DEVICE_FILE PhyPos = VirtPos = realNewPosition; #endif if (newPosition) *newPosition = realNewPosition; return ConvertBoolToHRESULT(result); #else off_t res = File.Seek((off_t)offset, seekOrigin); if (res == -1) return E_FAIL; if (newPosition) *newPosition = (UInt64)res; return S_OK; #endif } STDMETHODIMP CInFileStream::GetSize(UInt64 *size) { return ConvertBoolToHRESULT(File.GetLength(*size)); } #ifdef USE_WIN_FILE STDMETHODIMP CInFileStream::GetProps(UInt64 *size, FILETIME *cTime, FILETIME *aTime, FILETIME *mTime, UInt32 *attrib) { BY_HANDLE_FILE_INFORMATION info; if (File.GetFileInformation(&info)) { if (size) *size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow; if (cTime) *cTime = info.ftCreationTime; if (aTime) *aTime = info.ftLastAccessTime; if (mTime) *mTime = info.ftLastWriteTime; if (attrib) *attrib = info.dwFileAttributes; return S_OK; } return GetLastError(); } STDMETHODIMP CInFileStream::GetProps2(CStreamFileProps *props) { BY_HANDLE_FILE_INFORMATION info; if (File.GetFileInformation(&info)) { props->Size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow; props->VolID = info.dwVolumeSerialNumber; props->FileID_Low = (((UInt64)info.nFileIndexHigh) << 32) + info.nFileIndexLow; props->FileID_High = 0; props->NumLinks = SupportHardLinks ? info.nNumberOfLinks : 1; props->Attrib = info.dwFileAttributes; props->CTime = info.ftCreationTime; props->ATime = info.ftLastAccessTime; props->MTime = info.ftLastWriteTime; return S_OK; } return GetLastError(); } #endif ////////////////////////// // COutFileStream HRESULT COutFileStream::Close() { return ConvertBoolToHRESULT(File.Close()); } STDMETHODIMP COutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { #ifdef USE_WIN_FILE UInt32 realProcessedSize; bool result = File.WritePart(data, size, realProcessedSize); ProcessedSize += realProcessedSize; if (processedSize) *processedSize = realProcessedSize; return ConvertBoolToHRESULT(result); #else if (processedSize) *processedSize = 0; ssize_t res = File.Write(data, (size_t)size); if (res == -1) return E_FAIL; if (processedSize) *processedSize = (UInt32)res; ProcessedSize += res; return S_OK; #endif } STDMETHODIMP COutFileStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { if (seekOrigin >= 3) return STG_E_INVALIDFUNCTION; #ifdef USE_WIN_FILE UInt64 realNewPosition; bool result = File.Seek(offset, seekOrigin, realNewPosition); if (newPosition) *newPosition = realNewPosition; return ConvertBoolToHRESULT(result); #else off_t res = File.Seek((off_t)offset, seekOrigin); if (res == -1) return E_FAIL; if (newPosition) *newPosition = (UInt64)res; return S_OK; #endif } STDMETHODIMP COutFileStream::SetSize(UInt64 newSize) { #ifdef USE_WIN_FILE UInt64 currentPos; if (!File.Seek(0, FILE_CURRENT, currentPos)) return E_FAIL; bool result = File.SetLength(newSize); UInt64 currentPos2; result = result && File.Seek(currentPos, currentPos2); return result ? S_OK : E_FAIL; #else return E_FAIL; #endif } #ifdef UNDER_CE STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { size_t s2 = fwrite(data, 1, size, stdout); if (processedSize) *processedSize = s2; return (s2 == size) ? S_OK : E_FAIL; } #else STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; #ifdef _WIN32 UInt32 realProcessedSize; BOOL res = TRUE; if (size > 0) { // Seems that Windows doesn't like big amounts writing to stdout. // So we limit portions by 32KB. UInt32 sizeTemp = (1 << 15); if (sizeTemp > size) sizeTemp = size; res = ::WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), data, sizeTemp, (DWORD *)&realProcessedSize, NULL); size -= realProcessedSize; data = (const void *)((const Byte *)data + realProcessedSize); if (processedSize) *processedSize += realProcessedSize; } return ConvertBoolToHRESULT(res != FALSE); #else ssize_t res; do { res = write(1, data, (size_t)size); } while (res < 0 && (errno == EINTR)); if (res == -1) return E_FAIL; if (processedSize) *processedSize = (UInt32)res; return S_OK; return S_OK; #endif } #endif