Coverage Report

Created: 2025-06-13 07:22

/src/libfsntfs/libfsntfs/libfsntfs_compressed_block_data_handle.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * The compressed block data handle functions
3
 *
4
 * Copyright (C) 2010-2024, 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 <memory.h>
24
#include <types.h>
25
26
#include "libfsntfs_compressed_block.h"
27
#include "libfsntfs_compressed_block_data_handle.h"
28
#include "libfsntfs_compressed_block_vector.h"
29
#include "libfsntfs_compression.h"
30
#include "libfsntfs_definitions.h"
31
#include "libfsntfs_io_handle.h"
32
#include "libfsntfs_libbfio.h"
33
#include "libfsntfs_libcerror.h"
34
#include "libfsntfs_libcnotify.h"
35
#include "libfsntfs_mft_attribute.h"
36
#include "libfsntfs_unused.h"
37
38
/* Creates compressed block data handle
39
 * Make sure the value data_handle is referencing, is set to NULL
40
 * Returns 1 if successful or -1 on error
41
 */
42
int libfsntfs_compressed_block_data_handle_initialize(
43
     libfsntfs_compressed_block_data_handle_t **data_handle,
44
     libfsntfs_io_handle_t *io_handle,
45
     libfsntfs_mft_attribute_t *mft_attribute,
46
     libcerror_error_t **error )
47
2.13k
{
48
2.13k
  static char *function = "libfsntfs_compressed_block_data_handle_initialize";
49
50
2.13k
  if( data_handle == NULL )
51
0
  {
52
0
    libcerror_error_set(
53
0
     error,
54
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
55
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
56
0
     "%s: invalid data handle.",
57
0
     function );
58
59
0
    return( -1 );
60
0
  }
61
2.13k
  if( *data_handle != NULL )
62
0
  {
63
0
    libcerror_error_set(
64
0
     error,
65
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
66
0
     LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
67
0
     "%s: invalid data handle value already set.",
68
0
     function );
69
70
0
    return( -1 );
71
0
  }
72
2.13k
  if( io_handle == NULL )
73
0
  {
74
0
    libcerror_error_set(
75
0
     error,
76
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
77
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
78
0
     "%s: invalid IO handle.",
79
0
     function );
80
81
0
    return( -1 );
82
0
  }
83
2.13k
  *data_handle = memory_allocate_structure(
84
2.13k
                  libfsntfs_compressed_block_data_handle_t );
85
86
2.13k
  if( *data_handle == NULL )
87
0
  {
88
0
    libcerror_error_set(
89
0
     error,
90
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
91
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
92
0
     "%s: unable to create data handle.",
93
0
     function );
94
95
0
    goto on_error;
96
0
  }
97
2.13k
  if( memory_set(
98
2.13k
       *data_handle,
99
2.13k
       0,
100
2.13k
       sizeof( libfsntfs_compressed_block_data_handle_t ) ) == NULL )
101
0
  {
102
0
    libcerror_error_set(
103
0
     error,
104
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
105
0
     LIBCERROR_MEMORY_ERROR_SET_FAILED,
106
0
     "%s: unable to clear data handle.",
107
0
     function );
108
109
0
    memory_free(
110
0
     *data_handle );
111
112
0
    *data_handle = NULL;
113
114
0
    return( -1 );
115
0
  }
116
2.13k
  if( libfsntfs_compressed_block_vector_initialize(
117
2.13k
       &( ( *data_handle )->compressed_block_vector ),
118
2.13k
       io_handle,
119
2.13k
       mft_attribute,
120
2.13k
       error ) != 1 )
121
822
  {
122
822
    libcerror_error_set(
123
822
     error,
124
822
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
125
822
     LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
126
822
     "%s: unable to create compressed block vector.",
127
822
     function );
128
129
822
    goto on_error;
130
822
  }
131
1.31k
  if( libfdata_vector_get_size(
132
1.31k
       ( *data_handle )->compressed_block_vector,
133
1.31k
       &( ( *data_handle )->data_size ),
134
1.31k
       error ) != 1 )
135
0
  {
136
0
    libcerror_error_set(
137
0
     error,
138
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
139
0
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
140
0
     "%s: unable to retrieve size of compressed block vector.",
141
0
     function );
142
143
0
    goto on_error;
144
0
  }
145
1.31k
  if( libfcache_cache_initialize(
146
1.31k
       &( ( *data_handle )->compressed_block_cache ),
147
1.31k
       LIBFSNTFS_MAXIMUM_CACHE_ENTRIES_COMPRESSED_BLOCKS,
148
1.31k
       error ) != 1 )
149
0
  {
150
0
    libcerror_error_set(
151
0
     error,
152
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
153
0
     LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
154
0
     "%s: unable to create compressed block cache.",
155
0
     function );
156
157
0
    goto on_error;
158
0
  }
159
1.31k
  return( 1 );
160
161
822
on_error:
162
822
  if( *data_handle != NULL )
163
822
  {
164
822
    if( ( *data_handle )->compressed_block_vector != NULL )
165
0
    {
166
0
      libfdata_vector_free(
167
0
       &( ( *data_handle )->compressed_block_vector ),
168
0
       NULL );
169
0
    }
170
822
    memory_free(
171
822
     *data_handle );
172
173
822
    *data_handle = NULL;
174
822
  }
175
822
  return( -1 );
176
1.31k
}
177
178
/* Frees a data handle
179
 * Returns 1 if successful or -1 on error
180
 */
181
int libfsntfs_compressed_block_data_handle_free(
182
     libfsntfs_compressed_block_data_handle_t **data_handle,
183
     libcerror_error_t **error )
184
1.31k
{
185
1.31k
  static char *function = "libfsntfs_compressed_block_data_handle_free";
186
1.31k
  int result            = 1;
187
188
1.31k
  if( data_handle == NULL )
189
0
  {
190
0
    libcerror_error_set(
191
0
     error,
192
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
193
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
194
0
     "%s: invalid data handle.",
195
0
     function );
196
197
0
    return( -1 );
198
0
  }
199
1.31k
  if( *data_handle != NULL )
200
1.31k
  {
201
1.31k
    if( libfcache_cache_free(
202
1.31k
         &( ( *data_handle )->compressed_block_cache ),
203
1.31k
         error ) != 1 )
204
0
    {
205
0
      libcerror_error_set(
206
0
       error,
207
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
208
0
       LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
209
0
       "%s: unable to free compressed_block cache.",
210
0
       function );
211
212
0
      result = -1;
213
0
    }
214
1.31k
    if( libfdata_vector_free(
215
1.31k
         &( ( *data_handle )->compressed_block_vector ),
216
1.31k
         error ) != 1 )
217
0
    {
218
0
      libcerror_error_set(
219
0
       error,
220
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
221
0
       LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
222
0
       "%s: unable to free compressed block vector.",
223
0
       function );
224
225
0
      result = -1;
226
0
    }
227
1.31k
    memory_free(
228
1.31k
     *data_handle );
229
230
1.31k
    *data_handle = NULL;
231
1.31k
  }
232
1.31k
  return( result );
233
1.31k
}
234
235
/* Reads data from the current offset into a compressed block
236
 * Callback for the data stream
237
 * Returns the number of bytes read or -1 on error
238
 */
239
ssize_t libfsntfs_compressed_block_data_handle_read_segment_data(
240
         libfsntfs_compressed_block_data_handle_t *data_handle,
241
         libbfio_handle_t *file_io_handle,
242
         int segment_index LIBFSNTFS_ATTRIBUTE_UNUSED,
243
         int segment_file_index LIBFSNTFS_ATTRIBUTE_UNUSED,
244
         uint8_t *segment_data,
245
         size_t segment_data_size,
246
         uint32_t segment_flags LIBFSNTFS_ATTRIBUTE_UNUSED,
247
         uint8_t read_flags LIBFSNTFS_ATTRIBUTE_UNUSED,
248
         libcerror_error_t **error )
249
38.6k
{
250
38.6k
  libfsntfs_compressed_block_t *compressed_block = NULL;
251
38.6k
  static char *function                          = "libfsntfs_compressed_block_data_handle_read_segment_data";
252
38.6k
  size_t read_size                               = 0;
253
38.6k
  size_t segment_data_offset                     = 0;
254
38.6k
  ssize_t read_count                             = 0;
255
38.6k
  off64_t compressed_block_offset                = 0;
256
257
38.6k
  LIBFSNTFS_UNREFERENCED_PARAMETER( segment_index )
258
38.6k
  LIBFSNTFS_UNREFERENCED_PARAMETER( segment_file_index )
259
38.6k
  LIBFSNTFS_UNREFERENCED_PARAMETER( segment_flags )
260
38.6k
  LIBFSNTFS_UNREFERENCED_PARAMETER( read_flags )
261
262
38.6k
  if( data_handle == NULL )
263
0
  {
264
0
    libcerror_error_set(
265
0
     error,
266
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
267
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
268
0
     "%s: invalid data handle.",
269
0
     function );
270
271
0
    return( -1 );
272
0
  }
273
38.6k
  if( data_handle->current_offset < 0 )
274
0
  {
275
0
    libcerror_error_set(
276
0
     error,
277
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
278
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
279
0
     "%s: invalid data handle - current offset value out of bounds.",
280
0
     function );
281
282
0
    return( -1 );
283
0
  }
284
38.6k
  if( segment_data == NULL )
285
0
  {
286
0
    libcerror_error_set(
287
0
     error,
288
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
289
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
290
0
     "%s: invalid segment data.",
291
0
     function );
292
293
0
    return( -1 );
294
0
  }
295
38.6k
  if( segment_data_size > (size_t) SSIZE_MAX )
296
0
  {
297
0
    libcerror_error_set(
298
0
     error,
299
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
300
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
301
0
     "%s: invalid segment data size value exceeds maximum.",
302
0
     function );
303
304
0
    return( -1 );
305
0
  }
306
38.6k
  if( ( segment_flags & LIBFDATA_RANGE_FLAG_IS_SPARSE ) != 0 )
307
751
  {
308
751
    if( memory_set(
309
751
         segment_data,
310
751
         0,
311
751
         segment_data_size ) == NULL )
312
0
    {
313
0
      libcerror_error_set(
314
0
       error,
315
0
       LIBCERROR_ERROR_DOMAIN_MEMORY,
316
0
       LIBCERROR_MEMORY_ERROR_SET_FAILED,
317
0
       "%s: unable to clear segment data.",
318
0
       function );
319
320
0
      return( -1 );
321
0
    }
322
751
    read_count = (ssize_t) segment_data_size;
323
751
  }
324
37.8k
  else
325
37.8k
  {
326
37.8k
    if( (size64_t) data_handle->current_offset >= data_handle->data_size )
327
2
    {
328
2
      return( 0 );
329
2
    }
330
93.3k
    while( segment_data_size > 0 )
331
55.9k
    {
332
55.9k
      if( libfdata_vector_get_element_value_at_offset(
333
55.9k
           data_handle->compressed_block_vector,
334
55.9k
           (intptr_t *) file_io_handle,
335
55.9k
           (libfdata_cache_t *) data_handle->compressed_block_cache,
336
55.9k
           data_handle->current_offset,
337
55.9k
           &compressed_block_offset,
338
55.9k
           (intptr_t **) &compressed_block,
339
55.9k
           0,
340
55.9k
           error ) != 1 )
341
452
      {
342
452
        libcerror_error_set(
343
452
         error,
344
452
         LIBCERROR_ERROR_DOMAIN_RUNTIME,
345
452
         LIBCERROR_RUNTIME_ERROR_GET_FAILED,
346
452
         "%s: unable to retrieve compressed block at offset: %" PRIi64 " (0x%08" PRIx64 ").",
347
452
         function,
348
452
         data_handle->current_offset,
349
452
         data_handle->current_offset );
350
351
452
        return( -1 );
352
452
      }
353
55.4k
      if( compressed_block == NULL )
354
0
      {
355
0
        libcerror_error_set(
356
0
         error,
357
0
         LIBCERROR_ERROR_DOMAIN_RUNTIME,
358
0
         LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
359
0
         "%s: invalid compressed block.",
360
0
         function );
361
362
0
        return( -1 );
363
0
      }
364
55.4k
      if( compressed_block->data == NULL )
365
0
      {
366
0
        libcerror_error_set(
367
0
         error,
368
0
         LIBCERROR_ERROR_DOMAIN_RUNTIME,
369
0
         LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
370
0
         "%s: invalid compressed block - missing data.",
371
0
         function );
372
373
0
        return( -1 );
374
0
      }
375
55.4k
      if( ( compressed_block_offset < 0 )
376
55.4k
       || ( (size64_t) compressed_block_offset >= compressed_block->data_size ) )
377
0
      {
378
0
        libcerror_error_set(
379
0
         error,
380
0
         LIBCERROR_ERROR_DOMAIN_RUNTIME,
381
0
         LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
382
0
         "%s: invalid compressed block offset value out of bounds.",
383
0
         function );
384
385
0
        return( -1 );
386
0
      }
387
55.4k
      read_size = compressed_block->data_size - compressed_block_offset;
388
389
55.4k
      if( read_size > segment_data_size )
390
37.2k
      {
391
37.2k
        read_size = segment_data_size;
392
37.2k
      }
393
55.4k
      if( memory_copy(
394
55.4k
           &( segment_data[ segment_data_offset ] ),
395
55.4k
           &( ( compressed_block->data )[ compressed_block_offset ] ),
396
55.4k
           read_size ) == NULL )
397
0
      {
398
0
        libcerror_error_set(
399
0
         error,
400
0
         LIBCERROR_ERROR_DOMAIN_MEMORY,
401
0
         LIBCERROR_MEMORY_ERROR_COPY_FAILED,
402
0
         "%s: unable to copy compressed block data.",
403
0
         function );
404
405
0
        return( -1 );
406
0
      }
407
55.4k
      segment_data_offset += read_size;
408
55.4k
      segment_data_size   -= read_size;
409
410
55.4k
      data_handle->current_offset += read_size;
411
412
55.4k
      if( (size64_t) data_handle->current_offset >= data_handle->data_size )
413
32
      {
414
32
        break;
415
32
      }
416
55.4k
    }
417
37.4k
    read_count = (ssize_t) segment_data_offset;
418
37.4k
  }
419
38.1k
  return( read_count );
420
38.6k
}
421
422
/* Seeks a certain offset of the data
423
 * Callback for the data stream
424
 * Returns the offset if seek is successful or -1 on error
425
 */
426
off64_t libfsntfs_compressed_block_data_handle_seek_segment_offset(
427
         libfsntfs_compressed_block_data_handle_t *data_handle,
428
         intptr_t *file_io_handle LIBFSNTFS_ATTRIBUTE_UNUSED,
429
         int segment_index LIBFSNTFS_ATTRIBUTE_UNUSED,
430
         int segment_file_index LIBFSNTFS_ATTRIBUTE_UNUSED,
431
         off64_t segment_offset,
432
         libcerror_error_t **error )
433
38.6k
{
434
38.6k
  static char *function = "libfsntfs_compressed_block_data_handle_seek_segment_offset";
435
436
38.6k
  LIBFSNTFS_UNREFERENCED_PARAMETER( file_io_handle )
437
38.6k
  LIBFSNTFS_UNREFERENCED_PARAMETER( segment_index )
438
38.6k
  LIBFSNTFS_UNREFERENCED_PARAMETER( segment_file_index )
439
440
38.6k
  if( data_handle == NULL )
441
0
  {
442
0
    libcerror_error_set(
443
0
     error,
444
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
445
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
446
0
     "%s: invalid data handle.",
447
0
     function );
448
449
0
    return( -1 );
450
0
  }
451
38.6k
  if( segment_offset < 0 )
452
0
  {
453
0
    libcerror_error_set(
454
0
     error,
455
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
456
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
457
0
     "%s: invalid segment offset value out of bounds.",
458
0
     function );
459
460
0
    return( -1 );
461
0
  }
462
38.6k
  data_handle->current_offset = segment_offset;
463
464
38.6k
  return( segment_offset );
465
38.6k
}
466