Пример #1
0
static TSK_WALK_RET_ENUM
blkstat_act(const TSK_FS_BLOCK * fs_block, void *ptr)
{
    tsk_printf("%s: %" PRIuDADDR "\n", fs_block->fs_info->duname,
        fs_block->addr);
    tsk_printf("%sAllocated%s\n",
        (fs_block->flags & TSK_FS_BLOCK_FLAG_ALLOC) ? "" : "Not ",
        (fs_block->flags & TSK_FS_BLOCK_FLAG_META) ? " (Meta)" : "");

    if (TSK_FS_TYPE_ISFFS(fs_block->fs_info->ftype)) {
        FFS_INFO *ffs = (FFS_INFO *) fs_block->fs_info;
        tsk_printf("Group: %" PRI_FFSGRP "\n", ffs->grp_num);
    }
    else if (TSK_FS_TYPE_ISEXT(fs_block->fs_info->ftype)) {
        EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs_block->fs_info;
        if (fs_block->addr >= ext2fs->first_data_block)
            tsk_printf("Group: %" PRI_EXT2GRP "\n", ext2fs->grp_num);
    }
    else if (TSK_FS_TYPE_ISFAT(fs_block->fs_info->ftype)) {
        FATFS_INFO *fatfs = (FATFS_INFO *) fs_block->fs_info;
        /* Does this have a cluster address? */
        if (fs_block->addr >= fatfs->firstclustsect) {
            tsk_printf("Cluster: %" PRIuDADDR "\n",
                (2 + (fs_block->addr -
                        fatfs->firstclustsect) / fatfs->csize));
        }
    }

    return TSK_WALK_STOP;
}
Пример #2
0
/**
 * \ingroup fslib
 * Tries to process data in a disk image at a given offset as a file system. 
 * Returns a structure that can be used for analysis and reporting. 
 *
 * @param a_img_info Disk image to analyze
 * @param a_offset Byte offset to start analyzing from
 * @param a_ftype Type of file system (or autodetect)
 *
 * @return NULL on error 
 */
TSK_FS_INFO *
tsk_fs_open_img(TSK_IMG_INFO * a_img_info, TSK_OFF_T a_offset,
    TSK_FS_TYPE_ENUM a_ftype)
{
    if (a_img_info == NULL) {
        tsk_error_reset();
        tsk_errno = TSK_ERR_FS_ARG;
        snprintf(tsk_errstr, TSK_ERRSTR_L,
            "tsk_fs_open_img: Null image handle");
        return NULL;
    }

    /* We will try different file systems ... 
     * We need to try all of them in case more than one matches
     */
    if (a_ftype == TSK_FS_TYPE_DETECT) {
        TSK_FS_INFO *fs_info, *fs_set = NULL;
        char *set = NULL;

        if (tsk_verbose)
            tsk_fprintf(stderr,
                "fsopen: Auto detection mode at offset %" PRIuOFF "\n",
                a_offset);

        if ((fs_info =
                ntfs_open(a_img_info, a_offset, TSK_FS_TYPE_NTFS_DETECT,
                    1)) != NULL) {
            set = "NTFS";
            fs_set = fs_info;
        }
        else {
            tsk_error_reset();
        }

        if ((fs_info =
                fatfs_open(a_img_info, a_offset, TSK_FS_TYPE_FAT_DETECT,
                    1)) != NULL) {
            if (set == NULL) {
                set = "FAT";
                fs_set = fs_info;
            }
            else {
                fs_set->close(fs_set);
                fs_info->close(fs_info);
                tsk_error_reset();
                tsk_errno = TSK_ERR_FS_UNKTYPE;
                snprintf(tsk_errstr, TSK_ERRSTR_L, "FAT or %s", set);
                return NULL;
            }
        }
        else {
            tsk_error_reset();
        }

        if ((fs_info =
                ext2fs_open(a_img_info, a_offset, TSK_FS_TYPE_EXT_DETECT,
                    1)) != NULL) {
            if (set == NULL) {
                set = "EXT2/3";
                fs_set = fs_info;
            }
            else {
                fs_set->close(fs_set);
                fs_info->close(fs_info);
                tsk_error_reset();
                tsk_errno = TSK_ERR_FS_UNKTYPE;
                snprintf(tsk_errstr, TSK_ERRSTR_L, "EXT2/3 or %s", set);
                return NULL;
            }
        }
        else {
            tsk_error_reset();
        }

        if ((fs_info =
                ffs_open(a_img_info, a_offset,
                    TSK_FS_TYPE_FFS_DETECT)) != NULL) {
            if (set == NULL) {
                set = "UFS";
                fs_set = fs_info;
            }
            else {
                fs_set->close(fs_set);
                fs_info->close(fs_info);
                tsk_error_reset();
                tsk_errno = TSK_ERR_FS_UNKTYPE;
                snprintf(tsk_errstr, TSK_ERRSTR_L, "UFS or %s", set);
                return NULL;
            }
        }
        else {
            tsk_error_reset();
        }


#if TSK_USE_HFS
        if ((fs_info =
                hfs_open(a_img_info, a_offset, TSK_FS_TYPE_HFS_DETECT,
                    1)) != NULL) {
            if (set == NULL) {
                set = "HFS";
                fs_set = fs_info;
            }
            else {
                fs_set->close(fs_set);
                fs_info->close(fs_info);
                tsk_error_reset();
                tsk_errno = TSK_ERR_FS_UNKTYPE;
                snprintf(tsk_errstr, TSK_ERRSTR_L, "HFS or %s", set);
                return NULL;
            }
        }
        else {
            tsk_error_reset();
        }
#endif

        if ((fs_info =
                iso9660_open(a_img_info, a_offset,
                    TSK_FS_TYPE_ISO9660_DETECT, 1)) != NULL) {
            if (set != NULL) {
                fs_set->close(fs_set);
                fs_info->close(fs_info);
                tsk_error_reset();
                tsk_errno = TSK_ERR_FS_UNKTYPE;
                snprintf(tsk_errstr, TSK_ERRSTR_L, "ISO9660 or %s", set);
                return NULL;
            }
            fs_set = fs_info;
        }
        else {
            tsk_error_reset();
        }


        if (fs_set == NULL) {
            tsk_error_reset();
            tsk_errno = TSK_ERR_FS_UNKTYPE;
            tsk_errstr[0] = '\0';
            tsk_errstr2[0] = '\0';
            return NULL;
        }
        return fs_set;
    }
    else {
        if (TSK_FS_TYPE_ISNTFS(a_ftype))
            return ntfs_open(a_img_info, a_offset, a_ftype, 0);
        else if (TSK_FS_TYPE_ISFAT(a_ftype))
            return fatfs_open(a_img_info, a_offset, a_ftype, 0);
        else if (TSK_FS_TYPE_ISFFS(a_ftype))
            return ffs_open(a_img_info, a_offset, a_ftype);
        else if (TSK_FS_TYPE_ISEXT(a_ftype))
            return ext2fs_open(a_img_info, a_offset, a_ftype, 0);
        else if (TSK_FS_TYPE_ISHFS(a_ftype))
            return hfs_open(a_img_info, a_offset, a_ftype, 0);
        else if (TSK_FS_TYPE_ISISO9660(a_ftype))
            return iso9660_open(a_img_info, a_offset, a_ftype, 0);
        else if (TSK_FS_TYPE_ISRAW(a_ftype))
            return rawfs_open(a_img_info, a_offset);
        else if (TSK_FS_TYPE_ISSWAP(a_ftype))
            return swapfs_open(a_img_info, a_offset);
        else {
            tsk_error_reset();
            tsk_errno = TSK_ERR_FS_UNSUPTYPE;
            snprintf(tsk_errstr, TSK_ERRSTR_L, "%X", (int) a_ftype);
            return NULL;
        }
    }
}
Пример #3
0
/** \internal
 * Process an array of addresses and turn them into runs
 *
 * @param fs File system to analyze
 * @param fs_attr Data attribute to add runs to
 * @param addrs Buffer of addresses to process and turn into runs
 * @param addr_len Number of addresses in buffer
 * @param length Length of file remaining
 *
 * @returns the number of bytes processed and -1 if an error occurred
 */
static TSK_OFF_T
unix_make_data_run_direct(TSK_FS_INFO * fs, TSK_FS_ATTR * fs_attr,
    TSK_DADDR_T * addrs, size_t addr_len, TSK_OFF_T length)
{
    TSK_DADDR_T run_start = 0;
    TSK_DADDR_T run_len = 0;
    TSK_DADDR_T blks_processed = 0;
    size_t i;
    size_t fs_blen;             // how big is each "block" (in fragments)

    if (addr_len == 0) {
        return 0;
    }

    // block_size is a fragment size in UFS, so we need to maintain length in fragments
    if (TSK_FS_TYPE_ISFFS(fs->ftype)) {
        FFS_INFO *ffs = (FFS_INFO *) fs;
        fs_blen = ffs->ffsbsize_f;
    }
    else {
        fs_blen = 1;
    }

    run_start = addrs[0];
    run_len = fs_blen;

    /* Note that we are lazy about length.  We stop only when a run is past length,
     * we do not end exactly at length -- although that should happen anyway.  
     */
    for (i = 0; i < addr_len; i++) {

        /* Make a new run if:
         * - This is the last addresss in the buffer
         * - The next address is not part of the current run
         * -- special case for sparse since they use 0 as an address
         */
        if ((i + 1 == addr_len) ||
            ((run_start + run_len != addrs[i + 1]) && (run_start != 0)) ||
            ((run_start == 0) && (addrs[i + 1] != 0))) {

            TSK_FS_ATTR_RUN *data_run;

            // make a non-resident run
            data_run = tsk_fs_attr_run_alloc();
            if (data_run == NULL)
                return -1;

            data_run->addr = run_start;
            data_run->len = run_len;

            if (run_start == 0)
                data_run->flags = TSK_FS_ATTR_RUN_FLAG_SPARSE;

            // save the run
            tsk_fs_attr_append_run(fs, fs_attr, data_run);

            // get ready for the next run
            if (i + 1 != addr_len)
                run_start = addrs[i + 1];
            run_len = 0;

            // stop if we are past the length requested
            if (blks_processed * fs->block_size > (TSK_DADDR_T) length)
                break;
        }
        run_len += fs_blen;
        blks_processed += fs_blen;
    }

    return blks_processed * fs->block_size;
}
Пример #4
0
/** \internal
 *
 * @returns 1 on error and 0 on success
 */
uint8_t
tsk_fs_unix_make_data_run(TSK_FS_FILE * fs_file)
{
    TSK_OFF_T length = 0;
    TSK_OFF_T read_b = 0;
    TSK_FS_ATTR *fs_attr;
    TSK_FS_ATTR *fs_attr_indir;
    TSK_FS_META *fs_meta = fs_file->meta;
    TSK_FS_INFO *fs = fs_file->fs_info;

    // clean up any error messages that are lying around
    tsk_error_reset();

    if (tsk_verbose)
        tsk_fprintf(stderr,
            "unix_make_data_run: Processing file %" PRIuINUM "\n",
            fs_meta->addr);

    // see if we have already loaded the runs
    if ((fs_meta->attr != NULL)
        && (fs_meta->attr_state == TSK_FS_META_ATTR_STUDIED)) {
        return 0;
    }
    else if (fs_meta->attr_state == TSK_FS_META_ATTR_ERROR) {
        return 1;
    }
    // not sure why this would ever happen, but...
    else if (fs_meta->attr != NULL) {
        tsk_fs_attrlist_markunused(fs_meta->attr);
    }
    else if (fs_meta->attr == NULL) {
        fs_meta->attr = tsk_fs_attrlist_alloc();
    }

    if ((TSK_FS_TYPE_ISFFS(fs->ftype) == 0)
        && (TSK_FS_TYPE_ISEXT(fs->ftype) == 0)) {
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
        tsk_error_set_errstr
            ("unix_make_run: Called with non-Unix file system: %x",
            fs->ftype);
        return 1;
    }

    length = roundup(fs_meta->size, fs->block_size);

    if ((fs_attr =
            tsk_fs_attrlist_getnew(fs_meta->attr,
                TSK_FS_ATTR_NONRES)) == NULL) {
        return 1;
    }

    // initialize the data run
    if (tsk_fs_attr_set_run(fs_file, fs_attr, NULL, NULL,
            TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
            fs_meta->size, fs_meta->size, roundup(fs_meta->size,
                fs->block_size), 0, 0)) {
        return 1;
    }

    read_b =
        unix_make_data_run_direct(fs, fs_attr,
        (TSK_DADDR_T *) fs_meta->content_ptr, 12, length);
    if (read_b == -1) {
        fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
        if (fs_meta->flags & TSK_FS_META_FLAG_UNALLOC)
            tsk_error_set_errno(TSK_ERR_FS_RECOVER);
        return 1;
    }
    length -= read_b;

    /* if there is still data left, read the indirect */
    if (length > 0) {
        int level;
        char *buf[4] = {NULL};
        size_t fs_bufsize0;
        size_t fs_bufsize1;
        size_t ptrsperblock;
        int numBlocks = 0;
        int numSingIndirect = 0;
        int numDblIndirect = 0;
        int numTripIndirect = 0;


        /* With FFS/UFS a full block contains the addresses, but block_size is
         * only a fragment.  Figure out the scratch buffer size and the buffers to 
         * store the cleaned addresses (endian converted) */
        if (TSK_FS_TYPE_ISFFS(fs->ftype)) {
            FFS_INFO *ffs = (FFS_INFO *) fs;

            fs_bufsize0 = ffs->ffsbsize_b;
            if ((fs->ftype == TSK_FS_TYPE_FFS1)
                || (fs->ftype == TSK_FS_TYPE_FFS1B)) {
                ptrsperblock = fs_bufsize0 / 4;
            }
            else {
                ptrsperblock = fs_bufsize0 / 8;
            }
        }
        else {
            fs_bufsize0 = fs->block_size;
            ptrsperblock = fs_bufsize0 / 4;
        }
        fs_bufsize1 = sizeof(TSK_DADDR_T) * ptrsperblock;

        /*
         * Initialize a buffer for the 3 levels of indirection that are supported by
         * this inode.  Each level of indirection will have a buffer to store
         * addresses in.  buf[0] is a special scratch buffer that is used to store
         * raw data from the image (before endian conversions are applied).  It is
         * equal to one block size.  The others will store TSK_DADDR_T structures
         * and will have a size depending on the FS type. 
         */
        if ((fs_attr_indir =
                tsk_fs_attrlist_getnew(fs_meta->attr,
                    TSK_FS_ATTR_NONRES)) == NULL) {
            return 1;
        }

        // determine number of indirect lbocks needed for file size...
        numBlocks =
            (int) (((fs_meta->size + fs_bufsize0 - 1) / fs_bufsize0) - 12);
        numSingIndirect =
            (int) ((numBlocks + ptrsperblock - 1) / ptrsperblock);
        numDblIndirect = 0;
        numTripIndirect = 0;

        // double block pointer?
        if (numSingIndirect > 1) {
            numDblIndirect = (int)
                ((numSingIndirect - 1 + ptrsperblock - 1) / ptrsperblock);
            if (numDblIndirect > 1) {
                numTripIndirect = (int)
                    ((numDblIndirect - 1 + ptrsperblock -
                        1) / ptrsperblock);
            }
        }

        // initialize the data run
        if (tsk_fs_attr_set_run(fs_file, fs_attr_indir, NULL, NULL,
                TSK_FS_ATTR_TYPE_UNIX_INDIR, TSK_FS_ATTR_ID_DEFAULT,
                fs_bufsize0 * (numSingIndirect + numDblIndirect +
                    numTripIndirect),
                fs_bufsize0 * (numSingIndirect + numDblIndirect +
                    numTripIndirect),
                fs_bufsize0 * (numSingIndirect + numDblIndirect +
                    numTripIndirect), 0, 0)) {
            return 1;
        }

        if ((buf[0] = (char *) tsk_malloc(fs_bufsize0)) == NULL) {
            return 1;
        }

        for (level = 1; length > 0 && level < 4; level++) {
            TSK_DADDR_T *addr_ptr = (TSK_DADDR_T *) fs_meta->content_ptr;

            if ((buf[level] = (char *) tsk_malloc(fs_bufsize1)) == NULL) {
                int f;
                for (f = 0; f < level; f++) {
                    free(buf[f]);
                }
                return 1;
            }

            /* the indirect addresses are stored in addr_ptr after the 12
             * direct addresses */
            read_b =
                unix_make_data_run_indirect(fs, fs_attr, fs_attr_indir,
                buf, level, addr_ptr[12 + level - 1], length);
            if (read_b == -1)
                break;
            length -= read_b;
        }

        /*
         * Cleanup.
         */
        for (level = 0; level < 4; ++level) {
            if (buf[level]) {
                free(buf[level]);
            }
        }
    }

    if (read_b == -1) {
        fs_meta->attr_state = TSK_FS_META_ATTR_ERROR;
        if (fs_meta->flags & TSK_FS_META_FLAG_UNALLOC)
            tsk_error_set_errno(TSK_ERR_FS_RECOVER);
        return 1;
    }

    fs_meta->attr_state = TSK_FS_META_ATTR_STUDIED;

    return 0;
}
Пример #5
0
/** \internal
 * Read an indirect block and process the contents to make a runlist from the pointers. 
 *
 * @param fs File system to analyze
 * @param fs_attr Structure to save run data into
 * @param fs_attr_indir Structure to save addresses of indirect block pointers in
 * @param buf Buffers to read block data into (0 is block sized, 1+ are DADDR_T arrays based on FS type)
 * @param level Indirection level that this will process at (1+)
 * @param addr Address of block to read
 * @param length Length of file remaining
 *
 * @returns the number of bytes processed during call and -1 if an error occurred
 */
static TSK_OFF_T
unix_make_data_run_indirect(TSK_FS_INFO * fs, TSK_FS_ATTR * fs_attr,
    TSK_FS_ATTR * fs_attr_indir, char *buf[], int level, TSK_DADDR_T addr,
    TSK_OFF_T length)
{
    char *myname = "unix_make_data_run_indirect";
    size_t addr_cnt = 0;
    TSK_DADDR_T *myaddrs = (TSK_DADDR_T *) buf[level];
    TSK_OFF_T length_remain = length;
    TSK_OFF_T retval;
    size_t fs_bufsize;
    size_t fs_blen;
    TSK_FS_ATTR_RUN *data_run;

    if (tsk_verbose)
        tsk_fprintf(stderr, "%s: level %d block %" PRIuDADDR "\n", myname,
            level, addr);

    // block_size is a fragment size in UFS, so we need to maintain length in fragments
    if (TSK_FS_TYPE_ISFFS(fs->ftype)) {
        FFS_INFO *ffs = (FFS_INFO *) fs;
        fs_blen = ffs->ffsbsize_f;
        fs_bufsize = ffs->ffsbsize_b;
    }
    else {
        fs_blen = 1;
        fs_bufsize = fs->block_size;
    }

    if (addr > fs->last_block) {
        tsk_error_reset();
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
        tsk_error_set_errstr("unix: Indirect block address too large: %"
            PRIuDADDR "", addr);
        return -1;
    }

    // make a non-resident run
    data_run = tsk_fs_attr_run_alloc();
    if (data_run == NULL)
        return -1;

    data_run->addr = addr;
    data_run->len = fs_blen;

    /*
     * Read a block of disk addresses.
     */
    // sparse
    if (addr == 0) {
        memset(buf[0], 0, fs_bufsize);
        data_run->flags = TSK_FS_ATTR_RUN_FLAG_SPARSE;
    }
    else {
        ssize_t cnt;
        // read the data into the scratch buffer
        cnt = tsk_fs_read_block(fs, addr, buf[0], fs_bufsize);
        if (cnt != fs_bufsize) {
            if (cnt >= 0) {
                tsk_error_reset();
                tsk_error_set_errno(TSK_ERR_FS_READ);
            }
            tsk_error_set_errstr2("unix_make_data_run_indir: Block %"
                PRIuDADDR, addr);
            return -1;
        }
    }

    // save the run
    tsk_fs_attr_append_run(fs, fs_attr_indir, data_run);

    // convert the raw addresses to the correct endian ordering
    if ((fs->ftype == TSK_FS_TYPE_FFS1)
        || (fs->ftype == TSK_FS_TYPE_FFS1B)
        || (TSK_FS_TYPE_ISEXT(fs->ftype))) {
        size_t n;
        uint32_t *iaddr = (uint32_t *) buf[0];
        addr_cnt = fs_bufsize / sizeof(*iaddr);
        for (n = 0; n < addr_cnt; n++) {
            myaddrs[n] = tsk_getu32(fs->endian, (uint8_t *) & iaddr[n]);
        }
    }
    else if (fs->ftype == TSK_FS_TYPE_FFS2) {
        size_t n;
        uint64_t *iaddr = (uint64_t *) buf[0];
        addr_cnt = fs_bufsize / sizeof(*iaddr);
        for (n = 0; n < addr_cnt; n++) {
            myaddrs[n] = tsk_getu64(fs->endian, (uint8_t *) & iaddr[n]);
        }
    }

    // pass the addresses to the next level
    if (level == 1) {
        retval =
            unix_make_data_run_direct(fs, fs_attr, myaddrs, addr_cnt,
            length_remain);
        if (retval != -1) {
            length_remain -= retval;
        }
    }
    else {
        size_t i;
        retval = 0;
        for (i = 0; i < addr_cnt && retval != -1; i++) {
            retval =
                unix_make_data_run_indirect(fs, fs_attr, fs_attr_indir,
                buf, level - 1, myaddrs[i], length_remain);
            if (retval == -1) {
                break;
            }
            else {
                length_remain -= retval;
            }
        }
    }

    if (retval == -1)
        return -1;
    else
        return length - length_remain;
}
Пример #6
0
/**
 * \ingroup fslib
 * Tries to process data in a disk image at a given offset as a file system.
 * Returns a structure that can be used for analysis and reporting.
 *
 * @param a_img_info Disk image to analyze
 * @param a_offset Byte offset to start analyzing from
 * @param a_ftype Type of file system (or autodetect)
 *
 * @return NULL on error
 */
TSK_FS_INFO *
tsk_fs_open_img(TSK_IMG_INFO * a_img_info, TSK_OFF_T a_offset,
    TSK_FS_TYPE_ENUM a_ftype)
{
    TSK_FS_INFO *fs_info;

    const struct {
        char* name;
        TSK_FS_INFO* (*open)(TSK_IMG_INFO*, TSK_OFF_T,
                                 TSK_FS_TYPE_ENUM, uint8_t);
        TSK_FS_TYPE_ENUM type;
    } FS_OPENERS[] = {
        { "NTFS",     ntfs_open,    TSK_FS_TYPE_NTFS_DETECT    },
        { "FAT",      fatfs_open,   TSK_FS_TYPE_FAT_DETECT     },
        { "EXT2/3/4", ext2fs_open,  TSK_FS_TYPE_EXT_DETECT     },
        { "UFS",      ffs_open,     TSK_FS_TYPE_FFS_DETECT     },
        { "YAFFS2",   yaffs2_open,  TSK_FS_TYPE_YAFFS2_DETECT  },
#if TSK_USE_HFS
        { "HFS",      hfs_open,     TSK_FS_TYPE_HFS_DETECT     },
#endif
        { "ISO9660",  iso9660_open, TSK_FS_TYPE_ISO9660_DETECT }
    };

    if (a_img_info == NULL) {
        tsk_error_reset();
        tsk_error_set_errno(TSK_ERR_FS_ARG);
        tsk_error_set_errstr("tsk_fs_open_img: Null image handle");
        return NULL;
    }

    /* We will try different file systems ...
     * We need to try all of them in case more than one matches
     */
    if (a_ftype == TSK_FS_TYPE_DETECT) {
        unsigned long i;
        const char *name_first = "";
        TSK_FS_INFO *fs_first = NULL;

        if (tsk_verbose)
            tsk_fprintf(stderr,
                "fsopen: Auto detection mode at offset %" PRIuOFF "\n",
                a_offset);

        for (i = 0; i < sizeof(FS_OPENERS)/sizeof(FS_OPENERS[0]); ++i) {
            if ((fs_info = FS_OPENERS[i].open(
                    a_img_info, a_offset, FS_OPENERS[i].type, 1)) != NULL) {
                // fs opens as type i
                if (fs_first == NULL) {
                    // first success opening fs
                    name_first = FS_OPENERS[i].name;
                    fs_first = fs_info;
                }
                else {
                    // second success opening fs, which means we
                    // cannot autodetect the fs type and must give up
                    fs_first->close(fs_first);
                    fs_info->close(fs_info);
                    tsk_error_reset();
                    tsk_error_set_errno(TSK_ERR_FS_UNKTYPE);
                    tsk_error_set_errstr(
                        "%s or %s", FS_OPENERS[i].name, name_first);
                    return NULL;
                }
            }
            else {
                // fs does not open as type i
                tsk_error_reset();
            }
        }

        if (fs_first == NULL) {
            tsk_error_reset();
            tsk_error_set_errno(TSK_ERR_FS_UNKTYPE);
        }

        return fs_first;
    }
    else if (TSK_FS_TYPE_ISNTFS(a_ftype)) {
        return ntfs_open(a_img_info, a_offset, a_ftype, 0);
    }
    else if (TSK_FS_TYPE_ISFAT(a_ftype)) {
        return fatfs_open(a_img_info, a_offset, a_ftype, 0);
    }
    else if (TSK_FS_TYPE_ISFFS(a_ftype)) {
        return ffs_open(a_img_info, a_offset, a_ftype, 0);
    }
    else if (TSK_FS_TYPE_ISEXT(a_ftype)) {
        return ext2fs_open(a_img_info, a_offset, a_ftype, 0);
    }
    else if (TSK_FS_TYPE_ISHFS(a_ftype)) {
        return hfs_open(a_img_info, a_offset, a_ftype, 0);
    }
    else if (TSK_FS_TYPE_ISISO9660(a_ftype)) {
        return iso9660_open(a_img_info, a_offset, a_ftype, 0);
    }
    else if (TSK_FS_TYPE_ISRAW(a_ftype)) {
        return rawfs_open(a_img_info, a_offset);
    }
    else if (TSK_FS_TYPE_ISSWAP(a_ftype)) {
        return swapfs_open(a_img_info, a_offset);
    }
    else if (TSK_FS_TYPE_ISYAFFS2(a_ftype)) {
        return yaffs2_open(a_img_info, a_offset, a_ftype, 0);
    }
    tsk_error_reset();
    tsk_error_set_errno(TSK_ERR_FS_UNSUPTYPE);
    tsk_error_set_errstr("%X", (int) a_ftype);
    return NULL;
}