| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 
 | 
static bool UnixSymlink(CommandData *Cmd,const std::string &Target,const wchar *LinkName,RarTime *ftm,RarTime *fta)
{
  CreatePath(LinkName,true,Cmd->DisableNames);
  // Overwrite prompt was already issued and confirmed earlier, so we can
  // remove existing symlink or regular file here. PrepareToDelete was also
  // called earlier inside of uiAskReplaceEx.
  DelFile(LinkName);
  std::string LinkNameA;
  WideToChar(LinkName,LinkNameA);
  if (symlink(Target.c_str(),LinkNameA.c_str())==-1) // Error.
  {
    if (errno==EEXIST)
      uiMsg(UIERROR_ULINKEXIST,LinkName);
    else
    {
      uiMsg(UIERROR_SLINKCREATE,L"",LinkName);
      ErrHandler.SetErrorCode(RARX_WARNING);
    }
    return false;
  }
#ifdef USE_LUTIMES
#ifdef UNIX_TIME_NS
  timespec times[2];
  times[0].tv_sec=fta->GetUnix();
  times[0].tv_nsec=fta->IsSet() ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW;
  times[1].tv_sec=ftm->GetUnix();
  times[1].tv_nsec=ftm->IsSet() ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW;
  utimensat(AT_FDCWD,LinkNameA.c_str(),times,AT_SYMLINK_NOFOLLOW);
#else
  struct timeval tv[2];
  tv[0].tv_sec=fta->GetUnix();
  tv[0].tv_usec=long(fta->GetUnixNS()%1000000000/1000);
  tv[1].tv_sec=ftm->GetUnix();
  tv[1].tv_usec=long(ftm->GetUnixNS()%1000000000/1000);
  lutimes(LinkNameA.c_str(),tv);
#endif
#endif
  return true;
}
static bool IsFullPath(const char *PathA) // Unix ASCII version.
{
  return *PathA==CPATHDIVIDER;
}
// For security purpose we prefer to be sure that CharToWide completed
// successfully and even if it truncated a string for some reason,
// it didn't affect the number of path related characters we analyze
// in IsRelativeSymlinkSafe later.
// This check is likely to be excessive, but let's keep it anyway.
static bool SafeCharToWide(const std::string &Src,std::wstring &Dest)
{
  if (!CharToWide(Src,Dest) || Dest.empty())
    return false;
  uint SrcChars=0,DestChars=0;
  for (uint I=0;Src[I]!=0;I++)
    if (Src[I]=='/' || Src[I]=='.')
      SrcChars++;
  for (uint I=0;Dest[I]!=0;I++)
    if (Dest[I]=='/' || Dest[I]=='.')
      DestChars++;
  return SrcChars==DestChars;
}
static bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,
                              const wchar *LinkName,bool &UpLink)
{
  if (IsLink(Arc.FileHead.FileAttr))
  {
    size_t DataSize=(size_t)Arc.FileHead.PackSize;
    if (DataSize>MAXPATHSIZE)
      return false;
    std::vector<char> TargetBuf(DataSize+1);
    if ((size_t)DataIO.UnpRead((byte*)TargetBuf.data(),DataSize)!=DataSize)
      return false;
    std::string Target(TargetBuf.data(),TargetBuf.size());
    DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,1);
    DataIO.UnpHash.Update(Target.data(),strlen(Target.data()));
    DataIO.UnpHash.Result(&Arc.FileHead.FileHash);
    // Return true in case of bad checksum, so link will be processed further
    // and extraction routine will report the checksum error.
    if (!DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL))
      return true;
    std::wstring TargetW;
    if (!SafeCharToWide(Target.data(),TargetW))
      return false;
    TruncateAtZero(TargetW);
    // Use Arc.FileHead.FileName instead of LinkName, since LinkName
    // can include the destination path as a prefix, which can
    // confuse IsRelativeSymlinkSafe algorithm.
    if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) ||
        !IsRelativeSymlinkSafe(Cmd,Arc.FileHead.FileName.c_str(),LinkName,TargetW.c_str())))
    {
      uiMsg(UIERROR_SKIPUNSAFELINK,Arc.FileHead.FileName,TargetW);
      ErrHandler.SetErrorCode(RARX_WARNING);
      return false;
    }
    UpLink=Target.find("..")!=std::string::npos;
    return UnixSymlink(Cmd,Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime);
  }
  return false;
}
static bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd)
{
  std::string Target;
  WideToChar(hd->RedirName,Target);
  if (hd->RedirType==FSREDIR_WINSYMLINK || hd->RedirType==FSREDIR_JUNCTION)
  {
    // Windows absolute path symlinks in Unix. RAR 5.0 used \??\ prefix
    // for Windows absolute symlinks, since RAR 5.1 /??/ is used.
    // We escape ? as \? to avoid "trigraph" warning
    if (Target.rfind("\\??\\",0)!=std::string::npos || 
        Target.rfind("/\?\?/",0)!=std::string::npos)
    {
#if 0 // 2024.12.26: Not used anymore. We unpack absolute Windows symlinks even in Unix.
      uiMsg(UIERROR_SLINKCREATE,nullptr,L"\"" + hd->FileName + L"\" -> \"" + hd->RedirName + L"\"");
      ErrHandler.SetErrorCode(RARX_WARNING);
      return false;
#endif
      // 2024.12.26: User asked to unpack absolute Windows symlinks even in Unix.
      Target=Target.substr(4); // Remove \??\ prefix.
    }
    DosSlashToUnix(Target,Target);
  }
  std::wstring TargetW;
  if (!SafeCharToWide(Target,TargetW))
    return false;
  // Use hd->FileName instead of LinkName, since LinkName can include
  // the destination path as a prefix, which can confuse
  // IsRelativeSymlinkSafe algorithm.
  if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) ||
      !IsRelativeSymlinkSafe(Cmd,hd->FileName.c_str(),Name,TargetW.c_str())))
  {
    uiMsg(UIERROR_SKIPUNSAFELINK,hd->FileName,TargetW);
    ErrHandler.SetErrorCode(RARX_WARNING);
    return false;
  }
  return UnixSymlink(Cmd,Target,Name,&hd->mtime,&hd->atime);
}
 |