@@ -16,7 +16,18 @@ CmdExtract::CmdExtract(CommandData *Cmd)
16
16
// Common for all archives involved. Set here instead of DoExtract()
17
17
// to use in unrar.dll too. Allows to avoid LinksToDirs() calls
18
18
// and save CPU time in no symlinks including ".." in target were extracted.
19
- UpLinkExtracted=false ;
19
+ #if defined(_WIN_ALL)
20
+ // We can't expand symlink path components in another symlink target
21
+ // in Windows. We can't create symlinks in Android now. Even though we do not
22
+ // really need LinksToDirs() calls in these systems, we still call it
23
+ // for extra safety, but only if symlink with ".." in target was extracted.
24
+ ConvertSymlinkPaths=false ;
25
+ #else
26
+ // We enable it by default in Unix to care about the case when several
27
+ // archives are unpacked to same directory with several independent RAR runs.
28
+ // Worst case performance penalty for a lot of small files seems to be ~3%.
29
+ ConvertSymlinkPaths=true ;
30
+ #endif
20
31
21
32
Unp=new Unpack (&DataIO);
22
33
#ifdef RAR_SMP
@@ -486,6 +497,13 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
486
497
487
498
if (!Cmd->Test ) // While harmless, it is useless for 't'.
488
499
{
500
+ // If reference source isn't selected, but target is selected,
501
+ // we unpack the source under the temporary name and then rename
502
+ // or copy it to target name. We do not unpack it under the target
503
+ // name immediately, because the same source can be used by multiple
504
+ // targets and it is possible that first target isn't unpacked
505
+ // for some reason. Also targets might have associated service blocks
506
+ // like ACLs. All this would complicate processing a lot.
489
507
wcsncpyz (DestFileName,*Cmd->TempPath !=0 ? Cmd->TempPath :Cmd->ExtrPath ,ASIZE (DestFileName));
490
508
AddEndSlash (DestFileName,ASIZE (DestFileName));
491
509
wcsncatz (DestFileName,L" __tmp_reference_source_" ,ASIZE (DestFileName));
@@ -627,7 +645,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
627
645
#endif
628
646
629
647
if (ExtrFile && Command!=' P' && !Cmd->Test && !Cmd->AbsoluteLinks &&
630
- UpLinkExtracted )
648
+ ConvertSymlinkPaths )
631
649
ExtrFile=LinksToDirs (DestFileName,Cmd->ExtrPath ,LastCheckedSymlink);
632
650
633
651
File CurFile;
@@ -786,7 +804,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
786
804
{
787
805
bool UpLink;
788
806
LinkSuccess=ExtractSymlink (Cmd,DataIO,Arc,DestFileName,UpLink);
789
- UpLinkExtracted |=LinkSuccess && UpLink;
807
+ ConvertSymlinkPaths |=LinkSuccess && UpLink;
790
808
791
809
// We do not actually need to reset the cache here if we cache
792
810
// only the single last checked path, because at this point
@@ -1304,7 +1322,7 @@ void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName)
1304
1322
DirExist=FileExist (DestFileName) && IsDir (GetFileAttr (DestFileName));
1305
1323
if (!DirExist)
1306
1324
{
1307
- if (!Cmd->AbsoluteLinks && UpLinkExtracted )
1325
+ if (!Cmd->AbsoluteLinks && ConvertSymlinkPaths )
1308
1326
LinksToDirs (DestFileName,Cmd->ExtrPath ,LastCheckedSymlink);
1309
1327
CreatePath (DestFileName,true ,Cmd->DisableNames );
1310
1328
MDCode=MakeDir (DestFileName,!Cmd->IgnoreGeneralAttr ,Arc.FileHead .FileAttr );
@@ -1387,7 +1405,7 @@ bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile)
1387
1405
1388
1406
MakeNameUsable (DestFileName,true );
1389
1407
1390
- if (!Cmd->AbsoluteLinks && UpLinkExtracted )
1408
+ if (!Cmd->AbsoluteLinks && ConvertSymlinkPaths )
1391
1409
LinksToDirs (DestFileName,Cmd->ExtrPath ,LastCheckedSymlink);
1392
1410
CreatePath (DestFileName,true ,Cmd->DisableNames );
1393
1411
if (FileCreate (Cmd,&CurFile,DestFileName,ASIZE (DestFileName),&UserReject,Arc.FileHead .UnpSize ,&Arc.FileHead .mtime ,true ))
@@ -1499,6 +1517,8 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi
1499
1517
{
1500
1518
if (!MatchFound && !Arc.FileHead .Solid ) // Can start extraction from here.
1501
1519
{
1520
+ // We would gain nothing and unnecessarily complicate extraction
1521
+ // by setting these values for first volume or first archived file.
1502
1522
if (!FirstVolume)
1503
1523
wcsncpyz (Analyze->StartName ,NextName,ASIZE (Analyze->StartName ));
1504
1524
if (!FirstFile)
@@ -1511,6 +1531,10 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi
1511
1531
MatchFound = true ;
1512
1532
PrevMatched = true ;
1513
1533
1534
+ // Reset the previously set early exit position, if any, because
1535
+ // we found a new matched file.
1536
+ Analyze->EndPos =0 ;
1537
+
1514
1538
// Matched file reference pointing at maybe non-matched source file.
1515
1539
// Even though we know RedirName, we can't check if source file
1516
1540
// is certainly non-matched, because it can be filtered out by
@@ -1545,6 +1569,8 @@ void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumberi
1545
1569
{
1546
1570
if (PrevMatched) // First non-matched item after matched.
1547
1571
{
1572
+ // We would gain nothing and unnecessarily complicate extraction
1573
+ // by setting these values for first volume or first archived file.
1548
1574
if (!FirstVolume)
1549
1575
wcsncpyz (Analyze->EndName ,NextName,ASIZE (Analyze->EndName ));
1550
1576
if (!FirstFile)
0 commit comments