/** \internal * Proces a file system and populate a list of the metadata structures * that are reachable by file names. This is used to find orphan files. * Each file system has code that does the populating. */ TSK_RETVAL_ENUM tsk_fs_dir_load_inum_named(TSK_FS_INFO * a_fs) { tsk_take_lock(&a_fs->list_inum_named_lock); if (a_fs->list_inum_named != NULL) { tsk_release_lock(&a_fs->list_inum_named_lock); if (tsk_verbose) fprintf(stderr, "tsk_fs_dir_load_inum_named: List already populated. Skipping walk.\n"); return TSK_OK; } tsk_release_lock(&a_fs->list_inum_named_lock); if (tsk_verbose) fprintf(stderr, "tsk_fs_dir_load_inum_named: Performing dir walk to find named files\n"); /* Do a dir_walk. There is internal caching code that will populate * the structure. The callback is really a dummy call. This could * be made more effecient in the future (not do callbacks...). We * specify UNALLOC only as a flag on the assumption that there will * be fewer callbacks for UNALLOC than ALLOC. */ if (tsk_fs_dir_walk(a_fs, a_fs->root_inum, TSK_FS_NAME_FLAG_UNALLOC | TSK_FS_DIR_WALK_FLAG_RECURSE | TSK_FS_DIR_WALK_FLAG_NOORPHAN, load_named_dir_walk_cb, NULL)) { tsk_error_errstr2_concat ("- tsk_fs_dir_load_inum_named: identifying inodes allocated by file names"); return TSK_ERR; } return TSK_OK; }
void ntfs_orphan_map_free(NTFS_INFO * a_ntfs) { NTFS_PAR_MAP *tmp = NULL; // This routine is only called from ntfs_close, so it wouldn't // normally need a lock. However, it's an extern function, so be // safe in case someone else calls it. (Perhaps it's extern by // mistake?) tsk_take_lock(&a_ntfs->orphan_map_lock); if (a_ntfs->orphan_map == NULL) { tsk_release_lock(&a_ntfs->orphan_map_lock); return; } tmp = a_ntfs->orphan_map; while (tmp) { NTFS_PAR_MAP *tmp2; free(tmp->addrs); tmp2 = tmp->next; free(tmp); tmp = tmp2; } a_ntfs->orphan_map = NULL; tsk_release_lock(&a_ntfs->orphan_map_lock); }
static ssize_t ewf_image_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf, size_t len) { #if defined( HAVE_LIBEWF_V2_API ) char error_string[TSK_EWF_ERROR_STRING_SIZE]; libewf_error_t *ewf_error = NULL; #endif ssize_t cnt; IMG_EWF_INFO *ewf_info = (IMG_EWF_INFO *) img_info; if (tsk_verbose) tsk_fprintf(stderr, "ewf_image_read: byte offset: %" PRIuOFF " len: %" PRIuSIZE "\n", offset, len); if (offset > img_info->size) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_READ_OFF); tsk_error_set_errstr("ewf_image_read - %" PRIuOFF, offset); return -1; } tsk_take_lock(&(ewf_info->read_lock)); #if defined( HAVE_LIBEWF_V2_API ) cnt = libewf_handle_read_random(ewf_info->handle, buf, len, offset, &ewf_error); if (cnt < 0) { char *errmsg = NULL; tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_READ); if (getError(ewf_error, error_string)) errmsg = strerror(errno); else errmsg = error_string; tsk_error_set_errstr("ewf_image_read - offset: %" PRIuOFF " - len: %" PRIuSIZE " - %s", offset, len, errmsg); tsk_release_lock(&(ewf_info->read_lock)); return -1; } #else cnt = libewf_read_random(ewf_info->handle, buf, len, offset); if (cnt < 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_READ); tsk_error_set_errstr("ewf_image_read - offset: %" PRIuOFF " - len: %" PRIuSIZE " - %s", offset, len, strerror(errno)); tsk_release_lock(&(ewf_info->read_lock)); return -1; } #endif tsk_release_lock(&(ewf_info->read_lock)); return cnt; }
/** * Frees the memory associated with the parent map */ void fatfs_dir_buf_free(FATFS_INFO *fatfs) { tsk_take_lock(&fatfs->dir_lock); if (fatfs->inum2par != NULL) { std::map<TSK_INUM_T, TSK_INUM_T> *tmpMap = getParentMap(fatfs); delete tmpMap; fatfs->inum2par = NULL; } tsk_release_lock(&fatfs->dir_lock); }
/** * Frees the memory associated with the parent map */ void xtaffs_dir_buf_free(XTAFFS_INFO *xtaffs) { tsk_take_lock(&xtaffs->dir_lock); if (xtaffs->inum2par != NULL) { std::map<TSK_INUM_T, TSK_INUM_T> *tmpMap = getParentMap(xtaffs); delete tmpMap; xtaffs->inum2par = NULL; } tsk_release_lock(&xtaffs->dir_lock); }
/** \internal * Searches the list of metadata addresses that are pointed to * by unallocated names. Used to find orphan files. * @param a_fs File system being analyzed. * @param a_inum Metadata address to lookup in list. * @returns 1 if metadata address is pointed to by an unallocated * file name or 0 if not. */ uint8_t tsk_fs_dir_find_inum_named(TSK_FS_INFO * a_fs, TSK_INUM_T a_inum) { uint8_t retval = 0; tsk_take_lock(&a_fs->list_inum_named_lock); // list can be null if no unallocated file names exist if (a_fs->list_inum_named) retval = tsk_list_find(a_fs->list_inum_named, a_inum); tsk_release_lock(&a_fs->list_inum_named_lock); return retval; }
/** * Adds an entry to the parent directory map. Used to make further processing * faster. * @param fatfs File system * @param par_inum Parent folder meta data address. * @param dir_inum Sub-folder meta data address. * @returns 0 */ uint8_t fatfs_dir_buf_add(FATFS_INFO * fatfs, TSK_INUM_T par_inum, TSK_INUM_T dir_inum) { tsk_take_lock(&fatfs->dir_lock); std::map<TSK_INUM_T, TSK_INUM_T> *tmpMap = getParentMap(fatfs); (*tmpMap)[dir_inum] = par_inum; tsk_release_lock(&fatfs->dir_lock); return 0; }
uint8_t fatfs_dir_buf_add(FATFS_INFO * fatfs, TSK_INUM_T par_inum, TSK_INUM_T dir_inum) { size_t q; tsk_take_lock(&fatfs->dir_lock); for (q = 0; q < fatfs->dir_buf_next; q++) { if (fatfs->dir_buf[q] == dir_inum) { tsk_release_lock(&fatfs->dir_lock); return 0; } } // make sure we have room if (fatfs->dir_buf_next == fatfs->dir_buf_size) { fatfs->dir_buf_size += 256; if ((fatfs->dir_buf = (TSK_INUM_T *) tsk_realloc(fatfs->dir_buf, fatfs->dir_buf_size * sizeof(TSK_INUM_T))) == NULL) { tsk_release_lock(&fatfs->dir_lock); return 1; } if ((fatfs->par_buf = (TSK_INUM_T *) tsk_realloc(fatfs->par_buf, fatfs->dir_buf_size * sizeof(TSK_INUM_T))) == NULL) { tsk_release_lock(&fatfs->dir_lock); return 1; } } //add them fatfs->dir_buf[fatfs->dir_buf_next] = dir_inum; fatfs->par_buf[fatfs->dir_buf_next] = par_inum; fatfs->dir_buf_next++; tsk_release_lock(&fatfs->dir_lock); return 0; }
/** * Looks up the parent meta address for a child from the cached list. * @param fatfs File system * @param dir_inum Inode of sub-directory to look up * @param par_inum [out] Result of lookup * @returns 0 if found and 1 if not. */ static uint8_t fatfs_dir_buf_get(FATFS_INFO * fatfs, TSK_INUM_T dir_inum, TSK_INUM_T *par_inum) { uint8_t retval = 1; tsk_take_lock(&fatfs->dir_lock); std::map<TSK_INUM_T, TSK_INUM_T> *tmpMap = getParentMap(fatfs); if (tmpMap->count( dir_inum) > 0) { *par_inum = (*tmpMap)[dir_inum]; retval = 0; } tsk_release_lock(&fatfs->dir_lock); return retval; }
/* used to identify the unnamed metadata structures */ static TSK_WALK_RET_ENUM find_orphan_meta_walk_cb(TSK_FS_FILE * a_fs_file, void *a_ptr) { FIND_ORPHAN_DATA *data = (FIND_ORPHAN_DATA *) a_ptr; TSK_FS_INFO *fs = a_fs_file->fs_info; /* We want only orphans, then check if this * inode is in the seen list */ tsk_take_lock(&fs->list_inum_named_lock); if ((fs->list_inum_named) && (tsk_list_find(fs->list_inum_named, a_fs_file->meta->addr))) { tsk_release_lock(&fs->list_inum_named_lock); return TSK_WALK_CONT; } tsk_release_lock(&fs->list_inum_named_lock); // check if we have already added it as an orphan (in a subdirectory) if (tsk_list_find(data->orphan_subdir_list, a_fs_file->meta->addr)) { return TSK_WALK_CONT; } // use their name if they have one if (a_fs_file->meta->name2) { strncpy(data->fs_name->name, a_fs_file->meta->name2->name, data->fs_name->name_size); } else { snprintf(data->fs_name->name, data->fs_name->name_size, "OrphanFile-%" PRIuINUM, a_fs_file->meta->addr); } data->fs_name->meta_addr = a_fs_file->meta->addr; data->fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC; data->fs_name->type = TSK_FS_NAME_TYPE_UNDEF; if (tsk_fs_dir_add(data->fs_dir, data->fs_name)) return TSK_WALK_ERROR; /* FAT file systems spend a lot of time hunting for parent * directory addresses, so we put this code in here to save * the info when we have it. */ if (TSK_FS_TYPE_ISFAT(fs->ftype)) { if (fatfs_dir_buf_add((FATFS_INFO *) fs, TSK_FS_ORPHANDIR_INUM(fs), a_fs_file->meta->addr)) return TSK_WALK_ERROR; } /* Go into directories to mark their contents as "seen" */ if (a_fs_file->meta->type == TSK_FS_META_TYPE_DIR) { if (tsk_verbose) fprintf(stderr, "find_orphan_meta_walk_cb: Going into directory %" PRIuINUM " to mark contents as seen\n", a_fs_file->meta->addr); if (tsk_fs_dir_walk(fs, a_fs_file->meta->addr, TSK_FS_DIR_WALK_FLAG_UNALLOC | TSK_FS_DIR_WALK_FLAG_RECURSE | TSK_FS_DIR_WALK_FLAG_NOORPHAN, load_orphan_dir_walk_cb, data)) { tsk_error_errstr2_concat (" - find_orphan_meta_walk_cb: identifying inodes allocated by file names"); return TSK_WALK_ERROR; } } return TSK_WALK_CONT; }
/** \ingroup fslib * Walk the file names in a directory and obtain the details of the files via a callback. * * @param a_fs File system to analyze * @param a_addr Metadata address of the directory to analyze * @param a_flags Flags used during analysis * @param a_action Callback function that is called for each file name * @param a_ptr Pointer to data that is passed to the callback function each time * @returns 1 on error and 0 on success */ uint8_t tsk_fs_dir_walk(TSK_FS_INFO * a_fs, TSK_INUM_T a_addr, TSK_FS_DIR_WALK_FLAG_ENUM a_flags, TSK_FS_DIR_WALK_CB a_action, void *a_ptr) { DENT_DINFO dinfo; TSK_WALK_RET_ENUM retval; 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_dir_walk: called with NULL or unallocated structures"); return 1; } memset(&dinfo, 0, sizeof(DENT_DINFO)); if ((dinfo.stack_seen = tsk_stack_create()) == NULL) return 1; /* Sanity check on flags -- make sure at least one ALLOC is set */ if (((a_flags & TSK_FS_DIR_WALK_FLAG_ALLOC) == 0) && ((a_flags & TSK_FS_DIR_WALK_FLAG_UNALLOC) == 0)) { a_flags |= (TSK_FS_DIR_WALK_FLAG_ALLOC | TSK_FS_DIR_WALK_FLAG_UNALLOC); } /* if the flags are right, we can collect info that may be needed * for an orphan walk. If the walk fails or stops, the code that * calls the action will clear this stuff. */ tsk_take_lock(&a_fs->list_inum_named_lock); if ((a_fs->list_inum_named == NULL) && (a_addr == a_fs->root_inum) && (a_flags & TSK_FS_DIR_WALK_FLAG_RECURSE)) { dinfo.save_inum_named = 1; } tsk_release_lock(&a_fs->list_inum_named_lock); retval = tsk_fs_dir_walk_lcl(a_fs, &dinfo, a_addr, a_flags, a_action, a_ptr); if (dinfo.save_inum_named == 1) { if (retval != TSK_WALK_CONT) { /* There was an error and we stopped early, so we should get * rid of the partial list we were making. */ tsk_list_free(dinfo.list_inum_named); dinfo.list_inum_named = NULL; } else { /* We finished the dir walk successfully, so reassign * ownership of the dinfo's list_inum_named to the shared * list_inum_named in TSK_FS_INFO, under a lock, if * another thread hasn't already done so. */ tsk_take_lock(&a_fs->list_inum_named_lock); if (a_fs->list_inum_named == NULL) { a_fs->list_inum_named = dinfo.list_inum_named; } else { tsk_list_free(dinfo.list_inum_named); } tsk_release_lock(&a_fs->list_inum_named_lock); dinfo.list_inum_named = NULL; } } tsk_stack_free(dinfo.stack_seen); if (retval == TSK_WALK_ERROR) return 1; else return 0; }
/** \internal * Search the file system for orphan files and create the orphan file directory. * @param a_fs File system to search * @param a_fs_dir Structure to store the orphan file directory info in. */ TSK_RETVAL_ENUM tsk_fs_dir_find_orphans(TSK_FS_INFO * a_fs, TSK_FS_DIR * a_fs_dir) { FIND_ORPHAN_DATA data; size_t i; tsk_take_lock(&a_fs->orphan_dir_lock); if (a_fs->orphan_dir != NULL) { if (tsk_fs_dir_copy(a_fs->orphan_dir, a_fs_dir)) { tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_ERR; } if (tsk_fs_dir_add_orphan_dir_meta(a_fs, a_fs_dir)) { tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_ERR; } tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_OK; } if (tsk_verbose) fprintf(stderr, "tsk_fs_dir_find_orphans: Searching for orphan files\n"); memset(&data, 0, sizeof(FIND_ORPHAN_DATA)); /* We first need to determine which of the unallocated meta structures * have a name pointing to them. We cache this data, so see if it is * already known. */ if (tsk_fs_dir_load_inum_named(a_fs) != TSK_OK) { tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_ERR; } // note that list_inum_named could still be NULL if there are no deleted names. /* Now we walk the unallocated metadata structures and find ones that are * not named. The callback will add the names to the FS_DIR structure. */ data.fs_dir = a_fs_dir; // allocate a name once so that we will reuse for each name we add to FS_DIR if ((data.fs_name = tsk_fs_name_alloc(256, 0)) == NULL) { tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_ERR; } if (tsk_verbose) fprintf(stderr, "tsk_fs_dir_find_orphans: Performing inode_walk to find unnamed metadata structures\n"); if (tsk_fs_meta_walk(a_fs, a_fs->first_inum, a_fs->last_inum, TSK_FS_META_FLAG_UNALLOC | TSK_FS_META_FLAG_USED, find_orphan_meta_walk_cb, &data)) { tsk_fs_name_free(data.fs_name); tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_ERR; } tsk_fs_name_free(data.fs_name); data.fs_name = NULL; if (tsk_verbose) fprintf(stderr, "tsk_fs_dir_find_orphans: De-duping orphan files and directories\n"); /* do some cleanup on the final list. This cleanup will compare the * entries in the root orphan directory with files that can be accessed * from subdirectories of the orphan directory. These entries will exist if * they were added before their parent directory was added to the orphan directory. */ for (i = 0; i < a_fs_dir->names_used; i++) { if (tsk_list_find(data.orphan_subdir_list, a_fs_dir->names[i].meta_addr)) { if (a_fs_dir->names_used > 1) { tsk_fs_name_copy(&a_fs_dir->names[i], &a_fs_dir->names[a_fs_dir->names_used - 1]); } a_fs_dir->names_used--; } } if (data.orphan_subdir_list) { tsk_list_free(data.orphan_subdir_list); data.orphan_subdir_list = NULL; } // make copy of this so that we don't need to do it again. if ((a_fs->orphan_dir = tsk_fs_dir_alloc(a_fs, a_fs_dir->addr, a_fs_dir->names_used)) == NULL) { tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_ERR; } if (tsk_fs_dir_copy(a_fs_dir, a_fs->orphan_dir)) { tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_ERR; } // populate the fake FS_FILE structure in the struct to be returned for the "Orphan Directory" if (tsk_fs_dir_add_orphan_dir_meta(a_fs, a_fs_dir)) { tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_ERR; } tsk_release_lock(&a_fs->orphan_dir_lock); return TSK_OK; }
/* * Process the contents of a directory and add them to FS_DIR. * * @param fatfs File system information structure * @param a_fs_dir Structure to store the files in. * @param list_seen List of directory inodes that have been seen thus far in * directory walking (can be a pointer to a NULL pointer on first call). * @param buf Buffer that contains the directory contents. * @param len Length of buffer in bytes (must be a multiple of sector size) * @param addrs Array where each element is the original address of the * corresponding block in buf (size of array is number of blocks in directory). * * @return -1 on error, 0 on success, and 1 to stop */ static TSK_RETVAL_ENUM fatfs_dent_parse_buf(FATFS_INFO * fatfs, TSK_FS_DIR * a_fs_dir, char *buf, TSK_OFF_T len, TSK_DADDR_T * addrs) { unsigned int idx, sidx; int a, b; TSK_INUM_T inode, ibase; fatfs_dentry *dep; TSK_FS_INFO *fs = (TSK_FS_INFO *) & fatfs->fs_info; int sectalloc; TSK_FS_NAME *fs_name; FATFS_LFN lfninfo; if (buf == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("fatfs_dent_parse_buf: buffer is NULL"); return TSK_ERR; } dep = (fatfs_dentry *) buf; if ((fs_name = tsk_fs_name_alloc(FATFS_MAXNAMLEN_UTF8, 32)) == NULL) { return TSK_ERR; } memset(&lfninfo, 0, sizeof(FATFS_LFN)); lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1; for (sidx = 0; sidx < (unsigned int) (len / fatfs->ssize); sidx++) { /* Get the base inode for this sector */ ibase = FATFS_SECT_2_INODE(fatfs, addrs[sidx]); if (ibase > fs->last_inum) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("fatfs_parse: inode address is too large"); tsk_fs_name_free(fs_name); return TSK_COR; } if (tsk_verbose) tsk_fprintf(stderr, "fatfs_dent_parse_buf: Parsing sector %" PRIuDADDR "\n", addrs[sidx]); if ((sectalloc = fatfs_is_sectalloc(fatfs, addrs[sidx])) == -1) { if (tsk_verbose) { tsk_fprintf(stderr, "fatfs_dent_parse_buf: Error looking up sector allocation: %" PRIuDADDR "\n", addrs[sidx]); tsk_error_print(stderr); } tsk_error_reset(); continue; } /* cycle through the directory entries */ for (idx = 0; idx < fatfs->dentry_cnt_se; idx++, dep++) { fatfs_dentry *dir; /* is it a valid dentry? */ if (0 == fatfs_isdentry(fatfs, dep, (sectalloc) ? 1 : 0)) { if (tsk_verbose) tsk_fprintf(stderr, "fatfs_dent_parse_buf: Entry %u is invalid\n", idx); continue; } /* Copy the directory entry into the TSK_FS_NAME structure */ dir = (fatfs_dentry *) dep; inode = ibase + idx; /* Take care of the name * Copy a long name to a buffer and take action if it * is a small name */ if ((dir->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) { fatfs_dentry_lfn *dirl = (fatfs_dentry_lfn *) dir; /* Store the name in dinfo until we get the 8.3 name * Use the checksum to identify a new sequence * */ if (((dirl->seq & FATFS_LFN_SEQ_FIRST) && (dirl->seq != FATFS_SLOT_DELETED)) || (dirl->chksum != lfninfo.chk)) { // @@@ Do a partial output here /* Reset the values */ lfninfo.seq = dirl->seq & FATFS_LFN_SEQ_MASK; lfninfo.chk = dirl->chksum; lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1; } else if (dirl->seq != lfninfo.seq - 1) { // @@@ Check the sequence number - the checksum is correct though... } /* Copy the UTF16 values starting at end of buffer */ for (a = 3; a >= 0; a--) { if ((lfninfo.start > 0)) lfninfo.name[lfninfo.start--] = dirl->part3[a]; } for (a = 11; a >= 0; a--) { if ((lfninfo.start > 0)) lfninfo.name[lfninfo.start--] = dirl->part2[a]; } for (a = 9; a >= 0; a--) { if ((lfninfo.start > 0)) lfninfo.name[lfninfo.start--] = dirl->part1[a]; } // Skip ahead until we get a new sequence num or the 8.3 name continue; } /* Special case for volume label: name does not have an * extension and we add a note at the end that it is a label */ else if ((dir->attrib & FATFS_ATTR_VOLUME) == FATFS_ATTR_VOLUME) { a = 0; for (b = 0; b < 8; b++) { if ((dir->name[b] >= 0x20) && (dir->name[b] != 0xff)) { fs_name->name[a++] = dir->name[b]; } else { fs_name->name[a++] = '^'; } } for (b = 0; b < 3; b++) { if ((dir->ext[b] >= 0x20) && (dir->ext[b] != 0xff)) { fs_name->name[a++] = dir->ext[b]; } else { fs_name->name[a++] = '^'; } } fs_name->name[a] = '\0'; /* Append a string to show it is a label */ if (a + 22 < FATFS_MAXNAMLEN_UTF8) { char *volstr = " (Volume Label Entry)"; strncat(fs_name->name, volstr, FATFS_MAXNAMLEN_UTF8 - a); } } /* A short (8.3) entry */ else { char *name_ptr; // The dest location for the short name /* if we have a lfn, copy it into fs_name->name * and put the short name in fs_name->shrt_name */ if (lfninfo.start != FATFS_MAXNAMLEN_UTF8 - 1) { int retVal; /* @@@ Check the checksum */ /* Convert the UTF16 to UTF8 */ UTF16 *name16 = (UTF16 *) ((uintptr_t) & lfninfo.name[lfninfo. start + 1]); UTF8 *name8 = (UTF8 *) fs_name->name; retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16, (UTF16 *) & lfninfo.name[FATFS_MAXNAMLEN_UTF8], &name8, (UTF8 *) ((uintptr_t) name8 + FATFS_MAXNAMLEN_UTF8), TSKlenientConversion); if (retVal != TSKconversionOK) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_UNICODE); tsk_error_set_errstr ("fatfs_parse: Error converting FAT LFN to UTF8: %d", retVal); continue; } /* Make sure it is NULL Terminated */ if ((uintptr_t) name8 > (uintptr_t) fs_name->name + FATFS_MAXNAMLEN_UTF8) fs_name->name[FATFS_MAXNAMLEN_UTF8 - 1] = '\0'; else *name8 = '\0'; lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1; name_ptr = fs_name->shrt_name; // put 8.3 into shrt_name } /* We don't have a LFN, so put the short name in * fs_name->name */ else { fs_name->shrt_name[0] = '\0'; name_ptr = fs_name->name; // put 8.3 into normal location } /* copy in the short name into the place specified above. * Skip spaces and put in the . */ a = 0; for (b = 0; b < 8; b++) { if ((dir->name[b] != 0) && (dir->name[b] != 0xff) && (dir->name[b] != 0x20)) { if ((b == 0) && (dir->name[0] == FATFS_SLOT_DELETED)) { name_ptr[a++] = '_'; } else if ((dir->lowercase & FATFS_CASE_LOWER_BASE) && (dir->name[b] >= 'A') && (dir->name[b] <= 'Z')) { name_ptr[a++] = dir->name[b] + 32; } else { name_ptr[a++] = dir->name[b]; } } } for (b = 0; b < 3; b++) { if ((dir->ext[b] != 0) && (dir->ext[b] != 0xff) && (dir->ext[b] != 0x20)) { if (b == 0) name_ptr[a++] = '.'; if ((dir->lowercase & FATFS_CASE_LOWER_EXT) && (dir->ext[b] >= 'A') && (dir->ext[b] <= 'Z')) name_ptr[a++] = dir->ext[b] + 32; else name_ptr[a++] = dir->ext[b]; } } name_ptr[a] = '\0'; // make sure that only ASCII is in the short name fatfs_cleanup_ascii(name_ptr); } /* file type: FAT only knows DIR and FILE */ if ((dir->attrib & FATFS_ATTR_DIRECTORY) == FATFS_ATTR_DIRECTORY) fs_name->type = TSK_FS_NAME_TYPE_DIR; else fs_name->type = TSK_FS_NAME_TYPE_REG; /* Get inode */ fs_name->meta_addr = inode; /* Handle the . and .. entries specially * The current inode 'address' they have is for the current * slot in the cluster, but it needs to refer to the original * slot */ if (TSK_FS_ISDOT(fs_name->name)) { if (fs_name->name[1] == '\0') { inode = fs_name->meta_addr = a_fs_dir->fs_file->meta->addr; } /* for the parent directory, look up in the list that * is maintained in fafs_info */ else if (fs_name->name[1] == '.') { size_t q; uint8_t dir_found = 0; tsk_take_lock(&fatfs->dir_lock); for (q = 0; q < fatfs->dir_buf_next; q++) { if (fatfs->dir_buf[q] == a_fs_dir->fs_file->meta->addr) { inode = fs_name->meta_addr = fatfs->par_buf[q]; dir_found = 1; break; } } tsk_release_lock(&fatfs->dir_lock); if ((dir_found == 0) && (addrs[0] == fatfs->firstdatasect)) { /* if we are currently in the root directory, we aren't going to find * a parent. This shouldn't happen, but could result in an infinite loop. */ inode = fs_name->meta_addr = 0; dir_found = 1; } if (dir_found == 0) { if (tsk_verbose) fprintf(stderr, "fatfs_dent_parse_buf: Walking directory to find parent\n"); /* The parent directory is not in the list. We are going to walk * the directory until we hit this directory. This process will * populate the buffer table and we will then rescan it */ if (tsk_fs_dir_walk(fs, fs->root_inum, TSK_FS_DIR_WALK_FLAG_ALLOC | TSK_FS_DIR_WALK_FLAG_UNALLOC | TSK_FS_DIR_WALK_FLAG_RECURSE, find_parent_act, (void *) &a_fs_dir->fs_file->meta->addr)) { return 0; } if (tsk_verbose) fprintf(stderr, "fatfs_dent_parse_buf: Finished walking directory to find parent\n"); tsk_take_lock(&fatfs->dir_lock); for (q = 0; q < fatfs->dir_buf_next; q++) { if (fatfs->dir_buf[q] == a_fs_dir->fs_file->meta->addr) { inode = fs_name->meta_addr = fatfs->par_buf[q]; dir_found = 1; break; } } tsk_release_lock(&fatfs->dir_lock); // if we did not find it, then it was probably // from the orphan directory... if (dir_found == 0) inode = fs_name->meta_addr = TSK_FS_ORPHANDIR_INUM(fs); } } } else { /* Save the (non-. or ..) directory to parent directory info to local * structures so that we can later fill into the inode * info for '..' entries */ if (fs_name->type == TSK_FS_NAME_TYPE_DIR) { if (fatfs_dir_buf_add(fatfs, a_fs_dir->fs_file->meta->addr, inode)) return TSK_ERR; } } /* The allocation status of an entry is based on the allocation * status of the sector it is in and the flag. Deleted directories * do not always clear the flags of each entry */ if (sectalloc == 1) { fs_name->flags = (dep->name[0] == FATFS_SLOT_DELETED) ? TSK_FS_NAME_FLAG_UNALLOC : TSK_FS_NAME_FLAG_ALLOC; } else { fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC; } tsk_fs_dir_add(a_fs_dir, fs_name); } } tsk_fs_name_free(fs_name); return TSK_OK; }
/** * \ingroup imglib * Reads data from an open disk image * @param a_img_info Disk image to read from * @param a_off Byte offset to start reading from * @param a_buf Buffer to read into * @param a_len Number of bytes to read into buffer * @returns -1 on error or number of bytes read */ ssize_t tsk_img_read(TSK_IMG_INFO * a_img_info, TSK_OFF_T a_off, char *a_buf, size_t a_len) { #define CACHE_AGE 1000 ssize_t retval = 0; int i; int cache_next = 0; // index to lowest age cache (to use next) size_t len2; if (a_img_info == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_ARG); tsk_error_set_errstr("tsk_img_read: pointer is NULL"); return -1; } /* cache_lock is used for both the cache in IMG_INFO and * the shared variables in the img type specific INFO structs. * grab it now so that it is held before any reads. */ tsk_take_lock(&(a_img_info->cache_lock)); // if they ask for more than the cache length, skip the cache if ((a_len + a_off % 512) > TSK_IMG_INFO_CACHE_LEN) { ssize_t nbytes = a_img_info->read(a_img_info, a_off, a_buf, a_len); tsk_release_lock(&(a_img_info->cache_lock)); return nbytes; } if (a_off >= a_img_info->size) { tsk_release_lock(&(a_img_info->cache_lock)); tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_READ_OFF); tsk_error_set_errstr("tsk_img_read - %" PRIuOFF, a_off); return -1; } /* See if the requested length is going to be too long. * we'll use this length when checking the cache. */ len2 = a_len; if (a_off + len2 > a_img_info->size) len2 = (size_t) (a_img_info->size - a_off); // check if it is in the cache for (i = 0; i < TSK_IMG_INFO_CACHE_NUM; i++) { // Look into the in-use cache entries if (a_img_info->cache_len[i] > 0) { // the retval check makes sure we don't go back in after data was read if ((retval == 0) && (a_img_info->cache_off[i] <= a_off) && (a_img_info->cache_off[i] + a_img_info->cache_len[i] >= a_off + len2)) { /* if (tsk_verbose) fprintf(stderr, "tsk_img_read: Read found in cache %d\n", i); */ // We found it... memcpy(a_buf, &a_img_info->cache[i][a_off - a_img_info->cache_off[i]], len2); retval = (ssize_t) len2; // reset its "age" since it was useful a_img_info->cache_age[i] = CACHE_AGE; // we don't break out of the loop so that we update all ages } else { /* decrease its "age" since it was not useful. * We don't let used ones go below 1 so that they are not * confused with entries that have never been used. */ a_img_info->cache_age[i]--; // see if this is the most eligible replacement if ((a_img_info->cache_len[cache_next] > 0) && (a_img_info->cache_age[i] < a_img_info->cache_age[cache_next])) cache_next = i; } } else { cache_next = i; } } // if we didn't find it, then load it into the cache_next entry if (retval == 0) { size_t rlen; // round the offset down to a sector boundary a_img_info->cache_off[cache_next] = (a_off / 512) * 512; /* if (tsk_verbose) fprintf(stderr, "tsk_img_read: Loading data into cache %d (%" PRIuOFF ")\n", cache_next, a_img_info->cache_off[cache_next]); */ // figure out the length to read into the cache rlen = TSK_IMG_INFO_CACHE_LEN; if (a_img_info->cache_off[cache_next] + rlen > a_img_info->size) { rlen = (size_t) (a_img_info->size - a_img_info->cache_off[cache_next]); } retval = a_img_info->read(a_img_info, a_img_info->cache_off[cache_next], a_img_info->cache[cache_next], rlen); // if no error, then set the variables and copy the data if (retval != -1) { a_img_info->cache_age[cache_next] = CACHE_AGE; a_img_info->cache_len[cache_next] = retval; // update the length we can actually copy (in case we did not get to read all that we wanted) if (a_off + len2 > a_img_info->cache_off[cache_next] + retval) len2 = (size_t) (a_img_info->cache_off[cache_next] + retval - a_off); memcpy(a_buf, &a_img_info->cache[cache_next][a_off - a_img_info->cache_off[cache_next]], len2); retval = (ssize_t) len2; } else { a_img_info->cache_len[cache_next] = 0; a_img_info->cache_age[cache_next] = 0; a_img_info->cache_off[cache_next] = 0; } } tsk_release_lock(&(a_img_info->cache_lock)); return retval; }
/** \internal * Process a directory and load up FS_DIR with the entries. If a pointer to * an already allocated FS_DIR struture is given, it will be cleared. If no existing * FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return * value is error or corruption, then the FS_DIR structure could * have entries (depending on when the error occured). * * @param a_fs File system to analyze * @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated * structure or a new structure. * @param a_addr Address of directory to process. * @returns error, corruption, ok etc. */ TSK_RETVAL_ENUM ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir, TSK_INUM_T a_addr) { NTFS_INFO *ntfs = (NTFS_INFO *) a_fs; TSK_FS_DIR *fs_dir; const TSK_FS_ATTR *fs_attr_root = NULL; const TSK_FS_ATTR *fs_attr_idx; char *idxalloc; ntfs_idxentry *idxe; ntfs_idxroot *idxroot; ntfs_idxelist *idxelist; ntfs_idxrec *idxrec_p, *idxrec; int off; TSK_OFF_T idxalloc_len; TSK_FS_LOAD_FILE load_file; NTFS_PAR_MAP *map; /* In this function, we will return immediately if we get an error. * If we get corruption though, we will record that in 'retval_final' * and continue processing. */ TSK_RETVAL_ENUM retval_final = TSK_OK; TSK_RETVAL_ENUM retval_tmp; /* sanity check */ if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); tsk_error_set_errstr("ntfs_dir_open_meta: inode value: %" PRIuINUM "\n", a_addr); return TSK_ERR; } else if (a_fs_dir == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("ntfs_dir_open_meta: NULL fs_attr argument given"); return TSK_ERR; } if (tsk_verbose) tsk_fprintf(stderr, "ntfs_open_dir: Processing directory %" PRIuINUM "\n", a_addr); fs_dir = *a_fs_dir; if (fs_dir) { tsk_fs_dir_reset(fs_dir); } else { if ((*a_fs_dir = fs_dir = tsk_fs_dir_alloc(a_fs, a_addr, 128)) == NULL) { return TSK_ERR; } } // handle the orphan directory if its contents were requested if (a_addr == TSK_FS_ORPHANDIR_INUM(a_fs)) { return tsk_fs_dir_find_orphans(a_fs, fs_dir); } /* Get the inode and verify it has attributes */ if ((fs_dir->fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_addr)) == NULL) { tsk_error_errstr2_concat("- ntfs_dir_open_meta"); return TSK_COR; } if (!(fs_dir->fs_file->meta->attr)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_INODE_COR); tsk_error_set_errstr("dent_walk: Error: Directory address %" PRIuINUM " has no attributes", a_addr); return TSK_COR; } /* * Read the Index Root Attribute -- we do some sanity checking here * to report errors before we start to make up data for the "." and ".." * entries */ fs_attr_root = tsk_fs_attrlist_get(fs_dir->fs_file->meta->attr, NTFS_ATYPE_IDXROOT); if (!fs_attr_root) { tsk_error_errstr2_concat(" - dent_walk: $IDX_ROOT not found"); return TSK_COR; } if (fs_attr_root->flags & TSK_FS_ATTR_NONRES) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_INODE_COR); tsk_error_set_errstr ("dent_walk: $IDX_ROOT is not resident - it should be"); return TSK_COR; } idxroot = (ntfs_idxroot *) fs_attr_root->rd.buf; /* Verify that the attribute type is $FILE_NAME */ if (tsk_getu32(a_fs->endian, idxroot->type) == 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_INODE_COR); tsk_error_set_errstr ("dent_walk: Attribute type in index root is 0"); return TSK_COR; } else if (tsk_getu32(a_fs->endian, idxroot->type) != NTFS_ATYPE_FNAME) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_INODE_COR); tsk_error_set_errstr("ERROR: Directory index is sorted by type: %" PRIu32 ".\nOnly $FNAME is currently supported", tsk_getu32(a_fs->endian, idxroot->type)); return TSK_COR; } /* Get the header of the index entry list */ idxelist = &idxroot->list; /* Get the offset to the start of the index entry list */ idxe = (ntfs_idxentry *) ((uintptr_t) idxelist + tsk_getu32(a_fs->endian, idxelist->begin_off)); /* * NTFS does not have "." and ".." entries in the index trees * (except for a "." entry in the root directory) * * So, we'll make 'em up by making a TSK_FS_NAME structure for * a '.' and '..' entry and call the action */ if (a_addr != a_fs->root_inum) { // && (flags & TSK_FS_NAME_FLAG_ALLOC)) { TSK_FS_NAME *fs_name; TSK_FS_META_NAME_LIST *fs_name_list; if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dir_open_meta: Creating . and .. entries\n"); if ((fs_name = tsk_fs_name_alloc(16, 0)) == NULL) { return TSK_ERR; } /* * "." */ fs_name->meta_addr = a_addr; fs_name->meta_seq = fs_dir->fs_file->meta->seq; fs_name->type = TSK_FS_NAME_TYPE_DIR; strcpy(fs_name->name, "."); fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; if (tsk_fs_dir_add(fs_dir, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } /* * ".." */ strcpy(fs_name->name, ".."); fs_name->type = TSK_FS_NAME_TYPE_DIR; /* The fs_name structure holds the parent inode value, so we * just cycle using those */ for (fs_name_list = fs_dir->fs_file->meta->name2; fs_name_list != NULL; fs_name_list = fs_name_list->next) { fs_name->meta_addr = fs_name_list->par_inode; fs_name->meta_seq = fs_name_list->par_seq; if (tsk_fs_dir_add(fs_dir, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } } tsk_fs_name_free(fs_name); fs_name = NULL; } /* Now we return to processing the Index Root Attribute */ if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dir_open_meta: Processing $IDX_ROOT of inum %" PRIuINUM "\n", a_addr); /* Verify the offset pointers */ if ((tsk_getu32(a_fs->endian, idxelist->seqend_off) < tsk_getu32(a_fs->endian, idxelist->begin_off)) || (tsk_getu32(a_fs->endian, idxelist->bufend_off) < tsk_getu32(a_fs->endian, idxelist->seqend_off)) || (((uintptr_t) idxe + tsk_getu32(a_fs->endian, idxelist->bufend_off)) > ((uintptr_t) fs_attr_root->rd.buf + fs_attr_root->rd.buf_size))) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_INODE_COR); tsk_error_set_errstr ("Error: Index list offsets are invalid on entry: %" PRIuINUM, fs_dir->fs_file->meta->addr); return TSK_COR; } retval_tmp = ntfs_proc_idxentry(ntfs, fs_dir, (fs_dir->fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC) ? 1 : 0, idxe, tsk_getu32(a_fs->endian, idxelist->bufend_off) - tsk_getu32(a_fs->endian, idxelist->begin_off), tsk_getu32(a_fs->endian, idxelist->seqend_off) - tsk_getu32(a_fs->endian, idxelist->begin_off)); // stop if we get an error, continue if we got corruption if (retval_tmp == TSK_ERR) { return TSK_ERR; } else if (retval_tmp == TSK_COR) { retval_final = TSK_COR; } /* * get the index allocation attribute if it exists (it doesn't for * small directories */ fs_attr_idx = tsk_fs_attrlist_get(fs_dir->fs_file->meta->attr, NTFS_ATYPE_IDXALLOC); /* if we don't have an index alloc then return, we have processed * all of the entries */ if (!fs_attr_idx) { if (tsk_getu32(a_fs->endian, idxelist->flags) & NTFS_IDXELIST_CHILD) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_INODE_COR); tsk_error_set_errstr ("Error: $IDX_ROOT says there should be children, but there isn't"); return TSK_COR; } } else { if (fs_attr_idx->flags & TSK_FS_ATTR_RES) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_INODE_COR); tsk_error_set_errstr ("$IDX_ALLOC is Resident - it shouldn't be"); return TSK_COR; } /* * Copy the index allocation run into a big buffer */ idxalloc_len = fs_attr_idx->nrd.allocsize; if ((idxalloc = tsk_malloc((size_t) idxalloc_len)) == NULL) { return TSK_ERR; } /* Fill in the loading data structure */ load_file.total = load_file.left = (size_t) idxalloc_len; load_file.cur = load_file.base = idxalloc; if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dir_open_meta: Copying $IDX_ALLOC into buffer\n"); if (tsk_fs_attr_walk(fs_attr_idx, TSK_FS_FILE_WALK_FLAG_SLACK, tsk_fs_load_file_action, (void *) &load_file)) { free(idxalloc); tsk_error_errstr2_concat(" - ntfs_dir_open_meta"); return TSK_COR; // this could be an error though } /* Not all of the directory was copied, so we exit */ if (load_file.left > 0) { free(idxalloc); tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_FWALK); tsk_error_set_errstr("Error reading directory contents: %" PRIuINUM "\n", a_addr); return TSK_COR; } /* * The idxalloc is a big buffer that contains one or more * idx buffer structures. Each idxrec is a node in the B-Tree. * We do not process the tree as a tree because then we could * not find the deleted file names. * * Therefore, we scan the big buffer looking for the index record * structures. We save a pointer to the known beginning (idxrec_p). * Then we scan for the beginning of the next one (idxrec) and process * everything in the middle as an ntfs_idxrec. We can't use the * size given because then we wouldn't see the deleted names */ /* Set the previous pointer to NULL */ idxrec_p = idxrec = NULL; /* Loop by cluster size */ for (off = 0; off < idxalloc_len; off += ntfs->csize_b) { uint32_t list_len, rec_len; idxrec = (ntfs_idxrec *) & idxalloc[off]; if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dir_open_meta: Index Buffer Offset: %d Magic: %" PRIx32 "\n", off, tsk_getu32(a_fs->endian, idxrec->magic)); /* Is this the begining of an index record? */ if (tsk_getu32(a_fs->endian, idxrec->magic) != NTFS_IDXREC_MAGIC) continue; /* idxrec_p is only NULL for the first time * Set it and start again to find the next one */ if (idxrec_p == NULL) { idxrec_p = idxrec; continue; } /* Process the previous structure */ /* idxrec points to the next idxrec structure, idxrec_p * points to the one we are going to process */ rec_len = (uint32_t) ((uintptr_t) idxrec - (uintptr_t) idxrec_p); if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dir_open_meta: Processing previous index record (len: %" PRIu32 ")\n", rec_len); /* remove the update sequence in the index record */ if (ntfs_fix_idxrec(ntfs, idxrec_p, rec_len)) { free(idxalloc); return TSK_COR; } /* Locate the start of the index entry list */ idxelist = &idxrec_p->list; idxe = (ntfs_idxentry *) ((uintptr_t) idxelist + tsk_getu32(a_fs->endian, idxelist->begin_off)); /* the length from the start of the next record to where our * list starts. * This should be the same as bufend_off in idxelist, but we don't * trust it. */ list_len = (uint32_t) ((uintptr_t) idxrec - (uintptr_t) idxe); /* Verify the offset pointers */ if (((uintptr_t) idxe > (uintptr_t) idxrec) || ((uintptr_t) idxelist + tsk_getu32(a_fs->endian, idxelist->seqend_off) > (uintptr_t) idxrec)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_INODE_COR); tsk_error_set_errstr ("Error: Index list offsets are invalid on entry: %" PRIuINUM, fs_dir->fs_file->meta->addr); free(idxalloc); return TSK_COR; } /* process the list of index entries */ retval_tmp = ntfs_proc_idxentry(ntfs, fs_dir, (fs_dir->fs_file->meta-> flags & TSK_FS_META_FLAG_UNALLOC) ? 1 : 0, idxe, list_len, tsk_getu32(a_fs->endian, idxelist->seqend_off) - tsk_getu32(a_fs->endian, idxelist->begin_off)); // stop if we get an error, record if we get corruption if (retval_tmp == TSK_ERR) { free(idxalloc); return TSK_ERR; } else if (retval_tmp == TSK_COR) { retval_final = TSK_COR; } /* reset the pointer to the next record */ idxrec_p = idxrec; } /* end of cluster loop */ /* Process the final record */ if (idxrec_p) { uint32_t list_len, rec_len; /* Length from end of attribute to start of this */ rec_len = (uint32_t) (idxalloc_len - (uintptr_t) idxrec_p - (uintptr_t) idxalloc); if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dir_open_meta: Processing final index record (len: %" PRIu32 ")\n", rec_len); /* remove the update sequence */ if (ntfs_fix_idxrec(ntfs, idxrec_p, rec_len)) { free(idxalloc); return TSK_COR; } idxelist = &idxrec_p->list; idxe = (ntfs_idxentry *) ((uintptr_t) idxelist + tsk_getu32(a_fs->endian, idxelist->begin_off)); /* This is the length of the idx entries */ list_len = (uint32_t) ((uintptr_t) idxalloc + idxalloc_len) - (uintptr_t) idxe; /* Verify the offset pointers */ if ((list_len > rec_len) || ((uintptr_t) idxelist + tsk_getu32(a_fs->endian, idxelist->seqend_off) > (uintptr_t) idxalloc + idxalloc_len)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_INODE_COR); tsk_error_set_errstr ("Error: Index list offsets are invalid on entry: %" PRIuINUM, fs_dir->fs_file->meta->addr); free(idxalloc); return TSK_COR; } /* process the list of index entries */ retval_tmp = ntfs_proc_idxentry(ntfs, fs_dir, (fs_dir->fs_file->meta-> flags & TSK_FS_META_FLAG_UNALLOC) ? 1 : 0, idxe, list_len, tsk_getu32(a_fs->endian, idxelist->seqend_off) - tsk_getu32(a_fs->endian, idxelist->begin_off)); // stop if we get an error, record if we get corruption if (retval_tmp == TSK_ERR) { free(idxalloc); return TSK_ERR; } else if (retval_tmp == TSK_COR) { retval_final = TSK_COR; } } free(idxalloc); } // get the orphan files // load and cache the map if it has not already been done tsk_take_lock(&ntfs->orphan_map_lock); if (ntfs->orphan_map == NULL) { if (a_fs->inode_walk(a_fs, a_fs->first_inum, a_fs->last_inum, TSK_FS_META_FLAG_UNALLOC, ntfs_orphan_act, NULL)) { tsk_release_lock(&ntfs->orphan_map_lock); return TSK_ERR; } } // see if there are any entries for this dir map = ntfs_orphan_map_get(ntfs, a_addr); if (map != NULL) { int a; TSK_FS_NAME *fs_name; TSK_FS_FILE *fs_file_orp = NULL; if ((fs_name = tsk_fs_name_alloc(256, 0)) == NULL) return TSK_ERR; fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC; fs_name->type = TSK_FS_NAME_TYPE_UNDEF; for (a = 0; a < map->used_cnt; a++) { /* Fill in the basics of the fs_name entry * so we can print in the fls formats */ fs_name->meta_addr = map->addrs[a]; // lookup the file to get its name (we did not cache that) fs_file_orp = tsk_fs_file_open_meta(a_fs, fs_file_orp, map->addrs[a]); if ((fs_file_orp) && (fs_file_orp->meta) && (fs_file_orp->meta->name2)) { TSK_FS_META_NAME_LIST *n2 = fs_file_orp->meta->name2; while (n2) { if (n2->par_inode == a_addr) { strncpy(fs_name->name, n2->name, fs_name->name_size); tsk_fs_dir_add(fs_dir, fs_name); } n2 = n2->next; } } } tsk_fs_name_free(fs_name); } tsk_release_lock(&ntfs->orphan_map_lock); // if we are listing the root directory, add the Orphan directory entry if (a_addr == a_fs->root_inum) { TSK_FS_NAME *fs_name; if ((fs_name = tsk_fs_name_alloc(256, 0)) == NULL) return TSK_ERR; if (tsk_fs_dir_make_orphan_dir_name(a_fs, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } if (tsk_fs_dir_add(fs_dir, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } tsk_fs_name_free(fs_name); } return retval_final; }