Coverage Report

Created: 2026-10-15 06:31

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
WINPR_ATTR_NODISCARD
58
static BOOL persist_cache_get_data(rdpPersistentCache* persistent, PERSISTENT_CACHE_ENTRY* entry)
59
0
{
60
0
  WINPR_ASSERT(persistent);
61
0
  WINPR_ASSERT(entry);
62
63
0
  const UINT64 expected = 4ull * entry->width * entry->height;
64
0
  const UINT64 allocated = MAX(0x4000, expected);
65
0
  if (expected > UINT32_MAX)
66
0
    return FALSE;
67
68
0
  if (allocated > persistent->bmpSize)
69
0
  {
70
0
    BYTE* bmpData = (BYTE*)winpr_aligned_recalloc(persistent->bmpData, allocated, sizeof(BYTE),
71
0
                                                  PERSIST_ALIGN);
72
73
0
    if (!bmpData)
74
0
      return FALSE;
75
0
    persistent->bmpSize = allocated;
76
0
    persistent->bmpData = bmpData;
77
0
  }
78
0
  entry->data = persistent->bmpData;
79
0
  entry->size = WINPR_ASSERTING_INT_CAST(UINT32, expected);
80
0
  return TRUE;
81
0
}
82
83
WINPR_ATTR_NODISCARD
84
static int persistent_cache_read_entry_v2(rdpPersistentCache* persistent,
85
                                          PERSISTENT_CACHE_ENTRY* entry)
86
0
{
87
0
  PERSISTENT_CACHE_ENTRY_V2 entry2 = WINPR_C_ARRAY_INIT;
88
89
0
  WINPR_ASSERT(persistent);
90
0
  WINPR_ASSERT(entry);
91
92
0
  if (fread(&entry2, sizeof(entry2), 1, persistent->fp) != 1)
93
0
    return -1;
94
95
0
  entry->key64 = entry2.key64;
96
0
  entry->width = entry2.width;
97
0
  entry->height = entry2.height;
98
0
  entry->flags = entry2.flags;
99
100
0
  if (!persist_cache_get_data(persistent, entry))
101
0
    return -1;
102
103
0
  if (fread(entry->data, 0x4000, 1, persistent->fp) != 1)
104
0
    return -1;
105
106
0
  return 1;
107
0
}
108
109
WINPR_ATTR_NODISCARD
110
static int persistent_cache_write_entry_v2(rdpPersistentCache* persistent,
111
                                           const PERSISTENT_CACHE_ENTRY* entry)
112
0
{
113
0
  PERSISTENT_CACHE_ENTRY_V2 entry2 = WINPR_C_ARRAY_INIT;
114
115
0
  WINPR_ASSERT(persistent);
116
0
  WINPR_ASSERT(entry);
117
0
  entry2.key64 = entry->key64;
118
0
  entry2.width = entry->width;
119
0
  entry2.height = entry->height;
120
0
  entry2.size = entry->size;
121
0
  entry2.flags = entry->flags;
122
123
0
  if (!entry2.flags)
124
0
    entry2.flags = 0x00000011;
125
126
0
  if (fwrite(&entry2, sizeof(entry2), 1, persistent->fp) != 1)
127
0
    return -1;
128
129
0
  if (fwrite(entry->data, entry->size, 1, persistent->fp) != 1)
130
0
    return -1;
131
132
0
  if (0x4000 > entry->size)
133
0
  {
134
0
    const size_t padding = 0x4000 - entry->size;
135
136
0
    PERSISTENT_CACHE_ENTRY dummyentry = WINPR_C_ARRAY_INIT;
137
0
    if (!persist_cache_get_data(persistent, &dummyentry))
138
0
      return -1;
139
140
0
    if (fwrite(persistent->bmpData, padding, 1, persistent->fp) != 1)
141
0
      return -1;
142
0
  }
143
144
0
  persistent->count++;
145
146
0
  return 1;
147
0
}
148
149
WINPR_ATTR_NODISCARD
150
static int persistent_cache_read_v2(rdpPersistentCache* persistent)
151
0
{
152
0
  WINPR_ASSERT(persistent);
153
0
  while (1)
154
0
  {
155
0
    PERSISTENT_CACHE_ENTRY_V2 entry = WINPR_C_ARRAY_INIT;
156
157
0
    if (fread(&entry, sizeof(entry), 1, persistent->fp) != 1)
158
0
      break;
159
160
0
    if (fseek(persistent->fp, 0x4000, SEEK_CUR) != 0)
161
0
      break;
162
163
0
    persistent->count++;
164
0
  }
165
166
0
  return 1;
167
0
}
168
169
WINPR_ATTR_NODISCARD
170
static int persistent_cache_read_entry_v3(rdpPersistentCache* persistent,
171
                                          PERSISTENT_CACHE_ENTRY* entry)
172
0
{
173
0
  PERSISTENT_CACHE_ENTRY_V3 entry3 = WINPR_C_ARRAY_INIT;
174
175
0
  WINPR_ASSERT(persistent);
176
0
  WINPR_ASSERT(entry);
177
178
0
  if (fread(&entry3, sizeof(entry3), 1, persistent->fp) != 1)
179
0
    return -1;
180
181
0
  entry->key64 = entry3.key64;
182
0
  entry->width = entry3.width;
183
0
  entry->height = entry3.height;
184
0
  entry->flags = 0;
185
0
  if (!persist_cache_get_data(persistent, entry))
186
0
    return -1;
187
188
0
  if (fread(entry->data, entry->size, 1, persistent->fp) != 1)
189
0
    return -1;
190
191
0
  return 1;
192
0
}
193
194
WINPR_ATTR_NODISCARD
195
static int persistent_cache_write_entry_v3(rdpPersistentCache* persistent,
196
                                           const PERSISTENT_CACHE_ENTRY* entry)
197
0
{
198
0
  PERSISTENT_CACHE_ENTRY_V3 entry3 = WINPR_C_ARRAY_INIT;
199
200
0
  WINPR_ASSERT(persistent);
201
0
  WINPR_ASSERT(entry);
202
203
0
  entry3.key64 = entry->key64;
204
0
  entry3.width = entry->width;
205
0
  entry3.height = entry->height;
206
207
0
  if (fwrite((void*)&entry3, sizeof(entry3), 1, persistent->fp) != 1)
208
0
    return -1;
209
210
0
  if (fwrite((void*)entry->data, entry->size, 1, persistent->fp) != 1)
211
0
    return -1;
212
213
0
  persistent->count++;
214
215
0
  return 1;
216
0
}
217
218
WINPR_ATTR_NODISCARD
219
static int persistent_cache_read_v3(rdpPersistentCache* persistent)
220
0
{
221
0
  WINPR_ASSERT(persistent);
222
0
  while (1)
223
0
  {
224
0
    PERSISTENT_CACHE_ENTRY_V3 entry = WINPR_C_ARRAY_INIT;
225
226
0
    if (fread(&entry, sizeof(entry), 1, persistent->fp) != 1)
227
0
      break;
228
229
0
    if (_fseeki64(persistent->fp, (4LL * entry.width * entry.height), SEEK_CUR) != 0)
230
0
      break;
231
232
0
    persistent->count++;
233
0
  }
234
235
0
  return 1;
236
0
}
237
238
int persistent_cache_read_entry(rdpPersistentCache* persistent, PERSISTENT_CACHE_ENTRY* entry)
239
0
{
240
0
  WINPR_ASSERT(persistent);
241
0
  WINPR_ASSERT(entry);
242
243
0
  if (persistent->version == 3)
244
0
    return persistent_cache_read_entry_v3(persistent, entry);
245
0
  else if (persistent->version == 2)
246
0
    return persistent_cache_read_entry_v2(persistent, entry);
247
248
0
  return -1;
249
0
}
250
251
int persistent_cache_write_entry(rdpPersistentCache* persistent,
252
                                 const PERSISTENT_CACHE_ENTRY* entry)
253
0
{
254
0
  WINPR_ASSERT(persistent);
255
0
  WINPR_ASSERT(entry);
256
257
0
  if (persistent->version == 3)
258
0
    return persistent_cache_write_entry_v3(persistent, entry);
259
0
  else if (persistent->version == 2)
260
0
    return persistent_cache_write_entry_v2(persistent, entry);
261
262
0
  return -1;
263
0
}
264
265
WINPR_ATTR_NODISCARD
266
static int persistent_cache_open_read(rdpPersistentCache* persistent)
267
0
{
268
0
  BYTE sig[8] = WINPR_C_ARRAY_INIT;
269
0
  int status = 1;
270
0
  long offset = 0;
271
272
0
  WINPR_ASSERT(persistent);
273
0
  persistent->fp = winpr_fopen(persistent->filename, "rb");
274
275
0
  if (!persistent->fp)
276
0
    return -1;
277
278
0
  if (fread(sig, sizeof(sig), 1, persistent->fp) != 1)
279
0
    return -1;
280
281
0
  if (memcmp(sig, sig_str, sizeof(sig_str)) == 0)
282
0
    persistent->version = 3;
283
0
  else
284
0
    persistent->version = 2;
285
286
0
  (void)fseek(persistent->fp, 0, SEEK_SET);
287
288
0
  if (persistent->version == 3)
289
0
  {
290
0
    PERSISTENT_CACHE_HEADER_V3 header;
291
292
0
    if (fread(&header, sizeof(header), 1, persistent->fp) != 1)
293
0
      return -1;
294
295
0
    status = persistent_cache_read_v3(persistent);
296
0
    offset = sizeof(header);
297
0
  }
298
0
  else
299
0
  {
300
0
    status = persistent_cache_read_v2(persistent);
301
0
    offset = 0;
302
0
  }
303
304
0
  (void)fseek(persistent->fp, offset, SEEK_SET);
305
306
0
  return status;
307
0
}
308
309
WINPR_ATTR_NODISCARD
310
static int persistent_cache_open_write(rdpPersistentCache* persistent)
311
0
{
312
0
  WINPR_ASSERT(persistent);
313
314
0
  persistent->fp = winpr_fopen(persistent->filename, "w+b");
315
316
0
  if (!persistent->fp)
317
0
    return -1;
318
319
0
  if (persistent->version == 3)
320
0
  {
321
0
    PERSISTENT_CACHE_HEADER_V3 header = WINPR_C_ARRAY_INIT;
322
0
    memcpy(header.sig, sig_str, MIN(sizeof(header.sig), sizeof(sig_str)));
323
0
    header.flags = 0x00000006;
324
325
0
    if (fwrite(&header, sizeof(header), 1, persistent->fp) != 1)
326
0
      return -1;
327
0
  }
328
329
0
  PERSISTENT_CACHE_ENTRY dummyentry = WINPR_C_ARRAY_INIT;
330
0
  if (!persist_cache_get_data(persistent, &dummyentry))
331
0
    return -1;
332
333
0
  return 1;
334
0
}
335
336
int persistent_cache_open(rdpPersistentCache* persistent, const char* filename, BOOL write,
337
                          UINT32 version)
338
0
{
339
0
  WINPR_ASSERT(persistent);
340
0
  WINPR_ASSERT(filename);
341
0
  persistent->write = write;
342
343
0
  persistent->filename = _strdup(filename);
344
345
0
  if (!persistent->filename)
346
0
    return -1;
347
348
0
  if (persistent->write)
349
0
  {
350
0
    WINPR_ASSERT(version <= INT32_MAX);
351
0
    persistent->version = (int)version;
352
0
    return persistent_cache_open_write(persistent);
353
0
  }
354
355
0
  return persistent_cache_open_read(persistent);
356
0
}
357
358
int persistent_cache_close(rdpPersistentCache* persistent)
359
0
{
360
0
  WINPR_ASSERT(persistent);
361
0
  if (persistent->fp)
362
0
  {
363
0
    (void)fclose(persistent->fp);
364
0
    persistent->fp = nullptr;
365
0
  }
366
367
0
  return 1;
368
0
}
369
370
rdpPersistentCache* persistent_cache_new(void)
371
0
{
372
0
  rdpPersistentCache* persistent = calloc(1, sizeof(rdpPersistentCache));
373
374
0
  if (!persistent)
375
0
    return nullptr;
376
377
0
  return persistent;
378
0
}
379
380
void persistent_cache_free(rdpPersistentCache* persistent)
381
0
{
382
0
  if (!persistent)
383
0
    return;
384
385
0
  persistent_cache_close(persistent);
386
387
0
  free(persistent->filename);
388
389
0
  winpr_aligned_free(persistent->bmpData);
390
391
0
  free(persistent);
392
0
}