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 28using namespace NWindows; 29 30HRESULT 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 48Z7_COM7F_IMF(COpenCallbackImp::SetSubArchiveName(const wchar_t *name)) 49{ 50 _subArchiveMode = true; 51 _subArchiveName = name; 52 // TotalSize = 0; 53 return S_OK; 54} 55 56Z7_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 67Z7_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 79Z7_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 109Z7_CLASS_IMP_COM_2( 110 CInFileStreamVol 111 , IInStream 112 , IStreamGetSize 113) 114 Z7_IFACE_COM7_IMP(ISequentialInStream) 115public: 116 unsigned FileIndex; 117 COpenCallbackImp *OpenCallbackImp; 118 CMyComPtr<IArchiveOpenCallback> OpenCallbackRef; 119 120 HRESULT EnsureOpen() 121 { 122 return OpenCallbackImp->Volumes.EnsureOpen(FileIndex); 123 } 124 125 ~CInFileStreamVol() 126 { 127 if (OpenCallbackRef) 128 OpenCallbackImp->AtCloseFile(FileIndex); 129 } 130}; 131 132 133void 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 152void 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 161void 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 178void CMultiStreams::Init() 179{ 180 Head = -1; 181 Tail = -1; 182 NumListItems = 0; 183 Streams.Clear(); 184} 185 186CMultiStreams::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 196HRESULT 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 209HRESULT 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 247Z7_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 259Z7_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 268Z7_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 277bool IsSafePath(const UString &path); 278 279Z7_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 365Z7_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 385Z7_COM7F_IMF(COpenCallbackImp::SetTotal(const UInt64 /* total */)) 386{ 387 return S_OK; 388} 389 390Z7_COM7F_IMF(COpenCallbackImp::SetCompleted(const UInt64 * /* completed */)) 391{ 392 return S_OK; 393} 394