コード例 #1
0
ファイル: fs_dir.c プロジェクト: bkerler/sleuthkit
/** \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;
}
コード例 #2
0
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);
}
コード例 #3
0
ファイル: ewf.c プロジェクト: 0xkasun/OpenDF
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;
}
コード例 #4
0
ファイル: fatfs_dent.cpp プロジェクト: 0xkasun/OpenDF
/**
* 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);
}
コード例 #5
0
ファイル: xtaffs_dent.cpp プロジェクト: egall/sleuthkit
/**
* 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);
}
コード例 #6
0
ファイル: fs_dir.c プロジェクト: bkerler/sleuthkit
/** \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;
}
コード例 #7
0
ファイル: fatfs_dent.cpp プロジェクト: 0xkasun/OpenDF
/**
* 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;
}
コード例 #8
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;
}
コード例 #9
0
ファイル: fatfs_dent.cpp プロジェクト: 0xkasun/OpenDF
/**
* 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;
}
コード例 #10
0
ファイル: fs_dir.c プロジェクト: bkerler/sleuthkit
/* 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;
}
コード例 #11
0
ファイル: fs_dir.c プロジェクト: bkerler/sleuthkit
/** \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;
}
コード例 #12
0
ファイル: fs_dir.c プロジェクト: bkerler/sleuthkit
/** \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;
}
コード例 #13
0
/*
 * 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;
}
コード例 #14
0
ファイル: img_io.c プロジェクト: 0xNF/sleuthkit
/**
 * \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;
}
コード例 #15
0
/** \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;
}