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