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