/** * Open a directory and cycle through its contents. Read each file and recurse * into each directory. * * @param fs_info File system to process * @param stack Stack to prevent infinite recursion loops * @param dir_inum Metadata address of directory to open * @param path Path of directory being opened * @returns 1 on error */ static uint8_t proc_dir(TSK_FS_INFO * fs_info, TSK_STACK * stack, TSK_INUM_T dir_inum, const char *path) { TSK_FS_DIR *fs_dir; size_t i; char *path2 = NULL; char *buf = NULL; // open the directory if ((fs_dir = tsk_fs_dir_open_meta(fs_info, dir_inum)) == NULL) { fprintf(stderr, "Error opening directory: %" PRIuINUM "\n", dir_inum); tsk_error_print(stderr); return 1; } /* These should be dynamic lengths, but this is just a sample program. * Allocate heap space instead of stack to prevent overflow for deep * directories. */ if ((path2 = (char *) malloc(4096)) == NULL) { return 1; } if ((buf = (char *) malloc(2048)) == NULL) { free(path2); return 1; } // cycle through each entry for (i = 0; i < tsk_fs_dir_getsize(fs_dir); i++) { TSK_FS_FILE *fs_file; TSK_OFF_T off = 0; size_t len = 0; // get the entry if ((fs_file = tsk_fs_dir_get(fs_dir, i)) == NULL) { fprintf(stderr, "Error getting directory entry %" PRIuSIZE " in directory %" PRIuINUM "\n", i, dir_inum); tsk_error_print(stderr); free(path2); free(buf); return 1; } /* Ignore NTFS System files */ if ((TSK_FS_TYPE_ISNTFS(fs_file->fs_info->ftype)) && (fs_file->name->name[0] == '$')) { tsk_fs_file_close(fs_file); continue; } //printf("Processing %s/%s\n", path, fs_file->name->name); // make sure it's got metadata and not only a name if (fs_file->meta) { ssize_t cnt; /* Note that we could also cycle through all of the attributes in the * file by using one of the tsk_fs_attr_get() functions and reading it * with tsk_fs_attr_read(). See the File Systems section of the Library * User's Guide for more details: * http://www.sleuthkit.org/sleuthkit/docs/api-docs/ */ // read file contents if (fs_file->meta->type == TSK_FS_META_TYPE_REG) { int myflags = 0; for (off = 0; off < fs_file->meta->size; off += len) { if (fs_file->meta->size - off < 2048) len = (size_t) (fs_file->meta->size - off); else len = 2048; cnt = tsk_fs_file_read(fs_file, off, buf, len, (TSK_FS_FILE_READ_FLAG_ENUM) myflags); if (cnt == -1) { // could check tsk_errno here for a recovery error (TSK_ERR_FS_RECOVER) fprintf(stderr, "Error reading %s file: %s\n", ((fs_file->name-> flags & TSK_FS_NAME_FLAG_UNALLOC) || (fs_file->meta-> flags & TSK_FS_META_FLAG_UNALLOC)) ? "unallocated" : "allocated", fs_file->name->name); tsk_error_print(stderr); break; } else if (cnt != (ssize_t) len) { fprintf(stderr, "Warning: %" PRIuSIZE " of %" PRIuSIZE " bytes read from %s file %s\n", cnt, len, ((fs_file->name-> flags & TSK_FS_NAME_FLAG_UNALLOC) || (fs_file->meta-> flags & TSK_FS_META_FLAG_UNALLOC)) ? "unallocated" : "allocated", fs_file->name->name); } // do something with the data... } } // recurse into another directory (unless it is a '.' or '..') else if (fs_file->meta->type == TSK_FS_META_TYPE_DIR) { if (TSK_FS_ISDOT(fs_file->name->name) == 0) { // only go in if it is not on our stack if (tsk_stack_find(stack, fs_file->meta->addr) == 0) { // add the address to the top of the stack tsk_stack_push(stack, fs_file->meta->addr); snprintf(path2, 4096, "%s/%s", path, fs_file->name->name); if (proc_dir(fs_info, stack, fs_file->meta->addr, path2)) { tsk_fs_file_close(fs_file); tsk_fs_dir_close(fs_dir); free(path2); free(buf); return 1; } // pop the address tsk_stack_pop(stack); } } } } tsk_fs_file_close(fs_file); } tsk_fs_dir_close(fs_dir); free(path2); free(buf); return 0; }
/* Compare the differences between dir_open_meta and dir_open * @param a_path Path of directory to open * @param a_addr The metadata address of the same directory as the path * @returns 1 if a test failed */ static int test_dir_open_apis(TSK_FS_INFO * a_fs, const char *a_path, TSK_INUM_T a_addr) { TSK_FS_DIR *fs_dir_m; TSK_FS_DIR *fs_dir_p; TSK_FS_DIR *fs_dir_tmp; TSK_FS_FILE *fs_file_m; TSK_FS_FILE *fs_file_p; int retval = 0; size_t entry = 0; // open via inode addr fs_dir_m = tsk_fs_dir_open_meta(a_fs, a_addr); if (!fs_dir_m) { fprintf(stderr, "Error opening dir %" PRIuINUM " via meta\n", a_addr); tsk_error_print(stderr); return 1; } /* open the root directory to throw some more state into the system * in case data is cached from first call */ fs_dir_tmp = tsk_fs_dir_open_meta(a_fs, a_fs->root_inum); if (!fs_dir_tmp) { fprintf(stderr, "Error opening root directory via meta\n"); tsk_error_print(stderr); return 1; } // open via path fs_dir_p = tsk_fs_dir_open(a_fs, a_path); if (!fs_dir_p) { fprintf(stderr, "Error opening directory %s\n", a_path); tsk_error_print(stderr); return 1; } // test that path has the name structure set (correctly) if ((fs_dir_p->fs_file == NULL) || (fs_dir_p->fs_file->name == NULL)) { fprintf(stderr, "dir opened via path has null name (%s)\n", a_path); retval = 1; goto open_cleanup; } if (fs_dir_p->fs_file->name->meta_addr != fs_dir_p->fs_file->meta->addr) { fprintf(stderr, "dir opened via path has different meta addresses in name and meta (%s) (%" PRIuINUM " vs %" PRIuINUM "\n", a_path, fs_dir_p->fs_file->name->meta_addr, fs_dir_p->fs_file->meta->addr); retval = 1; goto open_cleanup; } // verify both methods have same dir addr if (fs_dir_p->fs_file->meta->addr != fs_dir_m->fs_file->meta->addr) { fprintf(stderr, "parent dir addrs from fs_dir_open_meta and via path are different: %" PRIuINUM " vs %" PRIuINUM " (%s - %" PRIuINUM "\n", fs_dir_p->fs_file->meta->addr, fs_dir_m->fs_file->meta->addr, a_path, a_addr); retval = 1; goto open_cleanup; } // verify path method has same dir addr as open via meta if (fs_dir_p->fs_file->meta->addr != a_addr) { fprintf(stderr, "parent dir addrs from fs_dir_open is diff from meta address %" PRIuINUM " (%s - %" PRIuINUM "\n", fs_dir_p->fs_file->meta->addr, a_path, a_addr); retval = 1; goto open_cleanup; } // verify both have same size if (tsk_fs_dir_getsize(fs_dir_p) != tsk_fs_dir_getsize(fs_dir_m)) { fprintf(stderr, "sizes from fs_dir_open_meta and via path are different: %" PRIuSIZE " vs %" PRIuSIZE " (%s - %" PRIuINUM "\n", tsk_fs_dir_getsize(fs_dir_p), tsk_fs_dir_getsize(fs_dir_m), a_path, a_addr); retval = 1; goto open_cleanup; } // compare the first entry in both. if (tsk_fs_dir_getsize(fs_dir_p) == 0) { fprintf(stderr, "directory sizes are 0\n"); retval = 1; goto open_cleanup; } fs_file_m = tsk_fs_dir_get(fs_dir_m, 0); if (fs_file_m == NULL) { fprintf(stderr, "Error opening entry 0 from meta open: %" PRIuINUM "\n", a_addr); tsk_error_print(stderr); retval = 1; goto open_cleanup; } fs_file_p = tsk_fs_dir_get(fs_dir_p, 0); if (fs_file_p == NULL) { fprintf(stderr, "Error opening entry 0 from path open: %" PRIuINUM "\n", a_addr); tsk_error_print(stderr); retval = 1; goto open_cleanup; } if (compare_names(fs_file_p->name, fs_file_m->name, 1)) { fprintf(stderr, "results from entry 0 are different\n"); retval = 1; goto open_cleanup; } tsk_fs_file_close(fs_file_m); tsk_fs_file_close(fs_file_p); // compare the last entry in both entry = tsk_fs_dir_getsize(fs_dir_m) - 1; fs_file_m = tsk_fs_dir_get(fs_dir_m, entry); if (fs_file_m == NULL) { fprintf(stderr, "Error opening entry %" PRIuSIZE " from meta open: %" PRIuINUM "\n", entry, a_addr); tsk_error_print(stderr); retval = 1; goto open_cleanup; } fs_file_p = tsk_fs_dir_get(fs_dir_p, entry); if (fs_file_p == NULL) { fprintf(stderr, "Error opening entry %" PRIuSIZE " from path open: %" PRIuINUM "\n", entry, a_addr); tsk_error_print(stderr); retval = 1; goto open_cleanup; } if (compare_names(fs_file_p->name, fs_file_m->name, 1)) { fprintf(stderr, "results from entry %" PRIuSIZE " are different\n", entry); retval = 1; goto open_cleanup; } tsk_fs_file_close(fs_file_m); tsk_fs_file_close(fs_file_p); open_cleanup: tsk_fs_dir_close(fs_dir_p); tsk_fs_dir_close(fs_dir_tmp); tsk_fs_dir_close(fs_dir_m); return retval; }
/** * \ingroup fslib * * Find the meta data address for a given file name (UTF-8). * The basic idea of the function is to break the given name into its * subdirectories and start looking for each (starting in the root * directory). * * @param a_fs FS to analyze * @param a_path UTF-8 path of file to search for * @param [out] a_result Meta data address of file * @param [out] a_fs_name Copy of name details (or NULL if details not wanted) * @returns -1 on (system) error, 0 if found, and 1 if not found */ int8_t tsk_fs_path2inum(TSK_FS_INFO * a_fs, const char *a_path, TSK_INUM_T * a_result, TSK_FS_NAME * a_fs_name) { char *cpath; size_t clen; char *cur_dir; // The "current" directory or file we are looking for char *cur_attr; // The "current" attribute of the dir we are looking for TSK_INUM_T next_meta; uint8_t is_done; char *strtok_last; *a_result = 0; // copy path to a buffer that we can modify clen = strlen(a_path) + 1; if ((cpath = (char *) tsk_malloc(clen)) == NULL) { return -1; } strncpy(cpath, a_path, clen); // Get the first part of the directory path. cur_dir = (char *) strtok_r(cpath, "/", &strtok_last); cur_attr = NULL; /* If there is no token, then only a '/' was given */ if (cur_dir == NULL) { free(cpath); *a_result = a_fs->root_inum; // create the dummy entry if needed if (a_fs_name) { a_fs_name->meta_addr = a_fs->root_inum; // Note that we are not filling in meta_seq -- we could, we just aren't a_fs_name->type = TSK_FS_NAME_TYPE_DIR; a_fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; if (a_fs_name->name) a_fs_name->name[0] = '\0'; if (a_fs_name->shrt_name) a_fs_name->shrt_name[0] = '\0'; } return 0; } /* If this is NTFS, separate out the attribute of the current directory */ if (TSK_FS_TYPE_ISNTFS(a_fs->ftype) && ((cur_attr = strchr(cur_dir, ':')) != NULL)) { *(cur_attr) = '\0'; cur_attr++; } if (tsk_verbose) tsk_fprintf(stderr, "Looking for %s\n", cur_dir); // initialize the first place to look, the root dir next_meta = a_fs->root_inum; // we loop until we know the outcome and then exit. // everything should return from inside the loop. is_done = 0; while (is_done == 0) { size_t i; TSK_FS_FILE *fs_file_alloc = NULL; // set to the allocated file that is our target TSK_FS_FILE *fs_file_del = NULL; // set to an unallocated file that matches our criteria TSK_FS_DIR *fs_dir = NULL; // open the next directory in the recursion if ((fs_dir = tsk_fs_dir_open_meta(a_fs, next_meta)) == NULL) { free(cpath); return -1; } /* Verify this is indeed a directory. We had one reported * problem where a file was a disk image and opening it as * a directory found the directory entries inside of the file * and this caused problems... */ if ( !TSK_FS_IS_DIR_META(fs_dir->fs_file->meta->type)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_GENFS); tsk_error_set_errstr("Address %" PRIuINUM " is not for a directory\n", next_meta); free(cpath); return -1; } // cycle through each entry for (i = 0; i < tsk_fs_dir_getsize(fs_dir); i++) { TSK_FS_FILE *fs_file; uint8_t found_name = 0; if ((fs_file = tsk_fs_dir_get(fs_dir, i)) == NULL) { tsk_fs_dir_close(fs_dir); free(cpath); return -1; } /* * Check if this is the name that we are currently looking for, * as identified in 'cur_dir' */ if ((fs_file->name->name) && (a_fs->name_cmp(a_fs, fs_file->name->name, cur_dir) == 0)) { found_name = 1; } else if ((fs_file->name->shrt_name) && (a_fs->name_cmp(a_fs, fs_file->name->shrt_name, cur_dir) == 0)) { found_name = 1; } /* For NTFS, we have to check the attribute name. */ if ((found_name == 1) && (TSK_FS_TYPE_ISNTFS(a_fs->ftype))) { /* ensure we have the right attribute name */ if (cur_attr != NULL) { found_name = 0; if (fs_file->meta) { int cnt, i; // cycle through the attributes cnt = tsk_fs_file_attr_getsize(fs_file); for (i = 0; i < cnt; i++) { const TSK_FS_ATTR *fs_attr = tsk_fs_file_attr_get_idx(fs_file, i); if (!fs_attr) continue; if ((fs_attr->name) && (a_fs->name_cmp(a_fs, fs_attr->name, cur_attr) == 0)) { found_name = 1; break; } } } } } if (found_name) { /* If we found our file and it is allocated, then stop. If * it is unallocated, keep on going to see if we can get * an allocated hit */ if (fs_file->name->flags & TSK_FS_NAME_FLAG_ALLOC) { fs_file_alloc = fs_file; break; } else { // if we already have an unalloc and its addr is 0, then use the new one if ((fs_file_del) && (fs_file_del->name->meta_addr == 0)) { tsk_fs_file_close(fs_file_del); } fs_file_del = fs_file; } } // close the file if we did not save it for future analysis. else { tsk_fs_file_close(fs_file); fs_file = NULL; } } // we found a directory, go into it if ((fs_file_alloc) || (fs_file_del)) { const char *pname; TSK_FS_FILE *fs_file_tmp; // choose the alloc one first (if they both exist) if (fs_file_alloc) fs_file_tmp = fs_file_alloc; else fs_file_tmp = fs_file_del; pname = cur_dir; // save a copy of the current name pointer // advance to the next name cur_dir = (char *) strtok_r(NULL, "/", &(strtok_last)); cur_attr = NULL; if (tsk_verbose) tsk_fprintf(stderr, "Found it (%s), now looking for %s\n", pname, cur_dir); /* That was the last name in the path -- we found the file! */ if (cur_dir == NULL) { *a_result = fs_file_tmp->name->meta_addr; // make a copy if one was requested if (a_fs_name) { tsk_fs_name_copy(a_fs_name, fs_file_tmp->name); } if (fs_file_alloc) tsk_fs_file_close(fs_file_alloc); if (fs_file_del) tsk_fs_file_close(fs_file_del); tsk_fs_dir_close(fs_dir); free(cpath); return 0; } // update the attribute field, if needed if (TSK_FS_TYPE_ISNTFS(a_fs->ftype) && ((cur_attr = strchr(cur_dir, ':')) != NULL)) { *(cur_attr) = '\0'; cur_attr++; } // update the value for the next directory to open next_meta = fs_file_tmp->name->meta_addr; if (fs_file_alloc) { tsk_fs_file_close(fs_file_alloc); fs_file_alloc = NULL; } if (fs_file_del) { tsk_fs_file_close(fs_file_del); fs_file_del = NULL; } } // no hit in directory else { is_done = 1; } tsk_fs_dir_close(fs_dir); fs_dir = NULL; } free(cpath); return 1; }
/* Test function that compares the dir_open/dir_get() APIs * with the dir_walk results * @param a_addr Address of directory to analyze * @returns 1 if a test failed. */ static int test_walk_apis(TSK_FS_INFO * a_fs, TSK_INUM_T a_addr) { TSK_FS_DIR *fs_dir; int retval = 0; fs_dir = tsk_fs_dir_open_meta(a_fs, a_addr); if (!fs_dir) { fprintf(stderr, "Error opening dir %" PRIuINUM " via meta\n", a_addr); tsk_error_print(stderr); return 1; } // verify they have the same number of entries // walk the directory to get its count size_t walk_size = 0; if (tsk_fs_dir_walk(a_fs, a_addr, (TSK_FS_DIR_WALK_FLAG_ENUM) 0, dir_walk_count_cb, &walk_size)) { fprintf(stderr, "Error doing dent walk on dir %" PRIuINUM "\n", a_addr); retval = 1; goto walk_cleanup; } if (walk_size != tsk_fs_dir_getsize(fs_dir)) { fprintf(stderr, "Size returned by dir_walk different from dir_getsize: %" PRIuINUM ": %" PRIuSIZE " %" PRIuSIZE "\n", a_addr, walk_size, tsk_fs_dir_getsize(fs_dir)); retval = 1; goto walk_cleanup; } // verify each entry is the same for (size_t i = 0; i < tsk_fs_dir_getsize(fs_dir); i++) { TSK_FS_FILE *fs_file; fs_file = tsk_fs_dir_get(fs_dir, i); if (fs_file == NULL) { fprintf(stderr, "Error getting entry %" PRIuSIZE " from directory %" PRIuINUM "\n", i, a_addr); tsk_error_print(stderr); retval = 1; goto walk_cleanup; } if (fs_file->meta == NULL) { fprintf(stderr, "Error: %s (%" PRIuINUM ") does not have meta set in dir: \n", fs_file->name->name, fs_file->name->meta_addr); retval = 1; goto walk_cleanup; } s_found = 0; if (tsk_fs_dir_walk(a_fs, a_addr, (TSK_FS_DIR_WALK_FLAG_ENUM) 0, dir_walk_comp_cb, (void *) fs_file)) { fprintf(stderr, "Error doing dent walk on dir %" PRIuINUM "\n", a_addr); retval = 1; goto walk_cleanup; } if (s_found == 0) { fprintf(stderr, "entry %" PRIuSIZE " in dir not found via walk: %s\n", i, fs_file->name->name); retval = 1; goto walk_cleanup; } tsk_fs_file_close(fs_file); } walk_cleanup: tsk_fs_dir_close(fs_dir); return retval; }