diff options
Diffstat (limited to 'CPP/7zip/Common/FileStreams.cpp')
-rw-r--r-- | CPP/7zip/Common/FileStreams.cpp | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/CPP/7zip/Common/FileStreams.cpp b/CPP/7zip/Common/FileStreams.cpp new file mode 100644 index 0000000..9e0e79c --- /dev/null +++ b/CPP/7zip/Common/FileStreams.cpp | |||
@@ -0,0 +1,536 @@ | |||
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 | #include "../../Windows/FileFind.h" | ||
10 | #endif | ||
11 | |||
12 | #ifdef SUPPORT_DEVICE_FILE | ||
13 | #include "../../../C/Alloc.h" | ||
14 | #include "../../Common/Defs.h" | ||
15 | #endif | ||
16 | |||
17 | #include "FileStreams.h" | ||
18 | |||
19 | static inline HRESULT GetLastError_HRESULT() | ||
20 | { | ||
21 | DWORD lastError = ::GetLastError(); | ||
22 | if (lastError == 0) | ||
23 | return E_FAIL; | ||
24 | return HRESULT_FROM_WIN32(lastError); | ||
25 | } | ||
26 | |||
27 | static inline HRESULT ConvertBoolToHRESULT(bool result) | ||
28 | { | ||
29 | if (result) | ||
30 | return S_OK; | ||
31 | return GetLastError_HRESULT(); | ||
32 | } | ||
33 | |||
34 | |||
35 | #ifdef SUPPORT_DEVICE_FILE | ||
36 | static const UInt32 kClusterSize = 1 << 18; | ||
37 | #endif | ||
38 | |||
39 | CInFileStream::CInFileStream(): | ||
40 | #ifdef SUPPORT_DEVICE_FILE | ||
41 | VirtPos(0), | ||
42 | PhyPos(0), | ||
43 | Buf(0), | ||
44 | BufSize(0), | ||
45 | #endif | ||
46 | SupportHardLinks(false), | ||
47 | Callback(NULL), | ||
48 | CallbackRef(0) | ||
49 | { | ||
50 | } | ||
51 | |||
52 | CInFileStream::~CInFileStream() | ||
53 | { | ||
54 | #ifdef SUPPORT_DEVICE_FILE | ||
55 | MidFree(Buf); | ||
56 | #endif | ||
57 | |||
58 | if (Callback) | ||
59 | Callback->InFileStream_On_Destroy(CallbackRef); | ||
60 | } | ||
61 | |||
62 | STDMETHODIMP CInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize) | ||
63 | { | ||
64 | #ifdef USE_WIN_FILE | ||
65 | |||
66 | #ifdef SUPPORT_DEVICE_FILE | ||
67 | if (processedSize) | ||
68 | *processedSize = 0; | ||
69 | if (size == 0) | ||
70 | return S_OK; | ||
71 | if (File.IsDeviceFile) | ||
72 | { | ||
73 | if (File.SizeDefined) | ||
74 | { | ||
75 | if (VirtPos >= File.Size) | ||
76 | return VirtPos == File.Size ? S_OK : E_FAIL; | ||
77 | UInt64 rem = File.Size - VirtPos; | ||
78 | if (size > rem) | ||
79 | size = (UInt32)rem; | ||
80 | } | ||
81 | for (;;) | ||
82 | { | ||
83 | const UInt32 mask = kClusterSize - 1; | ||
84 | const UInt64 mask2 = ~(UInt64)mask; | ||
85 | UInt64 alignedPos = VirtPos & mask2; | ||
86 | if (BufSize > 0 && BufStartPos == alignedPos) | ||
87 | { | ||
88 | UInt32 pos = (UInt32)VirtPos & mask; | ||
89 | if (pos >= BufSize) | ||
90 | return S_OK; | ||
91 | UInt32 rem = MyMin(BufSize - pos, size); | ||
92 | memcpy(data, Buf + pos, rem); | ||
93 | VirtPos += rem; | ||
94 | if (processedSize) | ||
95 | *processedSize += rem; | ||
96 | return S_OK; | ||
97 | } | ||
98 | |||
99 | bool useBuf = false; | ||
100 | if ((VirtPos & mask) != 0 || ((ptrdiff_t)data & mask) != 0 ) | ||
101 | useBuf = true; | ||
102 | else | ||
103 | { | ||
104 | UInt64 end = VirtPos + size; | ||
105 | if ((end & mask) != 0) | ||
106 | { | ||
107 | end &= mask2; | ||
108 | if (end <= VirtPos) | ||
109 | useBuf = true; | ||
110 | else | ||
111 | size = (UInt32)(end - VirtPos); | ||
112 | } | ||
113 | } | ||
114 | if (!useBuf) | ||
115 | break; | ||
116 | if (alignedPos != PhyPos) | ||
117 | { | ||
118 | UInt64 realNewPosition; | ||
119 | bool result = File.Seek((Int64)alignedPos, FILE_BEGIN, realNewPosition); | ||
120 | if (!result) | ||
121 | return ConvertBoolToHRESULT(result); | ||
122 | PhyPos = realNewPosition; | ||
123 | } | ||
124 | |||
125 | BufStartPos = alignedPos; | ||
126 | UInt32 readSize = kClusterSize; | ||
127 | if (File.SizeDefined) | ||
128 | readSize = (UInt32)MyMin(File.Size - PhyPos, (UInt64)kClusterSize); | ||
129 | |||
130 | if (!Buf) | ||
131 | { | ||
132 | Buf = (Byte *)MidAlloc(kClusterSize); | ||
133 | if (!Buf) | ||
134 | return E_OUTOFMEMORY; | ||
135 | } | ||
136 | bool result = File.Read1(Buf, readSize, BufSize); | ||
137 | if (!result) | ||
138 | return ConvertBoolToHRESULT(result); | ||
139 | |||
140 | if (BufSize == 0) | ||
141 | return S_OK; | ||
142 | PhyPos += BufSize; | ||
143 | } | ||
144 | |||
145 | if (VirtPos != PhyPos) | ||
146 | { | ||
147 | UInt64 realNewPosition; | ||
148 | bool result = File.Seek((Int64)VirtPos, FILE_BEGIN, realNewPosition); | ||
149 | if (!result) | ||
150 | return ConvertBoolToHRESULT(result); | ||
151 | PhyPos = VirtPos = realNewPosition; | ||
152 | } | ||
153 | } | ||
154 | #endif | ||
155 | |||
156 | UInt32 realProcessedSize; | ||
157 | const bool result = File.ReadPart(data, size, realProcessedSize); | ||
158 | if (processedSize) | ||
159 | *processedSize = realProcessedSize; | ||
160 | |||
161 | #ifdef SUPPORT_DEVICE_FILE | ||
162 | VirtPos += realProcessedSize; | ||
163 | PhyPos += realProcessedSize; | ||
164 | #endif | ||
165 | |||
166 | if (result) | ||
167 | return S_OK; | ||
168 | |||
169 | #else // USE_WIN_FILE | ||
170 | |||
171 | if (processedSize) | ||
172 | *processedSize = 0; | ||
173 | const ssize_t res = File.read_part(data, (size_t)size); | ||
174 | if (res != -1) | ||
175 | { | ||
176 | if (processedSize) | ||
177 | *processedSize = (UInt32)res; | ||
178 | return S_OK; | ||
179 | } | ||
180 | #endif // USE_WIN_FILE | ||
181 | |||
182 | { | ||
183 | const DWORD error = ::GetLastError(); | ||
184 | if (Callback) | ||
185 | return Callback->InFileStream_On_Error(CallbackRef, error); | ||
186 | if (error == 0) | ||
187 | return E_FAIL; | ||
188 | return HRESULT_FROM_WIN32(error); | ||
189 | } | ||
190 | } | ||
191 | |||
192 | #ifdef UNDER_CE | ||
193 | STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize) | ||
194 | { | ||
195 | size_t s2 = fread(data, 1, size, stdin); | ||
196 | int error = ferror(stdin); | ||
197 | if (processedSize) | ||
198 | *processedSize = s2; | ||
199 | if (s2 <= size && error == 0) | ||
200 | return S_OK; | ||
201 | return E_FAIL; | ||
202 | } | ||
203 | #else | ||
204 | STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize) | ||
205 | { | ||
206 | #ifdef _WIN32 | ||
207 | |||
208 | DWORD realProcessedSize; | ||
209 | UInt32 sizeTemp = (1 << 20); | ||
210 | if (sizeTemp > size) | ||
211 | sizeTemp = size; | ||
212 | BOOL res = ::ReadFile(GetStdHandle(STD_INPUT_HANDLE), data, sizeTemp, &realProcessedSize, NULL); | ||
213 | if (processedSize) | ||
214 | *processedSize = realProcessedSize; | ||
215 | if (res == FALSE && GetLastError() == ERROR_BROKEN_PIPE) | ||
216 | return S_OK; | ||
217 | return ConvertBoolToHRESULT(res != FALSE); | ||
218 | |||
219 | #else | ||
220 | |||
221 | if (processedSize) | ||
222 | *processedSize = 0; | ||
223 | ssize_t res; | ||
224 | do | ||
225 | { | ||
226 | res = read(0, data, (size_t)size); | ||
227 | } | ||
228 | while (res < 0 && (errno == EINTR)); | ||
229 | if (res == -1) | ||
230 | return GetLastError_HRESULT(); | ||
231 | if (processedSize) | ||
232 | *processedSize = (UInt32)res; | ||
233 | return S_OK; | ||
234 | |||
235 | #endif | ||
236 | } | ||
237 | |||
238 | #endif | ||
239 | |||
240 | STDMETHODIMP CInFileStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) | ||
241 | { | ||
242 | if (seekOrigin >= 3) | ||
243 | return STG_E_INVALIDFUNCTION; | ||
244 | |||
245 | #ifdef USE_WIN_FILE | ||
246 | |||
247 | #ifdef SUPPORT_DEVICE_FILE | ||
248 | if (File.IsDeviceFile && (File.SizeDefined || seekOrigin != STREAM_SEEK_END)) | ||
249 | { | ||
250 | switch (seekOrigin) | ||
251 | { | ||
252 | case STREAM_SEEK_SET: break; | ||
253 | case STREAM_SEEK_CUR: offset += VirtPos; break; | ||
254 | case STREAM_SEEK_END: offset += File.Size; break; | ||
255 | default: return STG_E_INVALIDFUNCTION; | ||
256 | } | ||
257 | if (offset < 0) | ||
258 | return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; | ||
259 | VirtPos = (UInt64)offset; | ||
260 | if (newPosition) | ||
261 | *newPosition = (UInt64)offset; | ||
262 | return S_OK; | ||
263 | } | ||
264 | #endif | ||
265 | |||
266 | UInt64 realNewPosition = 0; | ||
267 | const bool result = File.Seek(offset, seekOrigin, realNewPosition); | ||
268 | const HRESULT hres = ConvertBoolToHRESULT(result); | ||
269 | |||
270 | /* 21.07: new File.Seek() in 21.07 already returns correct (realNewPosition) | ||
271 | in case of error. So we don't need additional code below */ | ||
272 | // if (!result) { realNewPosition = 0; File.GetPosition(realNewPosition); } | ||
273 | |||
274 | #ifdef SUPPORT_DEVICE_FILE | ||
275 | PhyPos = VirtPos = realNewPosition; | ||
276 | #endif | ||
277 | |||
278 | if (newPosition) | ||
279 | *newPosition = realNewPosition; | ||
280 | |||
281 | return hres; | ||
282 | |||
283 | #else | ||
284 | |||
285 | const off_t res = File.seek((off_t)offset, (int)seekOrigin); | ||
286 | if (res == -1) | ||
287 | { | ||
288 | const HRESULT hres = GetLastError_HRESULT(); | ||
289 | if (newPosition) | ||
290 | *newPosition = (UInt64)File.seekToCur(); | ||
291 | return hres; | ||
292 | } | ||
293 | if (newPosition) | ||
294 | *newPosition = (UInt64)res; | ||
295 | return S_OK; | ||
296 | |||
297 | #endif | ||
298 | } | ||
299 | |||
300 | STDMETHODIMP CInFileStream::GetSize(UInt64 *size) | ||
301 | { | ||
302 | return ConvertBoolToHRESULT(File.GetLength(*size)); | ||
303 | } | ||
304 | |||
305 | #ifdef USE_WIN_FILE | ||
306 | |||
307 | STDMETHODIMP CInFileStream::GetProps(UInt64 *size, FILETIME *cTime, FILETIME *aTime, FILETIME *mTime, UInt32 *attrib) | ||
308 | { | ||
309 | BY_HANDLE_FILE_INFORMATION info; | ||
310 | if (File.GetFileInformation(&info)) | ||
311 | { | ||
312 | if (size) *size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow; | ||
313 | if (cTime) *cTime = info.ftCreationTime; | ||
314 | if (aTime) *aTime = info.ftLastAccessTime; | ||
315 | if (mTime) *mTime = info.ftLastWriteTime; | ||
316 | if (attrib) *attrib = info.dwFileAttributes; | ||
317 | return S_OK; | ||
318 | } | ||
319 | return GetLastError_HRESULT(); | ||
320 | } | ||
321 | |||
322 | STDMETHODIMP CInFileStream::GetProps2(CStreamFileProps *props) | ||
323 | { | ||
324 | BY_HANDLE_FILE_INFORMATION info; | ||
325 | if (File.GetFileInformation(&info)) | ||
326 | { | ||
327 | props->Size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow; | ||
328 | props->VolID = info.dwVolumeSerialNumber; | ||
329 | props->FileID_Low = (((UInt64)info.nFileIndexHigh) << 32) + info.nFileIndexLow; | ||
330 | props->FileID_High = 0; | ||
331 | props->NumLinks = SupportHardLinks ? info.nNumberOfLinks : 1; | ||
332 | props->Attrib = info.dwFileAttributes; | ||
333 | props->CTime = info.ftCreationTime; | ||
334 | props->ATime = info.ftLastAccessTime; | ||
335 | props->MTime = info.ftLastWriteTime; | ||
336 | return S_OK; | ||
337 | } | ||
338 | return GetLastError_HRESULT(); | ||
339 | } | ||
340 | |||
341 | #elif !defined(_WIN32) | ||
342 | |||
343 | STDMETHODIMP CInFileStream::GetProps(UInt64 *size, FILETIME *cTime, FILETIME *aTime, FILETIME *mTime, UInt32 *attrib) | ||
344 | { | ||
345 | struct stat st; | ||
346 | if (File.my_fstat(&st) != 0) | ||
347 | return GetLastError_HRESULT(); | ||
348 | |||
349 | if (size) *size = (UInt64)st.st_size; | ||
350 | #ifdef __APPLE__ | ||
351 | if (cTime) NWindows::NFile::NFind::timespec_To_FILETIME(st.st_ctimespec, *cTime); | ||
352 | if (aTime) NWindows::NFile::NFind::timespec_To_FILETIME(st.st_atimespec, *aTime); | ||
353 | if (mTime) NWindows::NFile::NFind::timespec_To_FILETIME(st.st_mtimespec, *mTime); | ||
354 | #else | ||
355 | if (cTime) NWindows::NFile::NFind::timespec_To_FILETIME(st.st_ctim, *cTime); | ||
356 | if (aTime) NWindows::NFile::NFind::timespec_To_FILETIME(st.st_atim, *aTime); | ||
357 | if (mTime) NWindows::NFile::NFind::timespec_To_FILETIME(st.st_mtim, *mTime); | ||
358 | #endif | ||
359 | if (attrib) *attrib = NWindows::NFile::NFind::Get_WinAttribPosix_From_PosixMode(st.st_mode); | ||
360 | |||
361 | return S_OK; | ||
362 | } | ||
363 | |||
364 | // #include <stdio.h> | ||
365 | |||
366 | STDMETHODIMP CInFileStream::GetProps2(CStreamFileProps *props) | ||
367 | { | ||
368 | struct stat st; | ||
369 | if (File.my_fstat(&st) != 0) | ||
370 | return GetLastError_HRESULT(); | ||
371 | |||
372 | props->Size = (UInt64)st.st_size; | ||
373 | /* | ||
374 | dev_t stat::st_dev: | ||
375 | GCC:Linux long unsigned int : __dev_t | ||
376 | Mac: int | ||
377 | */ | ||
378 | props->VolID = (UInt64)(Int64)st.st_dev; | ||
379 | props->FileID_Low = st.st_ino; | ||
380 | props->FileID_High = 0; | ||
381 | props->NumLinks = (UInt32)st.st_nlink; // we reduce to UInt32 from (nlink_t) that is (unsigned long) | ||
382 | props->Attrib = NWindows::NFile::NFind::Get_WinAttribPosix_From_PosixMode(st.st_mode); | ||
383 | |||
384 | #ifdef __APPLE__ | ||
385 | NWindows::NFile::NFind::timespec_To_FILETIME(st.st_ctimespec, props->CTime); | ||
386 | NWindows::NFile::NFind::timespec_To_FILETIME(st.st_atimespec, props->ATime); | ||
387 | NWindows::NFile::NFind::timespec_To_FILETIME(st.st_mtimespec, props->MTime); | ||
388 | #else | ||
389 | NWindows::NFile::NFind::timespec_To_FILETIME(st.st_ctim, props->CTime); | ||
390 | NWindows::NFile::NFind::timespec_To_FILETIME(st.st_atim, props->ATime); | ||
391 | NWindows::NFile::NFind::timespec_To_FILETIME(st.st_mtim, props->MTime); | ||
392 | #endif | ||
393 | |||
394 | /* | ||
395 | printf("\nGetProps2() NumLinks=%d = st_dev=%d st_ino = %d\n" | ||
396 | , (unsigned)(props->NumLinks) | ||
397 | , (unsigned)(st.st_dev) | ||
398 | , (unsigned)(st.st_ino) | ||
399 | ); | ||
400 | */ | ||
401 | |||
402 | return S_OK; | ||
403 | } | ||
404 | |||
405 | #endif | ||
406 | |||
407 | ////////////////////////// | ||
408 | // COutFileStream | ||
409 | |||
410 | HRESULT COutFileStream::Close() | ||
411 | { | ||
412 | return ConvertBoolToHRESULT(File.Close()); | ||
413 | } | ||
414 | |||
415 | STDMETHODIMP COutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize) | ||
416 | { | ||
417 | #ifdef USE_WIN_FILE | ||
418 | |||
419 | UInt32 realProcessedSize; | ||
420 | const bool result = File.Write(data, size, realProcessedSize); | ||
421 | ProcessedSize += realProcessedSize; | ||
422 | if (processedSize) | ||
423 | *processedSize = realProcessedSize; | ||
424 | return ConvertBoolToHRESULT(result); | ||
425 | |||
426 | #else | ||
427 | |||
428 | if (processedSize) | ||
429 | *processedSize = 0; | ||
430 | size_t realProcessedSize; | ||
431 | const ssize_t res = File.write_full(data, (size_t)size, realProcessedSize); | ||
432 | ProcessedSize += realProcessedSize; | ||
433 | if (processedSize) | ||
434 | *processedSize = (UInt32)realProcessedSize; | ||
435 | if (res == -1) | ||
436 | return GetLastError_HRESULT(); | ||
437 | return S_OK; | ||
438 | |||
439 | #endif | ||
440 | } | ||
441 | |||
442 | STDMETHODIMP COutFileStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) | ||
443 | { | ||
444 | if (seekOrigin >= 3) | ||
445 | return STG_E_INVALIDFUNCTION; | ||
446 | |||
447 | #ifdef USE_WIN_FILE | ||
448 | |||
449 | UInt64 realNewPosition = 0; | ||
450 | const bool result = File.Seek(offset, seekOrigin, realNewPosition); | ||
451 | if (newPosition) | ||
452 | *newPosition = realNewPosition; | ||
453 | return ConvertBoolToHRESULT(result); | ||
454 | |||
455 | #else | ||
456 | |||
457 | const off_t res = File.seek((off_t)offset, (int)seekOrigin); | ||
458 | if (res == -1) | ||
459 | return GetLastError_HRESULT(); | ||
460 | if (newPosition) | ||
461 | *newPosition = (UInt64)res; | ||
462 | return S_OK; | ||
463 | |||
464 | #endif | ||
465 | } | ||
466 | |||
467 | STDMETHODIMP COutFileStream::SetSize(UInt64 newSize) | ||
468 | { | ||
469 | return ConvertBoolToHRESULT(File.SetLength_KeepPosition(newSize)); | ||
470 | } | ||
471 | |||
472 | HRESULT COutFileStream::GetSize(UInt64 *size) | ||
473 | { | ||
474 | return ConvertBoolToHRESULT(File.GetLength(*size)); | ||
475 | } | ||
476 | |||
477 | #ifdef UNDER_CE | ||
478 | |||
479 | STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize) | ||
480 | { | ||
481 | size_t s2 = fwrite(data, 1, size, stdout); | ||
482 | if (processedSize) | ||
483 | *processedSize = s2; | ||
484 | return (s2 == size) ? S_OK : E_FAIL; | ||
485 | } | ||
486 | |||
487 | #else | ||
488 | |||
489 | STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize) | ||
490 | { | ||
491 | if (processedSize) | ||
492 | *processedSize = 0; | ||
493 | |||
494 | #ifdef _WIN32 | ||
495 | |||
496 | UInt32 realProcessedSize; | ||
497 | BOOL res = TRUE; | ||
498 | if (size > 0) | ||
499 | { | ||
500 | // Seems that Windows doesn't like big amounts writing to stdout. | ||
501 | // So we limit portions by 32KB. | ||
502 | UInt32 sizeTemp = (1 << 15); | ||
503 | if (sizeTemp > size) | ||
504 | sizeTemp = size; | ||
505 | res = ::WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), | ||
506 | data, sizeTemp, (DWORD *)&realProcessedSize, NULL); | ||
507 | _size += realProcessedSize; | ||
508 | size -= realProcessedSize; | ||
509 | data = (const void *)((const Byte *)data + realProcessedSize); | ||
510 | if (processedSize) | ||
511 | *processedSize += realProcessedSize; | ||
512 | } | ||
513 | return ConvertBoolToHRESULT(res != FALSE); | ||
514 | |||
515 | #else | ||
516 | |||
517 | ssize_t res; | ||
518 | |||
519 | do | ||
520 | { | ||
521 | res = write(1, data, (size_t)size); | ||
522 | } | ||
523 | while (res < 0 && (errno == EINTR)); | ||
524 | |||
525 | if (res == -1) | ||
526 | return GetLastError_HRESULT(); | ||
527 | |||
528 | _size += (size_t)res; | ||
529 | if (processedSize) | ||
530 | *processedSize = (UInt32)res; | ||
531 | return S_OK; | ||
532 | |||
533 | #endif | ||
534 | } | ||
535 | |||
536 | #endif | ||