Coverage Report

Created: 2025-07-01 06:46

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