/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 | } |