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