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