1 // ArchiveOpenCallback.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../Common/ComTry.h"
6
7 #include "../../../Windows/FileName.h"
8 #include "../../../Windows/PropVariant.h"
9 #include "../../../Windows/System.h"
10
11 #include "../../Common/StreamUtils.h"
12
13 #include "ArchiveOpenCallback.h"
14
15 // #define DEBUG_VOLUMES
16
17 #ifdef DEBUG_VOLUMES
18 #include <stdio.h>
19 #endif
20
21
22 #ifdef DEBUG_VOLUMES
23 #define PRF(x) x
24 #else
25 #define PRF(x)
26 #endif
27
28 using namespace NWindows;
29
Init2(const FString &folderPrefix, const FString &fileName)30 HRESULT COpenCallbackImp::Init2(const FString &folderPrefix, const FString &fileName)
31 {
32 Volumes.Init();
33 FileNames.Clear();
34 FileNames_WasUsed.Clear();
35 FileSizes.Clear();
36 _subArchiveMode = false;
37 // TotalSize = 0;
38 PasswordWasAsked = false;
39 _folderPrefix = folderPrefix;
40 if (!_fileInfo.Find_FollowLink(_folderPrefix + fileName))
41 {
42 // throw 20121118;
43 return GetLastError_noZero_HRESULT();
44 }
45 return S_OK;
46 }
47
SetSubArchiveName(const wchar_t *name)48 Z7_COM7F_IMF(COpenCallbackImp::SetSubArchiveName(const wchar_t *name))
49 {
50 _subArchiveMode = true;
51 _subArchiveName = name;
52 // TotalSize = 0;
53 return S_OK;
54 }
55
SetTotal(const UInt64 *files, const UInt64 *bytes)56 Z7_COM7F_IMF(COpenCallbackImp::SetTotal(const UInt64 *files, const UInt64 *bytes))
57 {
58 COM_TRY_BEGIN
59 if (ReOpenCallback)
60 return ReOpenCallback->SetTotal(files, bytes);
61 if (!Callback)
62 return S_OK;
63 return Callback->Open_SetTotal(files, bytes);
64 COM_TRY_END
65 }
66
SetCompleted(const UInt64 *files, const UInt64 *bytes)67 Z7_COM7F_IMF(COpenCallbackImp::SetCompleted(const UInt64 *files, const UInt64 *bytes))
68 {
69 COM_TRY_BEGIN
70 if (ReOpenCallback)
71 return ReOpenCallback->SetCompleted(files, bytes);
72 if (!Callback)
73 return S_OK;
74 return Callback->Open_SetCompleted(files, bytes);
75 COM_TRY_END
76 }
77
78
GetProperty(PROPID propID, PROPVARIANT *value)79 Z7_COM7F_IMF(COpenCallbackImp::GetProperty(PROPID propID, PROPVARIANT *value))
80 {
81 COM_TRY_BEGIN
82 NCOM::CPropVariant prop;
83 if (_subArchiveMode)
84 switch (propID)
85 {
86 case kpidName: prop = _subArchiveName; break;
87 // case kpidSize: prop = _subArchiveSize; break; // we don't use it now
88 }
89 else
90 switch (propID)
91 {
92 case kpidName: prop = fs2us(_fileInfo.Name); break;
93 case kpidIsDir: prop = _fileInfo.IsDir(); break;
94 case kpidSize: prop = _fileInfo.Size; break;
95 case kpidAttrib: prop = (UInt32)_fileInfo.GetWinAttrib(); break;
96 case kpidPosixAttrib: prop = (UInt32)_fileInfo.GetPosixAttrib(); break;
97 case kpidCTime: PropVariant_SetFrom_FiTime(prop, _fileInfo.CTime); break;
98 case kpidATime: PropVariant_SetFrom_FiTime(prop, _fileInfo.ATime); break;
99 case kpidMTime: PropVariant_SetFrom_FiTime(prop, _fileInfo.MTime); break;
100 }
101 prop.Detach(value);
102 return S_OK;
103 COM_TRY_END
104 }
105
106
107 // ---------- CInFileStreamVol ----------
108
109 Z7_CLASS_IMP_COM_2(
110 CInFileStreamVol
111 , IInStream
112 , IStreamGetSize
113 )
114 Z7_IFACE_COM7_IMP(ISequentialInStream)
115 public:
116 unsigned FileIndex;
117 COpenCallbackImp *OpenCallbackImp;
118 CMyComPtr<IArchiveOpenCallback> OpenCallbackRef;
119
EnsureOpen()120 HRESULT EnsureOpen()
121 {
122 return OpenCallbackImp->Volumes.EnsureOpen(FileIndex);
123 }
124
~CInFileStreamVol()125 ~CInFileStreamVol()
126 {
127 if (OpenCallbackRef)
128 OpenCallbackImp->AtCloseFile(FileIndex);
129 }
130 };
131
132
133 void CMultiStreams::InsertToList(unsigned index)
134 {
135 {
136 CSubStream &s = Streams[index];
137 s.Next = Head;
138 s.Prev = -1;
139 }
140 if (Head != -1)
141 Streams[(unsigned)Head].Prev = (int)index;
142 else
143 {
144 // if (Tail != -1) throw 1;
145 Tail = (int)index;
146 }
147 Head = (int)index;
148 NumListItems++;
149 }
150
151 // s must bee in List
152 void CMultiStreams::RemoveFromList(CSubStream &s)
153 {
154 if (s.Next != -1) Streams[(unsigned)s.Next].Prev = s.Prev; else Tail = s.Prev;
155 if (s.Prev != -1) Streams[(unsigned)s.Prev].Next = s.Next; else Head = s.Next;
156 s.Next = -1; // optional
157 s.Prev = -1; // optional
158 NumListItems--;
159 }
160
161 void CMultiStreams::CloseFile(unsigned index)
162 {
163 CSubStream &s = Streams[index];
164 if (s.Stream)
165 {
166 s.Stream.Release();
167 RemoveFromList(s);
168 // s.InFile->Close();
169 // s.IsOpen = false;
170 #ifdef DEBUG_VOLUMES
171 static int numClosing = 0;
172 numClosing++;
173 printf("\nCloseFile %u, total_closes = %u, num_open_files = %u\n", index, numClosing, NumListItems);
174 #endif
175 }
176 }
177
178 void CMultiStreams::Init()
179 {
180 Head = -1;
181 Tail = -1;
182 NumListItems = 0;
183 Streams.Clear();
184 }
185
186 CMultiStreams::CMultiStreams():
187 Head(-1),
188 Tail(-1),
189 NumListItems(0)
190 {
191 NumOpenFiles_AllowedMax = NSystem::Get_File_OPEN_MAX_Reduced_for_3_tasks();
192 PRF(printf("\nNumOpenFiles_Limit = %u\n", NumOpenFiles_AllowedMax));
193 }
194
195
196 HRESULT CMultiStreams::PrepareToOpenNew()
197 {
198 if (NumListItems < NumOpenFiles_AllowedMax)
199 return S_OK;
200 if (Tail == -1)
201 return E_FAIL;
202 CMultiStreams::CSubStream &tailStream = Streams[(unsigned)Tail];
203 RINOK(InStream_GetPos(tailStream.Stream, tailStream.LocalPos))
204 CloseFile((unsigned)Tail);
205 return S_OK;
206 }
207
208
209 HRESULT CMultiStreams::EnsureOpen(unsigned index)
210 {
211 CMultiStreams::CSubStream &s = Streams[index];
212 if (s.Stream)
213 {
214 if ((int)index != Head)
215 {
216 RemoveFromList(s);
217 InsertToList(index);
218 }
219 }
220 else
221 {
222 RINOK(PrepareToOpenNew())
223 {
224 CInFileStream *inFile = new CInFileStream;
225 CMyComPtr<IInStream> inStreamTemp = inFile;
226 if (!inFile->Open(s.Path))
227 return GetLastError_noZero_HRESULT();
228 s.FileSpec = inFile;
229 s.Stream = s.FileSpec;
230 InsertToList(index);
231 }
232 // s.IsOpen = true;
233 if (s.LocalPos != 0)
234 {
235 RINOK(s.Stream->Seek((Int64)s.LocalPos, STREAM_SEEK_SET, &s.LocalPos))
236 }
237 #ifdef DEBUG_VOLUMES
238 static int numOpens = 0;
239 numOpens++;
240 printf("\n-- %u, ReOpen, total_reopens = %u, num_open_files = %u\n", index, numOpens, NumListItems);
241 #endif
242 }
243 return S_OK;
244 }
245
246
247 Z7_COM7F_IMF(CInFileStreamVol::Read(void *data, UInt32 size, UInt32 *processedSize))
248 {
249 if (processedSize)
250 *processedSize = 0;
251 if (size == 0)
252 return S_OK;
253 RINOK(EnsureOpen())
254 CMultiStreams::CSubStream &s = OpenCallbackImp->Volumes.Streams[FileIndex];
255 PRF(printf("\n== %u, Read =%u \n", FileIndex, size));
256 return s.Stream->Read(data, size, processedSize);
257 }
258
259 Z7_COM7F_IMF(CInFileStreamVol::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition))
260 {
261 // if (seekOrigin >= 3) return STG_E_INVALIDFUNCTION;
262 RINOK(EnsureOpen())
263 CMultiStreams::CSubStream &s = OpenCallbackImp->Volumes.Streams[FileIndex];
264 PRF(printf("\n-- %u, Seek seekOrigin=%u Seek =%u\n", FileIndex, seekOrigin, (unsigned)offset));
265 return s.Stream->Seek(offset, seekOrigin, newPosition);
266 }
267
268 Z7_COM7F_IMF(CInFileStreamVol::GetSize(UInt64 *size))
269 {
270 RINOK(EnsureOpen())
271 CMultiStreams::CSubStream &s = OpenCallbackImp->Volumes.Streams[FileIndex];
272 return s.FileSpec->GetSize(size);
273 }
274
275
276 // from ArchiveExtractCallback.cpp
277 bool IsSafePath(const UString &path);
278
279 Z7_COM7F_IMF(COpenCallbackImp::GetStream(const wchar_t *name, IInStream **inStream))
280 {
281 COM_TRY_BEGIN
282 *inStream = NULL;
283
284 if (_subArchiveMode)
285 return S_FALSE;
286 if (Callback)
287 {
288 RINOK(Callback->Open_CheckBreak())
289 }
290
291 UString name2 = name;
292
293
294 #ifndef Z7_SFX
295
296 #ifdef _WIN32
297 name2.Replace(L'/', WCHAR_PATH_SEPARATOR);
298 #endif
299
300 // if (!allowAbsVolPaths)
301 if (!IsSafePath(name2))
302 return S_FALSE;
303
304 #ifdef _WIN32
305 /* WIN32 allows wildcards in Find() function
306 and doesn't allow wildcard in File.Open()
307 so we can work without the following wildcard check here */
308 if (name2.Find(L'*') >= 0)
309 return S_FALSE;
310 {
311 unsigned startPos = 0;
312 if (name2.IsPrefixedBy_Ascii_NoCase("\\\\?\\"))
313 startPos = 3;
314 if (name2.Find(L'?', startPos) >= 0)
315 return S_FALSE;
316 }
317 #endif
318
319 #endif
320
321
322 FString fullPath;
323 if (!NFile::NName::GetFullPath(_folderPrefix, us2fs(name2), fullPath))
324 return S_FALSE;
325 if (!_fileInfo.Find_FollowLink(fullPath))
326 return S_FALSE;
327 if (_fileInfo.IsDir())
328 return S_FALSE;
329
330 CMultiStreams::CSubStream s;
331
332 {
333 CInFileStream *inFile = new CInFileStream;
334 CMyComPtr<IInStream> inStreamTemp = inFile;
335 if (!inFile->Open(fullPath))
336 return GetLastError_noZero_HRESULT();
337 RINOK(Volumes.PrepareToOpenNew())
338 s.FileSpec = inFile;
339 s.Stream = s.FileSpec;
340 s.Path = fullPath;
341 // s.Size = _fileInfo.Size;
342 // s.IsOpen = true;
343 }
344
345 const unsigned fileIndex = Volumes.Streams.Add(s);
346 Volumes.InsertToList(fileIndex);
347
348 FileSizes.Add(_fileInfo.Size);
349 FileNames.Add(name2);
350 FileNames_WasUsed.Add(true);
351
352 CInFileStreamVol *inFile = new CInFileStreamVol;
353 CMyComPtr<IInStream> inStreamTemp = inFile;
354 inFile->FileIndex = fileIndex;
355 inFile->OpenCallbackImp = this;
356 inFile->OpenCallbackRef = this;
357 // TotalSize += _fileInfo.Size;
358 *inStream = inStreamTemp.Detach();
359 return S_OK;
360 COM_TRY_END
361 }
362
363
364 #ifndef Z7_NO_CRYPTO
365 Z7_COM7F_IMF(COpenCallbackImp::CryptoGetTextPassword(BSTR *password))
366 {
367 COM_TRY_BEGIN
368 if (ReOpenCallback)
369 {
370 Z7_DECL_CMyComPtr_QI_FROM(
371 ICryptoGetTextPassword,
372 getTextPassword, ReOpenCallback)
373 if (getTextPassword)
374 return getTextPassword->CryptoGetTextPassword(password);
375 }
376 if (!Callback)
377 return E_NOTIMPL;
378 PasswordWasAsked = true;
379 return Callback->Open_CryptoGetTextPassword(password);
380 COM_TRY_END
381 }
382 #endif
383
384 // IProgress
385 Z7_COM7F_IMF(COpenCallbackImp::SetTotal(const UInt64 /* total */))
386 {
387 return S_OK;
388 }
389
390 Z7_COM7F_IMF(COpenCallbackImp::SetCompleted(const UInt64 * /* completed */))
391 {
392 return S_OK;
393 }
394