GCC Code Coverage Report


Directory: src/
File: src/mpq-source.c
Date: 2026-10-06 13:47:41
Exec Total Coverage
Lines: 237 294 80.6%
Functions: 26 26 100.0%
Branches: 119 194 61.3%

Line Branch Exec Source
1 /*
2 * mpq-source.c -- private random-access archive source implementations.
3 *
4 * Copyright (c) 2026-2026 Maik Broemme <mbroemme@libmpq.org>
5 *
6 * This file is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
10 *
11 * This file is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this file; if not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "mpq-file.h"
25 #include "mpq-mpqe.h"
26 #include "mpq-source.h"
27
28 #include <errno.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/types.h>
33
34 #define LIBMPQ_MPQE_READ_BUFFER_SIZE (LIBMPQ_MPQE_CHUNK_SIZE * 64U)
35
36 typedef struct
37 {
38 FILE *file;
39 uint64_t size;
40 uint8_t owned;
41 } mpq_file_backend_s;
42
43 typedef struct
44 {
45 void *context;
46 libmpq_read_at_fn read_at;
47 uint64_t size;
48 } mpq_custom_backend_s;
49
50 /* Seek through the project offset type without narrowing large file positions. */
51 static int32_t
52 35731 file_backend_seek(mpq_file_backend_s *backend, uint64_t offset)
53 {
54 35731 return libmpq__file_seek(backend->file, offset, SEEK_SET);
55 }
56
57 /* Read an exact physical byte range from an ordinary filesystem backend. */
58 static int32_t
59 35731 file_backend_read_at(void *context, uint64_t offset, uint8_t *buffer, size_t size)
60 {
61 35731 mpq_file_backend_s *backend = context;
62
63
2/6
✓ Branch 0 taken 35731 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 35731 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
35731 if (backend == NULL || (buffer == NULL && size != 0))
64 ✗ return LIBMPQ_ERROR_EXIST;
65
2/4
✓ Branch 0 taken 35731 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 35731 times.
35731 if (offset > backend->size || size > backend->size - offset)
66 ✗ return LIBMPQ_ERROR_READ;
67
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35731 times.
35731 if (size == 0)
68 ✗ return 0;
69
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 35731 times.
35731 if (file_backend_seek(backend, offset) != 0)
70 ✗ return LIBMPQ_ERROR_SEEK;
71
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 35731 times.
35731 if (fread(buffer, 1, size, backend->file) != size)
72 ✗ return LIBMPQ_ERROR_READ;
73 35731 return 0;
74 }
75
76 static int32_t
77 1598 file_backend_close(void *context)
78 {
79 1598 mpq_file_backend_s *backend = context;
80 1598 int32_t result = 0;
81
82
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1598 times.
1598 if (backend == NULL)
83 ✗ return 0;
84
4/6
✓ Branch 0 taken 1597 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1597 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1597 times.
1598 if (backend->owned && backend->file != NULL && fclose(backend->file) != 0)
85 ✗ result = LIBMPQ_ERROR_CLOSE;
86 1598 free(backend);
87 1598 return result;
88 }
89
90 static void
91 50 file_backend_discard(void *context)
92 {
93 50 mpq_file_backend_s *backend = context;
94
95
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 if (backend == NULL)
96 ✗ return;
97
3/4
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 37 times.
✗ Branch 3 not taken.
50 if (backend->owned && backend->file != NULL)
98 37 (void)fclose(backend->file);
99 50 free(backend);
100 }
101
102 static int32_t
103 1848 file_backend_identity(void *context, uint64_t *device, uint64_t *inode)
104 {
105 1848 mpq_file_backend_s *backend = context;
106
107
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1848 times.
1848 if (backend == NULL)
108 ✗ return LIBMPQ_ERROR_EXIST;
109 1848 return libmpq__file_identity(backend->file, device, inode);
110 }
111
112 static int32_t
113 183 custom_backend_read_at(void *context, uint64_t offset, uint8_t *buffer, size_t size)
114 {
115 183 mpq_custom_backend_s *backend = context;
116 int32_t result;
117
118
2/4
✓ Branch 0 taken 183 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 183 times.
183 if (backend == NULL || backend->read_at == NULL)
119 ✗ return LIBMPQ_ERROR_EXIST;
120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 183 times.
183 if (offset > INT64_MAX)
121 ✗ return LIBMPQ_ERROR_READ;
122 183 result = backend->read_at(backend->context, (libmpq__off_t)offset, buffer, size);
123
2/2
✓ Branch 0 taken 181 times.
✓ Branch 1 taken 2 times.
183 return result > 0 ? LIBMPQ_ERROR_READ : result;
124 }
125
126 static int32_t
127 28 custom_backend_close(void *context)
128 {
129 28 free(context);
130 28 return 0;
131 }
132
133 static void
134 7 custom_backend_discard(void *context)
135 {
136 7 free(context);
137 7 }
138
139 static int32_t
140 13 custom_backend_clone(void *context, mpq_io_backend_s *clone)
141 {
142 13 mpq_custom_backend_s *source = context;
143 mpq_custom_backend_s *backend;
144
145
2/4
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 13 times.
13 if (source == NULL || clone == NULL)
146 ✗ return LIBMPQ_ERROR_EXIST;
147 13 backend = malloc(sizeof(*backend));
148
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if (backend == NULL)
149 ✗ return LIBMPQ_ERROR_MALLOC;
150 13 *backend = *source;
151 13 memset(clone, 0, sizeof(*clone));
152 13 clone->context = backend;
153 13 clone->size = backend->size;
154 13 clone->read_at = custom_backend_read_at;
155 13 clone->close = custom_backend_close;
156 13 clone->discard = custom_backend_discard;
157 13 clone->clone = custom_backend_clone;
158 13 return 0;
159 }
160
161 static int32_t
162 1648 source_set_file_backend(mpq_source_s *source, FILE *file, uint64_t size, uint8_t owned)
163 {
164 mpq_file_backend_s *backend;
165
166
2/4
✓ Branch 0 taken 1648 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1648 times.
1648 if (source == NULL || file == NULL)
167 ✗ return LIBMPQ_ERROR_EXIST;
168 1648 backend = calloc(1, sizeof(*backend));
169
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1648 times.
1648 if (backend == NULL)
170 ✗ return LIBMPQ_ERROR_MALLOC;
171 1648 backend->file = file;
172 1648 backend->size = size;
173 1648 backend->owned = owned;
174 1648 source->backend.context = backend;
175 1648 source->backend.size = size;
176 1648 source->backend.read_at = file_backend_read_at;
177 1648 source->backend.identity = file_backend_identity;
178 1648 source->backend.close = file_backend_close;
179 1648 source->backend.discard = file_backend_discard;
180 1648 source->size = size;
181 1648 return 0;
182 }
183
184 static int32_t
185 22 source_set_custom_backend(
186 mpq_source_s *source, void *context, libmpq_read_at_fn read_at, uint64_t size
187 )
188 {
189 mpq_custom_backend_s *backend;
190
191
2/4
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 22 times.
22 if (source == NULL || read_at == NULL)
192 ✗ return LIBMPQ_ERROR_EXIST;
193 22 backend = malloc(sizeof(*backend));
194
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (backend == NULL)
195 ✗ return LIBMPQ_ERROR_MALLOC;
196 22 backend->context = context;
197 22 backend->read_at = read_at;
198 22 backend->size = size;
199 22 source->backend.context = backend;
200 22 source->backend.size = size;
201 22 source->backend.read_at = custom_backend_read_at;
202 22 source->backend.close = custom_backend_close;
203 22 source->backend.discard = custom_backend_discard;
204 22 source->backend.clone = custom_backend_clone;
205 22 source->size = size;
206 22 return 0;
207 }
208
209 static int32_t
210 36059 backend_read_at(const mpq_io_backend_s *backend, uint64_t offset, uint8_t *buffer, size_t size)
211 {
212
3/8
✓ Branch 0 taken 36059 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 36059 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 36059 times.
36059 if (backend == NULL || (buffer == NULL && size != 0) || backend->read_at == NULL)
213 ✗ return LIBMPQ_ERROR_EXIST;
214
2/4
✓ Branch 0 taken 36059 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 36059 times.
36059 if (offset > backend->size || size > backend->size - offset)
215 ✗ return LIBMPQ_ERROR_READ;
216
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36059 times.
36059 if (size == 0)
217 ✗ return 0;
218 36059 return backend->read_at(backend->context, offset, buffer, size);
219 }
220
221 /* Open a filesystem backend and capture its immutable size for range validation. */
222 static int32_t
223 1640 libmpq__source_open_common(mpq_source_s **source, const char *path)
224 {
225 libmpq__off_t end;
226 FILE *file;
227 int32_t result;
228
229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1640 times.
1640 if (source == NULL)
230 ✗ return LIBMPQ_ERROR_EXIST;
231 1640 *source = NULL;
232
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1640 times.
1640 if (path == NULL)
233 ✗ return LIBMPQ_ERROR_EXIST;
234 1640 *source = calloc(1, sizeof(**source));
235
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1640 times.
1640 if (*source == NULL)
236 ✗ return LIBMPQ_ERROR_MALLOC;
237 1640 (*source)->allocated = 1;
238 1640 file = libmpq__file_open(path, "rb");
239
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1634 times.
1640 if (file == NULL) {
240 6 free(*source);
241 6 *source = NULL;
242
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 return errno == ENOENT ? LIBMPQ_ERROR_EXIST : LIBMPQ_ERROR_OPEN;
243 }
244
2/4
✓ Branch 1 taken 1634 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1634 times.
3268 if (libmpq__file_seek(file, (libmpq__off_t)0, SEEK_END) < 0 ||
245 1634 (end = (libmpq__off_t)libmpq__file_tell(file)) < 0) {
246 ✗ fclose(file);
247 ✗ free(*source);
248 ✗ *source = NULL;
249 ✗ return LIBMPQ_ERROR_SEEK;
250 }
251 1634 result = source_set_file_backend(*source, file, (uint64_t)end, 1);
252
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1634 times.
1634 if (result != 0) {
253 ✗ fclose(file);
254 ✗ free(*source);
255 ✗ *source = NULL;
256 }
257 1634 return result;
258 }
259
260 /* Adapt a writer's flushed FILE without taking ownership or reopening its path. */
261 int32_t
262 14 libmpq__source_borrow_file(mpq_source_s *source, FILE *file, uint64_t size)
263 {
264
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (source == NULL)
265 ✗ return LIBMPQ_ERROR_EXIST;
266 14 memset(source, 0, sizeof(*source));
267 14 return source_set_file_backend(source, file, size, 0);
268 }
269
270 int32_t
271 1296 libmpq__source_open_file(mpq_source_s **source, const char *path)
272 {
273 1296 return libmpq__source_open_common(source, path);
274 }
275
276 int32_t
277 22 libmpq__source_open_io(
278 mpq_source_s **source, void *context, libmpq_read_at_fn read_at, uint64_t size
279 )
280 {
281 int32_t result;
282
283
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (source == NULL)
284 ✗ return LIBMPQ_ERROR_EXIST;
285 22 *source = NULL;
286
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (read_at == NULL)
287 ✗ return LIBMPQ_ERROR_EXIST;
288 22 *source = calloc(1, sizeof(**source));
289
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (*source == NULL)
290 ✗ return LIBMPQ_ERROR_MALLOC;
291 22 (*source)->allocated = 1;
292 22 result = source_set_custom_backend(*source, context, read_at, size);
293
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (result != 0) {
294 ✗ free(*source);
295 ✗ *source = NULL;
296 }
297 22 return result;
298 }
299
300 int32_t
301 139 libmpq__source_open_mpqe(
302 mpq_source_s **source, const char *path, const uint8_t *auth_code, size_t auth_code_size
303 )
304 {
305 uint8_t key[LIBMPQ_MPQE_CHUNK_SIZE];
306 int32_t result;
307
308
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 139 times.
139 if (source == NULL)
309 ✗ return LIBMPQ_ERROR_EXIST;
310 139 *source = NULL;
311 139 result = libmpq__mpqe_key(key, auth_code, auth_code_size);
312
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 136 times.
139 if (result != 0)
313 3 return result;
314 136 result = libmpq__source_open_common(source, path);
315
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 135 times.
136 if (result != 0) {
316 1 libmpq__mpqe_clear(key, sizeof(key));
317 1 return result;
318 }
319 135 (*source)->mpqe = 1;
320 135 memcpy((*source)->key, key, sizeof(key));
321 135 libmpq__mpqe_clear(key, sizeof(key));
322 135 return 0;
323 }
324
325 int32_t
326 6 libmpq__source_open_mpqe_io(
327 mpq_source_s **source, void *context, libmpq_read_at_fn read_at, uint64_t size,
328 const uint8_t *auth_code, size_t auth_code_size
329 )
330 {
331 uint8_t key[LIBMPQ_MPQE_CHUNK_SIZE];
332 int32_t result;
333
334
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (source == NULL)
335 ✗ return LIBMPQ_ERROR_EXIST;
336 6 *source = NULL;
337 6 result = libmpq__mpqe_key(key, auth_code, auth_code_size);
338
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (result != 0)
339 ✗ return result;
340 6 result = libmpq__source_open_io(source, context, read_at, size);
341
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (result != 0) {
342 ✗ libmpq__mpqe_clear(key, sizeof(key));
343 ✗ return result;
344 }
345 6 (*source)->mpqe = 1;
346 6 memcpy((*source)->key, key, sizeof(key));
347 6 libmpq__mpqe_clear(key, sizeof(key));
348 6 return 0;
349 }
350
351 int32_t
352 221 libmpq__source_clone(mpq_source_s **clone, const mpq_source_s *source, const char *path)
353 {
354 int32_t result;
355
356
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 221 times.
221 if (clone == NULL)
357 ✗ return LIBMPQ_ERROR_EXIST;
358 221 *clone = NULL;
359
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 221 times.
221 if (source == NULL)
360 ✗ return LIBMPQ_ERROR_EXIST;
361
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 208 times.
221 if (source->backend.clone != NULL) {
362 13 *clone = calloc(1, sizeof(**clone));
363
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if (*clone == NULL)
364 ✗ return LIBMPQ_ERROR_MALLOC;
365 13 (*clone)->allocated = 1;
366 13 result = source->backend.clone(source->backend.context, &(*clone)->backend);
367 } else {
368
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 208 times.
208 if (path == NULL)
369 ✗ return LIBMPQ_ERROR_EXIST;
370 208 result = libmpq__source_open_common(clone, path);
371 }
372
373
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 221 times.
221 if (result != 0) {
374 ✗ libmpq__source_discard(*clone);
375 ✗ *clone = NULL;
376 ✗ return result;
377 }
378
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 208 times.
221 if (source->backend.clone != NULL)
379 13 (*clone)->size = (*clone)->backend.size;
380 221 (*clone)->mpqe = source->mpqe;
381
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 178 times.
221 if (source->mpqe)
382 43 memcpy((*clone)->key, source->key, sizeof((*clone)->key));
383 221 return 0;
384 }
385
386 static int32_t
387 3921 source_mpqe_read_at(mpq_source_s *source, uint64_t offset, uint8_t *buffer, size_t size)
388 {
389 3921 size_t copied = 0;
390
391
2/2
✓ Branch 0 taken 4107 times.
✓ Branch 1 taken 3920 times.
8027 while (copied < size) {
392 4107 uint64_t request_offset = offset + copied;
393 4107 uint64_t chunk_offset = request_offset & ~(uint64_t)(LIBMPQ_MPQE_CHUNK_SIZE - 1U);
394 4107 uint8_t chunks[LIBMPQ_MPQE_READ_BUFFER_SIZE] = { 0 };
395 4107 uint64_t remaining = source->size - chunk_offset;
396 4107 uint64_t request_end = offset + size;
397 4107 uint64_t required = request_end - chunk_offset;
398 uint64_t rounded_required;
399 uint64_t physical_u64;
400 size_t physical;
401 size_t decrypt_size;
402 4107 size_t batch_start = (size_t)(request_offset - chunk_offset);
403 size_t available;
404 size_t chunk;
405 int32_t result;
406
407
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4107 times.
4107 if (required > UINT64_MAX - (LIBMPQ_MPQE_CHUNK_SIZE - 1U)) {
408 ✗ libmpq__mpqe_clear(chunks, sizeof(chunks));
409 1 return LIBMPQ_ERROR_SIZE;
410 }
411 4107 rounded_required =
412 4107 (required + (LIBMPQ_MPQE_CHUNK_SIZE - 1U)) & ~(uint64_t)(LIBMPQ_MPQE_CHUNK_SIZE - 1U);
413
2/2
✓ Branch 0 taken 186 times.
✓ Branch 1 taken 3921 times.
4107 if (rounded_required > sizeof(chunks))
414 186 rounded_required = sizeof(chunks);
415 4107 physical_u64 = remaining < rounded_required ? remaining : rounded_required;
416
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4107 times.
4107 if (physical_u64 > sizeof(chunks)) {
417 ✗ libmpq__mpqe_clear(chunks, sizeof(chunks));
418 ✗ return LIBMPQ_ERROR_SIZE;
419 }
420 4107 physical = (size_t)physical_u64;
421 4107 decrypt_size =
422 4107 (physical + (LIBMPQ_MPQE_CHUNK_SIZE - 1U)) & ~(size_t)(LIBMPQ_MPQE_CHUNK_SIZE - 1U);
423
424
2/4
✓ Branch 0 taken 4107 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4107 times.
4107 if (chunk_offset >= source->size || physical <= batch_start) {
425 ✗ libmpq__mpqe_clear(chunks, sizeof(chunks));
426 ✗ return LIBMPQ_ERROR_READ;
427 }
428 4107 result = backend_read_at(&source->backend, chunk_offset, chunks, physical);
429
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4106 times.
4107 if (result != 0) {
430 1 libmpq__mpqe_clear(chunks, sizeof(chunks));
431 1 return result;
432 }
433
2/2
✓ Branch 0 taken 27864 times.
✓ Branch 1 taken 4106 times.
31970 for (chunk = 0; chunk < decrypt_size; chunk += LIBMPQ_MPQE_CHUNK_SIZE) {
434 27864 libmpq__mpqe_transform_chunk(
435 27864 chunks + chunk, source->key, chunk_offset + (uint64_t)chunk
436 );
437 }
438 4106 available = physical - batch_start;
439
2/2
✓ Branch 0 taken 3714 times.
✓ Branch 1 taken 392 times.
4106 if (available > size - copied)
440 3714 available = size - copied;
441 4106 memcpy(buffer + copied, chunks + batch_start, available);
442 4106 copied += available;
443 4106 libmpq__mpqe_clear(chunks, sizeof(chunks));
444 }
445 3920 return 0;
446 }
447
448 int32_t
449 35900 libmpq__source_read_at(mpq_source_s *source, uint64_t offset, uint8_t *buffer, size_t size)
450 {
451
5/8
✓ Branch 0 taken 35900 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 35896 times.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 35900 times.
35900 if (source == NULL || (buffer == NULL && size != 0) || source->backend.read_at == NULL)
452 ✗ return LIBMPQ_ERROR_EXIST;
453
4/4
✓ Branch 0 taken 35899 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 35895 times.
35900 if (offset > source->size || size > source->size - offset)
454 5 return LIBMPQ_ERROR_READ;
455
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 35873 times.
35895 if (size == 0)
456 22 return 0;
457
2/2
✓ Branch 0 taken 3921 times.
✓ Branch 1 taken 31952 times.
35873 if (source->mpqe)
458 3921 return source_mpqe_read_at(source, offset, buffer, size);
459 31952 return backend_read_at(&source->backend, offset, buffer, size);
460 }
461
462 uint64_t
463 1664 libmpq__source_size(const mpq_source_s *source)
464 {
465
1/2
✓ Branch 0 taken 1664 times.
✗ Branch 1 not taken.
1664 return source == NULL ? 0 : source->size;
466 }
467
468 int32_t
469 78 libmpq__source_is_mpqe(const mpq_source_s *source)
470 {
471
3/4
✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 70 times.
78 return source != NULL && source->mpqe;
472 }
473
474 int32_t
475 1882 libmpq__source_file_identity(const mpq_source_s *source, uint64_t *device, uint64_t *inode)
476 {
477
1/2
✓ Branch 0 taken 1882 times.
✗ Branch 1 not taken.
1882 if (device != NULL)
478 1882 *device = 0;
479
1/2
✓ Branch 0 taken 1882 times.
✗ Branch 1 not taken.
1882 if (inode != NULL)
480 1882 *inode = 0;
481
5/8
✓ Branch 0 taken 1882 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1882 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1882 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1848 times.
✓ Branch 7 taken 34 times.
1882 if (source == NULL || device == NULL || inode == NULL || source->backend.identity == NULL ||
482
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1848 times.
1848 source->backend.context == NULL)
483 34 return LIBMPQ_ERROR_EXIST;
484 1848 return source->backend.identity(source->backend.context, device, inode);
485 }
486
487 int32_t
488 1626 libmpq__source_close(mpq_source_s *source)
489 {
490 int32_t result;
491
492
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1626 times.
1626 if (source == NULL)
493 ✗ return LIBMPQ_ERROR_EXIST;
494
1/2
✓ Branch 0 taken 1626 times.
✗ Branch 1 not taken.
1626 result = source->backend.close == NULL ? 0 : source->backend.close(source->backend.context);
495 1626 libmpq__mpqe_clear(source->key, sizeof(source->key));
496
2/2
✓ Branch 0 taken 1625 times.
✓ Branch 1 taken 1 times.
1626 if (source->allocated)
497 1625 free(source);
498 1626 return result;
499 }
500
501 void
502 664 libmpq__source_discard(mpq_source_s *source)
503 {
504
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 664 times.
664 if (source == NULL)
505 ✗ return;
506
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 607 times.
664 if (source->backend.discard != NULL)
507 57 source->backend.discard(source->backend.context);
508
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 607 times.
607 else if (source->backend.close != NULL)
509 ✗ (void)source->backend.close(source->backend.context);
510 664 libmpq__mpqe_clear(source->key, sizeof(source->key));
511
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 620 times.
664 if (source->allocated)
512 44 free(source);
513 }
514