/** \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; }
/** * \internal * Prints file system category data for an exFAT file system to a file * handle. * * @param [in] a_fs Generic file system info structure for the file system. * @param [in] a_hFile The file handle. * @return 0 on success, 1 otherwise, per TSK convention. */ static uint8_t exfatfs_fsstat_fs_info(TSK_FS_INFO *a_fs, FILE *a_hFile) { FATFS_INFO *fatfs = NULL; EXFATFS_MASTER_BOOT_REC *exfatbs = NULL; TSK_FS_FILE *fs_file = NULL; assert(a_fs != NULL); assert(a_hFile != NULL); fatfs = (FATFS_INFO*)a_fs; exfatbs = (EXFATFS_MASTER_BOOT_REC*)&(fatfs->boot_sector_buffer); if ((fs_file = tsk_fs_file_alloc(a_fs)) == NULL) { return FATFS_FAIL; } if ((fs_file->meta = tsk_fs_meta_alloc(FATFS_FILE_CONTENT_LEN)) == NULL) { return FATFS_FAIL; } tsk_fprintf(a_hFile, "FILE SYSTEM INFORMATION\n"); tsk_fprintf(a_hFile, "--------------------------------------------\n"); tsk_fprintf(a_hFile, "File System Type: exFAT\n"); tsk_fprintf(a_hFile, "\nVolume Serial Number: %x%x-%x%x\n", exfatbs->vol_serial_no[3], exfatbs->vol_serial_no[2], exfatbs->vol_serial_no[1], exfatbs->vol_serial_no[0]); if (exfatfs_find_volume_label_dentry(fatfs, fs_file) == 0) { tsk_fprintf(a_hFile, "Volume Label (from root directory): %s\n", fs_file->meta->name2->name); } else { tsk_fprintf(a_hFile, "Volume Label:\n"); } tsk_fprintf(a_hFile, "File System Name (from MBR): %s\n", exfatbs->fs_name); tsk_fprintf(a_hFile, "File System Revision: %x.%x\n", exfatbs->fs_revision[1], exfatbs->fs_revision[0]); tsk_fprintf(a_hFile, "Partition Offset: %" PRIuDADDR "\n", tsk_getu64(a_fs->endian, exfatbs->partition_offset)); tsk_fprintf(a_hFile, "Number of FATs: %d\n", fatfs->numfat); tsk_fs_file_close(fs_file); return FATFS_OK; }
/** \internal * Adds the fake metadata entry in the FS_DIR->fs_file struct for the orphan files directory * * @returns 1 on error */ static uint8_t tsk_fs_dir_add_orphan_dir_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR * a_fs_dir) { // populate the fake FS_FILE structure for the "Orphan Directory" if ((a_fs_dir->fs_file = tsk_fs_file_alloc(a_fs)) == NULL) { return 1; } if ((a_fs_dir->fs_file->meta = tsk_fs_meta_alloc(sizeof(TSK_DADDR_T))) == NULL) { return 1; } if (tsk_fs_dir_make_orphan_dir_meta(a_fs, a_fs_dir->fs_file->meta)) { return 1; } return 0; }
/** * \ingroup fslib * * Open a file given its metadata address. This function loads the metadata * and returns a handle that can be used to read and process the file. Note * that the returned TSK_FS_FILE structure will not have the file name set because * it was not used to load the file and this function does not search the * directory structure to find the name that points to the address. In general, * if you know the metadata address of a file, this function is more efficient * then tsk_fs_file_open, which first maps a file name to the metadata address * and then opens the file using this function. * * @param a_fs File system to analyze * @param a_fs_file Structure to store file data in or NULL to have one allocated. * @param a_addr Metadata address of file to lookup * @returns NULL on error */ TSK_FS_FILE * tsk_fs_file_open_meta(TSK_FS_INFO * a_fs, TSK_FS_FILE * a_fs_file, TSK_INUM_T a_addr) { TSK_FS_FILE *fs_file; if ((a_fs == NULL) || (a_fs->tag != TSK_FS_INFO_TAG)) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_file_open_meta: called with NULL or unallocated structures"); return NULL; } fs_file = a_fs_file; if (fs_file == NULL) { if ((fs_file = tsk_fs_file_alloc(a_fs)) == NULL) return NULL; } else { /* if the structure passed has a name structure, free it * because we won't use it. */ if (fs_file->name) { tsk_fs_name_free(fs_file->name); fs_file->name = NULL; } // reset the rest of it tsk_fs_file_reset(fs_file); } if (a_fs->file_add_meta(a_fs, fs_file, a_addr)) { if (a_fs_file == NULL) tsk_fs_file_close(fs_file); return NULL; } return 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; }