/** \ingroup fslib * Return a specific file or subdirectory from an open directory. * @param a_fs_dir Directory to analyze * @param a_idx Index of file in directory to open (0-based) * @returns NULL on error */ TSK_FS_FILE * tsk_fs_dir_get(const TSK_FS_DIR * a_fs_dir, size_t a_idx) { TSK_FS_NAME *fs_name; TSK_FS_FILE *fs_file; if ((a_fs_dir == NULL) || (a_fs_dir->tag != TSK_FS_DIR_TAG) || (a_fs_dir->fs_info == NULL)) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_dir_get: called with NULL or unallocated structures"); return NULL; } if (a_fs_dir->names_used <= a_idx) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("tsk_fs_dir_get: Index (%" PRIuSIZE ") too large (%" PRIuSIZE ")", a_idx, a_fs_dir->names_used); return NULL; } // allocate a structure to return if ((fs_file = tsk_fs_file_alloc(a_fs_dir->fs_info)) == NULL) return NULL; fs_name = &(a_fs_dir->names[a_idx]); // copy the name into another structure that we can return and later free if ((fs_file->name = tsk_fs_name_alloc(fs_name->name ? strlen(fs_name->name) + 1 : 0, fs_name->shrt_name ? strlen(fs_name->shrt_name) + 1 : 0)) == NULL) { return NULL; } if (tsk_fs_name_copy(fs_file->name, fs_name)) return NULL; /* load the fs_meta structure if possible. * Must have non-zero inode addr or have allocated name (if inode is 0) */ if (((fs_name->meta_addr) || (fs_name->flags & TSK_FS_NAME_FLAG_ALLOC))) { if (a_fs_dir->fs_info->file_add_meta(a_fs_dir->fs_info, fs_file, fs_name->meta_addr)) { if (tsk_verbose) tsk_error_print(stderr); tsk_error_reset(); } // if the sequence numbers don't match, then don't load the meta // should ideally have sequence in previous lookup, but it isn't // in all APIs yet if (fs_file->meta->seq != fs_name->meta_seq) { tsk_fs_meta_close(fs_file->meta); fs_file->meta = NULL; } } return fs_file; }
/** * \ingroup fslib * Close an open file. * @param a_fs_file Pointer to open file */ void tsk_fs_file_close(TSK_FS_FILE * a_fs_file) { if ((a_fs_file == NULL) || (a_fs_file->tag != TSK_FS_FILE_TAG)) return; a_fs_file->tag = 0; if (a_fs_file->meta) { tsk_fs_meta_close(a_fs_file->meta); a_fs_file->meta = NULL; } if (a_fs_file->name) { tsk_fs_name_free(a_fs_file->name); a_fs_file->name = NULL; } free(a_fs_file); }
/* dir_walk local function that is used for recursive calls. Callers * should initially call the non-local version. */ static TSK_WALK_RET_ENUM tsk_fs_dir_walk_lcl(TSK_FS_INFO * a_fs, DENT_DINFO * a_dinfo, TSK_INUM_T a_addr, TSK_FS_DIR_WALK_FLAG_ENUM a_flags, TSK_FS_DIR_WALK_CB a_action, void *a_ptr) { TSK_FS_DIR *fs_dir; TSK_FS_FILE *fs_file; size_t i; // get the list of entries in the directory if ((fs_dir = tsk_fs_dir_open_meta(a_fs, a_addr)) == NULL) { return TSK_WALK_ERROR; } /* Allocate a file structure for the callbacks. We * will allocate fs_meta structures as needed and * point into the fs_dir structure for the names. */ if ((fs_file = tsk_fs_file_alloc(a_fs)) == NULL) { tsk_fs_dir_close(fs_dir); return TSK_WALK_ERROR; } for (i = 0; i < fs_dir->names_used; i++) { TSK_WALK_RET_ENUM retval; /* Point name to the buffer of names. We need to be * careful about resetting this before we free fs_file */ fs_file->name = (TSK_FS_NAME *) & fs_dir->names[i]; /* load the fs_meta structure if possible. * Must have non-zero inode addr or have allocated name (if inode is 0) */ if (((fs_file->name->meta_addr) || (fs_file->name->flags & TSK_FS_NAME_FLAG_ALLOC))) { if (a_fs->file_add_meta(a_fs, fs_file, fs_file->name->meta_addr)) { if (tsk_verbose) tsk_error_print(stderr); tsk_error_reset(); } } // call the action if we have the right flags. if ((fs_file->name->flags & a_flags) == fs_file->name->flags) { retval = a_action(fs_file, a_dinfo->dirs, a_ptr); if (retval == TSK_WALK_STOP) { tsk_fs_dir_close(fs_dir); fs_file->name = NULL; tsk_fs_file_close(fs_file); /* free the list -- fs_dir_walk has no way * of knowing that we stopped early w/out error. */ if (a_dinfo->save_inum_named) { tsk_list_free(a_dinfo->list_inum_named); a_dinfo->list_inum_named = NULL; a_dinfo->save_inum_named = 0; } return TSK_WALK_STOP; } else if (retval == TSK_WALK_ERROR) { tsk_fs_dir_close(fs_dir); fs_file->name = NULL; tsk_fs_file_close(fs_file); return TSK_WALK_ERROR; } } // save the inode info for orphan finding - if requested if ((a_dinfo->save_inum_named) && (fs_file->meta) && (fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC)) { if (tsk_list_add(&a_dinfo->list_inum_named, fs_file->meta->addr)) { // if there is an error, then clear the list tsk_list_free(a_dinfo->list_inum_named); a_dinfo->list_inum_named = NULL; a_dinfo->save_inum_named = 0; } } /* Recurse into a directory if: * - Both dir entry and inode have DIR type (or name is undefined) * - Recurse flag is set * - dir entry is allocated OR both are unallocated * - not one of the '.' or '..' entries * - A Non-Orphan Dir or the Orphan Dir with the NOORPHAN flag not set. */ if (((fs_file->name->type == TSK_FS_NAME_TYPE_DIR) || (fs_file->name->type == TSK_FS_NAME_TYPE_UNDEF)) && (fs_file->meta) && (fs_file->meta->type == TSK_FS_META_TYPE_DIR) && (a_flags & TSK_FS_DIR_WALK_FLAG_RECURSE) && ((fs_file->name->flags & TSK_FS_NAME_FLAG_ALLOC) || ((fs_file->name->flags & TSK_FS_NAME_FLAG_UNALLOC) && (fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC)) ) && (!TSK_FS_ISDOT(fs_file->name->name)) && ((fs_file->name->meta_addr != TSK_FS_ORPHANDIR_INUM(a_fs)) || ((a_flags & TSK_FS_DIR_WALK_FLAG_NOORPHAN) == 0)) ) { /* Make sure we do not get into an infinite loop */ if (0 == tsk_stack_find(a_dinfo->stack_seen, fs_file->name->meta_addr)) { int depth_added = 0; uint8_t save_bak = 0; if (tsk_stack_push(a_dinfo->stack_seen, fs_file->name->meta_addr)) { tsk_fs_dir_close(fs_dir); fs_file->name = NULL; tsk_fs_file_close(fs_file); return TSK_WALK_ERROR; } if ((a_dinfo->depth < MAX_DEPTH) && (DIR_STRSZ > strlen(a_dinfo->dirs) + strlen(fs_file->name->name))) { a_dinfo->didx[a_dinfo->depth] = &a_dinfo->dirs[strlen(a_dinfo->dirs)]; strncpy(a_dinfo->didx[a_dinfo->depth], fs_file->name->name, DIR_STRSZ - strlen(a_dinfo->dirs)); strncat(a_dinfo->dirs, "/", DIR_STRSZ); depth_added = 1; } a_dinfo->depth++; /* We do not want to save info about named unalloc files * when we go into the Orphan directory (because then we have * no orphans). So, disable it for this recursion. */ if (fs_file->name->meta_addr == TSK_FS_ORPHANDIR_INUM(a_fs)) { save_bak = a_dinfo->save_inum_named; a_dinfo->save_inum_named = 0; } retval = tsk_fs_dir_walk_lcl(a_fs, a_dinfo, fs_file->name->meta_addr, a_flags, a_action, a_ptr); if (retval == TSK_WALK_ERROR) { /* If this fails because the directory could not be * loaded, then we still continue */ if (tsk_verbose) { tsk_fprintf(stderr, "tsk_fs_dir_walk_lcl: error reading directory: %" PRIuINUM "\n", fs_file->name->meta_addr); tsk_error_print(stderr); } tsk_error_reset(); } else if (retval == TSK_WALK_STOP) { tsk_fs_dir_close(fs_dir); fs_file->name = NULL; tsk_fs_file_close(fs_file); return TSK_WALK_STOP; } // reset the save status if (fs_file->name->meta_addr == TSK_FS_ORPHANDIR_INUM(a_fs)) { a_dinfo->save_inum_named = save_bak; } tsk_stack_pop(a_dinfo->stack_seen); a_dinfo->depth--; if (depth_added) *a_dinfo->didx[a_dinfo->depth] = '\0'; } else { if (tsk_verbose) fprintf(stderr, "tsk_fs_dir_walk_lcl: Loop detected with address %" PRIuINUM, fs_file->name->meta_addr); } } // remove the pointer to name buffer fs_file->name = NULL; // free the metadata if we allocated it if (fs_file->meta) { tsk_fs_meta_close(fs_file->meta); fs_file->meta = NULL; } } tsk_fs_dir_close(fs_dir); fs_file->name = NULL; tsk_fs_file_close(fs_file); return TSK_WALK_CONT; }