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