Coverage Report

Created: 2024-10-29 06:31

/src/unrar/archive.cpp
Line
Count
Source (jump to first uncovered line)
1
#include "rar.hpp"
2
3
#include "arccmt.cpp"
4
5
6
Archive::Archive(CommandData *InitCmd)
7
2.68k
{
8
2.68k
  Cmd=NULL; // Just in case we'll have an exception in 'new' below.
9
10
2.68k
  DummyCmd=(InitCmd==NULL);
11
2.68k
  Cmd=DummyCmd ? (new CommandData):InitCmd;
12
13
2.68k
  OpenShared=Cmd->OpenShared;
14
2.68k
  Format=RARFMT_NONE;
15
2.68k
  Solid=false;
16
2.68k
  Volume=false;
17
2.68k
  MainComment=false;
18
2.68k
  Locked=false;
19
2.68k
  Signed=false;
20
2.68k
  FirstVolume=false;
21
2.68k
  NewNumbering=false;
22
2.68k
  SFXSize=0;
23
2.68k
  LatestTime.Reset();
24
2.68k
  Protected=false;
25
2.68k
  Encrypted=false;
26
2.68k
  FailedHeaderDecryption=false;
27
2.68k
  BrokenHeader=false;
28
2.68k
  LastReadBlock=0;
29
30
2.68k
  CurBlockPos=0;
31
2.68k
  NextBlockPos=0;
32
33
34
2.68k
  MainHead.Reset();
35
2.68k
  CryptHead={};
36
2.68k
  EndArcHead.Reset();
37
38
2.68k
  VolNumber=0;
39
2.68k
  VolWrite=0;
40
2.68k
  AddingFilesSize=0;
41
2.68k
  AddingHeadersSize=0;
42
43
2.68k
  Splitting=false;
44
2.68k
  NewArchive=false;
45
46
2.68k
  SilentOpen=false;
47
48
2.68k
#ifdef USE_QOPEN
49
2.68k
  ProhibitQOpen=false;
50
2.68k
#endif
51
52
2.68k
}
53
54
55
Archive::~Archive()
56
2.68k
{
57
2.68k
  if (DummyCmd)
58
0
    delete Cmd;
59
2.68k
}
60
61
62
void Archive::CheckArc(bool EnableBroken)
63
0
{
64
0
  if (!IsArchive(EnableBroken))
65
0
  {
66
    // If FailedHeaderDecryption is set, we already reported that archive
67
    // password is incorrect.
68
0
    if (!FailedHeaderDecryption)
69
0
      uiMsg(UIERROR_BADARCHIVE,FileName);
70
0
    ErrHandler.Exit(RARX_FATAL);
71
0
  }
72
0
}
73
74
75
#if !defined(SFX_MODULE)
76
void Archive::CheckOpen(const std::wstring &Name)
77
0
{
78
0
  TOpen(Name);
79
0
  CheckArc(false);
80
0
}
81
#endif
82
83
84
bool Archive::WCheckOpen(const std::wstring &Name)
85
0
{
86
0
  if (!WOpen(Name))
87
0
    return false;
88
0
  if (!IsArchive(false))
89
0
  {
90
0
    uiMsg(UIERROR_BADARCHIVE,FileName);
91
0
    Close();
92
0
    return false;
93
0
  }
94
0
  return true;
95
0
}
96
97
98
RARFORMAT Archive::IsSignature(const byte *D,size_t Size)
99
19.1k
{
100
19.1k
  RARFORMAT Type=RARFMT_NONE;
101
19.1k
  if (Size>=1 && D[0]==0x52)
102
17.8k
#ifndef SFX_MODULE
103
17.8k
    if (Size>=4 && D[1]==0x45 && D[2]==0x7e && D[3]==0x5e)
104
1.75k
      Type=RARFMT14;
105
16.1k
    else
106
16.1k
#endif
107
16.1k
      if (Size>=7 && D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07)
108
2.26k
      {
109
        // We check the last signature byte, so we can return a sensible
110
        // warning in case we'll want to change the archive format
111
        // sometimes in the future.
112
2.26k
        if (D[6]==0)
113
1.23k
          Type=RARFMT15;
114
1.03k
        else
115
1.03k
          if (D[6]==1)
116
893
            Type=RARFMT50;
117
140
          else
118
140
            if (D[6]>1 && D[6]<5)
119
3
              Type=RARFMT_FUTURE;
120
2.26k
      }
121
19.1k
  return Type;
122
19.1k
}
123
124
125
bool Archive::IsArchive(bool EnableBroken)
126
2.68k
{
127
2.68k
  Encrypted=false;
128
2.68k
  BrokenHeader=false; // Might be left from previous volume.
129
  
130
2.68k
#ifndef SFX_MODULE
131
2.68k
  if (IsDevice())
132
0
  {
133
0
    uiMsg(UIERROR_INVALIDNAME,FileName,FileName);
134
0
    return false;
135
0
  }
136
2.68k
#endif
137
2.68k
  if (Read(MarkHead.Mark,SIZEOF_MARKHEAD3)!=SIZEOF_MARKHEAD3)
138
3
    return false;
139
2.68k
  SFXSize=0;
140
  
141
2.68k
  RARFORMAT Type;
142
2.68k
  if ((Type=IsSignature(MarkHead.Mark,SIZEOF_MARKHEAD3))!=RARFMT_NONE)
143
864
  {
144
864
    Format=Type;
145
864
    if (Format==RARFMT14)
146
464
      Seek(Tell()-SIZEOF_MARKHEAD3,SEEK_SET);
147
864
  }
148
1.82k
  else
149
1.82k
  {
150
1.82k
    std::vector<char> Buffer(MAXSFXSIZE);
151
1.82k
    long CurPos=(long)Tell();
152
1.82k
    int ReadSize=Read(Buffer.data(),Buffer.size()-16);
153
4.64M
    for (int I=0;I<ReadSize;I++)
154
4.64M
      if (Buffer[I]==0x52 && (Type=IsSignature((byte *)&Buffer[I],ReadSize-I))!=RARFMT_NONE)
155
3.01k
      {
156
3.01k
        Format=Type;
157
3.01k
        if (Format==RARFMT14 && I>0 && CurPos<28 && ReadSize>31)
158
1.25k
        {
159
1.25k
          char *D=&Buffer[28-CurPos];
160
1.25k
          if (D[0]!=0x52 || D[1]!=0x53 || D[2]!=0x46 || D[3]!=0x58)
161
1.25k
            continue;
162
1.25k
        }
163
1.75k
        SFXSize=CurPos+I;
164
1.75k
        Seek(SFXSize,SEEK_SET);
165
1.75k
        if (Format==RARFMT15 || Format==RARFMT50)
166
1.73k
          Read(MarkHead.Mark,SIZEOF_MARKHEAD3);
167
1.75k
        break;
168
3.01k
      }
169
1.82k
    if (SFXSize==0)
170
62
      return false;
171
1.82k
  }
172
2.62k
  if (Format==RARFMT_FUTURE)
173
3
  {
174
3
    uiMsg(UIERROR_NEWRARFORMAT,FileName);
175
3
    return false;
176
3
  }
177
2.62k
  if (Format==RARFMT50) // RAR 5.0 signature is by one byte longer.
178
893
  {
179
893
    if (Read(MarkHead.Mark+SIZEOF_MARKHEAD3,1)!=1 || MarkHead.Mark[SIZEOF_MARKHEAD3]!=0)
180
1
      return false;
181
892
    MarkHead.HeadSize=SIZEOF_MARKHEAD5;
182
892
  }
183
1.72k
  else
184
1.72k
    MarkHead.HeadSize=SIZEOF_MARKHEAD3;
185
186
2.61k
#ifdef RARDLL
187
  // If callback function is not set, we cannot get the password,
188
  // so we skip the initial header processing for encrypted header archive.
189
  // It leads to skipped archive comment, but the rest of archive data
190
  // is processed correctly.
191
2.61k
  if (Cmd->Callback==NULL)
192
2.61k
    SilentOpen=true;
193
2.61k
#endif
194
195
2.61k
  bool HeadersLeft; // Any headers left to read.
196
2.61k
  bool StartFound=false; // Main or encryption headers found.
197
  // Skip the archive encryption header if any and read the main header.
198
4.02k
  while ((HeadersLeft=(ReadHeader()!=0))==true) // Additional parentheses to silence Clang.
199
2.51k
  {
200
2.51k
    SeekToNext();
201
202
2.51k
    HEADER_TYPE Type=GetHeaderType();
203
    // In RAR 5.0 we need to quit after reading HEAD_CRYPT if we wish to
204
    // avoid the password prompt.
205
2.51k
    StartFound=Type==HEAD_MAIN || SilentOpen && Type==HEAD_CRYPT;
206
2.51k
    if (StartFound)
207
1.11k
      break;
208
2.51k
  }
209
210
  
211
  // We should not do it for EnableBroken or we'll get 'not RAR archive'
212
  // messages when extracting encrypted archives with wrong password.
213
2.61k
  if (FailedHeaderDecryption && !EnableBroken)
214
0
    return false;
215
216
2.61k
  if (BrokenHeader || !StartFound) // Main archive header is corrupt or missing.
217
2.30k
  {
218
2.30k
    if (!FailedHeaderDecryption) // If not reported a wrong password already.
219
2.30k
      uiMsg(UIERROR_MHEADERBROKEN,FileName);
220
2.30k
    if (!EnableBroken)
221
0
      return false;
222
2.30k
  }
223
224
2.61k
  MainComment=MainHead.CommentInHeader;
225
226
  // If we process non-encrypted archive or can request a password,
227
  // we set 'first volume' flag based on file attributes below.
228
  // It is necessary for RAR 2.x archives, which did not have 'first volume'
229
  // flag in main header. Also for all RAR formats we need to scan until
230
  // first file header to set "comment" flag when reading service header.
231
  // Unless we are in silent mode, we need to know about presence of comment
232
  // immediately after IsArchive call.
233
2.61k
  if (HeadersLeft && (!SilentOpen || !Encrypted) && IsSeekable())
234
1.10k
  {
235
1.10k
    int64 SavePos=Tell();
236
1.10k
    int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
237
1.10k
    HEADER_TYPE SaveCurHeaderType=CurHeaderType;
238
239
2.14k
    while (ReadHeader()!=0)
240
1.90k
    {
241
1.90k
      HEADER_TYPE HeaderType=GetHeaderType();
242
1.90k
      if (HeaderType==HEAD_SERVICE)
243
513
      {
244
        // If we have a split service headers, it surely indicates non-first
245
        // volume. But not split service header does not guarantee the first
246
        // volume, because we can have split file after non-split archive
247
        // comment. So we do not quit from loop here.
248
513
        FirstVolume=Volume && !SubHead.SplitBefore;
249
513
      }
250
1.39k
      else
251
1.39k
        if (HeaderType==HEAD_FILE)
252
858
        {
253
858
          FirstVolume=Volume && !FileHead.SplitBefore;
254
858
          break;
255
858
        }
256
533
        else
257
533
          if (HeaderType==HEAD_ENDARC) // Might happen if archive contains only a split service header.
258
6
            break;
259
1.04k
      SeekToNext();
260
1.04k
    }
261
1.10k
    CurBlockPos=SaveCurBlockPos;
262
1.10k
    NextBlockPos=SaveNextBlockPos;
263
1.10k
    CurHeaderType=SaveCurHeaderType;
264
1.10k
    Seek(SavePos,SEEK_SET);
265
1.10k
  }
266
2.61k
  if (!Volume || FirstVolume)
267
2.51k
    FirstVolumeName=FileName;
268
269
2.61k
  return true;
270
2.61k
}
271
272
273
274
275
void Archive::SeekToNext()
276
38.5k
{
277
38.5k
  Seek(NextBlockPos,SEEK_SET);
278
38.5k
}
279
280
281
282
283
284
285
// Calculate the block size including encryption fields and padding if any.
286
uint Archive::FullHeaderSize(size_t Size)
287
38.8k
{
288
38.8k
  if (Encrypted)
289
0
  {
290
0
    Size = ALIGN_VALUE(Size, CRYPT_BLOCK_SIZE); // Align to encryption block size.
291
0
    if (Format == RARFMT50)
292
0
      Size += SIZE_INITV;
293
0
    else
294
0
      Size += SIZE_SALT30;
295
0
  }
296
38.8k
  return uint(Size);
297
38.8k
}
298
299
300
301
302
#ifdef USE_QOPEN
303
bool Archive::Open(const std::wstring &Name,uint Mode)
304
2.68k
{
305
  // Important if we reuse Archive object and it has virtual QOpen
306
  // file position not matching real. For example, for 'l -v volname'.
307
2.68k
  QOpen.Unload();
308
309
2.68k
  return File::Open(Name,Mode);
310
2.68k
}
311
312
313
int Archive::Read(void *Data,size_t Size)
314
136k
{
315
136k
  size_t Result;
316
136k
  if (QOpen.Read(Data,Size,Result))
317
0
    return (int)Result;
318
136k
  return File::Read(Data,Size);
319
136k
}
320
321
322
void Archive::Seek(int64 Offset,int Method)
323
78.7k
{
324
78.7k
  if (!QOpen.Seek(Offset,Method))
325
78.7k
    File::Seek(Offset,Method);
326
78.7k
}
327
328
329
int64 Archive::Tell()
330
55.1k
{
331
55.1k
  int64 QPos;
332
55.1k
  if (QOpen.Tell(&QPos))
333
0
    return QPos;
334
55.1k
  return File::Tell();
335
55.1k
}
336
#endif
337
338
339
// Return 0 if dictionary size is invalid. If size is RAR7 only, return
340
// the adjusted nearest bottom value. Return header flags in Flags.
341
uint64 Archive::GetWinSize(uint64 Size,uint &Flags)
342
0
{
343
0
  Flags=0;
344
  // Allow 128 KB - 1 TB range.
345
0
  if (Size<0x20000 || Size>0x10000000000ULL)
346
0
    return 0;
347
0
  uint64 Pow2=0x20000; // Power of 2 dictionary size.
348
0
  for (;2*Pow2<=Size;Pow2*=2)
349
0
    Flags+=FCI_DICT_BIT0;
350
0
  if (Size==Pow2)
351
0
    return Size;  // If 'Size' is the power of 2, return it as is.
352
353
  // Get the number of Pow2/32 to add to Pow2 for nearest value not exceeding 'Size'.
354
0
  uint64 Fraction=(Size-Pow2)/(Pow2/32);
355
0
  Flags+=(uint)Fraction*FCI_DICT_FRACT0;
356
0
  return Pow2+Fraction*(Pow2/32);
357
0
}