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