Coverage Report

Created: 2026-02-14 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/sleuthkit/tsk/fs/fs_io.c
Line
Count
Source
1
/*
2
 * The Sleuth Kit
3
 *
4
 * Brian Carrier [carrier <at> sleuthkit [dot] org]
5
 * Copyright (c) 2006-2011 Brian Carrier, Basis Technology.  All Rights reserved
6
 * Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
7
 *
8
 * Copyright (c) 1997,1998,1999, International Business Machines
9
 * Corporation and others. All Rights Reserved.
10
 *
11
 *
12
 * LICENSE
13
 *  This software is distributed under the IBM Public License.
14
 * AUTHOR(S)
15
 *  Wietse Venema
16
 *  IBM T.J. Watson Research
17
 *  P.O. Box 704
18
 *  Yorktown Heights, NY 10598, USA
19
--*/
20
21
/** \file fs_io.c
22
 * Contains functions to read data from a disk image and wrapper functions to read file content.
23
 */
24
25
#include <errno.h>
26
27
#include "tsk/pool/tsk_pool.h"
28
29
#include "tsk_fs_i.h"
30
#include "encryptionHelper.h"
31
32
33
/** \internal
34
 * Internal method to deal with calculating correct offset when we have pre and post bytes
35
 * in teh file system blocks (i.e. RAW Cds)
36
 * @param a_fs File system being analyzed
37
 * @param a_off Byte offset into file system (i.e. not offset into image)
38
 * @param a_buf Buffer to write data into
39
 * @param a_len Number of bytes to read
40
 * @returns Number of bytes read or -1 on error
41
 */
42
static ssize_t
43
fs_prepost_read(TSK_FS_INFO * a_fs, TSK_OFF_T a_off, char *a_buf,
44
    size_t a_len)
45
661k
{
46
661k
    TSK_OFF_T cur_off = a_off;
47
661k
    TSK_OFF_T end_off = a_off + a_len;
48
661k
    ssize_t cur_idx = 0;
49
50
    // we need to read block by block so that we can skip the needed pre and post bytes
51
2.37M
    while (cur_off < end_off) {
52
1.71M
        TSK_OFF_T read_off;
53
1.71M
        ssize_t retval2 = 0;
54
1.71M
        TSK_DADDR_T blk = cur_off / a_fs->block_size;
55
1.71M
        size_t read_len = a_fs->block_size - cur_off % a_fs->block_size;
56
57
1.71M
        if ((TSK_OFF_T)read_len > end_off - cur_off)
58
531k
            read_len = (size_t) (end_off - cur_off);
59
60
1.71M
        read_off =
61
1.71M
            a_fs->offset + cur_off + blk * (a_fs->block_pre_size +
62
1.71M
            a_fs->block_post_size) + a_fs->block_pre_size;
63
1.71M
        if (tsk_verbose)
64
0
            fprintf(stderr,
65
0
                "fs_prepost_read: Mapped %" PRIdOFF " to %" PRIdOFF "\n",
66
0
                cur_off, read_off);
67
68
1.71M
        retval2 =
69
1.71M
            tsk_img_read(a_fs->img_info, read_off, &a_buf[cur_idx],
70
1.71M
            read_len);
71
1.71M
        if (retval2 == -1)
72
2.27k
            return -1;
73
1.71M
        else if (retval2 == 0)
74
0
            break;
75
1.71M
        cur_idx += retval2;
76
1.71M
        cur_off += retval2;
77
1.71M
    }
78
658k
    return cur_idx;
79
661k
}
80
81
/**
82
 * \ingroup fslib
83
 * Read arbitrary data from inside of the file system.
84
 * @param a_fs The file system handle.
85
 * @param a_off The byte offset to start reading from (relative to start of file system)
86
 * @param a_buf The buffer to store the block in.
87
 * @param a_len The number of bytes to read
88
 * @return The number of bytes read or -1 on error.
89
 */
90
ssize_t
91
tsk_fs_read(TSK_FS_INFO * a_fs, TSK_OFF_T a_off, char *a_buf, size_t a_len)
92
10.4M
{
93
10.4M
    return tsk_fs_read_decrypt(a_fs, a_off, a_buf, a_len, 0);
94
10.4M
}
95
/**
96
 * \ingroup fslib
97
 * Read arbitrary data from inside of the file system.
98
 * @param a_fs The file system handle.
99
 * @param a_off The byte offset to start reading from (relative to start of file system)
100
 * @param a_buf The buffer to store the block in.
101
 * @param a_len The number of bytes to read
102
 * @param crypto_id Starting block number needed for the XTS IV
103
 * @return The number of bytes read or -1 on error.
104
 */
105
ssize_t
106
tsk_fs_read_decrypt(TSK_FS_INFO * a_fs, TSK_OFF_T a_off, char *a_buf, size_t a_len,
107
    TSK_DADDR_T crypto_id)
108
11.4M
{
109
    // do a sanity check on the read bounds, but only if the block
110
    // value has been set.
111
    // note that this could prevent us from viewing the FS slack...
112
11.4M
    if ((a_fs->last_block_act > 0)
113
8.47M
        && ((TSK_DADDR_T) a_off >=
114
8.47M
            ((a_fs->last_block_act + 1) * a_fs->block_size))) {
115
167k
        tsk_error_reset();
116
167k
        tsk_error_set_errno(TSK_ERR_FS_READ);
117
167k
        if ((TSK_DADDR_T) a_off <
118
167k
            ((a_fs->last_block + 1) * a_fs->block_size))
119
155k
            tsk_error_set_errstr
120
155k
                ("tsk_fs_read: Offset missing in partial image: %"
121
155k
                PRIuDADDR ")", a_off);
122
11.6k
        else
123
11.6k
            tsk_error_set_errstr
124
11.6k
                ("tsk_fs_read: Offset is too large for image: %" PRIuDADDR
125
11.6k
                ")", a_off);
126
167k
        return -1;
127
167k
    }
128
129
    // We need different logic for encrypted file systems
130
11.3M
    if ((a_fs->flags & TSK_FS_INFO_FLAG_ENCRYPTED) && a_fs->block_size) {
131
        // If we're reading on block boundaries and a multiple of block
132
        // sizes, we can just decrypt directly to the buffer.
133
0
        if ((a_off % a_fs->block_size == 0) && (a_len % a_fs->block_size == 0)) {
134
0
           return tsk_fs_read_block_decrypt(a_fs, a_off / a_fs->block_size, a_buf, a_len, crypto_id);
135
0
        }
136
137
        // Since we can only decrypt on block boundaries, we'll need to
138
        // decrypt the blocks and then copy to the output buffer.
139
140
        // Starting address needs to be aligned by block size;
141
0
        TSK_OFF_T bit_magic = a_fs->block_size - 1;
142
0
        TSK_OFF_T start = a_off & ~bit_magic;
143
144
        // Ending address needs to be rounded up to the nearest
145
        // block boundary.
146
0
        TSK_OFF_T end = (a_off + a_len + bit_magic) & ~bit_magic;
147
0
        TSK_OFF_T len = end - start;
148
149
        // Decrypt the blocks to a temp buffer
150
0
        char * temp_buffer = tsk_malloc(len);
151
0
        if (temp_buffer == NULL) {
152
0
            return -1;
153
0
        }
154
155
0
        if (tsk_fs_read_block_decrypt(a_fs, start / a_fs->block_size, temp_buffer,
156
0
                              len, crypto_id) != len) {
157
0
            free(temp_buffer);
158
0
            return -1;
159
0
        }
160
161
        // Copy the decrypted data
162
0
        memcpy(a_buf, temp_buffer + a_off - start, a_len);
163
164
0
        free(temp_buffer);
165
166
0
        return a_len;
167
0
    }
168
169
11.3M
    if (((a_fs->block_pre_size) || (a_fs->block_post_size))
170
661k
        && (a_fs->block_size)) {
171
661k
        return fs_prepost_read(a_fs, a_off, a_buf, a_len);
172
661k
    }
173
10.6M
    else {
174
10.6M
        return tsk_img_read(a_fs->img_info, a_off + a_fs->offset, a_buf,
175
10.6M
            a_len);
176
10.6M
    }
177
11.3M
}
178
179
/**
180
 * \ingroup fslib
181
 * Read a file system block into a char* buffer.
182
 * This is actually a wrapper around the fs_read_random function,
183
 * but it allows the starting location to be specified as a block address.
184
 *
185
 * @param a_fs The file system structure.
186
 * @param a_addr The starting block file system address.
187
 * @param a_buf The char * buffer to store the block data in.
188
 * @param a_len The number of bytes to read (must be a multiple of the block size)
189
 * @return The number of bytes read or -1 on error.
190
 */
191
ssize_t
192
tsk_fs_read_block(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr, char *a_buf,
193
    size_t a_len)
194
54.0k
{
195
54.0k
    return tsk_fs_read_block_decrypt(a_fs, a_addr, a_buf, a_len, 0);
196
54.0k
}
197
198
/**
199
 * \ingroup fslib
200
 * Read a file system block into a char* buffer.
201
 * This is actually a wrapper around the fs_read_random function,
202
 * but it allows the starting location to be specified as a block address.
203
 *
204
 * @param a_fs The file system structure.
205
 * @param a_addr The starting block file system address.
206
 * @param a_buf The char * buffer to store the block data in.
207
 * @param a_len The number of bytes to read (must be a multiple of the block size)
208
 * @param crypto_id Starting block number needed for the XTS IV
209
 * @return The number of bytes read or -1 on error.
210
 */
211
ssize_t
212
tsk_fs_read_block_decrypt(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr, char *a_buf,
213
    size_t a_len, TSK_DADDR_T crypto_id)
214
925k
{
215
925k
    if (a_len % a_fs->block_size) {
216
0
        tsk_error_reset();
217
0
        tsk_error_set_errno(TSK_ERR_FS_READ);
218
0
        tsk_error_set_errstr("tsk_fs_read_block: length %" PRIuSIZE ""
219
0
            " not a multiple of %d", a_len, a_fs->block_size);
220
0
        return -1;
221
0
    }
222
223
925k
    if (a_addr > a_fs->last_block_act) {
224
8.95k
        tsk_error_reset();
225
8.95k
        tsk_error_set_errno(TSK_ERR_FS_READ);
226
8.95k
        if (a_addr <= a_fs->last_block)
227
6.61k
            tsk_error_set_errstr
228
6.61k
                ("tsk_fs_read_block: Address missing in partial image: %"
229
6.61k
                PRIuDADDR ")", a_addr);
230
2.34k
        else
231
2.34k
            tsk_error_set_errstr
232
2.34k
                ("tsk_fs_read_block: Address is too large for image: %"
233
2.34k
                PRIuDADDR ")", a_addr);
234
8.95k
        return -1;
235
8.95k
    }
236
237
#ifdef HAVE_LIBMBEDTLS
238
    if (a_fs->encryption_type == TSK_FS_ENCRYPTION_TYPE_BITLOCKER) {
239
        // Bitlocker moves some sectors from the beginning of the volume
240
        // to another spot later in the volume in addition to encrypting them,
241
        // so we need to use a custom method to read in the encrypted data
242
        // and decrypt it.
243
        TSK_DADDR_T offsetInVolume = (TSK_DADDR_T)(a_addr) * a_fs->block_size;
244
        return read_and_decrypt_bitlocker_blocks(a_fs, offsetInVolume, a_len, a_buf);
245
    }
246
#endif
247
248
916k
    ssize_t ret_len;
249
916k
    if ((a_fs->block_pre_size == 0) && (a_fs->block_post_size == 0)) {
250
916k
        TSK_OFF_T off =
251
916k
            a_fs->offset + (TSK_OFF_T) (a_addr) * a_fs->block_size;
252
253
916k
        ret_len = tsk_img_read(a_fs->img_info, off, a_buf, a_len);
254
916k
    }
255
0
    else {
256
0
        TSK_OFF_T off = (TSK_OFF_T) (a_addr) * a_fs->block_size;
257
0
        ret_len = fs_prepost_read(a_fs, off, a_buf, a_len);
258
0
    }
259
260
916k
    if ((a_fs->flags & TSK_FS_INFO_FLAG_ENCRYPTED)
261
0
        && ret_len > 0
262
0
        && a_fs->decrypt_block) {
263
0
  TSK_DADDR_T i;
264
0
        for (i = 0; i < a_len / a_fs->block_size; i++) {
265
0
            a_fs->decrypt_block(a_fs, crypto_id + i,
266
0
                a_buf + (a_fs->block_size * i));
267
0
        }
268
0
    }
269
270
916k
    return ret_len;
271
925k
}