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