/src/sleuthkit/tsk/fs/xfs_dent.cpp
Line | Count | Source |
1 | | /* |
2 | | ** The Sleuth Kit |
3 | | ** |
4 | | ** Brian Carrier [carrier <at> sleuthkit [dot] org] |
5 | | ** Copyright (c) 2003-2011 Brian Carrier. All rights reserved |
6 | | ** |
7 | | ** ICS Laboratory [515lab.ics <at> gmail [dot] com] |
8 | | ** Copyright (c) 2019 ICS Laboratory. All rights reserved. |
9 | | ** |
10 | | ** This software is distributed under the Common Public License 1.0 |
11 | | */ |
12 | | |
13 | | #include "tsk_fs_i.h" |
14 | | #include "tsk_xfs.h" |
15 | | |
16 | | /* |
17 | | * Inode numbers in short-form directories can come in two versions, |
18 | | * either 4 bytes or 8 bytes wide. These helpers deal with the |
19 | | * two forms transparently by looking at the headers i8count field. |
20 | | * |
21 | | * For 64-bit inode number the most significant byte must be zero. |
22 | | */ |
23 | | static xfs_ino_t |
24 | | xfs_dir2_sf_get_ino( |
25 | | struct xfs_dir2_sf_hdr *hdr, |
26 | | uint8_t *from) |
27 | 0 | { |
28 | 0 | if (hdr->i8count) |
29 | 0 | return get_unaligned_be64(from) & 0x00ffffffffffffffULL; |
30 | 0 | else |
31 | 0 | return get_unaligned_be32(from); |
32 | 0 | } |
33 | | |
34 | | static xfs_ino_t |
35 | | xfs_dir3_sfe_get_ino( |
36 | | struct xfs_dir2_sf_hdr *hdr, |
37 | | struct xfs_dir2_sf_entry *sfep) |
38 | 0 | { |
39 | 0 | return xfs_dir2_sf_get_ino(hdr, &sfep->name[sfep->namelen + 1]); |
40 | 0 | } |
41 | | |
42 | | static uint8_t |
43 | | xfs_dir3_sfe_get_ftype( |
44 | | struct xfs_dir2_sf_entry *sfep) |
45 | 0 | { |
46 | 0 | uint8_t ftype; |
47 | 0 | ftype = sfep->name[sfep->namelen]; |
48 | 0 | if (ftype >= XFS_DIR3_FT_MAX) |
49 | 0 | return XFS_DIR3_FT_UNKNOWN; |
50 | 0 | return ftype; |
51 | 0 | } |
52 | | |
53 | | static uint8_t |
54 | | xfs_dir3_blockentry_get_ftype( |
55 | | struct xfs_dir2_data_entry *daen) // inumber namelen name ftype tag |
56 | 0 | { |
57 | 0 | uint8_t ftype; |
58 | 0 | ftype = daen->name[daen->namelen]; |
59 | 0 | if (ftype >= XFS_DIR3_FT_MAX) |
60 | 0 | return XFS_DIR3_FT_UNKNOWN; |
61 | 0 | return ftype; |
62 | 0 | } |
63 | | |
64 | | static uint8_t |
65 | | xfs_dent_copy(XFS_INFO * xfs, |
66 | | char *xfs_dent, TSK_FS_NAME *fs_name, TSK_FS_FILE *fs_file) |
67 | 0 | { |
68 | 0 | if (fs_file->meta->content_type == |
69 | 0 | TSK_FS_META_CONTENT_TYPE_XFS_DATA_FORK_SHORTFORM) |
70 | 0 | { |
71 | 0 | xfs_dir2_sf_t *dir2_sf = (xfs_dir2_sf_t*)xfs_dent; |
72 | 0 | xfs_dir2_sf_hdr_t *hdr = (xfs_dir2_sf_hdr_t*)dir2_sf->hdr; |
73 | 0 | xfs_dir2_sf_entry_t *ent = (xfs_dir2_sf_entry_t*)dir2_sf->entry; |
74 | |
|
75 | 0 | strncpy(fs_name->name, (char*)ent->name, ent->namelen); |
76 | 0 | fs_name->name[ent->namelen] = '\0'; |
77 | 0 | fs_name->type = TSK_FS_NAME_TYPE_UNDEF; |
78 | 0 | fs_name->meta_addr = (TSK_INUM_T)xfs_dir3_sfe_get_ino(hdr, ent); |
79 | |
|
80 | 0 | if (ent->namelen >= fs_name->name_size){ |
81 | 0 | tsk_error_reset(); |
82 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
83 | 0 | tsk_error_set_errstr |
84 | 0 | ("xfs_dent_copy: Name Space too Small %d %" PRIuSIZE "", |
85 | 0 | ent->namelen, fs_name->name_size); |
86 | 0 | return 1; |
87 | 0 | } |
88 | | |
89 | 0 | switch (xfs_dir3_sfe_get_ftype(ent)) { |
90 | 0 | case XFS_DE_REG: |
91 | 0 | fs_name->type = TSK_FS_NAME_TYPE_REG; |
92 | 0 | break; |
93 | 0 | case XFS_DE_DIR: |
94 | 0 | fs_name->type = TSK_FS_NAME_TYPE_DIR; |
95 | 0 | break; |
96 | 0 | case XFS_DE_CHR: |
97 | 0 | fs_name->type = TSK_FS_NAME_TYPE_CHR; |
98 | 0 | break; |
99 | 0 | case XFS_DE_BLK: |
100 | 0 | fs_name->type = TSK_FS_NAME_TYPE_BLK; |
101 | 0 | break; |
102 | 0 | case XFS_DE_FIFO: |
103 | 0 | fs_name->type = TSK_FS_NAME_TYPE_FIFO; |
104 | 0 | break; |
105 | 0 | case XFS_DE_SOCK: |
106 | 0 | fs_name->type = TSK_FS_NAME_TYPE_SOCK; |
107 | 0 | break; |
108 | 0 | case XFS_DE_LNK: |
109 | 0 | fs_name->type = TSK_FS_NAME_TYPE_LNK; |
110 | 0 | break; |
111 | 0 | case XFS_DE_UNKNOWN: |
112 | 0 | default: |
113 | 0 | fs_name->type = TSK_FS_NAME_TYPE_UNDEF; |
114 | 0 | break; |
115 | 0 | } |
116 | 0 | } |
117 | 0 | else if (fs_file->meta->content_type == |
118 | 0 | TSK_FS_META_CONTENT_TYPE_XFS_DATA_FORK_EXTENTS) |
119 | 0 | { |
120 | 0 | xfs_dir2_data_entry_t *ent = (xfs_dir2_data_entry_t*)xfs_dent; |
121 | |
|
122 | 0 | strncpy(fs_name->name, (char*)ent->name, ent->namelen); |
123 | 0 | fs_name->name[ent->namelen] = '\0'; |
124 | 0 | fs_name->meta_addr = tsk_getu64(xfs->fs_info.endian, ent->inumber); |
125 | 0 | fs_name->type = TSK_FS_NAME_TYPE_UNDEF; |
126 | |
|
127 | 0 | if (ent->namelen >= fs_name->name_size){ |
128 | 0 | tsk_error_reset(); |
129 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
130 | 0 | tsk_error_set_errstr |
131 | 0 | ("xfs_dent_copy: Name Space too Small %d %" PRIuSIZE "", |
132 | 0 | ent->namelen, fs_name->name_size); |
133 | 0 | return 1; |
134 | 0 | } |
135 | | |
136 | 0 | switch (xfs_dir3_blockentry_get_ftype(ent)) { |
137 | 0 | case XFS_DE_REG: |
138 | 0 | fs_name->type = TSK_FS_NAME_TYPE_REG; |
139 | 0 | break; |
140 | 0 | case XFS_DE_DIR: |
141 | 0 | fs_name->type = TSK_FS_NAME_TYPE_DIR; |
142 | 0 | break; |
143 | 0 | case XFS_DE_CHR: |
144 | 0 | fs_name->type = TSK_FS_NAME_TYPE_CHR; |
145 | 0 | break; |
146 | 0 | case XFS_DE_BLK: |
147 | 0 | fs_name->type = TSK_FS_NAME_TYPE_BLK; |
148 | 0 | break; |
149 | 0 | case XFS_DE_FIFO: |
150 | 0 | fs_name->type = TSK_FS_NAME_TYPE_FIFO; |
151 | 0 | break; |
152 | 0 | case XFS_DE_SOCK: |
153 | 0 | fs_name->type = TSK_FS_NAME_TYPE_SOCK; |
154 | 0 | break; |
155 | 0 | case XFS_DE_LNK: |
156 | 0 | fs_name->type = TSK_FS_NAME_TYPE_LNK; |
157 | 0 | break; |
158 | 0 | case XFS_DE_UNKNOWN: |
159 | 0 | default: |
160 | 0 | fs_name->type = TSK_FS_NAME_TYPE_UNDEF; |
161 | 0 | break; |
162 | 0 | } |
163 | 0 | } |
164 | 0 | else fprintf(stderr, "[i] xfs_dent_copy: xfs.cpp: %d - unsupported metadata type detected\n", __LINE__); |
165 | | |
166 | 0 | fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; |
167 | |
|
168 | 0 | return 0; |
169 | 0 | } |
170 | | |
171 | | /* |
172 | | * Shortform directory ops |
173 | | */ |
174 | | static int |
175 | | xfs_dir2_sf_entsize( |
176 | | struct xfs_dir2_sf_hdr *hdr, |
177 | | int len) |
178 | 0 | { |
179 | 0 | int count = sizeof(struct xfs_dir2_sf_entry); /* namelen + offset */ |
180 | |
|
181 | 0 | count += len; /* name */ |
182 | 0 | count += hdr->i8count ? XFS_INO64_SIZE : XFS_INO32_SIZE; /* ino # */ |
183 | 0 | return count; |
184 | 0 | } |
185 | | |
186 | | static int |
187 | | xfs_dir3_sf_entsize( |
188 | | struct xfs_dir2_sf_hdr *hdr, |
189 | | int len) |
190 | 0 | { |
191 | 0 | return xfs_dir2_sf_entsize(hdr, len) + sizeof(uint8_t); |
192 | 0 | } |
193 | | |
194 | | static struct xfs_dir2_sf_entry * |
195 | | xfs_dir3_sf_nextentry( |
196 | | struct xfs_dir2_sf_hdr *hdr, |
197 | | struct xfs_dir2_sf_entry *sfep) |
198 | 0 | { |
199 | 0 | return (struct xfs_dir2_sf_entry *) |
200 | 0 | ((char *)sfep + xfs_dir3_sf_entsize(hdr, sfep->namelen)); |
201 | 0 | } |
202 | | |
203 | | static TSK_RETVAL_ENUM |
204 | | xfs_dent_parse_shortform(XFS_INFO * xfs, TSK_FS_DIR * a_fs_dir, char *buf) |
205 | 0 | { |
206 | 0 | TSK_FS_INFO *fs = &(xfs->fs_info); |
207 | | |
208 | 0 | TSK_FS_NAME *fs_name; |
209 | 0 | TSK_FS_FILE *fs_file = a_fs_dir->fs_file; |
210 | 0 | xfs_dir2_sf_hdr_t *hdr; |
211 | 0 | xfs_dir2_sf_entry_t *ent; |
212 | | |
213 | 0 | xfs_dir2_sf_t * dir2_sf = (xfs_dir2_sf_t *)tsk_malloc(sizeof(xfs_dir2_sf_t)); |
214 | 0 | hdr = (xfs_dir2_sf_hdr_t*)buf; |
215 | 0 | dir2_sf->hdr = hdr; |
216 | |
|
217 | 0 | if ((fs_name = tsk_fs_name_alloc(XFS_MAXNAMELEN + 1, 0)) == NULL) |
218 | 0 | return TSK_ERR; |
219 | | |
220 | 0 | ent = (xfs_dir2_sf_entry_t*)((char*)(hdr + 1) - (hdr->i8count == 0) * 4); // code of miracle |
221 | | |
222 | 0 | while (1) |
223 | 0 | { |
224 | 0 | uint8_t namelen; |
225 | 0 | uint64_t inode; |
226 | | |
227 | 0 | dir2_sf->entry = ent; |
228 | 0 | namelen = ent->namelen; |
229 | 0 | inode = xfs_dir2_sf_get_ino(hdr, (uint8_t*)ent); |
230 | | |
231 | 0 | if (inode > fs->last_inum || namelen == 0) { |
232 | 0 | break; |
233 | 0 | } |
234 | | |
235 | 0 | if (xfs_dent_copy(xfs, (char*)dir2_sf, fs_name, fs_file)) { |
236 | 0 | tsk_fs_name_free(fs_name); |
237 | 0 | return TSK_ERR; |
238 | 0 | } |
239 | | |
240 | 0 | fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; |
241 | |
|
242 | 0 | if (tsk_fs_dir_add(a_fs_dir, fs_name)) { |
243 | 0 | tsk_fs_name_free(fs_name); |
244 | 0 | return TSK_ERR; |
245 | 0 | } |
246 | | |
247 | 0 | ent = xfs_dir3_sf_nextentry(hdr, ent); |
248 | 0 | } |
249 | 0 | free(dir2_sf); |
250 | 0 | tsk_fs_name_free(fs_name); |
251 | 0 | return TSK_OK; |
252 | 0 | } |
253 | | |
254 | | /* |
255 | | * @param a_is_del Set to 1 if block is from a deleted directory |
256 | | * a_fs_dir = 채워야 할 것, 나머지는 채워져 있는 것 |
257 | | * parse_block = 최종목표: a_fs_dir 채우기 |
258 | | * inode format = local -> shortform |
259 | | * = block -> block |
260 | | * or leaf |
261 | | */ |
262 | | static TSK_RETVAL_ENUM |
263 | | xfs_dent_parse_block(XFS_INFO * xfs, TSK_FS_DIR * a_fs_dir, [[maybe_unused]]uint8_t a_is_del, [[maybe_unused]]TSK_LIST ** list_seen, char *buf,[[maybe_unused]] TSK_OFF_T offset) |
264 | 0 | { |
265 | 0 | TSK_FS_NAME *fs_name; |
266 | |
|
267 | 0 | if ((fs_name = tsk_fs_name_alloc(XFS_MAXNAMELEN + 1, 0)) == NULL) |
268 | 0 | return TSK_ERR; |
269 | | |
270 | 0 | xfs_bmbt_rec_t *rec; |
271 | 0 | xfs_bmbt_irec_t *irec; |
272 | |
|
273 | 0 | rec = (xfs_bmbt_rec_t*)buf; |
274 | 0 | irec = (xfs_bmbt_irec_t*)tsk_malloc(sizeof(xfs_bmbt_irec_t)); |
275 | |
|
276 | 0 | xfs_bmbt_disk_get_all(xfs, rec, irec); |
277 | |
|
278 | 0 | ssize_t len = irec->br_blockcount * tsk_getu32(xfs->fs_info.endian, xfs->fs->sb_blocksize); |
279 | |
|
280 | 0 | char *fbuf = (char*)tsk_malloc(sizeof(char) * len); |
281 | |
|
282 | 0 | struct xfs_dir3_data_hdr *hdr = (struct xfs_dir3_data_hdr*)fbuf; |
283 | | |
284 | | // sanity check |
285 | 0 | if (hdr->hdr.magic != 0x33424458) { // XDB3 |
286 | | // Trick to explore unalloc dent |
287 | 0 | a_fs_dir->fs_file->meta->content_type = TSK_FS_META_CONTENT_TYPE_XFS_DATA_FORK_SHORTFORM; |
288 | 0 | if (xfs_dent_parse_shortform(xfs, a_fs_dir, buf) == TSK_OK){ |
289 | 0 | return TSK_OK; |
290 | 0 | }else{ |
291 | 0 | fprintf(stderr, "[i] xfs_dent_parse_block: xfs_dent.cpp: %d - not a dir2_data_hdr: %8x\n", |
292 | 0 | __LINE__, hdr->hdr.magic); |
293 | 0 | return TSK_ERR; |
294 | 0 | } |
295 | 0 | } |
296 | | |
297 | 0 | xfs_dir2_data_entry_t *ent = (xfs_dir2_data_entry_t*)((char*)(hdr + 1) + 32); // magically should be happened |
298 | |
|
299 | 0 | while (true) |
300 | 0 | { |
301 | 0 | if (ent->namelen == 0) |
302 | 0 | break; |
303 | | |
304 | 0 | if (xfs_dent_copy(xfs, (char*)ent, fs_name, a_fs_dir->fs_file)) { |
305 | 0 | tsk_fs_name_free(fs_name); |
306 | 0 | return TSK_ERR; |
307 | 0 | } |
308 | | |
309 | 0 | fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; |
310 | |
|
311 | 0 | if (tsk_fs_dir_add(a_fs_dir, fs_name)) { |
312 | 0 | tsk_fs_name_free(fs_name); |
313 | 0 | return TSK_ERR; |
314 | 0 | } |
315 | | |
316 | 0 | ent = xfs_dir2_data_nextentry(ent); |
317 | 0 | } |
318 | | |
319 | 0 | return TSK_OK; |
320 | 0 | } |
321 | | |
322 | | static TSK_RETVAL_ENUM |
323 | | xfs_dent_parse(XFS_INFO * xfs, TSK_FS_DIR * a_fs_dir, uint8_t a_is_del, TSK_LIST ** list_seen, char *buf, TSK_OFF_T offset) |
324 | 0 | { |
325 | 0 | switch(a_fs_dir->fs_file->meta->content_type){ |
326 | 0 | case TSK_FS_META_CONTENT_TYPE_XFS_DATA_FORK_SHORTFORM: |
327 | 0 | xfs_dent_parse_shortform(xfs, a_fs_dir, buf); |
328 | 0 | break; |
329 | | |
330 | 0 | case TSK_FS_META_CONTENT_TYPE_XFS_DATA_FORK_EXTENTS: |
331 | 0 | xfs_dent_parse_block(xfs, a_fs_dir, a_is_del, list_seen, buf, offset); |
332 | 0 | break; |
333 | 0 | default: |
334 | 0 | return TSK_ERR; |
335 | 0 | } |
336 | 0 | return TSK_OK; |
337 | 0 | } |
338 | | |
339 | | /** \internal |
340 | | * Process a directory and load up FS_DIR with the entries. If a pointer to |
341 | | * an already allocated FS_DIR structure is given, it will be cleared. If no existing |
342 | | * FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return |
343 | | * value is error or corruption, then the FS_DIR structure could |
344 | | * have entries (depending on when the error occurred). |
345 | | * |
346 | | * @param a_fs File system to analyze |
347 | | * @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated |
348 | | * structure or a new structure. |
349 | | * @param a_addr Address of directory to process. |
350 | | * @returns error, corruption, ok etc. |
351 | | */ |
352 | | TSK_RETVAL_ENUM |
353 | | xfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir, |
354 | | TSK_INUM_T a_addr,[[maybe_unused]] int recursion_depth) |
355 | 0 | { |
356 | 0 | XFS_INFO * xfs = (XFS_INFO *) a_fs; |
357 | 0 | TSK_FS_DIR * fs_dir; |
358 | 0 | TSK_LIST *list_seen = NULL; |
359 | |
|
360 | 0 | char *dirbuf; |
361 | | |
362 | 0 | TSK_RETVAL_ENUM retval_tmp; |
363 | 0 | TSK_RETVAL_ENUM retval_final = TSK_OK; |
364 | |
|
365 | 0 | if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) { |
366 | 0 | tsk_error_reset(); |
367 | 0 | tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); |
368 | 0 | tsk_error_set_errstr("xfs_dir_open_meta: inode value: %" PRIuINUM |
369 | 0 | "\n", a_addr); |
370 | 0 | return TSK_ERR; |
371 | 0 | } |
372 | 0 | else if (a_fs_dir == NULL) { |
373 | 0 | tsk_error_reset(); |
374 | 0 | tsk_error_set_errno(TSK_ERR_FS_ARG); |
375 | 0 | tsk_error_set_errstr |
376 | 0 | ("xfs_dir_open_meta: NULL fs_attr argument given"); |
377 | 0 | return TSK_ERR; |
378 | 0 | } |
379 | | |
380 | 0 | if (tsk_verbose) { |
381 | 0 | tsk_fprintf(stderr, |
382 | 0 | "xfs_dir_open_meta: Processing directory %" PRIuINUM |
383 | 0 | "\n", a_addr); |
384 | 0 | } |
385 | |
|
386 | 0 | fs_dir = *a_fs_dir; |
387 | |
|
388 | 0 | if (fs_dir) { |
389 | 0 | tsk_fs_dir_reset(fs_dir); |
390 | 0 | fs_dir->addr = a_addr; |
391 | 0 | } |
392 | 0 | else { |
393 | 0 | if((*a_fs_dir = fs_dir = |
394 | 0 | tsk_fs_dir_alloc(a_fs, a_addr, 128)) == NULL) { |
395 | 0 | return TSK_ERR; |
396 | 0 | } |
397 | 0 | } |
398 | | |
399 | 0 | if ((fs_dir->fs_file = |
400 | 0 | tsk_fs_file_open_meta(a_fs, NULL, a_addr)) == NULL) { // inode_lookup -> content_ptr 채움 |
401 | 0 | fprintf(stderr, "xfs_fs_dir_open_meta: failed to obtain fs_file meta info\n"); |
402 | 0 | tsk_error_errstr2_concat("- xfs_dir_open_meta"); |
403 | 0 | return TSK_COR; |
404 | 0 | } |
405 | | |
406 | | // We only read in and process a single block at a time |
407 | 0 | if ((dirbuf = (char*)tsk_malloc((size_t)a_fs->block_size)) == NULL) { |
408 | 0 | fprintf(stderr, "[i] xfs_load_attr_block: xfs.cpp: %d - failed to malloc\n", __LINE__); |
409 | 0 | return TSK_ERR; |
410 | 0 | } |
411 | | |
412 | 0 | memcpy(dirbuf, fs_dir->fs_file->meta->content_ptr, XFS_CONTENT_LEN_V5(xfs)); |
413 | |
|
414 | 0 | retval_tmp = |
415 | 0 | xfs_dent_parse(xfs, fs_dir, (fs_dir->fs_file->meta-> |
416 | 0 | flags & TSK_FS_META_FLAG_UNALLOC) ? 1 : 0, &list_seen, |
417 | 0 | dirbuf, XFS_CONTENT_LEN_V5(xfs)); |
418 | | |
419 | 0 | if (retval_tmp == TSK_ERR) |
420 | 0 | retval_final = TSK_ERR; |
421 | 0 | else if (retval_tmp == TSK_COR) |
422 | 0 | retval_final = TSK_COR; |
423 | |
|
424 | 0 | free(dirbuf); |
425 | |
|
426 | 0 | return retval_final; |
427 | 0 | } |
428 | | |
429 | | uint8_t xfs_jentry_walk([[maybe_unused]]TSK_FS_INFO *info, [[maybe_unused]]int a, |
430 | | [[maybe_unused]]TSK_FS_JENTRY_WALK_CB c, [[maybe_unused]]void *b) |
431 | 0 | { |
432 | 0 | return -1; |
433 | 0 | } |
434 | | |
435 | | uint8_t xfs_jblk_walk([[maybe_unused]]TSK_FS_INFO *a, [[maybe_unused]]TSK_DADDR_T b, |
436 | | [[maybe_unused]]TSK_DADDR_T c, [[maybe_unused]]int d, [[maybe_unused]]TSK_FS_JBLK_WALK_CB e, [[maybe_unused]]void *f) |
437 | 0 | { |
438 | 0 | return -1; |
439 | 0 | } |
440 | | |
441 | | uint8_t xfs_jopen([[maybe_unused]]TSK_FS_INFO *a, [[maybe_unused]]TSK_INUM_T b) |
442 | 0 | { |
443 | 0 | return -1; |
444 | 0 | } |