"Fossies" - the Fresh Open Source Software Archive 
Member "unrar/extract.cpp" (4 May 2022, 41862 Bytes) of package /linux/misc/unrarsrc-6.1.7.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "extract.cpp" see the
Fossies "Dox" file reference documentation and the last
Fossies "Diffs" side-by-side code changes report:
6.1.4_vs_6.1.5.
1 #include "rar.hpp"
2
3 CmdExtract::CmdExtract(CommandData *Cmd)
4 {
5 CmdExtract::Cmd=Cmd;
6
7 *ArcName=0;
8
9 *DestFileName=0;
10
11 TotalFileCount=0;
12 Unp=new Unpack(&DataIO);
13 #ifdef RAR_SMP
14 Unp->SetThreads(Cmd->Threads);
15 #endif
16 }
17
18
19 CmdExtract::~CmdExtract()
20 {
21 delete Unp;
22 }
23
24
25 void CmdExtract::DoExtract()
26 {
27 #if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
28 Fat32=NotFat32=false;
29 #endif
30 PasswordCancelled=false;
31 DataIO.SetCurrentCommand(Cmd->Command[0]);
32
33 FindData FD;
34 while (Cmd->GetArcName(ArcName,ASIZE(ArcName)))
35 if (FindFile::FastFind(ArcName,&FD))
36 DataIO.TotalArcSize+=FD.Size;
37
38 Cmd->ArcNames.Rewind();
39 while (Cmd->GetArcName(ArcName,ASIZE(ArcName)))
40 {
41 if (Cmd->ManualPassword)
42 Cmd->Password.Clean(); // Clean user entered password before processing next archive.
43
44 ReconstructDone=false; // Must be reset here, not in ExtractArchiveInit().
45 UseExactVolName=false; // Must be reset here, not in ExtractArchiveInit().
46 while (true)
47 {
48 EXTRACT_ARC_CODE Code=ExtractArchive();
49 if (Code!=EXTRACT_ARC_REPEAT)
50 break;
51 }
52 DataIO.ProcessedArcSize+=DataIO.LastArcSize;
53 }
54
55 // Clean user entered password. Not really required, just for extra safety.
56 if (Cmd->ManualPassword)
57 Cmd->Password.Clean();
58
59 if (TotalFileCount==0 && Cmd->Command[0]!='I' &&
60 ErrHandler.GetErrorCode()!=RARX_BADPWD) // Not in case of wrong archive password.
61 {
62 if (!PasswordCancelled)
63 uiMsg(UIERROR_NOFILESTOEXTRACT,ArcName);
64
65 // Other error codes may explain a reason of "no files extracted" clearer,
66 // so set it only if no other errors found (wrong mask set by user).
67 if (ErrHandler.GetErrorCode()==RARX_SUCCESS)
68 ErrHandler.SetErrorCode(RARX_NOFILES);
69 }
70 else
71 if (!Cmd->DisableDone)
72 if (Cmd->Command[0]=='I')
73 mprintf(St(MDone));
74 else
75 if (ErrHandler.GetErrorCount()==0)
76 mprintf(St(MExtrAllOk));
77 else
78 mprintf(St(MExtrTotalErr),ErrHandler.GetErrorCount());
79 }
80
81
82 void CmdExtract::ExtractArchiveInit(Archive &Arc)
83 {
84 DataIO.AdjustTotalArcSize(&Arc);
85
86 FileCount=0;
87 MatchedArgs=0;
88 #ifndef SFX_MODULE
89 FirstFile=true;
90 #endif
91
92 GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet();
93
94 DataIO.UnpVolume=false;
95
96 PrevProcessed=false;
97 AllMatchesExact=true;
98 AnySolidDataUnpackedWell=false;
99
100 StartTime.SetCurrentTime();
101 }
102
103
104 EXTRACT_ARC_CODE CmdExtract::ExtractArchive()
105 {
106 Archive Arc(Cmd);
107 if (*Cmd->UseStdin!=0)
108 {
109 Arc.SetHandleType(FILE_HANDLESTD);
110 #ifdef USE_QOPEN
111 Arc.SetProhibitQOpen(true);
112 #endif
113 }
114 else
115 {
116 #if defined(_WIN_ALL) && !defined(SFX_MODULE) // WinRAR GUI code also resets the cache.
117 if (*Cmd->Command=='T' || Cmd->Test)
118 ResetFileCache(ArcName); // Reset the file cache when testing an archive.
119 #endif
120 if (!Arc.WOpen(ArcName))
121 return EXTRACT_ARC_NEXT;
122 }
123
124 if (!Arc.IsArchive(true))
125 {
126 #if !defined(SFX_MODULE) && !defined(RARDLL)
127 if (CmpExt(ArcName,L"rev"))
128 {
129 wchar FirstVolName[NM];
130 VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),true);
131
132 // If several volume names from same volume set are specified
133 // and current volume is not first in set and first volume is present
134 // and specified too, let's skip the current volume.
135 if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) &&
136 Cmd->ArcNames.Search(FirstVolName,false))
137 return EXTRACT_ARC_NEXT;
138 RecVolumesTest(Cmd,NULL,ArcName);
139 TotalFileCount++; // Suppress "No files to extract" message.
140 return EXTRACT_ARC_NEXT;
141 }
142 #endif
143
144 mprintf(St(MNotRAR),ArcName);
145
146 #ifndef SFX_MODULE
147 if (CmpExt(ArcName,L"rar"))
148 #endif
149 ErrHandler.SetErrorCode(RARX_WARNING);
150 return EXTRACT_ARC_NEXT;
151 }
152
153 if (Arc.FailedHeaderDecryption) // Bad archive password.
154 return EXTRACT_ARC_NEXT;
155
156 #ifndef SFX_MODULE
157 if (Arc.Volume && !Arc.FirstVolume && !UseExactVolName)
158 {
159 wchar FirstVolName[NM];
160 VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),Arc.NewNumbering);
161
162 // If several volume names from same volume set are specified
163 // and current volume is not first in set and first volume is present
164 // and specified too, let's skip the current volume.
165 if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) &&
166 Cmd->ArcNames.Search(FirstVolName,false))
167 return EXTRACT_ARC_NEXT;
168 }
169 #endif
170
171 int64 VolumeSetSize=0; // Total size of volumes after the current volume.
172
173 if (Arc.Volume)
174 {
175 #ifndef SFX_MODULE
176 // Try to speed up extraction for independent solid volumes by starting
177 // extraction from non-first volume if we can.
178 if (!UseExactVolName && Arc.Solid && DetectStartVolume(Arc.FileName,Arc.NewNumbering))
179 {
180 UseExactVolName=true;
181 return EXTRACT_ARC_REPEAT;
182 }
183 #endif
184
185 // Calculate the total size of all accessible volumes.
186 // This size is necessary to display the correct total progress indicator.
187
188 wchar NextName[NM];
189 wcsncpyz(NextName,Arc.FileName,ASIZE(NextName));
190
191 while (true)
192 {
193 // First volume is already added to DataIO.TotalArcSize
194 // in initial TotalArcSize calculation in DoExtract.
195 // So we skip it and start from second volume.
196 NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering);
197 FindData FD;
198 if (FindFile::FastFind(NextName,&FD))
199 VolumeSetSize+=FD.Size;
200 else
201 break;
202 }
203 DataIO.TotalArcSize+=VolumeSetSize;
204 }
205
206 ExtractArchiveInit(Arc);
207
208 if (*Cmd->Command=='T' || *Cmd->Command=='I')
209 Cmd->Test=true;
210
211
212 if (*Cmd->Command=='I')
213 {
214 Cmd->DisablePercentage=true;
215 }
216 else
217 uiStartArchiveExtract(!Cmd->Test,ArcName);
218
219 Arc.ViewComment();
220
221
222 while (1)
223 {
224 size_t Size=Arc.ReadHeader();
225
226
227 bool Repeat=false;
228 if (!ExtractCurrentFile(Arc,Size,Repeat))
229 if (Repeat)
230 {
231 // If we started extraction from not first volume and need to
232 // restart it from first, we must set DataIO.TotalArcSize to size
233 // of new first volume to display the total progress correctly.
234 FindData NewArc;
235 if (FindFile::FastFind(ArcName,&NewArc))
236 DataIO.TotalArcSize=NewArc.Size;
237 return EXTRACT_ARC_REPEAT;
238 }
239 else
240 break;
241 }
242
243
244 #if !defined(SFX_MODULE) && !defined(RARDLL)
245 if (Cmd->Test && Arc.Volume)
246 RecVolumesTest(Cmd,&Arc,ArcName);
247 #endif
248
249 return EXTRACT_ARC_NEXT;
250 }
251
252
253 bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
254 {
255 wchar Command=Cmd->Command[0];
256 if (HeaderSize==0)
257 if (DataIO.UnpVolume)
258 {
259 #ifdef NOVOLUME
260 return false;
261 #else
262 // Supposing we unpack an old RAR volume without the end of archive
263 // record and last file is not split between volumes.
264 if (!MergeArchive(Arc,&DataIO,false,Command))
265 {
266 ErrHandler.SetErrorCode(RARX_WARNING);
267 return false;
268 }
269 #endif
270 }
271 else
272 return false;
273
274 HEADER_TYPE HeaderType=Arc.GetHeaderType();
275 if (HeaderType!=HEAD_FILE)
276 {
277 #ifndef SFX_MODULE
278 if (Arc.Format==RARFMT15 && HeaderType==HEAD3_OLDSERVICE && PrevProcessed)
279 SetExtraInfo20(Cmd,Arc,DestFileName);
280 #endif
281 if (HeaderType==HEAD_SERVICE && PrevProcessed)
282 SetExtraInfo(Cmd,Arc,DestFileName);
283 if (HeaderType==HEAD_ENDARC)
284 if (Arc.EndArcHead.NextVolume)
285 {
286 #ifdef NOVOLUME
287 return false;
288 #else
289 if (!MergeArchive(Arc,&DataIO,false,Command))
290 {
291 ErrHandler.SetErrorCode(RARX_WARNING);
292 return false;
293 }
294 Arc.Seek(Arc.CurBlockPos,SEEK_SET);
295 return true;
296 #endif
297 }
298 else
299 return false;
300 Arc.SeekToNext();
301 return true;
302 }
303 PrevProcessed=false;
304
305 // We can get negative sizes in corrupt archive and it is unacceptable
306 // for size comparisons in ComprDataIO::UnpRead, where we cast sizes
307 // to size_t and can exceed another read or available size. We could fix it
308 // when reading an archive. But we prefer to do it here, because this
309 // function is called directly in unrar.dll, so we fix bad parameters
310 // passed to dll. Also we want to see real negative sizes in the listing
311 // of corrupt archive. To prevent uninitialized data access perform
312 // these checks after rejecting zero length and non-file headers above.
313 if (Arc.FileHead.PackSize<0)
314 Arc.FileHead.PackSize=0;
315 if (Arc.FileHead.UnpSize<0)
316 Arc.FileHead.UnpSize=0;
317
318 if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact)
319 return false;
320
321 int MatchType=MATCH_WILDSUBPATH;
322
323 bool EqualNames=false;
324 wchar MatchedArg[NM];
325 int MatchNumber=Cmd->IsProcessFile(Arc.FileHead,&EqualNames,MatchType,0,MatchedArg,ASIZE(MatchedArg));
326 bool MatchFound=MatchNumber!=0;
327 #ifndef SFX_MODULE
328 if (Cmd->ExclPath==EXCL_BASEPATH)
329 {
330 wcsncpyz(Cmd->ArcPath,MatchedArg,ASIZE(Cmd->ArcPath));
331 *PointToName(Cmd->ArcPath)=0;
332 if (IsWildcard(Cmd->ArcPath)) // Cannot correctly process path*\* masks here.
333 *Cmd->ArcPath=0;
334 }
335 #endif
336 if (MatchFound && !EqualNames)
337 AllMatchesExact=false;
338
339 Arc.ConvertAttributes();
340
341 #if !defined(SFX_MODULE) && !defined(RARDLL)
342 if (Arc.FileHead.SplitBefore && FirstFile && !UseExactVolName)
343 {
344 wchar CurVolName[NM];
345 wcsncpyz(CurVolName,ArcName,ASIZE(CurVolName));
346 GetFirstVolIfFullSet(ArcName,Arc.NewNumbering,ArcName,ASIZE(ArcName));
347
348 if (wcsicomp(ArcName,CurVolName)!=0 && FileExist(ArcName))
349 {
350 wcsncpyz(Cmd->ArcName,ArcName,ASIZE(ArcName)); // For GUI "Delete archive after extraction".
351 // If first volume name does not match the current name and if such
352 // volume name really exists, let's unpack from this first volume.
353 Repeat=true;
354 return false;
355 }
356 #ifndef RARDLL
357 if (!ReconstructDone)
358 {
359 ReconstructDone=true;
360 if (RecVolumesRestore(Cmd,Arc.FileName,true))
361 {
362 Repeat=true;
363 return false;
364 }
365 }
366 #endif
367 wcsncpyz(ArcName,CurVolName,ASIZE(ArcName));
368 }
369 #endif
370
371 wchar ArcFileName[NM];
372 ConvertPath(Arc.FileHead.FileName,ArcFileName,ASIZE(ArcFileName));
373
374 if (Arc.FileHead.Version)
375 {
376 if (Cmd->VersionControl!=1 && !EqualNames)
377 {
378 if (Cmd->VersionControl==0)
379 MatchFound=false;
380 int Version=ParseVersionFileName(ArcFileName,false);
381 if (Cmd->VersionControl-1==Version)
382 ParseVersionFileName(ArcFileName,true);
383 else
384 MatchFound=false;
385 }
386 }
387 else
388 if (!Arc.IsArcDir() && Cmd->VersionControl>1)
389 MatchFound=false;
390
391 DataIO.UnpVolume=Arc.FileHead.SplitAfter;
392 DataIO.NextVolumeMissing=false;
393
394 Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET);
395
396 bool ExtrFile=false;
397 bool SkipSolid=false;
398
399 #ifndef SFX_MODULE
400 if (FirstFile && (MatchFound || Arc.Solid) && Arc.FileHead.SplitBefore)
401 {
402 if (MatchFound)
403 {
404 uiMsg(UIERROR_NEEDPREVVOL,Arc.FileName,ArcFileName);
405 #ifdef RARDLL
406 Cmd->DllError=ERAR_BAD_DATA;
407 #endif
408 ErrHandler.SetErrorCode(RARX_OPEN);
409 }
410 MatchFound=false;
411 }
412
413 FirstFile=false;
414 #endif
415
416 if (Arc.FileHead.Encrypted && Cmd->SkipEncrypted)
417 if (Arc.Solid)
418 return false; // Abort the entire extraction for solid archive.
419 else
420 MatchFound=false; // Skip only the current file for non-solid archive.
421
422 if (MatchFound || (SkipSolid=Arc.Solid)!=0)
423 {
424 // First common call of uiStartFileExtract. It is done before overwrite
425 // prompts, so if SkipSolid state is changed below, we'll need to make
426 // additional uiStartFileExtract calls with updated parameters.
427 if (!uiStartFileExtract(ArcFileName,!Cmd->Test,Cmd->Test && Command!='I',SkipSolid))
428 return false;
429
430 ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName));
431
432 // DestFileName can be set empty in case of excessive -ap switch.
433 ExtrFile=!SkipSolid && *DestFileName!=0 && !Arc.FileHead.SplitBefore;
434
435 if ((Cmd->FreshFiles || Cmd->UpdateFiles) && (Command=='E' || Command=='X'))
436 {
437 FindData FD;
438 if (FindFile::FastFind(DestFileName,&FD))
439 {
440 if (FD.mtime >= Arc.FileHead.mtime)
441 {
442 // If directory already exists and its modification time is newer
443 // than start of extraction, it is likely it was created
444 // when creating a path to one of already extracted items.
445 // In such case we'll better update its time even if archived
446 // directory is older.
447
448 if (!FD.IsDir || FD.mtime<StartTime)
449 ExtrFile=false;
450 }
451 }
452 else
453 if (Cmd->FreshFiles)
454 ExtrFile=false;
455 }
456
457 if (!CheckUnpVer(Arc,ArcFileName))
458 {
459 ErrHandler.SetErrorCode(RARX_FATAL);
460 #ifdef RARDLL
461 Cmd->DllError=ERAR_UNKNOWN_FORMAT;
462 #endif
463 Arc.SeekToNext();
464 return !Arc.Solid; // Can try extracting next file only in non-solid archive.
465 }
466
467 while (true) // Repeat the password prompt for wrong and empty passwords.
468 {
469 if (Arc.FileHead.Encrypted)
470 {
471 // Stop archive extracting if user cancelled a password prompt.
472 #ifdef RARDLL
473 if (!ExtrDllGetPassword())
474 {
475 Cmd->DllError=ERAR_MISSING_PASSWORD;
476 return false;
477 }
478 #else
479 if (!ExtrGetPassword(Arc,ArcFileName))
480 {
481 PasswordCancelled=true;
482 return false;
483 }
484 #endif
485 }
486
487 // Set a password before creating the file, so we can skip creating
488 // in case of wrong password.
489 SecPassword FilePassword=Cmd->Password;
490 #if defined(_WIN_ALL) && !defined(SFX_MODULE)
491 ConvertDosPassword(Arc,FilePassword);
492 #endif
493
494 byte PswCheck[SIZE_PSWCHECK];
495 DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword,
496 Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL,
497 Arc.FileHead.InitV,Arc.FileHead.Lg2Count,
498 Arc.FileHead.HashKey,PswCheck);
499
500 // If header is damaged, we cannot rely on password check value,
501 // because it can be damaged too.
502 if (Arc.FileHead.Encrypted && Arc.FileHead.UsePswCheck &&
503 memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0 &&
504 !Arc.BrokenHeader)
505 {
506 if (GlobalPassword) // For -p<pwd> or Ctrl+P to avoid the infinite loop.
507 {
508 // This message is used by Android GUI to reset cached passwords.
509 // Update appropriate code if changed.
510 uiMsg(UIERROR_BADPSW,Arc.FileName,ArcFileName);
511 }
512 else // For passwords entered manually.
513 {
514 // This message is used by Android GUI and Windows GUI and SFX to
515 // reset cached passwords. Update appropriate code if changed.
516 uiMsg(UIWAIT_BADPSW,Arc.FileName,ArcFileName);
517 Cmd->Password.Clean();
518
519 // Avoid new requests for unrar.dll to prevent the infinite loop
520 // if app always returns the same password.
521 #ifndef RARDLL
522 continue; // Request a password again.
523 #endif
524 }
525 #ifdef RARDLL
526 // If we already have ERAR_EOPEN as result of missing volume,
527 // we should not replace it with less precise ERAR_BAD_PASSWORD.
528 if (Cmd->DllError!=ERAR_EOPEN)
529 Cmd->DllError=ERAR_BAD_PASSWORD;
530 #endif
531 ErrHandler.SetErrorCode(RARX_BADPWD);
532 ExtrFile=false;
533 }
534 break;
535 }
536
537 #ifdef RARDLL
538 if (*Cmd->DllDestName!=0)
539 wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName));
540 #endif
541
542 File CurFile;
543
544 bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE;
545 if (LinkEntry && Arc.FileHead.RedirType!=FSREDIR_FILECOPY)
546 {
547 if (ExtrFile && Command!='P' && !Cmd->Test)
548 {
549 // Overwrite prompt for symbolic and hard links.
550 bool UserReject=false;
551 if (FileExist(DestFileName) && !UserReject)
552 FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime);
553 if (UserReject)
554 ExtrFile=false;
555 }
556 }
557 else
558 if (Arc.IsArcDir())
559 {
560 if (!ExtrFile || Command=='P' || Command=='I' || Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH)
561 return true;
562 TotalFileCount++;
563 ExtrCreateDir(Arc,ArcFileName);
564 // It is important to not increment MatchedArgs here, so we extract
565 // dir with its entire contents and not dir record only even if
566 // dir record precedes files.
567 return true;
568 }
569 else
570 if (ExtrFile) // Create files and file copies (FSREDIR_FILECOPY).
571 ExtrFile=ExtrCreateFile(Arc,CurFile);
572
573 if (!ExtrFile && Arc.Solid)
574 {
575 SkipSolid=true;
576 ExtrFile=true;
577
578 // We changed SkipSolid, so we need to call uiStartFileExtract
579 // with "Skip" parameter to change the operation status
580 // from "extracting" to "skipping". For example, it can be necessary
581 // if user answered "No" to overwrite prompt when unpacking
582 // a solid archive.
583 if (!uiStartFileExtract(ArcFileName,false,false,true))
584 return false;
585 }
586 if (ExtrFile)
587 {
588 // Set it in test mode, so we also test subheaders such as NTFS streams
589 // after tested file.
590 if (Cmd->Test)
591 PrevProcessed=true;
592
593 bool TestMode=Cmd->Test || SkipSolid; // Unpack to memory, not to disk.
594
595 if (!SkipSolid)
596 {
597 if (!TestMode && Command!='P' && CurFile.IsDevice())
598 {
599 uiMsg(UIERROR_INVALIDNAME,Arc.FileName,DestFileName);
600 ErrHandler.WriteError(Arc.FileName,DestFileName);
601 }
602 TotalFileCount++;
603 }
604 FileCount++;
605 if (Command!='I' && !Cmd->DisableNames)
606 if (SkipSolid)
607 mprintf(St(MExtrSkipFile),ArcFileName);
608 else
609 switch(Cmd->Test ? 'T':Command) // "Test" can be also enabled by -t switch.
610 {
611 case 'T':
612 mprintf(St(MExtrTestFile),ArcFileName);
613 break;
614 #ifndef SFX_MODULE
615 case 'P':
616 mprintf(St(MExtrPrinting),ArcFileName);
617 break;
618 #endif
619 case 'X':
620 case 'E':
621 mprintf(St(MExtrFile),DestFileName);
622 break;
623 }
624 if (!Cmd->DisablePercentage && !Cmd->DisableNames)
625 mprintf(L" ");
626 if (Cmd->DisableNames)
627 uiEolAfterMsg(); // Avoid erasing preceding messages by percentage indicator in -idn mode.
628
629 DataIO.CurUnpRead=0;
630 DataIO.CurUnpWrite=0;
631 DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads);
632 DataIO.PackedDataHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads);
633 DataIO.SetPackedSizeToRead(Arc.FileHead.PackSize);
634 DataIO.SetFiles(&Arc,&CurFile);
635 DataIO.SetTestMode(TestMode);
636 DataIO.SetSkipUnpCRC(SkipSolid);
637
638 #if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
639 if (!TestMode && !Arc.BrokenHeader &&
640 Arc.FileHead.UnpSize>0xffffffff && (Fat32 || !NotFat32))
641 {
642 if (!Fat32) // Not detected yet.
643 NotFat32=!(Fat32=IsFAT(Cmd->ExtrPath));
644 if (Fat32)
645 uiMsg(UIMSG_FAT32SIZE); // Inform user about FAT32 size limit.
646 }
647 #endif
648
649 uint64 Preallocated=0;
650 if (!TestMode && !Arc.BrokenHeader && Arc.FileHead.UnpSize>1000000 &&
651 Arc.FileHead.PackSize*1024>Arc.FileHead.UnpSize && Arc.IsSeekable() &&
652 (Arc.FileHead.UnpSize<100000000 || Arc.FileLength()>Arc.FileHead.PackSize))
653 {
654 CurFile.Prealloc(Arc.FileHead.UnpSize);
655 Preallocated=Arc.FileHead.UnpSize;
656 }
657 CurFile.SetAllowDelete(!Cmd->KeepBroken);
658
659 bool FileCreateMode=!TestMode && !SkipSolid && Command!='P';
660 bool ShowChecksum=true; // Display checksum verification result.
661
662 bool LinkSuccess=true; // Assume success for test mode.
663 if (LinkEntry)
664 {
665 FILE_SYSTEM_REDIRECT Type=Arc.FileHead.RedirType;
666
667 if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY)
668 {
669 wchar RedirName[NM];
670 ConvertPath(Arc.FileHead.RedirName,RedirName,ASIZE(RedirName));
671
672 wchar NameExisting[NM];
673 ExtrPrepareName(Arc,RedirName,NameExisting,ASIZE(NameExisting));
674 if (FileCreateMode && *NameExisting!=0) // *NameExisting can be 0 in case of excessive -ap switch.
675 if (Type==FSREDIR_HARDLINK)
676 LinkSuccess=ExtractHardlink(Cmd,DestFileName,NameExisting,ASIZE(NameExisting));
677 else
678 LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,DestFileName,NameExisting,ASIZE(NameExisting));
679 }
680 else
681 if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION)
682 {
683 if (FileCreateMode)
684 LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName);
685 }
686 else
687 {
688 uiMsg(UIERROR_UNKNOWNEXTRA,Arc.FileName,DestFileName);
689 LinkSuccess=false;
690 }
691
692 if (!LinkSuccess || Arc.Format==RARFMT15 && !FileCreateMode)
693 {
694 // RAR 5.x links have a valid data checksum even in case of
695 // failure, because they do not store any data.
696 // We do not want to display "OK" in this case.
697 // For 4.x symlinks we verify the checksum only when extracting,
698 // but not when testing an archive.
699 ShowChecksum=false;
700 }
701 PrevProcessed=FileCreateMode && LinkSuccess;
702 }
703 else
704 if (!Arc.FileHead.SplitBefore)
705 if (Arc.FileHead.Method==0)
706 UnstoreFile(DataIO,Arc.FileHead.UnpSize);
707 else
708 {
709 Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid);
710 Unp->SetDestSize(Arc.FileHead.UnpSize);
711 #ifndef SFX_MODULE
712 if (Arc.Format!=RARFMT50 && Arc.FileHead.UnpVer<=15)
713 Unp->DoUnpack(15,FileCount>1 && Arc.Solid);
714 else
715 #endif
716 Unp->DoUnpack(Arc.FileHead.UnpVer,Arc.FileHead.Solid);
717 }
718
719 Arc.SeekToNext();
720
721 // We check for "split after" flag to detect partially extracted files
722 // from incomplete volume sets. For them file header contains packed
723 // data hash, which must not be compared against unpacked data hash
724 // to prevent accidental match. Moreover, for -m0 volumes packed data
725 // hash would match truncated unpacked data hash and lead to fake "OK"
726 // in incomplete volume set.
727 bool ValidCRC=!Arc.FileHead.SplitAfter && DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL);
728
729 // We set AnySolidDataUnpackedWell to true if we found at least one
730 // valid non-zero solid file in preceding solid stream. If it is true
731 // and if current encrypted file is broken, we do not need to hint
732 // about a wrong password and can report CRC error only.
733 if (!Arc.FileHead.Solid)
734 AnySolidDataUnpackedWell=false; // Reset the flag, because non-solid file is found.
735 else
736 if (Arc.FileHead.Method!=0 && Arc.FileHead.UnpSize>0 && ValidCRC)
737 AnySolidDataUnpackedWell=true;
738
739 bool BrokenFile=false;
740
741 // Checksum is not calculated in skip solid mode for performance reason.
742 if (!SkipSolid && ShowChecksum)
743 {
744 if (ValidCRC)
745 {
746 if (Command!='P' && Command!='I' && !Cmd->DisableNames)
747 mprintf(L"%s%s ",Cmd->DisablePercentage ? L" ":L"\b\b\b\b\b ",
748 Arc.FileHead.FileHash.Type==HASH_NONE ? L" ?":St(MOk));
749 }
750 else
751 {
752 if (Arc.FileHead.Encrypted && (!Arc.FileHead.UsePswCheck ||
753 Arc.BrokenHeader) && !AnySolidDataUnpackedWell)
754 uiMsg(UIERROR_CHECKSUMENC,Arc.FileName,ArcFileName);
755 else
756 uiMsg(UIERROR_CHECKSUM,Arc.FileName,ArcFileName);
757 BrokenFile=true;
758 ErrHandler.SetErrorCode(RARX_CRC);
759 #ifdef RARDLL
760 // If we already have ERAR_EOPEN as result of missing volume
761 // or ERAR_BAD_PASSWORD for RAR5 wrong password,
762 // we should not replace it with less precise ERAR_BAD_DATA.
763 if (Cmd->DllError!=ERAR_EOPEN && Cmd->DllError!=ERAR_BAD_PASSWORD)
764 Cmd->DllError=ERAR_BAD_DATA;
765 #endif
766 }
767 }
768 else
769 {
770 // We check SkipSolid to remove percent for skipped solid files only.
771 // We must not apply these \b to links with ShowChecksum==false
772 // and their possible error messages.
773 if (SkipSolid)
774 mprintf(L"\b\b\b\b\b ");
775 }
776
777 // If we successfully unpacked a hard link, we wish to set its file
778 // attributes. Hard link shares file metadata with link target,
779 // so we do not need to set link time or owner. But when we overwrite
780 // an existing link, we can call PrepareToDelete(), which affects
781 // link target attributes as well. So we set link attributes to restore
782 // both target and link attributes if PrepareToDelete() changed them.
783 bool SetAttrOnly=LinkEntry && Arc.FileHead.RedirType==FSREDIR_HARDLINK && LinkSuccess;
784
785 if (!TestMode && (Command=='X' || Command=='E') &&
786 (!LinkEntry || SetAttrOnly || Arc.FileHead.RedirType==FSREDIR_FILECOPY && LinkSuccess) &&
787 (!BrokenFile || Cmd->KeepBroken))
788 {
789 // Below we use DestFileName instead of CurFile.FileName,
790 // so we can set file attributes also for hard links, which do not
791 // have the open CurFile. These strings are the same for other items.
792
793 if (!SetAttrOnly)
794 {
795 // We could preallocate more space that really written to broken file
796 // or file with crafted header.
797 if (Preallocated>0 && (BrokenFile || DataIO.CurUnpWrite!=Preallocated))
798 CurFile.Truncate();
799
800
801 CurFile.SetOpenFileTime(
802 Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
803 Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime,
804 Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
805 CurFile.Close();
806
807 SetFileHeaderExtra(Cmd,Arc,DestFileName);
808
809 CurFile.SetCloseFileTime(
810 Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
811 Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
812 }
813
814 #if defined(_WIN_ALL) && !defined(SFX_MODULE)
815 if (Cmd->SetCompressedAttr &&
816 (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0)
817 SetFileCompression(DestFileName,true);
818 if (Cmd->ClearArc)
819 Arc.FileHead.FileAttr&=~FILE_ATTRIBUTE_ARCHIVE;
820 #endif
821 if (!Cmd->IgnoreGeneralAttr && !SetFileAttr(DestFileName,Arc.FileHead.FileAttr))
822 {
823 uiMsg(UIERROR_FILEATTR,Arc.FileName,DestFileName);
824 // Android cannot set file attributes and while UIERROR_FILEATTR
825 // above is handled by Android RAR silently, this call would cause
826 // "Operation not permitted" message for every unpacked file.
827 ErrHandler.SysErrMsg();
828 }
829
830 PrevProcessed=true;
831 }
832 }
833 }
834 // It is important to increment it for files, but not dirs. So we extract
835 // dir with its entire contents, not just dir record only even if dir
836 // record precedes files.
837 if (MatchFound)
838 MatchedArgs++;
839 if (DataIO.NextVolumeMissing)
840 return false;
841 if (!ExtrFile)
842 if (!Arc.Solid)
843 Arc.SeekToNext();
844 else
845 if (!SkipSolid)
846 return false;
847 return true;
848 }
849
850
851 void CmdExtract::UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize)
852 {
853 Array<byte> Buffer(File::CopyBufferSize());
854 while (true)
855 {
856 int ReadSize=DataIO.UnpRead(&Buffer[0],Buffer.Size());
857 if (ReadSize<=0)
858 break;
859 int WriteSize=ReadSize<DestUnpSize ? ReadSize:(int)DestUnpSize;
860 if (WriteSize>0)
861 {
862 DataIO.UnpWrite(&Buffer[0],WriteSize);
863 DestUnpSize-=WriteSize;
864 }
865 }
866 }
867
868
869 bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize)
870 {
871 SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives.
872
873 File Existing;
874 if (!Existing.WOpen(NameExisting))
875 {
876 uiMsg(UIERROR_FILECOPY,ArcName,NameExisting,NameNew);
877 uiMsg(UIERROR_FILECOPYHINT,ArcName);
878 #ifdef RARDLL
879 Cmd->DllError=ERAR_EREFERENCE;
880 #endif
881 return false;
882 }
883
884 Array<char> Buffer(0x100000);
885 int64 CopySize=0;
886
887 while (true)
888 {
889 Wait();
890 int ReadSize=Existing.Read(&Buffer[0],Buffer.Size());
891 if (ReadSize==0)
892 break;
893 New.Write(&Buffer[0],ReadSize);
894 CopySize+=ReadSize;
895 }
896
897 return true;
898 }
899
900
901 void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize)
902 {
903 wcsncpyz(DestName,Cmd->ExtrPath,DestSize);
904
905 if (*Cmd->ExtrPath!=0)
906 {
907 wchar LastChar=*PointToLastChar(Cmd->ExtrPath);
908 // We need IsPathDiv check here to correctly handle Unix forward slash
909 // in the end of destination path in Windows: rar x arc dest/
910 // so we call IsPathDiv first instead of just calling AddEndSlash,
911 // which checks for only one type of path separator.
912 // IsDriveDiv is needed for current drive dir: rar x arc d:
913 if (!IsPathDiv(LastChar) && !IsDriveDiv(LastChar))
914 {
915 // Destination path can be without trailing slash if it come from GUI shell.
916 AddEndSlash(DestName,DestSize);
917 }
918 }
919
920 #ifndef SFX_MODULE
921 if (Cmd->AppendArcNameToPath!=APPENDARCNAME_NONE)
922 {
923 switch(Cmd->AppendArcNameToPath)
924 {
925 case APPENDARCNAME_DESTPATH: // To subdir of destination path.
926 wcsncatz(DestName,PointToName(Arc.FirstVolumeName),DestSize);
927 SetExt(DestName,NULL,DestSize);
928 break;
929 case APPENDARCNAME_OWNSUBDIR: // To subdir of archive own dir.
930 wcsncpyz(DestName,Arc.FirstVolumeName,DestSize);
931 SetExt(DestName,NULL,DestSize);
932 break;
933 case APPENDARCNAME_OWNDIR: // To archive own dir.
934 wcsncpyz(DestName,Arc.FirstVolumeName,DestSize);
935 RemoveNameFromPath(DestName);
936 break;
937 }
938 AddEndSlash(DestName,DestSize);
939 }
940 #endif
941
942 #ifndef SFX_MODULE
943 wchar *ArcPath=*Cmd->ExclArcPath!=0 ? Cmd->ExclArcPath:Cmd->ArcPath;
944 size_t ArcPathLength=wcslen(ArcPath);
945 if (ArcPathLength>0)
946 {
947 size_t NameLength=wcslen(ArcFileName);
948 if (NameLength>=ArcPathLength && wcsnicompc(ArcPath,ArcFileName,ArcPathLength)==0 &&
949 (IsPathDiv(ArcPath[ArcPathLength-1]) ||
950 IsPathDiv(ArcFileName[ArcPathLength]) || ArcFileName[ArcPathLength]==0))
951 {
952 ArcFileName+=Min(ArcPathLength,NameLength);
953 while (IsPathDiv(*ArcFileName))
954 ArcFileName++;
955 if (*ArcFileName==0) // Excessive -ap switch.
956 {
957 *DestName=0;
958 return;
959 }
960 }
961 }
962 #endif
963
964 wchar Command=Cmd->Command[0];
965 // Use -ep3 only in systems, where disk letters are exist, not in Unix.
966 bool AbsPaths=Cmd->ExclPath==EXCL_ABSPATH && Command=='X' && IsDriveDiv(':');
967
968 // We do not use any user specified destination paths when extracting
969 // absolute paths in -ep3 mode.
970 if (AbsPaths)
971 *DestName=0;
972
973 if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH)
974 wcsncatz(DestName,PointToName(ArcFileName),DestSize);
975 else
976 wcsncatz(DestName,ArcFileName,DestSize);
977
978 #ifdef _WIN_ALL
979 // Must do after Cmd->ArcPath processing above, so file name and arc path
980 // trailing spaces are in sync.
981 if (!Cmd->AllowIncompatNames)
982 MakeNameCompatible(DestName,DestSize);
983 #endif
984
985 wchar DiskLetter=toupperw(DestName[0]);
986
987 if (AbsPaths)
988 {
989 if (DestName[1]=='_' && IsPathDiv(DestName[2]) &&
990 DiskLetter>='A' && DiskLetter<='Z')
991 DestName[1]=':';
992 else
993 if (DestName[0]=='_' && DestName[1]=='_')
994 {
995 // Convert __server\share to \\server\share.
996 DestName[0]=CPATHDIVIDER;
997 DestName[1]=CPATHDIVIDER;
998 }
999 }
1000 }
1001
1002
1003 #ifdef RARDLL
1004 bool CmdExtract::ExtrDllGetPassword()
1005 {
1006 if (!Cmd->Password.IsSet())
1007 {
1008 if (Cmd->Callback!=NULL)
1009 {
1010 wchar PasswordW[MAXPASSWORD];
1011 *PasswordW=0;
1012 if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1)
1013 *PasswordW=0;
1014 if (*PasswordW==0)
1015 {
1016 char PasswordA[MAXPASSWORD];
1017 *PasswordA=0;
1018 if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1)
1019 *PasswordA=0;
1020 GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW));
1021 cleandata(PasswordA,sizeof(PasswordA));
1022 }
1023 Cmd->Password.Set(PasswordW);
1024 cleandata(PasswordW,sizeof(PasswordW));
1025 Cmd->ManualPassword=true;
1026 }
1027 if (!Cmd->Password.IsSet())
1028 return false;
1029 }
1030 return true;
1031 }
1032 #endif
1033
1034
1035 #ifndef RARDLL
1036 bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName)
1037 {
1038 if (!Cmd->Password.IsSet())
1039 {
1040 if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)/* || !Cmd->Password.IsSet()*/)
1041 {
1042 // Suppress "test is ok" message if user cancelled the password prompt.
1043 uiMsg(UIERROR_INCERRCOUNT);
1044 return false;
1045 }
1046 Cmd->ManualPassword=true;
1047 }
1048 #if !defined(SILENT)
1049 else
1050 if (!GlobalPassword && !Arc.FileHead.Solid)
1051 {
1052 eprintf(St(MUseCurPsw),ArcFileName);
1053 switch(Cmd->AllYes ? 1 : Ask(St(MYesNoAll)))
1054 {
1055 case -1:
1056 ErrHandler.Exit(RARX_USERBREAK);
1057 case 2:
1058 if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password))
1059 return false;
1060 break;
1061 case 3:
1062 GlobalPassword=true;
1063 break;
1064 }
1065 }
1066 #endif
1067 return true;
1068 }
1069 #endif
1070
1071
1072 #if defined(_WIN_ALL) && !defined(SFX_MODULE)
1073 void CmdExtract::ConvertDosPassword(Archive &Arc,SecPassword &DestPwd)
1074 {
1075 if (Arc.Format==RARFMT15 && Arc.FileHead.HostOS==HOST_MSDOS)
1076 {
1077 // We need the password in OEM encoding if file was encrypted by
1078 // native RAR/DOS (not extender based). Let's make the conversion.
1079 wchar PlainPsw[MAXPASSWORD];
1080 Cmd->Password.Get(PlainPsw,ASIZE(PlainPsw));
1081 char PswA[MAXPASSWORD];
1082 CharToOemBuffW(PlainPsw,PswA,ASIZE(PswA));
1083 PswA[ASIZE(PswA)-1]=0;
1084 CharToWide(PswA,PlainPsw,ASIZE(PlainPsw));
1085 DestPwd.Set(PlainPsw);
1086 cleandata(PlainPsw,sizeof(PlainPsw));
1087 cleandata(PswA,sizeof(PswA));
1088 }
1089 }
1090 #endif
1091
1092
1093 void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName)
1094 {
1095 if (Cmd->Test)
1096 {
1097 if (!Cmd->DisableNames)
1098 {
1099 mprintf(St(MExtrTestFile),ArcFileName);
1100 mprintf(L" %s",St(MOk));
1101 }
1102 return;
1103 }
1104
1105 MKDIR_CODE MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr);
1106 bool DirExist=false;
1107 if (MDCode!=MKDIR_SUCCESS)
1108 {
1109 DirExist=FileExist(DestFileName);
1110 if (DirExist && !IsDir(GetFileAttr(DestFileName)))
1111 {
1112 // File with name same as this directory exists. Propose user
1113 // to overwrite it.
1114 bool UserReject;
1115 FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime);
1116 DirExist=false;
1117 }
1118 if (!DirExist)
1119 {
1120 CreatePath(DestFileName,true,Cmd->DisableNames);
1121 MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr);
1122 if (MDCode!=MKDIR_SUCCESS && !IsNameUsable(DestFileName))
1123 {
1124 uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName);
1125 wchar OrigName[ASIZE(DestFileName)];
1126 wcsncpyz(OrigName,DestFileName,ASIZE(OrigName));
1127 MakeNameUsable(DestFileName,true);
1128 #ifndef SFX_MODULE
1129 uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName);
1130 #endif
1131 DirExist=FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName));
1132 if (!DirExist)
1133 {
1134 CreatePath(DestFileName,true,Cmd->DisableNames);
1135 MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr);
1136 }
1137 }
1138 }
1139 }
1140 if (MDCode==MKDIR_SUCCESS)
1141 {
1142 if (!Cmd->DisableNames)
1143 {
1144 mprintf(St(MCreatDir),DestFileName);
1145 mprintf(L" %s",St(MOk));
1146 }
1147 PrevProcessed=true;
1148 }
1149 else
1150 if (DirExist)
1151 {
1152 if (!Cmd->IgnoreGeneralAttr)
1153 SetFileAttr(DestFileName,Arc.FileHead.FileAttr);
1154 PrevProcessed=true;
1155 }
1156 else
1157 {
1158 uiMsg(UIERROR_DIRCREATE,Arc.FileName,DestFileName);
1159 ErrHandler.SysErrMsg();
1160 #ifdef RARDLL
1161 Cmd->DllError=ERAR_ECREATE;
1162 #endif
1163 ErrHandler.SetErrorCode(RARX_CREATE);
1164 }
1165 if (PrevProcessed)
1166 {
1167 #if defined(_WIN_ALL) && !defined(SFX_MODULE)
1168 if (Cmd->SetCompressedAttr &&
1169 (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT()!=WNT_NONE)
1170 SetFileCompression(DestFileName,true);
1171 #endif
1172 SetFileHeaderExtra(Cmd,Arc,DestFileName);
1173 SetDirTime(DestFileName,
1174 Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
1175 Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime,
1176 Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
1177 }
1178 }
1179
1180
1181 bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile)
1182 {
1183 bool Success=true;
1184 wchar Command=Cmd->Command[0];
1185 #if !defined(SFX_MODULE)
1186 if (Command=='P')
1187 CurFile.SetHandleType(FILE_HANDLESTD);
1188 #endif
1189 if ((Command=='E' || Command=='X') && !Cmd->Test)
1190 {
1191 bool UserReject;
1192 // Specify "write only" mode to avoid OpenIndiana NAS problems
1193 // with SetFileTime and read+write files.
1194 if (!FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true))
1195 {
1196 Success=false;
1197 if (!UserReject)
1198 {
1199 ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName);
1200 if (FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName)))
1201 uiMsg(UIERROR_DIRNAMEEXISTS);
1202
1203 #ifdef RARDLL
1204 Cmd->DllError=ERAR_ECREATE;
1205 #endif
1206 if (!IsNameUsable(DestFileName))
1207 {
1208 uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName);
1209
1210 wchar OrigName[ASIZE(DestFileName)];
1211 wcsncpyz(OrigName,DestFileName,ASIZE(OrigName));
1212
1213 MakeNameUsable(DestFileName,true);
1214
1215 CreatePath(DestFileName,true,Cmd->DisableNames);
1216 if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true))
1217 {
1218 #ifndef SFX_MODULE
1219 uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName);
1220 #endif
1221 Success=true;
1222 }
1223 else
1224 ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName);
1225 }
1226 }
1227 }
1228 }
1229 return Success;
1230 }
1231
1232
1233 bool CmdExtract::CheckUnpVer(Archive &Arc,const wchar *ArcFileName)
1234 {
1235 bool WrongVer;
1236 if (Arc.Format==RARFMT50) // Both SFX and RAR can unpack RAR 5.0 archives.
1237 WrongVer=Arc.FileHead.UnpVer>VER_UNPACK5;
1238 else
1239 {
1240 #ifdef SFX_MODULE // SFX can unpack only RAR 2.9 archives.
1241 WrongVer=Arc.FileHead.UnpVer!=VER_UNPACK;
1242 #else // All formats since 1.3 for RAR.
1243 WrongVer=Arc.FileHead.UnpVer<13 || Arc.FileHead.UnpVer>VER_UNPACK;
1244 #endif
1245 }
1246
1247 // We can unpack stored files regardless of compression version field.
1248 if (Arc.FileHead.Method==0)
1249 WrongVer=false;
1250
1251 if (WrongVer)
1252 {
1253 ErrHandler.UnknownMethodMsg(Arc.FileName,ArcFileName);
1254 uiMsg(UIERROR_NEWERRAR,Arc.FileName);
1255 }
1256 return !WrongVer;
1257 }
1258
1259
1260 #ifndef SFX_MODULE
1261 // To speed up solid volumes extraction, try to find a non-first start volume,
1262 // which still allows to unpack all files. It is possible for independent
1263 // solid volumes with solid statistics reset in the beginning.
1264 bool CmdExtract::DetectStartVolume(const wchar *VolName,bool NewNumbering)
1265 {
1266 wchar *ArgName=Cmd->FileArgs.GetString();
1267 Cmd->FileArgs.Rewind();
1268 if (ArgName!=NULL && (wcscmp(ArgName,L"*")==0 || wcscmp(ArgName,L"*.*")==0))
1269 return false; // No need to check further for * and *.* masks.
1270
1271 wchar StartName[NM];
1272 *StartName=0;
1273
1274 // Start search from first volume if all volumes preceding current are available.
1275 wchar NextName[NM];
1276 GetFirstVolIfFullSet(VolName,NewNumbering,NextName,ASIZE(NextName));
1277
1278 bool Matched=false;
1279 while (!Matched)
1280 {
1281 Archive Arc(Cmd);
1282 if (!Arc.Open(NextName) || !Arc.IsArchive(false) || !Arc.Volume)
1283 break;
1284
1285 bool OpenNext=false;
1286 while (Arc.ReadHeader()>0)
1287 {
1288 Wait();
1289
1290 HEADER_TYPE HeaderType=Arc.GetHeaderType();
1291 if (HeaderType==HEAD_ENDARC)
1292 {
1293 OpenNext|=Arc.EndArcHead.NextVolume; // Allow open next volume.
1294 break;
1295 }
1296 if (HeaderType==HEAD_FILE)
1297 {
1298 if (!Arc.FileHead.SplitBefore)
1299 {
1300 if (!Arc.FileHead.Solid) // Can start extraction from here.
1301 wcsncpyz(StartName,NextName,ASIZE(StartName));
1302
1303 if (Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0)
1304 {
1305 Matched=true; // First matched file found, must stop further scan.
1306 break;
1307 }
1308 }
1309 if (Arc.FileHead.SplitAfter)
1310 {
1311 OpenNext=true; // Allow open next volume.
1312 break;
1313 }
1314 }
1315 Arc.SeekToNext();
1316 }
1317 Arc.Close();
1318
1319 if (!OpenNext)
1320 break;
1321
1322 NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering);
1323 }
1324 bool NewStartFound=wcscmp(VolName,StartName)!=0;
1325 if (NewStartFound) // Found a new volume to start extraction.
1326 wcsncpyz(ArcName,StartName,ASIZE(ArcName));
1327
1328 return NewStartFound;
1329 }
1330 #endif
1331
1332
1333 #ifndef SFX_MODULE
1334 // Return the first volume name if all volumes preceding the specified
1335 // are available. Otherwise return the specified volume name.
1336 void CmdExtract::GetFirstVolIfFullSet(const wchar *SrcName,bool NewNumbering,wchar *DestName,size_t DestSize)
1337 {
1338 wchar FirstVolName[NM];
1339 VolNameToFirstName(SrcName,FirstVolName,ASIZE(FirstVolName),NewNumbering);
1340 wchar NextName[NM];
1341 wcsncpyz(NextName,FirstVolName,ASIZE(NextName));
1342 wchar ResultName[NM];
1343 wcsncpyz(ResultName,SrcName,ASIZE(ResultName));
1344 while (true)
1345 {
1346 if (wcscmp(SrcName,NextName)==0)
1347 {
1348 wcsncpyz(ResultName,FirstVolName,DestSize);
1349 break;
1350 }
1351 if (!FileExist(NextName))
1352 break;
1353 NextVolumeName(NextName,ASIZE(NextName),!NewNumbering);
1354 }
1355 wcsncpyz(DestName,ResultName,DestSize);
1356 }
1357
1358 #endif