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
Z7_COM7F_IMF(COpenCallbackImp::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
Z7_COM7F_IMF(COpenCallbackImp::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
Z7_COM7F_IMF(COpenCallbackImp::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
Z7_COM7F_IMF(COpenCallbackImp::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 default: break;
89 }
90 else
91 switch (propID)
92 {
93 case kpidName: prop = fs2us(_fileInfo.Name); break;
94 case kpidIsDir: prop = _fileInfo.IsDir(); break;
95 case kpidSize: prop = _fileInfo.Size; break;
96 case kpidAttrib: prop = (UInt32)_fileInfo.GetWinAttrib(); break;
97 case kpidPosixAttrib: prop = (UInt32)_fileInfo.GetPosixAttrib(); break;
98 case kpidCTime: PropVariant_SetFrom_FiTime(prop, _fileInfo.CTime); break;
99 case kpidATime: PropVariant_SetFrom_FiTime(prop, _fileInfo.ATime); break;
100 case kpidMTime: PropVariant_SetFrom_FiTime(prop, _fileInfo.MTime); break;
101 default: break;
102 }
103 prop.Detach(value);
104 return S_OK;
105 COM_TRY_END
106 }
107
108
109 // ---------- CInFileStreamVol ----------
110
111 Z7_class_final(CInFileStreamVol):
112 public IInStream
113 , public IStreamGetSize
114 , public CMyUnknownImp
115 {
116 Z7_IFACES_IMP_UNK_3(
117 IInStream,
118 ISequentialInStream,
119 IStreamGetSize)
120 public:
121 unsigned FileIndex;
122 COpenCallbackImp *OpenCallbackImp;
123 CMyComPtr<IArchiveOpenCallback> OpenCallbackRef;
124
125 HRESULT EnsureOpen()
126 {
127 return OpenCallbackImp->Volumes.EnsureOpen(FileIndex);
128 }
129
130 ~CInFileStreamVol()
131 {
132 if (OpenCallbackRef)
133 OpenCallbackImp->AtCloseFile(FileIndex);
134 }
135 };
136
137
138 void CMultiStreams::InsertToList(unsigned index)
139 {
140 {
141 CSubStream &s = Streams[index];
142 s.Next = Head;
143 s.Prev = -1;
144 }
145 if (Head != -1)
146 Streams[(unsigned)Head].Prev = (int)index;
147 else
148 {
149 // if (Tail != -1) throw 1;
150 Tail = (int)index;
151 }
152 Head = (int)index;
153 NumListItems++;
154 }
155
156 // s must bee in List
157 void CMultiStreams::RemoveFromList(CSubStream &s)
158 {
159 if (s.Next != -1) Streams[(unsigned)s.Next].Prev = s.Prev; else Tail = s.Prev;
160 if (s.Prev != -1) Streams[(unsigned)s.Prev].Next = s.Next; else Head = s.Next;
161 s.Next = -1; // optional
162 s.Prev = -1; // optional
163 NumListItems--;
164 }
165
166 void CMultiStreams::CloseFile(unsigned index)
167 {
168 CSubStream &s = Streams[index];
169 if (s.Stream)
170 {
171 s.Stream.Release();
172 RemoveFromList(s);
173 // s.InFile->Close();
174 // s.IsOpen = false;
175 #ifdef DEBUG_VOLUMES
176 static int numClosing = 0;
177 numClosing++;
178 printf("\nCloseFile %u, total_closes = %u, num_open_files = %u\n", index, numClosing, NumListItems);
179 #endif
180 }
181 }
182
183 void CMultiStreams::Init()
184 {
185 Head = -1;
186 Tail = -1;
187 NumListItems = 0;
188 Streams.Clear();
189 }
190
191 CMultiStreams::CMultiStreams():
192 Head(-1),
193 Tail(-1),
194 NumListItems(0)
195 {
196 NumOpenFiles_AllowedMax = NSystem::Get_File_OPEN_MAX_Reduced_for_3_tasks();
197 PRF(printf("\nNumOpenFiles_Limit = %u\n", NumOpenFiles_AllowedMax));
198 }
199
200
201 HRESULT CMultiStreams::PrepareToOpenNew()
202 {
203 if (NumListItems < NumOpenFiles_AllowedMax)
204 return S_OK;
205 if (Tail == -1)
206 return E_FAIL;
207 CMultiStreams::CSubStream &tailStream = Streams[(unsigned)Tail];
208 RINOK(InStream_GetPos(tailStream.Stream, tailStream.LocalPos))
209 CloseFile((unsigned)Tail);
210 return S_OK;
211 }
212
213
214 HRESULT CMultiStreams::EnsureOpen(unsigned index)
215 {
216 CMultiStreams::CSubStream &s = Streams[index];
217 if (s.Stream)
218 {
219 if ((int)index != Head)
220 {
221 RemoveFromList(s);
222 InsertToList(index);
223 }
224 }
225 else
226 {
227 RINOK(PrepareToOpenNew())
228 {
229 CInFileStream *inFile = new CInFileStream;
230 CMyComPtr<IInStream> inStreamTemp = inFile;
231 if (!inFile->Open(s.Path))
232 return GetLastError_noZero_HRESULT();
233 s.FileSpec = inFile;
234 s.Stream = s.FileSpec;
235 InsertToList(index);
236 }
237 // s.IsOpen = true;
238 if (s.LocalPos != 0)
239 {
240 RINOK(s.Stream->Seek((Int64)s.LocalPos, STREAM_SEEK_SET, &s.LocalPos))
241 }
242 #ifdef DEBUG_VOLUMES
243 static int numOpens = 0;
244 numOpens++;
245 printf("\n-- %u, ReOpen, total_reopens = %u, num_open_files = %u\n", index, numOpens, NumListItems);
246 #endif
247 }
248 return S_OK;
249 }
250
251
252 Z7_COM7F_IMF(CInFileStreamVol::Read(void *data, UInt32 size, UInt32 *processedSize))
253 {
254 if (processedSize)
255 *processedSize = 0;
256 if (size == 0)
257 return S_OK;
258 RINOK(EnsureOpen())
259 CMultiStreams::CSubStream &s = OpenCallbackImp->Volumes.Streams[FileIndex];
260 PRF(printf("\n== %u, Read =%u \n", FileIndex, size));
261 return s.Stream->Read(data, size, processedSize);
262 }
263
264 Z7_COM7F_IMF(CInFileStreamVol::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition))
265 {
266 // if (seekOrigin >= 3) return STG_E_INVALIDFUNCTION;
267 RINOK(EnsureOpen())
268 CMultiStreams::CSubStream &s = OpenCallbackImp->Volumes.Streams[FileIndex];
269 PRF(printf("\n-- %u, Seek seekOrigin=%u Seek =%u\n", FileIndex, seekOrigin, (unsigned)offset));
270 return s.Stream->Seek(offset, seekOrigin, newPosition);
271 }
272
273 Z7_COM7F_IMF(CInFileStreamVol::GetSize(UInt64 *size))
274 {
275 RINOK(EnsureOpen())
276 CMultiStreams::CSubStream &s = OpenCallbackImp->Volumes.Streams[FileIndex];
277 return s.FileSpec->GetSize(size);
278 }
279
280
281 // from ArchiveExtractCallback.cpp
282 bool IsSafePath(const UString &path);
283
284 Z7_COM7F_IMF(COpenCallbackImp::GetStream(const wchar_t *name, IInStream **inStream))
285 {
286 COM_TRY_BEGIN
287 *inStream = NULL;
288
289 if (_subArchiveMode)
290 return S_FALSE;
291 if (Callback)
292 {
293 RINOK(Callback->Open_CheckBreak())
294 }
295
296 UString name2 = name;
297
298
299 #ifndef Z7_SFX
300
301 #ifdef _WIN32
302 name2.Replace(L'/', WCHAR_PATH_SEPARATOR);
303 #endif
304
305 // if (!allowAbsVolPaths)
306 if (!IsSafePath(name2))
307 return S_FALSE;
308
309 #ifdef _WIN32
310 /* WIN32 allows wildcards in Find() function
311 and doesn't allow wildcard in File.Open()
312 so we can work without the following wildcard check here */
313 if (name2.Find(L'*') >= 0)
314 return S_FALSE;
315 {
316 unsigned startPos = 0;
317 if (name2.IsPrefixedBy_Ascii_NoCase("\\\\?\\"))
318 startPos = 3;
319 if (name2.Find(L'?', startPos) >= 0)
320 return S_FALSE;
321 }
322 #endif
323
324 #endif
325
326
327 FString fullPath;
328 if (!NFile::NName::GetFullPath(_folderPrefix, us2fs(name2), fullPath))
329 return S_FALSE;
330 if (!_fileInfo.Find_FollowLink(fullPath))
331 return S_FALSE;
332 if (_fileInfo.IsDir())
333 return S_FALSE;
334
335 CMultiStreams::CSubStream s;
336
337 {
338 CInFileStream *inFile = new CInFileStream;
339 CMyComPtr<IInStream> inStreamTemp = inFile;
340 if (!inFile->Open(fullPath))
341 return GetLastError_noZero_HRESULT();
342 RINOK(Volumes.PrepareToOpenNew())
343 s.FileSpec = inFile;
344 s.Stream = s.FileSpec;
345 s.Path = fullPath;
346 // s.Size = _fileInfo.Size;
347 // s.IsOpen = true;
348 }
349
350 const unsigned fileIndex = Volumes.Streams.Add(s);
351 Volumes.InsertToList(fileIndex);
352
353 FileSizes.Add(_fileInfo.Size);
354 FileNames.Add(name2);
355 FileNames_WasUsed.Add(true);
356
357 CInFileStreamVol *inFile = new CInFileStreamVol;
358 CMyComPtr<IInStream> inStreamTemp = inFile;
359 inFile->FileIndex = fileIndex;
360 inFile->OpenCallbackImp = this;
361 inFile->OpenCallbackRef = this;
362 // TotalSize += _fileInfo.Size;
363 *inStream = inStreamTemp.Detach();
364 return S_OK;
365 COM_TRY_END
366 }
367
368
369 #ifndef Z7_NO_CRYPTO
370 Z7_COM7F_IMF(COpenCallbackImp::CryptoGetTextPassword(BSTR *password))
371 {
372 COM_TRY_BEGIN
373 if (ReOpenCallback)
374 {
375 Z7_DECL_CMyComPtr_QI_FROM(
376 ICryptoGetTextPassword,
377 getTextPassword, ReOpenCallback)
378 if (getTextPassword)
379 return getTextPassword->CryptoGetTextPassword(password);
380 }
381 if (!Callback)
382 return E_NOTIMPL;
383 PasswordWasAsked = true;
384 return Callback->Open_CryptoGetTextPassword(password);
385 COM_TRY_END
386 }
387 #endif
388
389 // IProgress
390 Z7_COM7F_IMF(COpenCallbackImp::SetTotal(const UInt64 /* total */))
391 {
392 return S_OK;
393 }
394
395 Z7_COM7F_IMF(COpenCallbackImp::SetCompleted(const UInt64 * /* completed */))
396 {
397 return S_OK;
398 }
399