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 
Open(LPCTSTR fileName)32 bool CInFileStream::Open(LPCTSTR fileName)
33 {
34   return File.Open(fileName);
35 }
36 
37 #ifdef USE_WIN_FILE
38 #ifndef _UNICODE
Open(LPCWSTR fileName)39 bool CInFileStream::Open(LPCWSTR fileName)
40 {
41   return File.Open(fileName);
42 }
43 #endif
44 #endif
45 
OpenShared(LPCTSTR fileName,bool shareForWrite)46 bool CInFileStream::OpenShared(LPCTSTR fileName, bool shareForWrite)
47 {
48   return File.OpenShared(fileName, shareForWrite);
49 }
50 
51 #ifdef USE_WIN_FILE
52 #ifndef _UNICODE
OpenShared(LPCWSTR fileName,bool shareForWrite)53 bool CInFileStream::OpenShared(LPCWSTR fileName, bool shareForWrite)
54 {
55   return File.OpenShared(fileName, shareForWrite);
56 }
57 #endif
58 #endif
59 
60 #ifdef SUPPORT_DEVICE_FILE
61 
62 static const UInt32 kClusterSize = 1 << 18;
CInFileStream()63 CInFileStream::CInFileStream():
64   VirtPos(0),
65   PhyPos(0),
66   Buffer(0),
67   BufferSize(0)
68 {
69 }
70 
71 #endif
72 
~CInFileStream()73 CInFileStream::~CInFileStream()
74 {
75   #ifdef SUPPORT_DEVICE_FILE
76   MidFree(Buffer);
77   #endif
78 }
79 
Read(void * data,UInt32 size,UInt32 * processedSize)80 STDMETHODIMP CInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize)
81 {
82   #ifdef USE_WIN_FILE
83 
84   #ifdef SUPPORT_DEVICE_FILE
85   if (processedSize != NULL)
86     *processedSize = 0;
87   if (size == 0)
88     return S_OK;
89   if (File.IsDeviceFile)
90   {
91     if (File.LengthDefined)
92     {
93       if (VirtPos >= File.Length)
94         return VirtPos == File.Length ? S_OK : E_FAIL;
95       UInt64 rem = File.Length - VirtPos;
96       if (size > rem)
97         size = (UInt32)rem;
98     }
99     for (;;)
100     {
101       const UInt32 mask = kClusterSize - 1;
102       UInt64 mask2 = ~(UInt64)mask;
103       UInt64 alignedPos = VirtPos & mask2;
104       if (BufferSize > 0 && BufferStartPos == alignedPos)
105       {
106         UInt32 pos = (UInt32)VirtPos & mask;
107         if (pos >= BufferSize)
108           return S_OK;
109         UInt32 rem = MyMin(BufferSize - pos, size);
110         memcpy(data, Buffer + pos, rem);
111         VirtPos += rem;
112         if (processedSize != NULL)
113           *processedSize += rem;
114         return S_OK;
115       }
116 
117       bool useBuffer = false;
118       if ((VirtPos & mask) != 0 || ((ptrdiff_t)data & mask) != 0 )
119         useBuffer = true;
120       else
121       {
122         UInt64 end = VirtPos + size;
123         if ((end & mask) != 0)
124         {
125           end &= mask2;
126           if (end <= VirtPos)
127             useBuffer = true;
128           else
129             size = (UInt32)(end - VirtPos);
130         }
131       }
132       if (!useBuffer)
133         break;
134       if (alignedPos != PhyPos)
135       {
136         UInt64 realNewPosition;
137         bool result = File.Seek(alignedPos, FILE_BEGIN, realNewPosition);
138         if (!result)
139           return ConvertBoolToHRESULT(result);
140         PhyPos = realNewPosition;
141       }
142 
143       BufferStartPos = alignedPos;
144       UInt32 readSize = kClusterSize;
145       if (File.LengthDefined)
146         readSize = (UInt32)MyMin(File.Length - PhyPos, (UInt64)kClusterSize);
147 
148       if (Buffer == 0)
149       {
150         Buffer = (Byte *)MidAlloc(kClusterSize);
151         if (Buffer == 0)
152           return E_OUTOFMEMORY;
153       }
154       bool result = File.Read1(Buffer, readSize, BufferSize);
155       if (!result)
156         return ConvertBoolToHRESULT(result);
157 
158       if (BufferSize == 0)
159         return S_OK;
160       PhyPos += BufferSize;
161     }
162 
163     if (VirtPos != PhyPos)
164     {
165       UInt64 realNewPosition;
166       bool result = File.Seek(VirtPos, FILE_BEGIN, realNewPosition);
167       if (!result)
168         return ConvertBoolToHRESULT(result);
169       PhyPos = VirtPos = realNewPosition;
170     }
171   }
172   #endif
173 
174   UInt32 realProcessedSize;
175   bool result = File.ReadPart(data, size, realProcessedSize);
176   if (processedSize != NULL)
177     *processedSize = realProcessedSize;
178   #ifdef SUPPORT_DEVICE_FILE
179   VirtPos += realProcessedSize;
180   PhyPos += realProcessedSize;
181   #endif
182   return ConvertBoolToHRESULT(result);
183 
184   #else
185 
186   if (processedSize != NULL)
187     *processedSize = 0;
188   ssize_t res = File.Read(data, (size_t)size);
189   if (res == -1)
190     return E_FAIL;
191   if (processedSize != NULL)
192     *processedSize = (UInt32)res;
193   return S_OK;
194 
195   #endif
196 }
197 
198 #ifdef UNDER_CE
Read(void * data,UInt32 size,UInt32 * processedSize)199 STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize)
200 {
201   size_t s2 = fread(data, 1, size, stdout);
202   if (processedSize != 0)
203     *processedSize = s2;
204   return (s2 = size) ? S_OK : E_FAIL;
205 }
206 #else
Read(void * data,UInt32 size,UInt32 * processedSize)207 STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize)
208 {
209   #ifdef _WIN32
210 
211   DWORD realProcessedSize;
212   UInt32 sizeTemp = (1 << 20);
213   if (sizeTemp > size)
214     sizeTemp = size;
215   BOOL res = ::ReadFile(GetStdHandle(STD_INPUT_HANDLE), data, sizeTemp, &realProcessedSize, NULL);
216   if (processedSize != NULL)
217     *processedSize = realProcessedSize;
218   if (res == FALSE && GetLastError() == ERROR_BROKEN_PIPE)
219     return S_OK;
220   return ConvertBoolToHRESULT(res != FALSE);
221 
222   #else
223 
224   if (processedSize != NULL)
225     *processedSize = 0;
226   ssize_t res;
227   do
228   {
229     res = read(0, data, (size_t)size);
230   }
231   while (res < 0 && (errno == EINTR));
232   if (res == -1)
233     return E_FAIL;
234   if (processedSize != NULL)
235     *processedSize = (UInt32)res;
236   return S_OK;
237 
238   #endif
239 }
240 
241 #endif
242 
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)243 STDMETHODIMP CInFileStream::Seek(Int64 offset, UInt32 seekOrigin,
244     UInt64 *newPosition)
245 {
246   if (seekOrigin >= 3)
247     return STG_E_INVALIDFUNCTION;
248 
249   #ifdef USE_WIN_FILE
250 
251   #ifdef SUPPORT_DEVICE_FILE
252   if (File.IsDeviceFile)
253   {
254     UInt64 newVirtPos = offset;
255     switch(seekOrigin)
256     {
257       case STREAM_SEEK_SET: break;
258       case STREAM_SEEK_CUR: newVirtPos += VirtPos; break;
259       case STREAM_SEEK_END: newVirtPos += File.Length; break;
260       default: return STG_E_INVALIDFUNCTION;
261     }
262     VirtPos = newVirtPos;
263     if (newPosition)
264       *newPosition = newVirtPos;
265     return S_OK;
266   }
267   #endif
268 
269   UInt64 realNewPosition;
270   bool result = File.Seek(offset, seekOrigin, realNewPosition);
271 
272   #ifdef SUPPORT_DEVICE_FILE
273   PhyPos = VirtPos = realNewPosition;
274   #endif
275 
276   if (newPosition != NULL)
277     *newPosition = realNewPosition;
278   return ConvertBoolToHRESULT(result);
279 
280   #else
281 
282   off_t res = File.Seek(offset, seekOrigin);
283   if (res == -1)
284     return E_FAIL;
285   if (newPosition != NULL)
286     *newPosition = (UInt64)res;
287   return S_OK;
288 
289   #endif
290 }
291 
GetSize(UInt64 * size)292 STDMETHODIMP CInFileStream::GetSize(UInt64 *size)
293 {
294   return ConvertBoolToHRESULT(File.GetLength(*size));
295 }
296 
297 
298 //////////////////////////
299 // COutFileStream
300 
Close()301 HRESULT COutFileStream::Close()
302 {
303   return ConvertBoolToHRESULT(File.Close());
304 }
305 
Write(const void * data,UInt32 size,UInt32 * processedSize)306 STDMETHODIMP COutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
307 {
308   #ifdef USE_WIN_FILE
309 
310   UInt32 realProcessedSize;
311   bool result = File.WritePart(data, size, realProcessedSize);
312   ProcessedSize += realProcessedSize;
313   if (processedSize != NULL)
314     *processedSize = realProcessedSize;
315   return ConvertBoolToHRESULT(result);
316 
317   #else
318 
319   if (processedSize != NULL)
320     *processedSize = 0;
321   ssize_t res = File.Write(data, (size_t)size);
322   if (res == -1)
323     return E_FAIL;
324   if (processedSize != NULL)
325     *processedSize = (UInt32)res;
326   ProcessedSize += res;
327   return S_OK;
328 
329   #endif
330 }
331 
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)332 STDMETHODIMP COutFileStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
333 {
334   if (seekOrigin >= 3)
335     return STG_E_INVALIDFUNCTION;
336   #ifdef USE_WIN_FILE
337 
338   UInt64 realNewPosition;
339   bool result = File.Seek(offset, seekOrigin, realNewPosition);
340   if (newPosition != NULL)
341     *newPosition = realNewPosition;
342   return ConvertBoolToHRESULT(result);
343 
344   #else
345 
346   off_t res = File.Seek(offset, seekOrigin);
347   if (res == -1)
348     return E_FAIL;
349   if (newPosition != NULL)
350     *newPosition = (UInt64)res;
351   return S_OK;
352 
353   #endif
354 }
355 
SetSize(UInt64 newSize)356 STDMETHODIMP COutFileStream::SetSize(UInt64 newSize)
357 {
358   #ifdef USE_WIN_FILE
359   UInt64 currentPos;
360   if (!File.Seek(0, FILE_CURRENT, currentPos))
361     return E_FAIL;
362   bool result = File.SetLength(newSize);
363   UInt64 currentPos2;
364   result = result && File.Seek(currentPos, currentPos2);
365   return result ? S_OK : E_FAIL;
366   #else
367   return E_FAIL;
368   #endif
369 }
370 
371 #ifdef UNDER_CE
Write(const void * data,UInt32 size,UInt32 * processedSize)372 STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
373 {
374   size_t s2 = fwrite(data, 1, size, stdout);
375   if (processedSize != 0)
376     *processedSize = s2;
377   return (s2 = size) ? S_OK : E_FAIL;
378 }
379 #else
Write(const void * data,UInt32 size,UInt32 * processedSize)380 STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
381 {
382   if (processedSize != NULL)
383     *processedSize = 0;
384 
385   #ifdef _WIN32
386   UInt32 realProcessedSize;
387   BOOL res = TRUE;
388   if (size > 0)
389   {
390     // Seems that Windows doesn't like big amounts writing to stdout.
391     // So we limit portions by 32KB.
392     UInt32 sizeTemp = (1 << 15);
393     if (sizeTemp > size)
394       sizeTemp = size;
395     res = ::WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
396         data, sizeTemp, (DWORD *)&realProcessedSize, NULL);
397     size -= realProcessedSize;
398     data = (const void *)((const Byte *)data + realProcessedSize);
399     if (processedSize != NULL)
400       *processedSize += realProcessedSize;
401   }
402   return ConvertBoolToHRESULT(res != FALSE);
403 
404   #else
405 
406   ssize_t res;
407   do
408   {
409     res = write(1, data, (size_t)size);
410   }
411   while (res < 0 && (errno == EINTR));
412   if (res == -1)
413     return E_FAIL;
414   if (processedSize != NULL)
415     *processedSize = (UInt32)res;
416   return S_OK;
417 
418   return S_OK;
419   #endif
420 }
421 
422 #endif
423