Coverage Report

Created: 2026-04-12 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/cache/persistent.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Persistent Bitmap Cache
4
 *
5
 * Copyright 2016 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <freerdp/config.h>
21
22
#include <winpr/crt.h>
23
#include <winpr/stream.h>
24
#include <winpr/assert.h>
25
26
#include <freerdp/freerdp.h>
27
#include <freerdp/constants.h>
28
29
#include <freerdp/cache/persistent.h>
30
31
struct rdp_persistent_cache
32
{
33
  FILE* fp;
34
  BOOL write;
35
  int version;
36
  int count;
37
  char* filename;
38
  BYTE* bmpData;
39
  size_t bmpSize;
40
};
41
42
static const size_t PERSIST_ALIGN = 32;
43
static const char sig_str[] = "RDP8bmp";
44
45
int persistent_cache_get_version(rdpPersistentCache* persistent)
46
0
{
47
0
  WINPR_ASSERT(persistent);
48
0
  return persistent->version;
49
0
}
50
51
int persistent_cache_get_count(rdpPersistentCache* persistent)
52
0
{
53
0
  WINPR_ASSERT(persistent);
54
0
  return persistent->count;
55
0
}
56
57
static int persistent_cache_read_entry_v2(rdpPersistentCache* persistent,
58
                                          PERSISTENT_CACHE_ENTRY* entry)
59
0
{
60
0
  PERSISTENT_CACHE_ENTRY_V2 entry2 = WINPR_C_ARRAY_INIT;
61
62
0
  WINPR_ASSERT(persistent);
63
0
  WINPR_ASSERT(entry);
64
65
0
  if (fread((void*)&entry2, sizeof(entry2), 1, persistent->fp) != 1)
66
0
    return -1;
67
68
0
  entry->key64 = entry2.key64;
69
0
  entry->width = entry2.width;
70
0
  entry->height = entry2.height;
71
0
  entry->size = entry2.width * entry2.height * 4;
72
0
  entry->flags = entry2.flags;
73
74
0
  entry->data = persistent->bmpData;
75
76
0
  if (fread((void*)entry->data, 0x4000, 1, persistent->fp) != 1)
77
0
    return -1;
78
79
0
  return 1;
80
0
}
81
82
static int persistent_cache_write_entry_v2(rdpPersistentCache* persistent,
83
                                           const PERSISTENT_CACHE_ENTRY* entry)
84
0
{
85
0
  PERSISTENT_CACHE_ENTRY_V2 entry2 = WINPR_C_ARRAY_INIT;
86
87
0
  WINPR_ASSERT(persistent);
88
0
  WINPR_ASSERT(entry);
89
0
  entry2.key64 = entry->key64;
90
0
  entry2.width = entry->width;
91
0
  entry2.height = entry->height;
92
0
  entry2.size = entry->size;
93
0
  entry2.flags = entry->flags;
94
95
0
  if (!entry2.flags)
96
0
    entry2.flags = 0x00000011;
97
98
0
  if (fwrite(&entry2, sizeof(entry2), 1, persistent->fp) != 1)
99
0
    return -1;
100
101
0
  if (fwrite(entry->data, entry->size, 1, persistent->fp) != 1)
102
0
    return -1;
103
104
0
  if (0x4000 > entry->size)
105
0
  {
106
0
    const size_t padding = 0x4000 - entry->size;
107
108
0
    if (fwrite(persistent->bmpData, padding, 1, persistent->fp) != 1)
109
0
      return -1;
110
0
  }
111
112
0
  persistent->count++;
113
114
0
  return 1;
115
0
}
116
117
static int persistent_cache_read_v2(rdpPersistentCache* persistent)
118
0
{
119
0
  WINPR_ASSERT(persistent);
120
0
  while (1)
121
0
  {
122
0
    PERSISTENT_CACHE_ENTRY_V2 entry = WINPR_C_ARRAY_INIT;
123
124
0
    if (fread((void*)&entry, sizeof(entry), 1, persistent->fp) != 1)
125
0
      break;
126
127
0
    if (fseek(persistent->fp, 0x4000, SEEK_CUR) != 0)
128
0
      break;
129
130
0
    persistent->count++;
131
0
  }
132
133
0
  return 1;
134
0
}
135
136
static int persistent_cache_read_entry_v3(rdpPersistentCache* persistent,
137
                                          PERSISTENT_CACHE_ENTRY* entry)
138
0
{
139
0
  PERSISTENT_CACHE_ENTRY_V3 entry3 = WINPR_C_ARRAY_INIT;
140
141
0
  WINPR_ASSERT(persistent);
142
0
  WINPR_ASSERT(entry);
143
144
0
  if (fread(&entry3, sizeof(entry3), 1, persistent->fp) != 1)
145
0
    return -1;
146
147
0
  entry->key64 = entry3.key64;
148
0
  entry->width = entry3.width;
149
0
  entry->height = entry3.height;
150
0
  const UINT64 size = 4ull * entry3.width * entry3.height;
151
0
  if (size > UINT32_MAX)
152
0
    return -1;
153
0
  entry->flags = 0;
154
155
0
  if (size > persistent->bmpSize)
156
0
  {
157
0
    persistent->bmpSize = size;
158
0
    BYTE* bmpData = (BYTE*)winpr_aligned_recalloc(persistent->bmpData, persistent->bmpSize,
159
0
                                                  sizeof(BYTE), PERSIST_ALIGN);
160
161
0
    if (!bmpData)
162
0
      return -1;
163
164
0
    persistent->bmpData = bmpData;
165
0
  }
166
0
  entry->size = WINPR_ASSERTING_INT_CAST(UINT32, size);
167
168
0
  entry->data = persistent->bmpData;
169
170
0
  if (fread((void*)entry->data, entry->size, 1, persistent->fp) != 1)
171
0
    return -1;
172
173
0
  return 1;
174
0
}
175
176
static int persistent_cache_write_entry_v3(rdpPersistentCache* persistent,
177
                                           const PERSISTENT_CACHE_ENTRY* entry)
178
0
{
179
0
  PERSISTENT_CACHE_ENTRY_V3 entry3 = WINPR_C_ARRAY_INIT;
180
181
0
  WINPR_ASSERT(persistent);
182
0
  WINPR_ASSERT(entry);
183
184
0
  entry3.key64 = entry->key64;
185
0
  entry3.width = entry->width;
186
0
  entry3.height = entry->height;
187
188
0
  if (fwrite((void*)&entry3, sizeof(entry3), 1, persistent->fp) != 1)
189
0
    return -1;
190
191
0
  if (fwrite((void*)entry->data, entry->size, 1, persistent->fp) != 1)
192
0
    return -1;
193
194
0
  persistent->count++;
195
196
0
  return 1;
197
0
}
198
199
static int persistent_cache_read_v3(rdpPersistentCache* persistent)
200
0
{
201
0
  WINPR_ASSERT(persistent);
202
0
  while (1)
203
0
  {
204
0
    PERSISTENT_CACHE_ENTRY_V3 entry = WINPR_C_ARRAY_INIT;
205
206
0
    if (fread((void*)&entry, sizeof(entry), 1, persistent->fp) != 1)
207
0
      break;
208
209
0
    if (_fseeki64(persistent->fp, (4LL * entry.width * entry.height), SEEK_CUR) != 0)
210
0
      break;
211
212
0
    persistent->count++;
213
0
  }
214
215
0
  return 1;
216
0
}
217
218
int persistent_cache_read_entry(rdpPersistentCache* persistent, PERSISTENT_CACHE_ENTRY* entry)
219
0
{
220
0
  WINPR_ASSERT(persistent);
221
0
  WINPR_ASSERT(entry);
222
223
0
  if (persistent->version == 3)
224
0
    return persistent_cache_read_entry_v3(persistent, entry);
225
0
  else if (persistent->version == 2)
226
0
    return persistent_cache_read_entry_v2(persistent, entry);
227
228
0
  return -1;
229
0
}
230
231
int persistent_cache_write_entry(rdpPersistentCache* persistent,
232
                                 const PERSISTENT_CACHE_ENTRY* entry)
233
0
{
234
0
  WINPR_ASSERT(persistent);
235
0
  WINPR_ASSERT(entry);
236
237
0
  if (persistent->version == 3)
238
0
    return persistent_cache_write_entry_v3(persistent, entry);
239
0
  else if (persistent->version == 2)
240
0
    return persistent_cache_write_entry_v2(persistent, entry);
241
242
0
  return -1;
243
0
}
244
245
static int persistent_cache_open_read(rdpPersistentCache* persistent)
246
0
{
247
0
  BYTE sig[8] = WINPR_C_ARRAY_INIT;
248
0
  int status = 1;
249
0
  long offset = 0;
250
251
0
  WINPR_ASSERT(persistent);
252
0
  persistent->fp = winpr_fopen(persistent->filename, "rb");
253
254
0
  if (!persistent->fp)
255
0
    return -1;
256
257
0
  if (fread(sig, 8, 1, persistent->fp) != 1)
258
0
    return -1;
259
260
0
  if (memcmp(sig, sig_str, sizeof(sig_str)) == 0)
261
0
    persistent->version = 3;
262
0
  else
263
0
    persistent->version = 2;
264
265
0
  (void)fseek(persistent->fp, 0, SEEK_SET);
266
267
0
  if (persistent->version == 3)
268
0
  {
269
0
    PERSISTENT_CACHE_HEADER_V3 header;
270
271
0
    if (fread(&header, sizeof(header), 1, persistent->fp) != 1)
272
0
      return -1;
273
274
0
    status = persistent_cache_read_v3(persistent);
275
0
    offset = sizeof(header);
276
0
  }
277
0
  else
278
0
  {
279
0
    status = persistent_cache_read_v2(persistent);
280
0
    offset = 0;
281
0
  }
282
283
0
  (void)fseek(persistent->fp, offset, SEEK_SET);
284
285
0
  return status;
286
0
}
287
288
static int persistent_cache_open_write(rdpPersistentCache* persistent)
289
0
{
290
0
  WINPR_ASSERT(persistent);
291
292
0
  persistent->fp = winpr_fopen(persistent->filename, "w+b");
293
294
0
  if (!persistent->fp)
295
0
    return -1;
296
297
0
  if (persistent->version == 3)
298
0
  {
299
0
    PERSISTENT_CACHE_HEADER_V3 header = WINPR_C_ARRAY_INIT;
300
0
    memcpy(header.sig, sig_str, MIN(sizeof(header.sig), sizeof(sig_str)));
301
0
    header.flags = 0x00000006;
302
303
0
    if (fwrite(&header, sizeof(header), 1, persistent->fp) != 1)
304
0
      return -1;
305
0
  }
306
307
0
  ZeroMemory(persistent->bmpData, persistent->bmpSize);
308
309
0
  return 1;
310
0
}
311
312
int persistent_cache_open(rdpPersistentCache* persistent, const char* filename, BOOL write,
313
                          UINT32 version)
314
0
{
315
0
  WINPR_ASSERT(persistent);
316
0
  WINPR_ASSERT(filename);
317
0
  persistent->write = write;
318
319
0
  persistent->filename = _strdup(filename);
320
321
0
  if (!persistent->filename)
322
0
    return -1;
323
324
0
  if (persistent->write)
325
0
  {
326
0
    WINPR_ASSERT(version <= INT32_MAX);
327
0
    persistent->version = (int)version;
328
0
    return persistent_cache_open_write(persistent);
329
0
  }
330
331
0
  return persistent_cache_open_read(persistent);
332
0
}
333
334
int persistent_cache_close(rdpPersistentCache* persistent)
335
0
{
336
0
  WINPR_ASSERT(persistent);
337
0
  if (persistent->fp)
338
0
  {
339
0
    (void)fclose(persistent->fp);
340
0
    persistent->fp = nullptr;
341
0
  }
342
343
0
  return 1;
344
0
}
345
346
rdpPersistentCache* persistent_cache_new(void)
347
0
{
348
0
  rdpPersistentCache* persistent = calloc(1, sizeof(rdpPersistentCache));
349
350
0
  if (!persistent)
351
0
    return nullptr;
352
353
0
  persistent->bmpSize = 0x4000;
354
0
  persistent->bmpData = winpr_aligned_calloc(1, persistent->bmpSize, PERSIST_ALIGN);
355
356
0
  if (!persistent->bmpData)
357
0
  {
358
0
    free(persistent);
359
0
    return nullptr;
360
0
  }
361
362
0
  return persistent;
363
0
}
364
365
void persistent_cache_free(rdpPersistentCache* persistent)
366
0
{
367
0
  if (!persistent)
368
0
    return;
369
370
0
  persistent_cache_close(persistent);
371
372
0
  free(persistent->filename);
373
374
0
  winpr_aligned_free(persistent->bmpData);
375
376
0
  free(persistent);
377
0
}