Coverage Report

Created: 2026-08-31 07:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwtcdb/libwtcdb/libwtcdb_file_header.c
Line
Count
Source
1
/*
2
 * File header functions
3
 *
4
 * Copyright (C) 2010-2026, Joachim Metz <joachim.metz@gmail.com>
5
 *
6
 * Refer to AUTHORS for acknowledgements.
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Lesser General Public License as published by
10
 * the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
 */
21
22
#include <common.h>
23
#include <byte_stream.h>
24
#include <memory.h>
25
#include <types.h>
26
27
#include "libwtcdb_debug.h"
28
#include "libwtcdb_definitions.h"
29
#include "libwtcdb_file_header.h"
30
#include "libwtcdb_io_handle.h"
31
#include "libwtcdb_libcerror.h"
32
#include "libwtcdb_libcnotify.h"
33
34
#include "wtcdb_file_header.h"
35
36
/* Creates file header
37
 * Make sure the value file_header is referencing, is set to NULL
38
 * Returns 1 if successful or -1 on error
39
 */
40
int libwtcdb_file_header_initialize(
41
     libwtcdb_file_header_t **file_header,
42
     libcerror_error_t **error )
43
930
{
44
930
  static char *function = "libwtcdb_file_header_initialize";
45
46
930
  if( file_header == NULL )
47
0
  {
48
0
    libcerror_error_set(
49
0
     error,
50
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
51
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
52
0
     "%s: invalid file header.",
53
0
     function );
54
55
0
    return( -1 );
56
0
  }
57
930
  if( *file_header != NULL )
58
0
  {
59
0
    libcerror_error_set(
60
0
     error,
61
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
62
0
     LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
63
0
     "%s: invalid file header value already set.",
64
0
     function );
65
66
0
    return( -1 );
67
0
  }
68
930
  *file_header = memory_allocate_structure(
69
930
                  libwtcdb_file_header_t );
70
71
930
  if( *file_header == NULL )
72
0
  {
73
0
    libcerror_error_set(
74
0
     error,
75
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
76
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
77
0
     "%s: unable to create file header.",
78
0
     function );
79
80
0
    goto on_error;
81
0
  }
82
930
  if( memory_set(
83
930
       *file_header,
84
930
       0,
85
930
       sizeof( libwtcdb_file_header_t ) ) == NULL )
86
0
  {
87
0
    libcerror_error_set(
88
0
     error,
89
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
90
0
     LIBCERROR_MEMORY_ERROR_SET_FAILED,
91
0
     "%s: unable to clear file header.",
92
0
     function );
93
94
0
    goto on_error;
95
0
  }
96
930
  return( 1 );
97
98
0
on_error:
99
0
  if( *file_header != NULL )
100
0
  {
101
0
    memory_free(
102
0
     *file_header );
103
104
0
    *file_header = NULL;
105
0
  }
106
0
  return( -1 );
107
930
}
108
109
/* Frees file header
110
 * Returns 1 if successful or -1 on error
111
 */
112
int libwtcdb_file_header_free(
113
     libwtcdb_file_header_t **file_header,
114
     libcerror_error_t **error )
115
930
{
116
930
  static char *function = "libwtcdb_file_header_free";
117
118
930
  if( file_header == NULL )
119
0
  {
120
0
    libcerror_error_set(
121
0
     error,
122
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
123
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
124
0
     "%s: invalid file header.",
125
0
     function );
126
127
0
    return( -1 );
128
0
  }
129
930
  if( *file_header != NULL )
130
930
  {
131
930
    memory_free(
132
930
     *file_header );
133
134
930
    *file_header = NULL;
135
930
  }
136
930
  return( 1 );
137
930
}
138
139
/* Reads the file header data
140
 * Returns 1 if successful or -1 on error
141
 */
142
int libwtcdb_file_header_read_data(
143
     libwtcdb_file_header_t *file_header,
144
     const uint8_t *data,
145
     size_t data_size,
146
     libcerror_error_t **error )
147
921
{
148
921
  static char *function         = "libwtcdb_file_header_read_data";
149
921
  size_t file_header_data_size  = 0;
150
921
  uint8_t file_type             = 0;
151
921
  uint8_t number_of_cache_types = 0;
152
153
#if defined( HAVE_DEBUG_OUTPUT )
154
  uint64_t value_64bit          = 0;
155
  uint32_t value_32bit          = 0;
156
#endif
157
158
921
  if( file_header == NULL )
159
0
  {
160
0
    libcerror_error_set(
161
0
     error,
162
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
163
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
164
0
     "%s: invalid file header.",
165
0
     function );
166
167
0
    return( -1 );
168
0
  }
169
921
  if( data == NULL )
170
0
  {
171
0
    libcerror_error_set(
172
0
     error,
173
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
174
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
175
0
     "%s: invalid data.",
176
0
     function );
177
178
0
    return( -1 );
179
0
  }
180
921
  if( ( data_size < 8 )
181
921
   || ( data_size > (size_t) SSIZE_MAX ) )
182
0
  {
183
0
    libcerror_error_set(
184
0
     error,
185
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
186
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
187
0
     "%s: invalid data size value out of bounds.",
188
0
     function );
189
190
0
    return( -1 );
191
0
  }
192
921
  if( memory_compare(
193
921
       data,
194
921
       wtcdb_cache_file_signature,
195
921
       4 ) == 0 )
196
581
  {
197
581
    file_type             = LIBWTCDB_FILE_TYPE_CACHE;
198
581
    file_header_data_size = 24;
199
581
  }
200
340
  else if( memory_compare(
201
340
            data,
202
340
            wtcdb_index_file_signature,
203
340
            4 ) == 0 )
204
162
  {
205
162
    file_type             = LIBWTCDB_FILE_TYPE_INDEX_V20;
206
162
    file_header_data_size = 24;
207
162
  }
208
178
  else if( memory_compare(
209
178
            &( data[ 4 ] ),
210
178
            wtcdb_index_file_signature,
211
178
            4 ) == 0 )
212
149
  {
213
149
    file_type             = LIBWTCDB_FILE_TYPE_INDEX_V30;
214
149
    file_header_data_size = 28;
215
149
  }
216
921
  if( data_size < file_header_data_size )
217
3
  {
218
3
    libcerror_error_set(
219
3
     error,
220
3
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
221
3
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
222
3
     "%s: invalid data size value out of bounds.",
223
3
     function );
224
225
3
    return( -1 );
226
3
  }
227
#if defined( HAVE_DEBUG_OUTPUT )
228
  if( libcnotify_verbose != 0 )
229
  {
230
    libcnotify_printf(
231
     "%s: file header:\n",
232
     function );
233
    libcnotify_print_data(
234
     data,
235
     file_header_data_size,
236
     0 );
237
  }
238
#endif
239
918
  if( file_type == 0 )
240
29
  {
241
29
    libcerror_error_set(
242
29
     error,
243
29
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
244
29
     LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
245
29
     "%s: unsupported file signature.",
246
29
     function );
247
248
29
    return( -1 );
249
29
  }
250
889
  if( ( file_type == LIBWTCDB_FILE_TYPE_CACHE )
251
308
   || ( file_type == LIBWTCDB_FILE_TYPE_INDEX_V20 ) )
252
743
  {
253
743
    byte_stream_copy_to_uint32_little_endian(
254
743
     &( data[ 4 ] ),
255
743
     file_header->format_version );
256
743
  }
257
146
  else if( file_type == LIBWTCDB_FILE_TYPE_INDEX_V30 )
258
146
  {
259
146
    byte_stream_copy_to_uint32_little_endian(
260
146
     &( data[ 8 ] ),
261
146
     file_header->format_version );
262
146
  }
263
889
  if( file_type == LIBWTCDB_FILE_TYPE_CACHE )
264
581
  {
265
581
    byte_stream_copy_to_uint32_little_endian(
266
581
     ( (wtcdb_cache_file_header_t *) data )->cache_type,
267
581
     file_header->cache_type );
268
269
581
    byte_stream_copy_to_uint32_little_endian(
270
581
     ( (wtcdb_cache_file_header_t *) data )->first_cache_entry_offset,
271
581
     file_header->first_entry_offset );
272
273
581
    byte_stream_copy_to_uint32_little_endian(
274
581
     ( (wtcdb_cache_file_header_t *) data )->available_cache_entry_offset,
275
581
     file_header->available_cache_entry_offset );
276
277
581
    byte_stream_copy_to_uint32_little_endian(
278
581
     ( (wtcdb_cache_file_header_t *) data )->number_of_cache_entries,
279
581
     file_header->number_of_entries );
280
581
  }
281
308
  else if( file_type == LIBWTCDB_FILE_TYPE_INDEX_V20 )
282
162
  {
283
162
    byte_stream_copy_to_uint32_little_endian(
284
162
     ( (wtcdb_index_file_header_v20_t *) data )->number_of_index_entries,
285
162
     file_header->number_of_entries );
286
162
  }
287
146
  else if( file_type == LIBWTCDB_FILE_TYPE_INDEX_V30 )
288
146
  {
289
146
    byte_stream_copy_to_uint32_little_endian(
290
146
     ( (wtcdb_index_file_header_v30_t *) data )->number_of_index_entries,
291
146
     file_header->number_of_entries );
292
146
  }
293
#if defined( HAVE_DEBUG_OUTPUT )
294
  if( libcnotify_verbose != 0 )
295
  {
296
    if( ( file_type == LIBWTCDB_FILE_TYPE_CACHE )
297
     || ( file_type == LIBWTCDB_FILE_TYPE_INDEX_V20 ) )
298
    {
299
      libcnotify_printf(
300
       "%s: signature\t\t\t\t: %c%c%c%c\n",
301
       function,
302
       data[ 0 ],
303
       data[ 1 ],
304
       data[ 2 ],
305
       data[ 3 ] );
306
    }
307
    else if( file_type == LIBWTCDB_FILE_TYPE_INDEX_V30 )
308
    {
309
      byte_stream_copy_to_uint32_little_endian(
310
       ( (wtcdb_index_file_header_v30_t *) data )->unknown1,
311
       value_32bit );
312
      libcnotify_printf(
313
       "%s: unknown1\t\t\t\t: 0x%08" PRIx32 "\n",
314
       function,
315
       value_32bit );
316
317
      libcnotify_printf(
318
       "%s: signature\t\t\t\t: %c%c%c%c\n",
319
       function,
320
       data[ 4 ],
321
       data[ 5 ],
322
       data[ 6 ],
323
       data[ 7 ] );
324
    }
325
    libcnotify_printf(
326
     "%s: format version\t\t\t\t: %" PRIu32 "\n",
327
     function,
328
     file_header->format_version );
329
330
    if( file_type == LIBWTCDB_FILE_TYPE_CACHE )
331
    {
332
      libcnotify_printf(
333
       "%s: cache type\t\t\t\t: %" PRIu32 "\n",
334
       function,
335
       file_header->cache_type );
336
337
      libcnotify_printf(
338
       "%s: first cache entry offset\t\t: 0x%08" PRIx32 "\n",
339
       function,
340
       file_header->first_entry_offset );
341
342
      libcnotify_printf(
343
       "%s: available cache entry offset\t\t: 0x%08" PRIx32 "\n",
344
       function,
345
       file_header->available_cache_entry_offset );
346
347
      libcnotify_printf(
348
       "%s: number of cache entries\t\t\t: %" PRIu32 "\n",
349
       function,
350
       file_header->number_of_entries );
351
    }
352
    else if( file_type == LIBWTCDB_FILE_TYPE_INDEX_V20 )
353
    {
354
      byte_stream_copy_to_uint32_little_endian(
355
       ( (wtcdb_index_file_header_v20_t *) data )->unknown1,
356
       value_32bit );
357
      libcnotify_printf(
358
       "%s: unknown1\t\t\t\t: 0x%08" PRIx32 "\n",
359
       function,
360
       value_32bit );
361
362
      byte_stream_copy_to_uint32_little_endian(
363
       ( (wtcdb_index_file_header_v20_t *) data )->number_of_index_entries_used,
364
       value_32bit );
365
      libcnotify_printf(
366
       "%s: number of index entries used\t\t: %" PRIu32 "\n",
367
       function,
368
       value_32bit );
369
370
      libcnotify_printf(
371
       "%s: number of index entries\t\t\t: %" PRIu32 "\n",
372
       function,
373
       file_header->number_of_entries );
374
375
      byte_stream_copy_to_uint32_little_endian(
376
       ( (wtcdb_index_file_header_v20_t *) data )->unknown2,
377
       value_32bit );
378
      libcnotify_printf(
379
       "%s: unknown2\t\t\t\t: 0x%08" PRIx32 "\n",
380
       function,
381
       value_32bit );
382
    }
383
    else if( file_type == LIBWTCDB_FILE_TYPE_INDEX_V30 )
384
    {
385
      byte_stream_copy_to_uint64_little_endian(
386
       ( (wtcdb_index_file_header_v30_t *) data )->unknown2,
387
       value_64bit );
388
      libcnotify_printf(
389
       "%s: unknown2\t\t\t\t: 0x%08" PRIx64 "\n",
390
       function,
391
       value_64bit );
392
393
      byte_stream_copy_to_uint32_little_endian(
394
       ( (wtcdb_index_file_header_v30_t *) data )->number_of_index_entries_used,
395
       value_32bit );
396
      libcnotify_printf(
397
       "%s: number of index entries used\t\t: %" PRIu32 "\n",
398
       function,
399
       value_32bit );
400
401
      libcnotify_printf(
402
       "%s: number of index entries\t\t\t: %" PRIu32 "\n",
403
       function,
404
       file_header->number_of_entries );
405
    }
406
    libcnotify_printf(
407
     "\n" );
408
  }
409
#endif /* defined( HAVE_DEBUG_OUTPUT ) */
410
411
889
  if( ( file_header->format_version != 20 )
412
845
   && ( file_header->format_version != 21 )
413
193
   && ( file_header->format_version != 30 )
414
146
   && ( file_header->format_version != 31 )
415
100
   && ( file_header->format_version != 32 ) )
416
55
  {
417
55
    libcerror_error_set(
418
55
     error,
419
55
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
420
55
     LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
421
55
     "%s: unsupported format version: %" PRIu32 ".",
422
55
     function,
423
55
     file_header->format_version );
424
425
55
    return( -1 );
426
55
  }
427
834
  file_header->file_type = file_type;
428
429
834
  if( file_header->file_type == LIBWTCDB_FILE_TYPE_CACHE )
430
537
  {
431
537
    if( ( file_header->format_version == 20 )
432
513
     || ( file_header->format_version == 21 ) )
433
457
    {
434
457
      number_of_cache_types = 5;
435
457
    }
436
80
    else if( file_header->format_version == 30 )
437
28
    {
438
28
      number_of_cache_types = 9;
439
28
    }
440
52
    else if( file_header->format_version == 31 )
441
28
    {
442
28
      number_of_cache_types = 11;
443
28
    }
444
24
    else if( file_header->format_version == 32 )
445
24
    {
446
24
      number_of_cache_types = 14;
447
24
    }
448
537
    if( file_header->cache_type >= number_of_cache_types )
449
45
    {
450
45
      libcerror_error_set(
451
45
       error,
452
45
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
453
45
       LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
454
45
       "%s: unsupported cache type: %" PRIu32 ".",
455
45
       function,
456
45
       file_header->cache_type );
457
458
45
      return( -1 );
459
45
    }
460
492
    if( file_header->first_entry_offset == 0 )
461
175
    {
462
175
      file_header->first_entry_offset = 24;
463
175
    }
464
317
    else if( file_header->first_entry_offset < 24 )
465
4
    {
466
4
      libcerror_error_set(
467
4
       error,
468
4
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
469
4
       LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
470
4
       "%s: unsupported first entry offset: %" PRIu32 ".",
471
4
       function,
472
4
       file_header->first_entry_offset );
473
474
4
      return( -1 );
475
4
    }
476
492
  }
477
297
  else if( file_header->file_type == LIBWTCDB_FILE_TYPE_INDEX_V20 )
478
151
  {
479
151
    file_header->first_entry_offset = sizeof( wtcdb_index_file_header_v20_t );
480
151
  }
481
146
  else if( file_header->file_type == LIBWTCDB_FILE_TYPE_INDEX_V30 )
482
146
  {
483
146
    if( file_header->format_version == 30 )
484
1
    {
485
1
      file_header->first_entry_offset = 0x110;
486
1
    }
487
145
    else if( file_header->format_version == 31 )
488
1
    {
489
1
      file_header->first_entry_offset = 0x78;
490
1
    }
491
144
    else if( file_header->format_version == 32 )
492
2
    {
493
2
      file_header->first_entry_offset = 0xd8;
494
2
    }
495
146
  }
496
785
  return( 1 );
497
834
}
498
499
/* Reads the file header
500
 * Returns 1 if successful or -1 on error
501
 */
502
int libwtcdb_file_header_read_file_io_handle(
503
     libwtcdb_file_header_t *file_header,
504
     libbfio_handle_t *file_io_handle,
505
     libcerror_error_t **error )
506
930
{
507
930
  uint8_t file_header_data[ 32 ];
508
509
930
  static char *function = "libwtcdb_file_header_read_file_io_handle";
510
930
  ssize_t read_count    = 0;
511
512
930
  if( file_header == NULL )
513
0
  {
514
0
    libcerror_error_set(
515
0
     error,
516
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
517
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
518
0
     "%s: invalid file header.",
519
0
     function );
520
521
0
    return( -1 );
522
0
  }
523
#if defined( HAVE_DEBUG_OUTPUT )
524
  if( libcnotify_verbose != 0 )
525
  {
526
    libcnotify_printf(
527
     "%s: reading file header at offset: 0 (0x00000000)\n",
528
     function );
529
  }
530
#endif
531
930
  read_count = libbfio_handle_read_buffer_at_offset(
532
930
                file_io_handle,
533
930
                file_header_data,
534
930
                32,
535
930
                0,
536
930
                error );
537
538
930
  if( read_count < (ssize_t) 24 )
539
9
  {
540
9
    libcerror_error_set(
541
9
     error,
542
9
     LIBCERROR_ERROR_DOMAIN_IO,
543
9
     LIBCERROR_IO_ERROR_READ_FAILED,
544
9
     "%s: unable to read file header data at offset: 0 (0x00000000).",
545
9
     function );
546
547
9
    return( -1 );
548
9
  }
549
921
  if( libwtcdb_file_header_read_data(
550
921
       file_header,
551
921
       file_header_data,
552
921
       (size_t) read_count,
553
921
       error ) != 1 )
554
136
  {
555
136
    libcerror_error_set(
556
136
     error,
557
136
     LIBCERROR_ERROR_DOMAIN_IO,
558
136
     LIBCERROR_IO_ERROR_READ_FAILED,
559
136
     "%s: unable to read file header.",
560
136
     function );
561
562
136
    return( -1 );
563
136
  }
564
785
  return( 1 );
565
921
}
566