Coverage Report

Created: 2025-06-24 07:14

/src/libfsntfs/libfsntfs/libfsntfs_data_run.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Data run 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_data_run.h"
27
#include "libfsntfs_definitions.h"
28
#include "libfsntfs_io_handle.h"
29
#include "libfsntfs_libcerror.h"
30
#include "libfsntfs_libcnotify.h"
31
32
/* Creates a data run
33
 * Make sure the value data_run is referencing, is set to NULL
34
 * Returns 1 if successful or -1 on error
35
 */
36
int libfsntfs_data_run_initialize(
37
     libfsntfs_data_run_t **data_run,
38
     libcerror_error_t **error )
39
1.05M
{
40
1.05M
  static char *function = "libfsntfs_data_run_initialize";
41
42
1.05M
  if( data_run == NULL )
43
0
  {
44
0
    libcerror_error_set(
45
0
     error,
46
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
47
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
48
0
     "%s: invalid data run.",
49
0
     function );
50
51
0
    return( -1 );
52
0
  }
53
1.05M
  if( *data_run != NULL )
54
0
  {
55
0
    libcerror_error_set(
56
0
     error,
57
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
58
0
     LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
59
0
     "%s: invalid data run value already set.",
60
0
     function );
61
62
0
    return( -1 );
63
0
  }
64
1.05M
  *data_run = memory_allocate_structure(
65
1.05M
               libfsntfs_data_run_t );
66
67
1.05M
  if( *data_run == NULL )
68
0
  {
69
0
    libcerror_error_set(
70
0
     error,
71
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
72
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
73
0
     "%s: unable to create data run.",
74
0
     function );
75
76
0
    goto on_error;
77
0
  }
78
1.05M
  if( memory_set(
79
1.05M
       *data_run,
80
1.05M
       0,
81
1.05M
       sizeof( libfsntfs_data_run_t ) ) == NULL )
82
0
  {
83
0
    libcerror_error_set(
84
0
     error,
85
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
86
0
     LIBCERROR_MEMORY_ERROR_SET_FAILED,
87
0
     "%s: unable to clear data run.",
88
0
     function );
89
90
0
    memory_free(
91
0
     *data_run );
92
93
0
    *data_run = NULL;
94
95
0
    return( -1 );
96
0
  }
97
1.05M
  return( 1 );
98
99
0
on_error:
100
0
  if( *data_run != NULL )
101
0
  {
102
0
    memory_free(
103
0
     *data_run );
104
105
0
    *data_run = NULL;
106
0
  }
107
0
  return( -1 );
108
1.05M
}
109
110
/* Frees a data run
111
 * Returns 1 if successful or -1 on error
112
 */
113
int libfsntfs_data_run_free(
114
     libfsntfs_data_run_t **data_run,
115
     libcerror_error_t **error )
116
1.17M
{
117
1.17M
  static char *function = "libfsntfs_data_run_free";
118
119
1.17M
  if( data_run == NULL )
120
0
  {
121
0
    libcerror_error_set(
122
0
     error,
123
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
124
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
125
0
     "%s: invalid data run.",
126
0
     function );
127
128
0
    return( -1 );
129
0
  }
130
1.17M
  if( *data_run != NULL )
131
1.17M
  {
132
1.17M
    memory_free(
133
1.17M
     *data_run );
134
135
1.17M
    *data_run = NULL;
136
1.17M
  }
137
1.17M
  return( 1 );
138
1.17M
}
139
140
/* Clones a data run
141
 * Returns 1 if successful or -1 on error
142
 */
143
int libfsntfs_data_run_clone(
144
     libfsntfs_data_run_t **destination_data_run,
145
     libfsntfs_data_run_t *source_data_run,
146
     libcerror_error_t **error )
147
119k
{
148
119k
  static char *function = "libfsntfs_data_run_clone";
149
150
119k
  if( destination_data_run == NULL )
151
0
  {
152
0
    libcerror_error_set(
153
0
     error,
154
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
155
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
156
0
     "%s: invalid data run.",
157
0
     function );
158
159
0
    return( -1 );
160
0
  }
161
119k
  if( *destination_data_run != NULL )
162
0
  {
163
0
    libcerror_error_set(
164
0
     error,
165
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
166
0
     LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
167
0
     "%s: invalid destination data run value already set.",
168
0
     function );
169
170
0
    return( -1 );
171
0
  }
172
119k
  if( source_data_run == NULL )
173
0
  {
174
0
    *destination_data_run = source_data_run;
175
176
0
    return( 1 );
177
0
  }
178
119k
  *destination_data_run = memory_allocate_structure(
179
119k
                           libfsntfs_data_run_t );
180
181
119k
  if( *destination_data_run == NULL )
182
0
  {
183
0
    libcerror_error_set(
184
0
     error,
185
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
186
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
187
0
     "%s: unable to create destnation data run.",
188
0
     function );
189
190
0
    goto on_error;
191
0
  }
192
119k
  if( memory_copy(
193
119k
       *destination_data_run,
194
119k
       source_data_run,
195
119k
       sizeof( libfsntfs_data_run_t ) ) == NULL )
196
0
  {
197
0
    libcerror_error_set(
198
0
     error,
199
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
200
0
     LIBCERROR_MEMORY_ERROR_SET_FAILED,
201
0
     "%s: unable to copy source data run to destination.",
202
0
     function );
203
204
0
    goto on_error;
205
0
  }
206
119k
  return( 1 );
207
208
0
on_error:
209
0
  if( *destination_data_run != NULL )
210
0
  {
211
0
    memory_free(
212
0
     *destination_data_run );
213
214
0
    *destination_data_run = NULL;
215
0
  }
216
0
  return( -1 );
217
119k
}
218
219
/* Reads the data run
220
 * Returns 1 if successful or -1 on error
221
 */
222
ssize_t libfsntfs_data_run_read_data(
223
         libfsntfs_data_run_t *data_run,
224
         libfsntfs_io_handle_t *io_handle,
225
         const uint8_t *data,
226
         size_t data_size,
227
         uint64_t last_cluster_block_number,
228
         libcerror_error_t **error )
229
1.05M
{
230
1.05M
  static char *function               = "libfsntfs_data_run_read_data";
231
1.05M
  size_t data_offset                  = 0;
232
1.05M
  size_t data_run_data_size           = 0;
233
1.05M
  uint64_t number_of_cluster_blocks   = 0;
234
1.05M
  int64_t cluster_block_number        = 0;
235
1.05M
  uint8_t cluster_block_value_size    = 0;
236
1.05M
  uint8_t number_of_blocks_value_size = 0;
237
1.05M
  uint8_t value_index                 = 0;
238
1.05M
  uint8_t value_size_tuple            = 0;
239
240
1.05M
  if( data_run == NULL )
241
0
  {
242
0
    libcerror_error_set(
243
0
     error,
244
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
245
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
246
0
     "%s: invalid data run.",
247
0
     function );
248
249
0
    return( -1 );
250
0
  }
251
1.05M
  if( io_handle == NULL )
252
0
  {
253
0
    libcerror_error_set(
254
0
     error,
255
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
256
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
257
0
     "%s: invalid IO handle.",
258
0
     function );
259
260
0
    return( -1 );
261
0
  }
262
1.05M
  if( data == NULL )
263
0
  {
264
0
    libcerror_error_set(
265
0
     error,
266
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
267
0
     LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
268
0
     "%s: invalid data.",
269
0
     function );
270
271
0
    return( -1 );
272
0
  }
273
1.05M
  if( ( data_size < 1 )
274
1.05M
   || ( data_size > (size_t) SSIZE_MAX ) )
275
0
  {
276
0
    libcerror_error_set(
277
0
     error,
278
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
279
0
     LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
280
0
     "%s: invalid data size value out of bounds.",
281
0
     function );
282
283
0
    return( -1 );
284
0
  }
285
1.05M
  value_size_tuple   = data[ 0 ];
286
1.05M
  number_of_blocks_value_size = value_size_tuple & 0x0f;
287
1.05M
  cluster_block_value_size    = ( value_size_tuple >> 4 ) & 0x0f;
288
1.05M
  data_run_data_size          = 1 + number_of_blocks_value_size + cluster_block_value_size;
289
290
1.05M
  if( data_run_data_size > data_size )
291
17
  {
292
17
    libcerror_error_set(
293
17
     error,
294
17
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
295
17
     LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
296
17
     "%s: data size value too small.",
297
17
     function );
298
299
17
    return( -1 );
300
17
  }
301
#if defined( HAVE_DEBUG_OUTPUT )
302
  if( libcnotify_verbose != 0 )
303
  {
304
    libcnotify_printf(
305
     "%s: data run:\n",
306
     function );
307
    libcnotify_print_data(
308
     data,
309
     data_run_data_size,
310
     0 );
311
  }
312
#endif
313
314
#if defined( HAVE_DEBUG_OUTPUT )
315
  if( libcnotify_verbose != 0 )
316
  {
317
    libcnotify_printf(
318
     "%s: value sizes\t\t\t\t: %" PRIu8 ", %" PRIu8 "\n",
319
     function,
320
     number_of_blocks_value_size,
321
     cluster_block_value_size );
322
  }
323
#endif
324
  /* A number of cluster blocks value size of zero indicates the end of the data runs.
325
   */
326
1.05M
  if( number_of_blocks_value_size == 0 )
327
25.5k
  {
328
#if defined( HAVE_DEBUG_OUTPUT )
329
    if( libcnotify_verbose != 0 )
330
    {
331
      libcnotify_printf(
332
       "\n%s: end of data runs (number of cluster blocks value size is zero).\n\n",
333
       function );
334
    }
335
#endif
336
25.5k
    data_run->start_offset = 0;
337
25.5k
    data_run->size         = 0;
338
25.5k
    data_run->range_flags  = 0;
339
340
25.5k
    return( 1 );
341
25.5k
  }
342
1.03M
  data_offset = 1;
343
344
  /* Determine the number of cluster blocks value
345
   */
346
1.03M
  number_of_cluster_blocks = 0;
347
348
1.03M
  for( value_index = number_of_blocks_value_size;
349
3.08M
       value_index > 0;
350
2.05M
       value_index-- )
351
2.05M
  {
352
2.05M
    number_of_cluster_blocks <<= 8;
353
2.05M
    number_of_cluster_blocks  |= data[ data_offset + value_index - 1 ];
354
2.05M
  }
355
1.03M
  data_offset += number_of_blocks_value_size;
356
357
1.03M
  if( cluster_block_value_size == 0 )
358
970k
  {
359
970k
    data_run->cluster_block_number = 0;
360
970k
    data_run->start_offset         = 0;
361
970k
    data_run->range_flags          = LIBFDATA_RANGE_FLAG_IS_SPARSE;
362
970k
  }
363
62.5k
  else
364
62.5k
  {
365
    /* Determine the cluster block number value
366
     */
367
62.5k
    if( ( last_cluster_block_number != 0 )
368
62.5k
     && ( ( data[ data_offset + cluster_block_value_size - 1 ] & 0x80 ) != 0 ) )
369
7.96k
    {
370
7.96k
      cluster_block_number = -1;
371
7.96k
    }
372
54.6k
    else
373
54.6k
    {
374
54.6k
      cluster_block_number = 0;
375
54.6k
    }
376
62.5k
    for( value_index = cluster_block_value_size;
377
433k
         value_index > 0;
378
370k
         value_index-- )
379
370k
    {
380
370k
      cluster_block_number  = (int64_t) ( (uint64_t) cluster_block_number << 8 );
381
370k
      cluster_block_number |= data[ data_offset + value_index - 1 ];
382
370k
    }
383
62.5k
    data_offset += cluster_block_value_size;
384
385
62.5k
    data_run->cluster_block_number = last_cluster_block_number + cluster_block_number;
386
62.5k
    data_run->start_offset         = (off64_t) ( data_run->cluster_block_number * io_handle->cluster_block_size );
387
62.5k
    data_run->range_flags          = 0;
388
62.5k
  }
389
1.03M
  data_run->size = (size64_t) ( number_of_cluster_blocks * io_handle->cluster_block_size );
390
391
#if defined( HAVE_DEBUG_OUTPUT )
392
  if( libcnotify_verbose != 0 )
393
  {
394
    libcnotify_printf(
395
     "%s: number of cluster blocks\t\t\t: %" PRIu64 " (size: %" PRIu64 ")\n",
396
     function,
397
     number_of_cluster_blocks,
398
     data_run->size );
399
400
    libcnotify_printf(
401
     "%s: cluster block number\t\t\t: %" PRIu64 " (%" PRIi64 ") (offset: 0x%08" PRIx64 ")\n",
402
     function,
403
     data_run->cluster_block_number,
404
     cluster_block_number,
405
     data_run->start_offset );
406
407
    if( ( data_run->range_flags & LIBFDATA_RANGE_FLAG_IS_SPARSE ) != 0 )
408
    {
409
      libcnotify_printf(
410
       "\tIs sparse\n" );
411
    }
412
    libcnotify_printf(
413
     "\n" );
414
  }
415
#endif /* defined( HAVE_DEBUG_OUTPUT ) */
416
417
1.03M
  return( (ssize_t) data_offset );
418
1.05M
}
419