Coverage Report

Created: 2025-04-11 06:56

/src/unrar/qopen.cpp
Line
Count
Source (jump to first uncovered line)
1
#include "rar.hpp"
2
3
QuickOpen::QuickOpen()
4
11.9k
{
5
11.9k
  Buf=NULL;
6
11.9k
  Init(NULL,false);
7
11.9k
}
8
9
10
QuickOpen::~QuickOpen()
11
11.9k
{
12
11.9k
  Close();
13
11.9k
  delete[] Buf;
14
11.9k
}
15
16
17
void QuickOpen::Init(Archive *Arc,bool WriteMode)
18
12.3k
{
19
12.3k
  if (Arc!=NULL) // Unless called from constructor.
20
398
    Close();
21
22
12.3k
  QuickOpen::Arc=Arc;
23
12.3k
  QuickOpen::WriteMode=WriteMode;
24
25
12.3k
  ListStart=NULL;
26
12.3k
  ListEnd=NULL;
27
28
12.3k
  if (Buf==NULL)
29
11.9k
    Buf=new byte[MaxBufSize];
30
31
12.3k
  CurBufSize=0; // Current size of buffered data in write mode.
32
33
12.3k
  Loaded=false;
34
12.3k
}
35
36
37
void QuickOpen::Close()
38
12.3k
{
39
12.3k
  QuickOpenItem *Item=ListStart;
40
12.3k
  while (Item!=NULL)
41
0
  {
42
0
    QuickOpenItem *Next=Item->Next;
43
0
    delete[] Item->Header;
44
0
    delete Item;
45
0
    Item=Next;
46
0
  }
47
12.3k
}
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
void QuickOpen::Load(uint64 BlockPos)
63
398
{
64
398
  if (!Loaded)
65
398
  {
66
    // If loading for the first time, perform additional intialization.
67
398
    SeekPos=Arc->Tell();
68
398
    UnsyncSeekPos=false;
69
70
398
    int64 SavePos=SeekPos;
71
398
    Arc->Seek(BlockPos,SEEK_SET);
72
73
    // If BlockPos points to original main header, we'll have the infinite
74
    // recursion, because ReadHeader() for main header will attempt to load
75
    // QOpen and call QuickOpen::Load again. If BlockPos points to long chain
76
    // of other main headers, we'll have multiple recursive calls of this
77
    // function wasting resources. So we prohibit QOpen temporarily to
78
    // prevent this. ReadHeader() calls QOpen.Init and sets MainHead Locator
79
    // and QOpenOffset fields, so we cannot use them to prohibit QOpen.
80
398
    Arc->SetProhibitQOpen(true);
81
398
    size_t ReadSize=Arc->ReadHeader();
82
398
    Arc->SetProhibitQOpen(false);
83
84
398
    if (ReadSize==0 || Arc->GetHeaderType()!=HEAD_SERVICE ||
85
398
        !Arc->SubHead.CmpName(SUBHEAD_TYPE_QOPEN))
86
352
    {
87
352
      Arc->Seek(SavePos,SEEK_SET);
88
352
      return;
89
352
    }
90
46
    QOHeaderPos=Arc->CurBlockPos;
91
46
    RawDataStart=Arc->Tell();
92
46
    RawDataSize=Arc->SubHead.UnpSize;
93
46
    Arc->Seek(SavePos,SEEK_SET);
94
95
46
    Loaded=true; // Set only after all file processing calls like Tell, Seek, ReadHeader.
96
46
  }
97
98
46
  if (Arc->SubHead.Encrypted)
99
0
  {
100
0
    CommandData *Cmd=Arc->GetCommandData();
101
0
#ifndef RAR_NOCRYPT
102
0
    if (Cmd->Password.IsSet())
103
0
      Crypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,Arc->SubHead.Salt,
104
0
                         Arc->SubHead.InitV,Arc->SubHead.Lg2Count,
105
0
                         Arc->SubHead.HashKey,Arc->SubHead.PswCheck);
106
0
    else
107
0
#endif
108
0
    {
109
0
      Loaded=false;
110
0
      return;
111
0
    }
112
0
  }
113
114
46
  RawDataPos=0;
115
46
  ReadBufSize=0;
116
46
  ReadBufPos=0;
117
46
  LastReadHeader.clear();
118
46
  LastReadHeaderPos=0;
119
120
46
  ReadBuffer();
121
46
}
122
123
124
bool QuickOpen::Read(void *Data,size_t Size,size_t &Result)
125
1.45M
{
126
1.45M
  if (!Loaded)
127
1.45M
    return false;
128
  // Find next suitable cached block.
129
0
  while (LastReadHeaderPos+LastReadHeader.size()<=SeekPos)
130
0
    if (!ReadNext())
131
0
      break;
132
0
  if (!Loaded)
133
0
  {
134
    // If something wrong happened, let's set the correct file pointer
135
    // and stop further quick open processing.
136
0
    if (UnsyncSeekPos)
137
0
      Arc->File::Seek(SeekPos,SEEK_SET);
138
0
    return false;
139
0
  }
140
141
0
  if (SeekPos>=LastReadHeaderPos && SeekPos+Size<=LastReadHeaderPos+LastReadHeader.size())
142
0
  {
143
0
    memcpy(Data,&LastReadHeader[size_t(SeekPos-LastReadHeaderPos)],Size);
144
0
    Result=Size;
145
0
    SeekPos+=Size;
146
0
    UnsyncSeekPos=true;
147
0
  }
148
0
  else
149
0
  {
150
0
    if (UnsyncSeekPos)
151
0
    {
152
0
      Arc->File::Seek(SeekPos,SEEK_SET);
153
0
      UnsyncSeekPos=false;
154
0
    }
155
0
    int ReadSize=Arc->File::Read(Data,Size);
156
0
    if (ReadSize<0)
157
0
    {
158
0
      Loaded=false;
159
0
      return false;
160
0
    }
161
0
    Result=ReadSize;
162
0
    SeekPos+=ReadSize;
163
0
  }
164
  
165
0
  return true;
166
0
}
167
168
169
bool QuickOpen::Seek(int64 Offset,int Method)
170
265k
{
171
265k
  if (!Loaded)
172
265k
    return false;
173
174
  // Normally we process an archive sequentially from beginning to end,
175
  // so we read quick open data sequentially. But some operations like
176
  // archive updating involve several passes. So if we detect that file
177
  // pointer is moved back, we reload quick open data from beginning.
178
0
  if (Method==SEEK_SET && (uint64)Offset<SeekPos && (uint64)Offset<LastReadHeaderPos)
179
0
    Load(QOHeaderPos);
180
181
0
  if (Method==SEEK_SET)
182
0
    SeekPos=Offset;
183
0
  if (Method==SEEK_CUR)
184
0
    SeekPos+=Offset;
185
0
  UnsyncSeekPos=true;
186
187
0
  if (Method==SEEK_END)
188
0
  {
189
0
    Arc->File::Seek(Offset,SEEK_END);
190
0
    SeekPos=Arc->File::Tell();
191
0
    UnsyncSeekPos=false;
192
0
  }
193
0
  return true;
194
265k
}
195
196
197
bool QuickOpen::Tell(int64 *Pos)
198
211k
{
199
211k
  if (!Loaded)
200
211k
    return false;
201
0
  *Pos=SeekPos;
202
0
  return true;
203
211k
}
204
205
206
uint QuickOpen::ReadBuffer()
207
0
{
208
0
  int64 SavePos=Arc->Tell();
209
0
  Arc->File::Seek(RawDataStart+RawDataPos,SEEK_SET);
210
0
  size_t SizeToRead=(size_t)Min(RawDataSize-RawDataPos,MaxBufSize-ReadBufSize);
211
0
  if (Arc->SubHead.Encrypted)
212
0
    SizeToRead &= ~CRYPT_BLOCK_MASK;
213
0
  int ReadSize=0;
214
0
  if (SizeToRead!=0)
215
0
  {
216
0
    ReadSize=Arc->File::Read(Buf+ReadBufSize,SizeToRead);
217
0
    if (ReadSize<=0)
218
0
      ReadSize=0;
219
0
    else
220
0
    {
221
0
#ifndef RAR_NOCRYPT
222
0
      if (Arc->SubHead.Encrypted)
223
0
        Crypt.DecryptBlock(Buf+ReadBufSize,ReadSize & ~CRYPT_BLOCK_MASK);
224
0
#endif
225
0
      RawDataPos+=ReadSize;
226
0
      ReadBufSize+=ReadSize;
227
0
    }
228
0
  }
229
0
  Arc->Seek(SavePos,SEEK_SET);
230
0
  return ReadSize;
231
0
}
232
233
234
// Fill RawRead object from buffer.
235
bool QuickOpen::ReadRaw(RawRead &Raw)
236
0
{
237
0
  if (MaxBufSize-ReadBufPos<0x100) // We are close to end of buffer.
238
0
  {
239
    // Ensure that we have enough data to read CRC and header size.
240
0
    size_t DataLeft=ReadBufSize-ReadBufPos;
241
0
    memcpy(Buf,Buf+ReadBufPos,DataLeft);
242
0
    ReadBufPos=0;
243
0
    ReadBufSize=DataLeft;
244
0
    ReadBuffer();
245
0
  }
246
0
  const size_t FirstReadSize=7;
247
0
  if (ReadBufPos+FirstReadSize>ReadBufSize)
248
0
    return false;
249
0
  Raw.Read(Buf+ReadBufPos,FirstReadSize);
250
0
  ReadBufPos+=FirstReadSize;
251
252
0
  uint SavedCRC=Raw.Get4();
253
0
  uint SizeBytes=Raw.GetVSize(4);
254
0
  uint64 BlockSize=Raw.GetV();
255
0
  int SizeToRead=int(BlockSize);
256
0
  SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any.
257
0
  if (SizeToRead<0 || SizeBytes==0 || BlockSize==0)
258
0
  {
259
0
    Loaded=false; // Invalid data.
260
0
    return false;
261
0
  }
262
263
  // If rest of block data crosses Buf boundary, read it in loop.
264
0
  while (SizeToRead>0)
265
0
  {
266
0
    size_t DataLeft=ReadBufSize-ReadBufPos;
267
0
    size_t CurSizeToRead=Min(DataLeft,(size_t)SizeToRead);
268
0
    Raw.Read(Buf+ReadBufPos,CurSizeToRead);
269
0
    ReadBufPos+=CurSizeToRead;
270
0
    SizeToRead-=int(CurSizeToRead);
271
0
    if (SizeToRead>0) // We read the entire buffer and still need more data.
272
0
    {
273
0
      ReadBufPos=0;
274
0
      ReadBufSize=0;
275
0
      if (ReadBuffer()==0)
276
0
        return false;
277
0
    }
278
0
  }
279
280
0
  return SavedCRC==Raw.GetCRC50();
281
0
}
282
283
284
// Read next cached header.
285
bool QuickOpen::ReadNext()
286
0
{
287
0
  RawRead Raw(NULL);
288
0
  if (!ReadRaw(Raw)) // Read internal quick open header preceding stored block.
289
0
    return false;
290
0
  uint Flags=(uint)Raw.GetV();
291
0
  uint64 Offset=Raw.GetV();
292
0
  size_t HeaderSize=(size_t)Raw.GetV();
293
0
  if (HeaderSize>MAX_HEADER_SIZE_RAR5)
294
0
    return false;
295
0
  LastReadHeader.resize(HeaderSize);
296
0
  Raw.GetB(LastReadHeader.data(),HeaderSize);
297
  // Calculate the absolute position as offset from quick open service header.
298
0
  LastReadHeaderPos=QOHeaderPos-Offset;
299
0
  return true;
300
0
}