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