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