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 
ConvertBoolToHRESULT(bool result)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;
CInFileStream()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 
~CInFileStream()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 
Read(void * data,UInt32 size,UInt32 * processedSize)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
Read(void * data,UInt32 size,UInt32 * processedSize)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
Read(void * data,UInt32 size,UInt32 * processedSize)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 
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)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 
GetSize(UInt64 * size)290 STDMETHODIMP CInFileStream::GetSize(UInt64 *size)
291 {
292   return ConvertBoolToHRESULT(File.GetLength(*size));
293 }
294 
295 #ifdef USE_WIN_FILE
296 
GetProps(UInt64 * size,FILETIME * cTime,FILETIME * aTime,FILETIME * mTime,UInt32 * attrib)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 
GetProps2(CStreamFileProps * props)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 
Close()336 HRESULT COutFileStream::Close()
337 {
338   return ConvertBoolToHRESULT(File.Close());
339 }
340 
Write(const void * data,UInt32 size,UInt32 * processedSize)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 
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)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 
SetSize(UInt64 newSize)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 
GetSize(UInt64 * size)411 HRESULT COutFileStream::GetSize(UInt64 *size)
412 {
413   return ConvertBoolToHRESULT(File.GetLength(*size));
414 }
415 
416 #ifdef UNDER_CE
417 
Write(const void * data,UInt32 size,UInt32 * processedSize)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 
Write(const void * data,UInt32 size,UInt32 * processedSize)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