Coverage Report

Created: 2026-02-14 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/sleuthkit/tsk/img/mult_files.cpp
Line
Count
Source
1
2
#include "mult_files.h"
3
4
#include <iomanip>
5
#include <vector>
6
7
namespace {
8
9
// return non-zero if str ends with suffix, ignoring case
10
0
int endsWith(const TSK_TCHAR * str, const TSK_TCHAR * suffix) {
11
0
  return TSTRLEN(str) >= TSTRLEN(suffix) ?
12
0
    TSTRICMP(&str[TSTRLEN(str) - TSTRLEN(suffix)], suffix) == 0 : 0;
13
0
}
14
15
}
16
17
0
std::function<TSK_TSTRING(size_t, TSK_TOSTRINGSTREAM&)> getSegmentPattern(const TSK_TCHAR* first) {
18
0
  const size_t flen = TSTRLEN(first);
19
20
  // zero-padded numeric counter, zero- or one-based:
21
  // [.000,] .001, .002, ... ; [_000,] _001, _002, ...
22
0
  if (first[flen-1] == '0' || first[flen-1] == '1') {
23
0
    const bool zero_based = first[flen-1] == '0';
24
25
    // find left end of zero padding
26
0
    int i;
27
0
    for (i = flen - 2; i >= 0 && first[i] == '0'; --i) ;
28
29
0
    if (first[i] == '.' || first[i] == '_') {
30
0
      const TSK_TSTRING base(first, first + i + 1);
31
0
      const size_t width = flen - (i + 1);
32
33
      // NB: digit overflow is ok; FTK apparently adds a fourth digit
34
      // when there are > 999 segments.
35
0
      return [base, width, zero_based](size_t i, TSK_TOSTRINGSTREAM& os) {
36
0
        os << base << std::setfill(_TSK_T('0')) << std::setw(width)
37
0
           << (i+1-zero_based);
38
0
        return os.str();
39
0
      };
40
0
    }
41
0
  }
42
  // alphabetic counter:
43
  // .aaa, .aab, .aac, ... ; _aaa, _aab, _aac, ... ; xaaa, xaab, xaac, ...
44
0
  else if (first[flen-1] == 'a') {
45
    // find left end of suffix
46
0
    int i;
47
0
    for (i = flen - 2; i >= 0 && first[i] == 'a'; --i) ;
48
49
0
    if (first[i] == '.' || first[i] == '_' || first[i] == 'x') {
50
0
      const TSK_TSTRING base(first);
51
0
      const size_t limit = i;
52
53
0
      return [base, limit](size_t i, TSK_TOSTRINGSTREAM&) {
54
0
        TSK_TSTRING seg(base);
55
0
        for (size_t d = seg.size() - 1; i; i /= 26, --d) {
56
0
          if (d == limit) {
57
            // we've exhausted the counter width
58
0
            return TSK_TSTRING();
59
0
          }
60
0
          seg[d] = i % 26 + 'a';
61
0
        }
62
0
        return seg;
63
0
      };
64
0
    }
65
0
  }
66
  // .dmg: .dmg, .002.dmgpart, .003.dmgpart, ...
67
0
  else if (endsWith(first, _TSK_T(".dmg"))) {
68
0
    const TSK_TSTRING base(first, first + flen - 3);
69
70
0
    return [base](size_t i, TSK_TOSTRINGSTREAM& os) {
71
0
      os << base << std::setfill(_TSK_T('0')) << std::setw(3) << (i+1)
72
0
         << ".dmgpart";
73
0
      return os.str();
74
0
    };
75
0
  }
76
  // .bin: .bin, (2).bin, (3).bin, ...
77
0
  else if (endsWith(first, _TSK_T(".bin"))) {
78
0
    const TSK_TSTRING base(first, first + flen - 4);
79
80
0
    return [base](size_t i, TSK_TOSTRINGSTREAM& os) {
81
0
      os << base << '(' << (i+1) << ").bin";
82
0
      return os.str();
83
0
    };
84
0
  }
85
86
  // no pattern detected
87
0
  return nullptr;
88
0
}
89
90
namespace {
91
92
template <class T>
93
0
void free_array(T** a, size_t len) {
94
0
  for (size_t i = 0; i < len; ++i) {
95
0
    free(a[i]);
96
0
  }
97
0
  free(a);
98
0
}
99
100
0
TSK_TCHAR** str_vec_to_array(const std::vector<TSK_TSTRING>& vec) {
101
0
  const size_t count = vec.size();
102
103
0
  TSK_TCHAR** arr = (TSK_TCHAR**) tsk_malloc(count * sizeof(TSK_TCHAR*));
104
0
  if (!arr) {
105
0
    return nullptr;
106
0
  }
107
108
0
  for (size_t i = 0; i < count; ++i) {
109
0
    const size_t len = vec[i].size() + 1;
110
0
    arr[i] = (TSK_TCHAR*) tsk_malloc(len * sizeof(TSK_TCHAR));
111
0
    if (!arr[i]) {
112
0
      free_array(arr, i);
113
0
      return nullptr;
114
0
    }
115
0
    TSTRNCPY(arr[i], vec[i].c_str(), len);
116
0
  }
117
118
0
  return arr;
119
0
}
120
121
0
bool add_if_exists(const TSK_TSTRING& name, std::vector<TSK_TSTRING>& names) {
122
0
  struct STAT_STR stat_buf;
123
124
  // does the file exist?
125
0
  if (TSTAT(name.c_str(), &stat_buf) < 0) {
126
0
    return false;
127
0
  }
128
129
0
  if (tsk_verbose) {
130
0
    tsk_fprintf(stderr, "tsk_img_findFiles: %" PRIttocTSK " found\n", name.c_str());
131
0
  }
132
133
0
  names.push_back(name);
134
0
  return true;
135
0
}
136
137
}
138
139
/**
140
 * @param a_startingName First name in the list (must be full name)
141
 * @param [out] a_numFound Number of images that are in returned list
142
 * @returns array of names that caller must free (NULL on error or if supplied file does not exist)
143
 */
144
TSK_TCHAR **
145
tsk_img_findFiles(const TSK_TCHAR * a_startingName, int *a_numFound)
146
0
{
147
0
  TSK_TCHAR** nlist = nullptr;
148
0
  std::vector<TSK_TSTRING> names;
149
0
  *a_numFound = 0;
150
151
  // get the first segment
152
0
  if (add_if_exists(a_startingName, names)) {
153
    // look for a pattern
154
0
    const auto pfunc = getSegmentPattern(a_startingName);
155
0
    if (pfunc) {
156
      // found a pattern, look for subsequent segments
157
0
      TSK_TOSTRINGSTREAM os;
158
0
      for (size_t i = 1; add_if_exists(pfunc(i, os), names); ++i, os.str(_TSK_T("")));
159
0
    }
160
161
    // copy the vector to a C array
162
0
    nlist = str_vec_to_array(names);
163
0
    if (nlist) {
164
0
      *a_numFound = names.size();
165
0
    }
166
0
  }
167
168
0
  if (tsk_verbose) {
169
    tsk_fprintf(stderr, "tsk_img_findFiles: %d total segments found\n", names.size());
170
0
  }
171
0
  return nlist;
172
0
}