| 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 |