Coverage Report

Created: 2025-12-05 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libesedb/libesedb/libesedb_page_header.c
Line
Count
Source
1
/*
2
 * Page header functions
3
 *
4
 * Copyright (C) 2009-2025, 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 "libesedb_debug.h"
28
#include "libesedb_definitions.h"
29
#include "libesedb_io_handle.h"
30
#include "libesedb_libcerror.h"
31
#include "libesedb_libcnotify.h"
32
#include "libesedb_page_header.h"
33
34
#include "esedb_page.h"
35
36
/* Creates a page header
37
 * Make sure the value page_header is referencing, is set to NULL
38
 * Returns 1 if successful or -1 on error
39
 */
40
int libesedb_page_header_initialize(
41
     libesedb_page_header_t **page_header,
42
     libcerror_error_t **error )
43
12.3k
{
44
12.3k
  static char *function = "libesedb_page_header_initialize";
45
46
12.3k
  if( page_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 page header.",
53
0
     function );
54
55
0
    return( -1 );
56
0
  }
57
12.3k
  if( *page_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 page header value already set.",
64
0
     function );
65
66
0
    return( -1 );
67
0
  }
68
12.3k
  *page_header = memory_allocate_structure(
69
12.3k
                  libesedb_page_header_t );
70
71
12.3k
  if( *page_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 page header.",
78
0
     function );
79
80
0
    goto on_error;
81
0
  }
82
12.3k
  if( memory_set(
83
12.3k
       *page_header,
84
12.3k
       0,
85
12.3k
       sizeof( libesedb_page_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 page header.",
92
0
     function );
93
94
0
    goto on_error;
95
0
  }
96
12.3k
  return( 1 );
97
98
0
on_error:
99
0
  if( *page_header != NULL )
100
0
  {
101
0
    memory_free(
102
0
     *page_header );
103
104
0
    *page_header = NULL;
105
0
  }
106
0
  return( -1 );
107
12.3k
}
108
109
/* Frees a page header
110
 * Returns 1 if successful or -1 on error
111
 */
112
int libesedb_page_header_free(
113
     libesedb_page_header_t **page_header,
114
     libcerror_error_t **error )
115
12.3k
{
116
12.3k
  static char *function = "libesedb_page_header_free";
117
118
12.3k
  if( page_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 page header.",
125
0
     function );
126
127
0
    return( -1 );
128
0
  }
129
12.3k
  if( *page_header != NULL )
130
12.3k
  {
131
12.3k
    memory_free(
132
12.3k
     *page_header );
133
134
12.3k
    *page_header = NULL;
135
12.3k
  }
136
12.3k
  return( 1 );
137
12.3k
}
138
139
/* Reads a page header
140
 * Returns 1 if successful or -1 on error
141
 */
142
int libesedb_page_header_read_data(
143
     libesedb_page_header_t *page_header,
144
     libesedb_io_handle_t *io_handle,
145
     const uint8_t *data,
146
     size_t data_size,
147
     libcerror_error_t **error )
148
12.1k
{
149
12.1k
  static char *function        = "libesedb_page_header_read_data";
150
12.1k
  size_t data_offset           = 0;
151
12.1k
  size_t minimum_data_size     = 0;
152
12.1k
  uint16_t available_data_size = 0;
153
12.1k
  uint8_t has_extended_header  = 0;
154
155
#if defined( HAVE_DEBUG_OUTPUT )
156
  uint64_t value_64bit         = 0;
157
  uint16_t value_16bit         = 0;
158
#endif
159
160
12.1k
  if( page_header == NULL )
161
0
  {
162
0
    libcerror_error_set(
163
0
     error,
164
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
165
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
166
0
     "%s: invalid page header.",
167
0
     function );
168
169
0
    return( -1 );
170
0
  }
171
12.1k
  if( io_handle == NULL )
172
0
  {
173
0
    libcerror_error_set(
174
0
     error,
175
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
176
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
177
0
     "%s: invalid IO handle.",
178
0
     function );
179
180
0
    return( -1 );
181
0
  }
182
12.1k
  if( data == NULL )
183
0
  {
184
0
    libcerror_error_set(
185
0
     error,
186
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
187
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
188
0
     "%s: invalid data.",
189
0
     function );
190
191
0
    return( -1 );
192
0
  }
193
12.1k
  minimum_data_size = sizeof( esedb_page_header_t );
194
195
12.1k
  if( ( io_handle->format_revision >= LIBESEDB_FORMAT_REVISION_EXTENDED_PAGE_HEADER )
196
12.0k
   && ( io_handle->page_size >= 16384 ) )
197
3.18k
  {
198
3.18k
    minimum_data_size  += sizeof( esedb_extended_page_header_t );
199
3.18k
    has_extended_header = 1;
200
3.18k
  }
201
12.1k
  if( ( data_size < minimum_data_size )
202
12.1k
   || ( data_size > (size_t) SSIZE_MAX ) )
203
0
  {
204
0
    libcerror_error_set(
205
0
     error,
206
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
207
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
208
0
     "%s: invalid data size value out of bounds.",
209
0
     function );
210
211
0
    return( -1 );
212
0
  }
213
#if defined( HAVE_DEBUG_OUTPUT )
214
  if( libcnotify_verbose != 0 )
215
  {
216
    libcnotify_printf(
217
     "%s: page header:\n",
218
     function );
219
    libcnotify_print_data(
220
     data,
221
     sizeof( esedb_page_header_t ),
222
     LIBCNOTIFY_PRINT_DATA_FLAG_GROUP_DATA );
223
  }
224
#endif
225
12.1k
  byte_stream_copy_to_uint16_little_endian(
226
12.1k
   ( (esedb_page_header_t *) data )->available_data_size,
227
12.1k
   available_data_size );
228
229
12.1k
  byte_stream_copy_to_uint32_little_endian(
230
12.1k
   ( (esedb_page_header_t *) data )->previous_page,
231
12.1k
   page_header->previous_page_number );
232
12.1k
  byte_stream_copy_to_uint32_little_endian(
233
12.1k
   ( (esedb_page_header_t *) data )->next_page,
234
12.1k
   page_header->next_page_number );
235
12.1k
  byte_stream_copy_to_uint32_little_endian(
236
12.1k
   ( (esedb_page_header_t *) data )->father_data_page_object_identifier,
237
12.1k
   page_header->father_data_page_object_identifier );
238
239
12.1k
  byte_stream_copy_to_uint16_little_endian(
240
12.1k
   ( (esedb_page_header_t *) data )->available_page_tag,
241
12.1k
   page_header->available_page_tag );
242
243
12.1k
  byte_stream_copy_to_uint32_little_endian(
244
12.1k
   ( (esedb_page_header_t *) data )->page_flags,
245
12.1k
   page_header->flags );
246
247
  /* Make sure to read after the page flags
248
   */
249
12.1k
  if( has_extended_header != 0 )
250
3.18k
  {
251
/* TODO store checksum into page header */
252
3.18k
  }
253
9.00k
  else
254
9.00k
  {
255
9.00k
    byte_stream_copy_to_uint32_little_endian(
256
9.00k
     ( (esedb_page_header_t *) data )->xor_checksum,
257
9.00k
     page_header->xor32_checksum );
258
259
9.00k
    if( ( io_handle->format_revision >= LIBESEDB_FORMAT_REVISION_NEW_RECORD_FORMAT )
260
8.88k
     && ( ( page_header->flags & LIBESEDB_PAGE_FLAG_IS_NEW_RECORD_FORMAT ) != 0 ) )
261
3.12k
    {
262
3.12k
      byte_stream_copy_to_uint32_little_endian(
263
3.12k
       ( (esedb_page_header_t *) data )->ecc_checksum,
264
3.12k
       page_header->ecc32_checksum );
265
3.12k
    }
266
5.88k
    else
267
5.88k
    {
268
5.88k
      byte_stream_copy_to_uint32_little_endian(
269
5.88k
       ( (esedb_page_header_t *) data )->page_number,
270
5.88k
       page_header->page_number );
271
5.88k
    }
272
9.00k
  }
273
#if defined( HAVE_DEBUG_OUTPUT )
274
  if( libcnotify_verbose != 0 )
275
  {
276
    if( has_extended_header != 0 )
277
    {
278
/* TODO print checksum from page header */
279
280
      byte_stream_copy_to_uint64_little_endian(
281
       data,
282
       value_64bit );
283
      libcnotify_printf(
284
       "%s: checksum\t\t\t\t\t: 0x%08" PRIx64 "\n",
285
       function,
286
       value_64bit );
287
    }
288
    else
289
    {
290
      libcnotify_printf(
291
       "%s: XOR checksum\t\t\t\t\t: 0x%08" PRIx32 "\n",
292
       function,
293
       page_header->xor32_checksum );
294
295
      if( ( io_handle->format_revision >= LIBESEDB_FORMAT_REVISION_NEW_RECORD_FORMAT )
296
       && ( ( page_header->flags & LIBESEDB_PAGE_FLAG_IS_NEW_RECORD_FORMAT ) != 0 ) )
297
      {
298
        libcnotify_printf(
299
         "%s: ECC checksum\t\t\t\t\t: 0x%08" PRIx32 "\n",
300
         function,
301
         page_header->ecc32_checksum );
302
      }
303
      else
304
      {
305
        libcnotify_printf(
306
         "%s: page number\t\t\t\t\t: %" PRIu32 "\n",
307
         function,
308
         page_header->page_number );
309
      }
310
    }
311
    libcnotify_printf(
312
     "%s: database modification time:\n",
313
     function );
314
    libcnotify_print_data(
315
     ( (esedb_page_header_t *) data )->database_modification_time,
316
     8,
317
     0 );
318
319
    libcnotify_printf(
320
     "%s: previous page number\t\t\t\t: %" PRIu32 "\n",
321
     function,
322
     page_header->previous_page_number );
323
324
    libcnotify_printf(
325
     "%s: next page number\t\t\t\t: %" PRIu32 "\n",
326
     function,
327
     page_header->next_page_number );
328
329
    libcnotify_printf(
330
     "%s: father data page (FDP) object identifier\t: %" PRIu32 "\n",
331
     function,
332
     page_header->father_data_page_object_identifier );
333
334
    libcnotify_printf(
335
     "%s: available data size\t\t\t\t: %" PRIu32 "\n",
336
     function,
337
     available_data_size );
338
339
    byte_stream_copy_to_uint16_little_endian(
340
     ( (esedb_page_header_t *) data )->available_uncommitted_data_size,
341
     value_16bit );
342
    libcnotify_printf(
343
     "%s: available uncommitted data size\t\t\t: %" PRIu16 "\n",
344
     function,
345
     value_16bit );
346
347
    byte_stream_copy_to_uint16_little_endian(
348
     ( (esedb_page_header_t *) data )->available_data_offset,
349
     value_16bit );
350
    libcnotify_printf(
351
     "%s: available data offset\t\t\t\t: %" PRIu16 "\n",
352
     function,
353
     value_16bit );
354
355
    libcnotify_printf(
356
     "%s: available page tag\t\t\t\t: %" PRIu32 "\n",
357
     function,
358
     page_header->available_page_tag );
359
360
    libcnotify_printf(
361
     "%s: page flags\t\t\t\t\t: 0x%08" PRIx32 "\n",
362
     function,
363
     page_header->flags );
364
    libesedb_debug_print_page_flags(
365
     page_header->flags );
366
    libcnotify_printf(
367
     "\n" );
368
  }
369
#endif /* defined( HAVE_DEBUG_OUTPUT ) */
370
371
12.1k
  data_offset = sizeof( esedb_page_header_t );
372
373
12.1k
  if( has_extended_header != 0 )
374
3.18k
  {
375
#if defined( HAVE_DEBUG_OUTPUT )
376
    if( libcnotify_verbose != 0 )
377
    {
378
      libcnotify_printf(
379
       "%s: extended page header:\n",
380
       function );
381
      libcnotify_print_data(
382
       &( data[ data_offset ] ),
383
       40,
384
       LIBCNOTIFY_PRINT_DATA_FLAG_GROUP_DATA );
385
    }
386
#endif
387
#if defined( HAVE_DEBUG_OUTPUT )
388
    if( libcnotify_verbose != 0 )
389
    {
390
      byte_stream_copy_to_uint64_little_endian(
391
       ( (esedb_extended_page_header_t *) &( data[ data_offset ] ) )->checksum1,
392
       value_64bit );
393
      libcnotify_printf(
394
       "%s: checksum1\t\t\t\t\t: 0x%08" PRIx64 "\n",
395
       function,
396
       value_64bit );
397
398
      byte_stream_copy_to_uint64_little_endian(
399
       ( (esedb_extended_page_header_t *) &( data[ data_offset ] ) )->checksum2,
400
       value_64bit );
401
      libcnotify_printf(
402
       "%s: checksum2\t\t\t\t\t: 0x%08" PRIx64 "\n",
403
       function,
404
       value_64bit );
405
406
      byte_stream_copy_to_uint64_little_endian(
407
       ( (esedb_extended_page_header_t *) &( data[ data_offset ] ) )->checksum3,
408
       value_64bit );
409
      libcnotify_printf(
410
       "%s: checksum3\t\t\t\t\t: 0x%08" PRIx64 "\n",
411
       function,
412
       value_64bit );
413
414
      byte_stream_copy_to_uint64_little_endian(
415
       ( (esedb_extended_page_header_t *) &( data[ data_offset ] ) )->page_number,
416
       value_64bit );
417
      libcnotify_printf(
418
       "%s: page number\t\t\t\t\t: %" PRIu64 "\n",
419
       function,
420
       value_64bit );
421
422
      libcnotify_printf(
423
       "\n" );
424
    }
425
#endif /* defined( HAVE_DEBUG_OUTPUT ) */
426
427
3.18k
    data_offset += sizeof( esedb_extended_page_header_t );
428
3.18k
  }
429
12.1k
  page_header->data_size = data_offset;
430
431
12.1k
  return( 1 );
432
12.1k
}
433
434
/* Retrieves the previous page number
435
 * Returns 1 if successful or -1 on error
436
 */
437
int libesedb_page_header_get_previous_page_number(
438
     libesedb_page_header_t *page_header,
439
     uint32_t *previous_page_number,
440
     libcerror_error_t **error )
441
10.2k
{
442
10.2k
  static char *function = "libesedb_page_header_get_previous_page_number";
443
444
10.2k
  if( page_header == NULL )
445
0
  {
446
0
    libcerror_error_set(
447
0
     error,
448
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
449
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
450
0
     "%s: invalid page header.",
451
0
     function );
452
453
0
    return( -1 );
454
0
  }
455
10.2k
  if( previous_page_number == NULL )
456
0
  {
457
0
    libcerror_error_set(
458
0
     error,
459
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
460
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
461
0
     "%s: invalid previous page number.",
462
0
     function );
463
464
0
    return( -1 );
465
0
  }
466
10.2k
  *previous_page_number = page_header->previous_page_number;
467
468
10.2k
  return( 1 );
469
10.2k
}
470
471
/* Retrieves the next page number
472
 * Returns 1 if successful or -1 on error
473
 */
474
int libesedb_page_header_get_next_page_number(
475
     libesedb_page_header_t *page_header,
476
     uint32_t *next_page_number,
477
     libcerror_error_t **error )
478
113k
{
479
113k
  static char *function = "libesedb_page_header_get_next_page_number";
480
481
113k
  if( page_header == NULL )
482
0
  {
483
0
    libcerror_error_set(
484
0
     error,
485
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
486
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
487
0
     "%s: invalid page header.",
488
0
     function );
489
490
0
    return( -1 );
491
0
  }
492
113k
  if( next_page_number == NULL )
493
0
  {
494
0
    libcerror_error_set(
495
0
     error,
496
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
497
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
498
0
     "%s: invalid next page number.",
499
0
     function );
500
501
0
    return( -1 );
502
0
  }
503
113k
  *next_page_number = page_header->next_page_number;
504
505
113k
  return( 1 );
506
113k
}
507
508
/* Retrieves the father data page object identifier
509
 * Returns 1 if successful or -1 on error
510
 */
511
int libesedb_page_header_get_father_data_page_object_identifier(
512
     libesedb_page_header_t *page_header,
513
     uint32_t *father_data_page_object_identifier,
514
     libcerror_error_t **error )
515
0
{
516
0
  static char *function = "libesedb_page_header_get_father_data_page_object_identifier";
517
518
0
  if( page_header == NULL )
519
0
  {
520
0
    libcerror_error_set(
521
0
     error,
522
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
523
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
524
0
     "%s: invalid page header.",
525
0
     function );
526
527
0
    return( -1 );
528
0
  }
529
0
  if( father_data_page_object_identifier == NULL )
530
0
  {
531
0
    libcerror_error_set(
532
0
     error,
533
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
534
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
535
0
     "%s: invalid father data page object identifier.",
536
0
     function );
537
538
0
    return( -1 );
539
0
  }
540
0
  *father_data_page_object_identifier = page_header->father_data_page_object_identifier;
541
542
0
  return( 1 );
543
0
}
544
545
/* Retrieves the flags
546
 * Returns 1 if successful or -1 on error
547
 */
548
int libesedb_page_header_get_flags(
549
     libesedb_page_header_t *page_header,
550
     uint32_t *flags,
551
     libcerror_error_t **error )
552
148k
{
553
148k
  static char *function = "libesedb_page_header_get_flags";
554
555
148k
  if( page_header == NULL )
556
0
  {
557
0
    libcerror_error_set(
558
0
     error,
559
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
560
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
561
0
     "%s: invalid page header.",
562
0
     function );
563
564
0
    return( -1 );
565
0
  }
566
148k
  if( flags == NULL )
567
0
  {
568
0
    libcerror_error_set(
569
0
     error,
570
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
571
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
572
0
     "%s: invalid flags.",
573
0
     function );
574
575
0
    return( -1 );
576
0
  }
577
148k
  *flags = page_header->flags;
578
579
148k
  return( 1 );
580
148k
}
581