123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- #include "rar.hpp"
- #include "hardlinks.cpp"
- #include "win32stm.cpp"
- #ifdef _WIN_ALL
- #include "win32acl.cpp"
- #include "win32lnk.cpp"
- #endif
- #ifdef _UNIX
- #include "uowners.cpp"
- #ifdef SAVE_LINKS
- #include "ulinks.cpp"
- #endif
- #endif
- // RAR2 service header extra records.
- #ifndef SFX_MODULE
- void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name)
- {
- if (Cmd->Test)
- return;
- switch(Arc.SubBlockHead.SubType)
- {
- #ifdef _UNIX
- case UO_HEAD:
- if (Cmd->ProcessOwners)
- ExtractUnixOwner20(Arc,Name);
- break;
- #endif
- #ifdef _WIN_ALL
- case NTACL_HEAD:
- if (Cmd->ProcessOwners)
- ExtractACL20(Arc,Name);
- break;
- case STREAM_HEAD:
- ExtractStreams20(Arc,Name);
- break;
- #endif
- }
- }
- #endif
- // RAR3 and RAR5 service header extra records.
- void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name)
- {
- #ifdef _UNIX
- if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 &&
- Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER))
- ExtractUnixOwner30(Arc,Name);
- #endif
- #ifdef _WIN_ALL
- if (!Cmd->Test && Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL))
- ExtractACL(Arc,Name);
- if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM))
- ExtractStreams(Arc,Name,Cmd->Test);
- #endif
- }
- // Extra data stored directly in file header.
- void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name)
- {
- #ifdef _UNIX
- if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet)
- SetUnixOwner(Arc,Name);
- #endif
- }
- // Calculate a number of path components except \. and \..
- static int CalcAllowedDepth(const wchar *Name)
- {
- int AllowedDepth=0;
- while (*Name!=0)
- {
- if (IsPathDiv(Name[0]) && Name[1]!=0 && !IsPathDiv(Name[1]))
- {
- bool Dot=Name[1]=='.' && (IsPathDiv(Name[2]) || Name[2]==0);
- bool Dot2=Name[1]=='.' && Name[2]=='.' && (IsPathDiv(Name[3]) || Name[3]==0);
- if (!Dot && !Dot2)
- AllowedDepth++;
- }
- Name++;
- }
- return AllowedDepth;
- }
- // Check if all existing path components are directories and not links.
- static bool LinkInPath(const wchar *Name)
- {
- wchar Path[NM];
- if (wcslen(Name)>=ASIZE(Path))
- return true; // It should not be that long, skip.
- wcsncpyz(Path,Name,ASIZE(Path));
- for (wchar *s=Path+wcslen(Path)-1;s>Path;s--)
- if (IsPathDiv(*s))
- {
- *s=0;
- FindData FD;
- if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir))
- return true;
- }
- return false;
- }
- bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName)
- {
- // Catch root dir based /path/file paths also as stuff like \\?\.
- // Do not check PrepSrcName here, it can be root based if destination path
- // is a root based.
- if (IsFullRootPath(SrcName) || IsFullRootPath(TargetName))
- return false;
- // Number of ".." in link target.
- int UpLevels=0;
- for (int Pos=0;*TargetName!=0;Pos++)
- {
- bool Dot2=TargetName[0]=='.' && TargetName[1]=='.' &&
- (IsPathDiv(TargetName[2]) || TargetName[2]==0) &&
- (Pos==0 || IsPathDiv(*(TargetName-1)));
- if (Dot2)
- UpLevels++;
- TargetName++;
- }
- // If link target includes "..", it must not have another links
- // in the path, because they can bypass our safety check. For example,
- // suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next
- // or "dir/lnk1" -> ".." first and "dir/lnk1/lnk2" -> ".." next.
- if (UpLevels>0 && LinkInPath(PrepSrcName))
- return false;
-
- // We could check just prepared src name, but for extra safety
- // we check both original (as from archive header) and prepared
- // (after applying the destination path and -ep switches) names.
- int AllowedDepth=CalcAllowedDepth(SrcName); // Original name depth.
- // Remove the destination path from prepared name if any. We should not
- // count the destination path depth, because the link target must point
- // inside of this path, not outside of it.
- size_t ExtrPathLength=wcslen(Cmd->ExtrPath);
- if (ExtrPathLength>0 && wcsncmp(PrepSrcName,Cmd->ExtrPath,ExtrPathLength)==0)
- {
- PrepSrcName+=ExtrPathLength;
- while (IsPathDiv(*PrepSrcName))
- PrepSrcName++;
- }
- int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName);
- return AllowedDepth>=UpLevels && PrepAllowedDepth>=UpLevels;
- }
- bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName)
- {
- #if defined(SAVE_LINKS) && defined(_UNIX)
- // For RAR 3.x archives we process links even in test mode to skip link data.
- if (Arc.Format==RARFMT15)
- return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName);
- if (Arc.Format==RARFMT50)
- return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead);
- #elif defined _WIN_ALL
- // RAR 5.0 archives store link information in file header, so there is
- // no need to additionally test it if we do not create a file.
- if (Arc.Format==RARFMT50)
- return CreateReparsePoint(Cmd,LinkName,&Arc.FileHead);
- #endif
- return false;
- }
|