Esempio n. 1
0
/*
 * Class:     edu_uw_apl_commons_tsk4j_filesys_FileSystem
 * Method:    readBlock
 * Signature: (JJ[BJ)I
 */
JNIEXPORT jint JNICALL 
Java_edu_uw_apl_commons_tsk4j_filesys_FileSystem_readBlock
(JNIEnv * env, jobject thiz, jlong nativePtr, jlong addr, jbyteArray buf,
 jlong nativeHeapPtr ) {

  TSK_FS_INFO* info = (TSK_FS_INFO*)nativePtr;
  jsize len = (*env)->GetArrayLength( env, buf );
  char* bufC = (char*)nativeHeapPtr;
  ssize_t result = tsk_fs_read_block( info, addr, bufC, len ); 
  if( result != -1 )
	(*env)->SetByteArrayRegion( env, buf, 0, len, (const jbyte*)bufC );
  return (jint)result;
}
Esempio n. 2
0
uint8_t
rawfs_block_walk(TSK_FS_INFO * fs, DADDR_T start_blk, DADDR_T end_blk,
    TSK_FS_BLOCK_FLAG_ENUM flags, TSK_FS_BLOCK_WALK_CB action, void *ptr)
{
    TSK_DATA_BUF *data_buf;
    DADDR_T addr;

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

    /*
     * Sanity checks.
     */
    if (start_blk < fs->first_block || start_blk > fs->last_block) {
        tsk_error_reset();
        tsk_errno = TSK_ERR_FS_WALK_RNG;
        snprintf(tsk_errstr, TSK_ERRSTR_L,
            "rawfs_block_walk: Start block number: %" PRIuDADDR,
            start_blk);
        return 1;
    }

    if (end_blk < fs->first_block || end_blk > fs->last_block
        || end_blk < start_blk) {
        tsk_error_reset();
        tsk_errno = TSK_ERR_FS_WALK_RNG;
        snprintf(tsk_errstr, TSK_ERRSTR_L,
            "rawfs_block_walk: Last block number: %" PRIuDADDR, end_blk);
        return 1;
    }

    /* If allocated is not wanted, then exit now */
    if (!(flags & TSK_FS_BLOCK_FLAG_ALLOC)) {
        return 0;
    }

    if ((data_buf = tsk_data_buf_alloc(fs->block_size)) == NULL) {
        return 1;
    }

    for (addr = start_blk; addr <= end_blk; addr++) {
        SSIZE_T cnt;
        int retval;

        cnt = tsk_fs_read_block(fs, data_buf, fs->block_size, addr);
        if (cnt != fs->block_size) {
            if (cnt != -1) {
                tsk_error_reset();
                tsk_errno = TSK_ERR_FS_READ;
            }
            snprintf(tsk_errstr2, TSK_ERRSTR_L,
                "rawfs_block_walk: Block %" PRIuDADDR, addr);
            tsk_data_buf_free(data_buf);
            return 1;
        }

        retval = action(fs, addr, data_buf->data,
            TSK_FS_BLOCK_FLAG_ALLOC | TSK_FS_BLOCK_FLAG_CONT, ptr);
        if (retval == TSK_WALK_STOP) {
            tsk_data_buf_free(data_buf);
            return 0;
        }
        else if (retval == TSK_WALK_ERROR) {
            tsk_data_buf_free(data_buf);
            return 1;
        }
    }

    /*
     * Cleanup.
     */
    tsk_data_buf_free(data_buf);
    return 0;
}
Esempio n. 3
0
/** \internal
 * Processes a non-resident TSK_FS_ATTR structure and calls the callback with the associated
 * data. 
 *
 * @param fs_attr Resident data structure to be walked
 * @param a_flags Flags for walking
 * @param a_action Callback action
 * @param a_ptr Pointer to data that is passed to callback
 * @returns 1 on error or 0 on success
 */
static uint8_t
tsk_fs_attr_walk_nonres(const TSK_FS_ATTR * fs_attr,
    TSK_FS_FILE_WALK_FLAG_ENUM a_flags, TSK_FS_FILE_WALK_CB a_action,
    void *a_ptr)
{
    char *buf = NULL;
    TSK_OFF_T tot_size;
    TSK_OFF_T off = 0;
    TSK_FS_ATTR_RUN *fs_attr_run;
    int retval;
    uint32_t skip_remain;
    TSK_FS_INFO *fs = fs_attr->fs_file->fs_info;
    uint8_t stop_loop = 0;

    if ((fs_attr->flags & TSK_FS_ATTR_NONRES) == 0) {
        tsk_error_set_errno(TSK_ERR_FS_ARG);
        tsk_error_set_errstr
            ("tsk_fs_file_walk_nonres: called with non-non-resident data");
        return 1;
    }

    /* if we want the slack space too, then use the allocsize  */
    if (a_flags & TSK_FS_FILE_WALK_FLAG_SLACK)
        tot_size = fs_attr->nrd.allocsize;
    else
        tot_size = fs_attr->size;

    skip_remain = fs_attr->nrd.skiplen;

    if ((a_flags & TSK_FS_FILE_WALK_FLAG_AONLY) == 0) {
        if ((buf = (char *) tsk_malloc(fs->block_size)) == NULL) {
            return 1;
        }
    }


    /* cycle through the number of runs we have */
    retval = TSK_WALK_CONT;
    for (fs_attr_run = fs_attr->nrd.run; fs_attr_run;
        fs_attr_run = fs_attr_run->next) {
        TSK_DADDR_T addr, len_idx;

        addr = fs_attr_run->addr;

        /* cycle through each block in the run */
        for (len_idx = 0; len_idx < fs_attr_run->len; len_idx++) {

            TSK_FS_BLOCK_FLAG_ENUM myflags;

            /* If the address is too large then give an error */
            if (addr + len_idx > fs->last_block) {
                if (fs_attr->fs_file->meta->
                    flags & TSK_FS_META_FLAG_UNALLOC)
                    tsk_error_set_errno(TSK_ERR_FS_RECOVER);
                else
                    tsk_error_set_errno(TSK_ERR_FS_BLK_NUM);
                tsk_error_set_errstr
                    ("Invalid address in run (too large): %" PRIuDADDR "",
                    addr + len_idx);
                return 1;
            }

            // load the buffer if they want more than just the address
            if ((a_flags & TSK_FS_FILE_WALK_FLAG_AONLY) == 0) {

                /* sparse files just get 0s */
                if (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE) {
                    memset(buf, 0, fs->block_size);
                }
                /* FILLER entries exist when the source file system can store run
                 * info out of order and we did not get all of the run info.  We
                 * return 0s if data is read from this type of run. */
                else if (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) {
                    memset(buf, 0, fs->block_size);
                    if (tsk_verbose)
                        fprintf(stderr,
                            "tsk_fs_attr_walk_nonres: File %" PRIuINUM
                            " has FILLER entry, using 0s\n",
                            fs_attr->fs_file->meta->addr);
                }

                // we return 0s for reads past the initsize
                else if ((off >= fs_attr->nrd.initsize)
                    && ((a_flags & TSK_FS_FILE_READ_FLAG_SLACK) == 0)) {
                    memset(buf, 0, fs->block_size);
                }
                else {
                    ssize_t cnt;

                    cnt = tsk_fs_read_block
                        (fs, addr + len_idx, buf, fs->block_size);
                    if (cnt != fs->block_size) {
                        if (cnt >= 0) {
                            tsk_error_reset();
                            tsk_error_set_errno(TSK_ERR_FS_READ);
                        }
                        tsk_error_set_errstr2
                            ("tsk_fs_file_walk: Error reading block at %"
                            PRIuDADDR, addr + len_idx);
                        return 1;
                    }
                    if ((off + fs->block_size > fs_attr->nrd.initsize)
                        && ((a_flags & TSK_FS_FILE_READ_FLAG_SLACK) == 0)) {
                        memset(&buf[fs_attr->nrd.initsize - off], 0,
                            fs->block_size -
                            (size_t) (fs_attr->nrd.initsize - off));
                    }
                }
            }

            /* Need to account for the skip length, which is the number of bytes
             * in the start of the attribute that are skipped and that are not
             * included in the overall length.  We will seek past those and not
             * return those in the action.  We just read a block size so check
             * if there is data to be returned in this buffer. */
            if (skip_remain >= fs->block_size) {
                skip_remain -= fs->block_size;
            }
            else {
                size_t ret_len;

                /* Do we want to return a full block, or just the end? */
                if ((TSK_OFF_T) fs->block_size - skip_remain <
                    tot_size - off)
                    ret_len = fs->block_size - skip_remain;
                else
                    ret_len = (size_t) (tot_size - off);

                /* Only do sparse or FILLER clusters if NOSPARSE is not set */
                if ((fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE) ||
                    (fs_attr_run->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) ||
                    (off > fs_attr->nrd.initsize)) {
                    myflags = fs->block_getflags(fs, 0);
                    myflags |= TSK_FS_BLOCK_FLAG_SPARSE;

                    if ((a_flags & TSK_FS_FILE_WALK_FLAG_NOSPARSE) == 0) {
                        retval =
                            a_action(fs_attr->fs_file, off, 0,
                            &buf[skip_remain], ret_len, myflags, a_ptr);
                    }
                }
                else {
                    myflags = fs->block_getflags(fs, addr + len_idx);
                    myflags |= TSK_FS_BLOCK_FLAG_RAW;

                    retval =
                        a_action(fs_attr->fs_file, off, addr + len_idx,
                        &buf[skip_remain], ret_len, myflags, a_ptr);
                }
                off += ret_len;
                skip_remain = 0;

                if (retval != TSK_WALK_CONT) {
                    stop_loop = 1;
                    break;
                }

                if (off >= tot_size) {
                    stop_loop = 1;
                    break;
                }
            }
        }
        if (stop_loop)
            break;
    }

    if (buf)
        free(buf);

    if (retval == TSK_WALK_ERROR)
        return 1;
    else
        return 0;
}
Esempio n. 4
0
/**
 * \internal
 * Searches an exFAT file system for its volume label directory entry, which 
 * should be in the root directory of the file system. If the entry is found, 
 * its metadata is copied into the TSK_FS_META object of a TSK_FS_FILE object.
 *
 * @param [in] a_fatfs Generic FAT file system info structure.
 * @param [out] a_fatfs Generic file system file structure.
 * @return 0 on success, 1 otherwise, per TSK convention.
 */
static uint8_t
exfatfs_find_volume_label_dentry(FATFS_INFO *a_fatfs, TSK_FS_FILE *a_fs_file)
{
    const char *func_name = "exfatfs_find_volume_label_dentry";
    TSK_FS_INFO *fs = (TSK_FS_INFO *)a_fatfs;
    TSK_DADDR_T current_sector = 0;
    TSK_DADDR_T last_sector_of_data_area = 0;
    char *sector_buf = NULL;
    ssize_t bytes_read = 0;
    TSK_INUM_T current_inum = 0;
    FATFS_DENTRY *dentry = NULL;
    uint64_t i = 0;

    assert(a_fatfs != NULL);
    assert(a_fs_file != NULL);

    tsk_error_reset();
    if (fatfs_ptr_arg_is_null(a_fatfs, "a_fatfs", func_name) ||
        fatfs_ptr_arg_is_null(a_fs_file, "a_fs_file", func_name)) {
        return FATFS_FAIL; 
    }

    /* Allocate or reset the TSK_FS_META object. */
    if (a_fs_file->meta == NULL) {
        if ((a_fs_file->meta =
                tsk_fs_meta_alloc(FATFS_FILE_CONTENT_LEN)) == NULL) {
            return FATFS_FAIL;
        }
    }
    else {
        tsk_fs_meta_reset(a_fs_file->meta);
    }

    /* Allocate a buffer for reading in sector-size chunks of the image. */
    if ((sector_buf = (char*)tsk_malloc(a_fatfs->ssize)) == NULL) {
        return FATFS_FAIL;
    }

    current_sector = a_fatfs->rootsect;
    last_sector_of_data_area = a_fatfs->firstdatasect + (a_fatfs->clustcnt * a_fatfs->csize) - 1;
    while (current_sector < last_sector_of_data_area) {
        int8_t sector_is_alloc = 0;

        /* Read in a sector from the root directory. The volume label
         * directory entry will probably be near the beginning of the 
         * directory, probably in the first sector. */
        bytes_read = tsk_fs_read_block(fs, current_sector, sector_buf, a_fatfs->ssize);
        if (bytes_read != a_fatfs->ssize) {
            if (bytes_read >= 0) {
                tsk_error_reset();
                tsk_error_set_errno(TSK_ERR_FS_READ);
            }
            tsk_error_set_errstr2("%s: error reading sector: %" PRIuDADDR, func_name, current_sector);
            free(sector_buf);
            return FATFS_FAIL;
        }

        /* Get the allocation status of the sector (yes, it should be allocated). */
        sector_is_alloc = fatfs_is_sectalloc(a_fatfs, current_sector);
        if (sector_is_alloc == -1) {
            return FATFS_FAIL;
        }

        /* Get the inode address of the first directory entry of the sector. */
        current_inum = FATFS_SECT_2_INODE(a_fatfs, current_sector);

        /* Loop through the putative directory entries in the sector, 
         * until the volume label entry is found.  */
        for (i = 0; i < a_fatfs->ssize; i += sizeof(FATFS_DENTRY)) {
            dentry = (FATFS_DENTRY*)&(sector_buf[i]); 

            /* The type of the directory entry is encoded in the first byte 
             * of the entry. See EXFATFS_DIR_ENTRY_TYPE_ENUM. */ 
            if (exfatfs_get_enum_from_type(dentry->data[0]) == EXFATFS_DIR_ENTRY_TYPE_VOLUME_LABEL) {
                if (!exfatfs_is_vol_label_dentry(dentry, FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN)) {
                    continue;
                }

                /* Found it, save it to the TSK_FS_META object of the 
                 * TSK_FS_FILE object and exit. */ 
                if (exfatfs_dinode_copy(a_fatfs, current_inum, dentry, 
                    sector_is_alloc, a_fs_file) == TSK_OK) {
                        return FATFS_OK;
                }
                else {
                    return FATFS_FAIL;
                }
            }
        }
    }

    free(sector_buf);
    return FATFS_OK;
}
Esempio n. 5
0
/**
 * \internal
 * Searches the root directory of an exFAT file system for an allocation bitmap
 * directory entry. If the entry is found, data from the entry is saved to a
 * FATFS_INFO object.
 *
 * @param [in, out] a_fatfs Generic FAT file system info structure.
 * @return 0 on success, 1 otherwise, per TSK convention.
 */
static uint8_t
exfatfs_get_alloc_bitmap(FATFS_INFO *a_fatfs)
{
    const char *func_name = "exfatfs_get_alloc_bitmap";
    TSK_FS_INFO *fs = &(a_fatfs->fs_info);
    TSK_DADDR_T current_sector = 0;
    TSK_DADDR_T last_sector_of_data_area = 0;
    char *sector_buf = NULL;
    EXFATFS_ALLOC_BITMAP_DIR_ENTRY *dentry = NULL;
    uint64_t i = 0;
    uint64_t first_sector_of_alloc_bitmap = 0;
    uint64_t alloc_bitmap_length_in_bytes = 0;
    uint64_t last_sector_of_alloc_bitmap = 0;

    assert(a_fatfs != NULL);
    
    if ((sector_buf = (char*)tsk_malloc(a_fatfs->ssize)) == NULL) {
        return FATFS_FAIL;
    }

    last_sector_of_data_area = a_fatfs->firstdatasect + (a_fatfs->clustcnt * a_fatfs->csize) - 1;
    for (current_sector = a_fatfs->rootsect; current_sector < last_sector_of_data_area; current_sector++) {
        ssize_t bytes_read = 0;

        /* Read in a sector from the root directory. The allocation bitmap
         * directory entries will probably be near the beginning of the 
         * directory, probably in the first sector. */
        bytes_read = tsk_fs_read_block(fs, current_sector, sector_buf, a_fatfs->ssize);
        if (bytes_read != a_fatfs->ssize) {
            if (bytes_read >= 0) {
                tsk_error_reset();
                tsk_error_set_errno(TSK_ERR_FS_READ);
            }
            tsk_error_set_errstr2("%s: sector: %" PRIuDADDR, func_name, current_sector);
            free(sector_buf);
            return FATFS_FAIL;
        }

        /* Read the directory entries in the sector, looking for allocation
         * bitmap entries. There will be one entry unless the file system is 
         * TexFAT (transactional exFAT), in which case there will be two. */
        for (i = 0; i < a_fatfs->ssize; i += sizeof(FATFS_DENTRY)) {
            dentry = (EXFATFS_ALLOC_BITMAP_DIR_ENTRY*)&(sector_buf[i]); 

            /* The type of the directory entry is encoded in the first byte 
             * of the entry. See EXFATFS_DIR_ENTRY_TYPE_ENUM. */ 
            if (exfatfs_get_enum_from_type(dentry->entry_type) == EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP) {
                /* Do an in-depth test. */
                if (!exfatfs_is_alloc_bitmap_dentry((FATFS_DENTRY*)dentry, FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN, a_fatfs)) {
                    continue;
                }

                /* The first bit of the flags byte is 0 for the first 
                 * allocation bitmap directory entry and 1 for the second 
                 * bitmap directory entry. If TexFAT is in use and there are
                 * two allocation bitmaps, the first bitmap should be the
                 * stable copy of the last known good allocation bitmap. 
                 * Therefore, the SleuthKit will use the first bitmap to 
                 * determine which clusters are allocated. */
                if (~(dentry->flags & 0x01)) {
                    first_sector_of_alloc_bitmap = FATFS_CLUST_2_SECT(a_fatfs, tsk_getu32(fs->endian, dentry->first_cluster_of_bitmap));
                    alloc_bitmap_length_in_bytes = tsk_getu64(fs->endian, dentry->length_of_alloc_bitmap_in_bytes);
                    last_sector_of_alloc_bitmap = first_sector_of_alloc_bitmap + (roundup(alloc_bitmap_length_in_bytes, a_fatfs->ssize) / a_fatfs->ssize) - 1;

                    /* The allocation bitmap must lie within the boundaries of the data area. 
                     * It also must be big enough for the number of clusters reported in the VBR. */
                    if ((first_sector_of_alloc_bitmap >= a_fatfs->firstdatasect) &&
                        (last_sector_of_alloc_bitmap <= last_sector_of_data_area) &&
                        (alloc_bitmap_length_in_bytes >= (a_fatfs->clustcnt + 7) / 8))
                    {
                        a_fatfs->EXFATFS_INFO.first_sector_of_alloc_bitmap = first_sector_of_alloc_bitmap; 
                        a_fatfs->EXFATFS_INFO.length_of_alloc_bitmap_in_bytes = alloc_bitmap_length_in_bytes;
                        free(sector_buf);
                        return FATFS_OK;
                    }
                }
            }
        }
    }
    free(sector_buf);

    return FATFS_FAIL;
}
Esempio n. 6
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;
}
Esempio n. 7
0
/**
 * Read a specific number of blocks and print the contents to STDOUT
 *
 * @param fs File system to analyze
 * @param lclflags flags
 * @param addr Starting block address to read from
 * @param read_num_units Number of blocks to read
 *
 * @return 1 on error and 0 on success
 */
uint8_t
tsk_fs_blkcat(TSK_FS_INFO * fs, TSK_FS_BLKCAT_FLAG_ENUM lclflags,
    TSK_DADDR_T addr, TSK_DADDR_T read_num_units)
{
    char *buf;
    ssize_t cnt;
    int i;

    if (lclflags & TSK_FS_BLKCAT_STAT) {
        stats(fs);
        return 0;
    }

    if (addr + read_num_units - 1 > fs->last_block) {
        tsk_error_reset();
        tsk_error_set_errno(TSK_ERR_FS_ARG);
        tsk_error_set_errstr
            ("tsk_fs_blkcat: requested size is larger than last block in image (%"
            PRIuDADDR ")", fs->last_block);
        return 1;
    }

#ifdef TSK_WIN32
    if (-1 == _setmode(_fileno(stdout), _O_BINARY)) {
        tsk_error_reset();
        tsk_error_set_errno(TSK_ERR_FS_WRITE);
        tsk_error_set_errstr
            ("blkcat_lib: error setting stdout to binary: %s",
            strerror(errno));
        return 1;
    }
#endif

    if (lclflags & TSK_FS_BLKCAT_HTML) {
        tsk_printf("<html>\n");
        tsk_printf("<head>\n");
        tsk_printf("<title>Unit: %" PRIuDADDR "   Size: %" PRIuOFF
            " bytes</title>\n", addr, read_num_units * fs->block_size);
        tsk_printf("</head>\n");
        tsk_printf("<body>\n");

    }

    if ((lclflags & TSK_FS_BLKCAT_HEX) && (lclflags & TSK_FS_BLKCAT_HTML))
        tsk_printf("<table border=0>\n");

    if ((buf = tsk_malloc(fs->block_size)) == NULL)
        return 1;

    for (i = 0; i < read_num_units; i++) {

        /* Read the block */
        cnt = tsk_fs_read_block(fs, addr + i, buf, fs->block_size);
        if (cnt != fs->block_size) {
            if (cnt >= 0) {
                tsk_error_reset();
                tsk_error_set_errno(TSK_ERR_FS_READ);
            }
            tsk_error_set_errstr("blkcat: Error reading block at %"
                PRIuDADDR, addr);
            return 1;
        }


        /* do a hexdump like printout */
        if (lclflags & TSK_FS_BLKCAT_HEX) {
            TSK_OFF_T idx1, idx2;

            for (idx1 = 0; idx1 < fs->block_size; idx1 += 16) {

                /* Print the offset */
                if (lclflags & TSK_FS_BLKCAT_HTML)
                    tsk_printf("<tr><td>%" PRIuOFF "</td>",
                        i * fs->block_size + idx1);
                else
                    tsk_printf("%" PRIuOFF "\t",
                        i * fs->block_size + idx1);


                /* Print the hex */
                for (idx2 = 0; idx2 < 16; idx2++) {
                    if ((lclflags & TSK_FS_BLKCAT_HTML)
                        && (0 == (idx2 % 4)))
                        tsk_printf("<td>");

                    tsk_printf("%.2x", buf[idx2 + idx1] & 0xff);

                    if (3 == (idx2 % 4)) {
                        if (lclflags & TSK_FS_BLKCAT_HTML)
                            tsk_printf("</td>");
                        else
                            tsk_printf(" ");
                    }
                }

                /* Print the ASCII */
                tsk_printf("\t");
                for (idx2 = 0; idx2 < 16; idx2++) {
                    if ((lclflags & TSK_FS_BLKCAT_HTML)
                        && (0 == (idx2 % 4)))
                        tsk_printf("<td>");

                    if ((isascii((int) buf[idx2 + idx1])) &&
                        (!iscntrl((int) buf[idx2 + idx1])))
                        tsk_printf("%c", buf[idx2 + idx1]);
                    else
                        tsk_printf(".");

                    if (3 == (idx2 % 4)) {
                        if (lclflags & TSK_FS_BLKCAT_HTML)
                            tsk_printf("</td>");
                        else
                            tsk_printf(" ");
                    }
                }

                if (lclflags & TSK_FS_BLKCAT_HTML)
                    tsk_printf("</tr>");
                tsk_printf("\n");
            }
        }


        /* print in all ASCII */
        else if (lclflags & TSK_FS_BLKCAT_ASCII) {
            TSK_OFF_T idx;
            for (idx = 0; idx < fs->block_size; idx++) {

                if ((isprint((int) buf[idx]))
                    || (buf[idx] == '\t')) {
                    tsk_printf("%c", buf[idx]);
                }
                else if ((buf[idx] == '\n')
                    || (buf[idx] == '\r')) {
                    if (lclflags & TSK_FS_BLKCAT_HTML)
                        tsk_printf("<br>");
                    tsk_printf("%c", buf[idx]);
                }
                else
                    tsk_printf(".");
            }
        }

        /* print raw */
        else {
            if (fwrite(buf, fs->block_size, 1, stdout) != 1) {
                tsk_error_reset();
                tsk_error_set_errno(TSK_ERR_FS_WRITE);
                tsk_error_set_errstr
                    ("blkcat_lib: error writing to stdout: %s",
                    strerror(errno));
                free(buf);
                return 1;
            }
        }
    }

    free(buf);

    if (lclflags & TSK_FS_BLKCAT_HEX) {
        if (lclflags & TSK_FS_BLKCAT_HTML)
            tsk_printf("</table>\n");
        else
            tsk_printf("\n");
    }
    else if (lclflags & TSK_FS_BLKCAT_ASCII) {
        if (lclflags & TSK_FS_BLKCAT_HTML)
            tsk_printf("<br>");
        tsk_printf("\n");
    }
    else {
        if (lclflags & TSK_FS_BLKCAT_HTML)
            tsk_printf("<br>");
    }

    if (lclflags & TSK_FS_BLKCAT_HTML)
        tsk_printf("</body>\n</html>\n");

    return 0;
}
Esempio n. 8
0
/**
 * Print details about the file system to a file handle. 
 *
 * @param fs File system to print details on
 * @param hFile File handle to print text to
 * 
 * @returns 1 on error and 0 on success
 */
static uint8_t
fatxxfs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
{
    unsigned int i;
    TSK_DADDR_T next, snext, sstart, send;
    FATFS_INFO *fatfs = (FATFS_INFO *) fs;
    FATXXFS_SB *sb = (FATXXFS_SB*)fatfs->boot_sector_buffer;
    char *data_buf;
    FATXXFS_DENTRY *vol_label_dentry = NULL;
    ssize_t cnt;

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

    if ((data_buf = (char *) tsk_malloc(fs->block_size)) == NULL) {
        return 1;
    }


    /* Read the root directory sector so that we can get the volume
     * label from it */
    cnt = tsk_fs_read_block(fs, fatfs->rootsect, data_buf, fs->block_size);
    if (cnt != fs->block_size) {
        if (cnt >= 0) {
            tsk_error_reset();
            tsk_error_set_errno(TSK_ERR_FS_READ);
        }
        tsk_error_set_errstr2("fatxxfs_fsstat: root directory: %" PRIuDADDR,
            fatfs->rootsect);
        free(data_buf);
        return 1;
    }


    /* Find the dentry that is set as the volume label */
    vol_label_dentry = NULL;
    if (fatfs->ssize <= fs->block_size) {
        FATXXFS_DENTRY *current_entry = (FATXXFS_DENTRY *) data_buf;
        for (i = 0; i < fatfs->ssize; i += sizeof(*current_entry)) {
            if (current_entry->attrib == FATFS_ATTR_VOLUME) {
                vol_label_dentry = current_entry;
                break;
            }
            current_entry++;
        }
    }


    /* Print the general file system information */

    tsk_fprintf(hFile, "FILE SYSTEM INFORMATION\n");
    tsk_fprintf(hFile, "--------------------------------------------\n");

    tsk_fprintf(hFile, "File System Type: FAT");
    if (fs->ftype == TSK_FS_TYPE_FAT12)
        tsk_fprintf(hFile, "12\n");
    else if (fs->ftype == TSK_FS_TYPE_FAT16)
        tsk_fprintf(hFile, "16\n");
    else if (fs->ftype == TSK_FS_TYPE_FAT32)
        tsk_fprintf(hFile, "32\n");
    else
        tsk_fprintf(hFile, "\n");

    tsk_fprintf(hFile, "\nOEM Name: %c%c%c%c%c%c%c%c\n", sb->oemname[0],
        sb->oemname[1], sb->oemname[2], sb->oemname[3], sb->oemname[4],
        sb->oemname[5], sb->oemname[6], sb->oemname[7]);


    if (fatfs->fs_info.ftype != TSK_FS_TYPE_FAT32) {
        tsk_fprintf(hFile, "Volume ID: 0x%" PRIx32 "\n",
            tsk_getu32(fs->endian, sb->a.f16.vol_id));

        tsk_fprintf(hFile,
            "Volume Label (Boot Sector): %c%c%c%c%c%c%c%c%c%c%c\n",
            sb->a.f16.vol_lab[0], sb->a.f16.vol_lab[1],
            sb->a.f16.vol_lab[2], sb->a.f16.vol_lab[3],
            sb->a.f16.vol_lab[4], sb->a.f16.vol_lab[5],
            sb->a.f16.vol_lab[6], sb->a.f16.vol_lab[7],
            sb->a.f16.vol_lab[8], sb->a.f16.vol_lab[9],
            sb->a.f16.vol_lab[10]);

        if ((vol_label_dentry) && (vol_label_dentry->name[0])) {
            tsk_fprintf(hFile,
                "Volume Label (Root Directory): %c%c%c%c%c%c%c%c%c%c%c\n",
                vol_label_dentry->name[0], vol_label_dentry->name[1], vol_label_dentry->name[2], vol_label_dentry->name[3],
                vol_label_dentry->name[4], vol_label_dentry->name[5], vol_label_dentry->name[6], vol_label_dentry->name[7],
                vol_label_dentry->ext[0], vol_label_dentry->ext[1], vol_label_dentry->ext[2]);
        }
        else {
            tsk_fprintf(hFile, "Volume Label (Root Directory):\n");
        }

        tsk_fprintf(hFile, "File System Type Label: %c%c%c%c%c%c%c%c\n",
            sb->a.f16.fs_type[0], sb->a.f16.fs_type[1],
            sb->a.f16.fs_type[2], sb->a.f16.fs_type[3],
            sb->a.f16.fs_type[4], sb->a.f16.fs_type[5],
            sb->a.f16.fs_type[6], sb->a.f16.fs_type[7]);
    }
    else {

        char *fat_fsinfo_buf;

        if ((fat_fsinfo_buf = (char *)
                tsk_malloc(sizeof(FATXXFS_FSINFO))) == NULL) {
            free(data_buf);
            return 1;
        }

        tsk_fprintf(hFile, "Volume ID: 0x%" PRIx32 "\n",
            tsk_getu32(fs->endian, sb->a.f32.vol_id));

        tsk_fprintf(hFile,
            "Volume Label (Boot Sector): %c%c%c%c%c%c%c%c%c%c%c\n",
            sb->a.f32.vol_lab[0], sb->a.f32.vol_lab[1],
            sb->a.f32.vol_lab[2], sb->a.f32.vol_lab[3],
            sb->a.f32.vol_lab[4], sb->a.f32.vol_lab[5],
            sb->a.f32.vol_lab[6], sb->a.f32.vol_lab[7],
            sb->a.f32.vol_lab[8], sb->a.f32.vol_lab[9],
            sb->a.f32.vol_lab[10]);

        if ((vol_label_dentry) && (vol_label_dentry->name[0])) {
            tsk_fprintf(hFile,
                "Volume Label (Root Directory): %c%c%c%c%c%c%c%c%c%c%c\n",
                vol_label_dentry->name[0], vol_label_dentry->name[1], vol_label_dentry->name[2], vol_label_dentry->name[3],
                vol_label_dentry->name[4], vol_label_dentry->name[5], vol_label_dentry->name[6], vol_label_dentry->name[7],
                vol_label_dentry->ext[0], vol_label_dentry->ext[1], vol_label_dentry->ext[2]);
        }
        else {
            tsk_fprintf(hFile, "Volume Label (Root Directory):\n");
        }

        tsk_fprintf(hFile, "File System Type Label: %c%c%c%c%c%c%c%c\n",
            sb->a.f32.fs_type[0], sb->a.f32.fs_type[1],
            sb->a.f32.fs_type[2], sb->a.f32.fs_type[3],
            sb->a.f32.fs_type[4], sb->a.f32.fs_type[5],
            sb->a.f32.fs_type[6], sb->a.f32.fs_type[7]);


        /* Process the FS info */
        if (tsk_getu16(fs->endian, sb->a.f32.fsinfo)) {
            FATXXFS_FSINFO *fat_info;
            cnt =
                tsk_fs_read(fs, 
                    (TSK_DADDR_T) tsk_getu16(fs->endian, sb->a.f32.fsinfo) * fs->block_size, 
                    fat_fsinfo_buf, sizeof(FATXXFS_FSINFO));

            if (cnt != sizeof(FATXXFS_FSINFO)) {
                if (cnt >= 0) {
                    tsk_error_reset();
                    tsk_error_set_errno(TSK_ERR_FS_READ);
                }
                tsk_error_set_errstr2
                    ("fatxxfs_fsstat: TSK_FS_TYPE_FAT32 FSINFO block: %"
                    PRIuDADDR, (TSK_DADDR_T) tsk_getu16(fs->endian,
                        sb->a.f32.fsinfo));
                free(data_buf);
                free(fat_fsinfo_buf);
                return 1;
            }


            fat_info = (FATXXFS_FSINFO *) fat_fsinfo_buf;
            tsk_fprintf(hFile,
                "Next Free Sector (FS Info): %" PRIuDADDR "\n",
                FATFS_CLUST_2_SECT(fatfs, tsk_getu32(fs->endian,
                        fat_info->nextfree)));

            tsk_fprintf(hFile,
                "Free Sector Count (FS Info): %" PRIu32 "\n",
                (tsk_getu32(fs->endian,
                        fat_info->freecnt) * fatfs->csize));

            free(fat_fsinfo_buf);
        }
    }

    free(data_buf);

    tsk_fprintf(hFile, "\nSectors before file system: %" PRIu32 "\n",
        tsk_getu32(fs->endian, sb->prevsect));

    tsk_fprintf(hFile, "\nFile System Layout (in sectors)\n");

    tsk_fprintf(hFile, "Total Range: %" PRIuDADDR " - %" PRIuDADDR "\n",
        fs->first_block, fs->last_block);

    if (fs->last_block != fs->last_block_act)
        tsk_fprintf(hFile,
            "Total Range in Image: %" PRIuDADDR " - %" PRIuDADDR "\n",
            fs->first_block, fs->last_block_act);

    tsk_fprintf(hFile, "* Reserved: 0 - %" PRIuDADDR "\n",
        fatfs->firstfatsect - 1);

    tsk_fprintf(hFile, "** Boot Sector: 0\n");

    if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32) {
        tsk_fprintf(hFile, "** FS Info Sector: %" PRIu16 "\n",
            tsk_getu16(fs->endian, sb->a.f32.fsinfo));

        tsk_fprintf(hFile, "** Backup Boot Sector: %" PRIu16 "\n",
            tsk_getu16(fs->endian, sb->a.f32.bs_backup));
    }

    for (i = 0; i < fatfs->numfat; i++) {
        TSK_DADDR_T base = fatfs->firstfatsect + i * (fatfs->sectperfat);

        tsk_fprintf(hFile, "* FAT %d: %" PRIuDADDR " - %" PRIuDADDR "\n",
            i, base, (base + fatfs->sectperfat - 1));
    }

    tsk_fprintf(hFile, "* Data Area: %" PRIuDADDR " - %" PRIuDADDR "\n",
        fatfs->firstdatasect, fs->last_block);

    if (fatfs->fs_info.ftype != TSK_FS_TYPE_FAT32) {
        TSK_DADDR_T x = fatfs->csize * fatfs->clustcnt;

        tsk_fprintf(hFile,
            "** Root Directory: %" PRIuDADDR " - %" PRIuDADDR "\n",
            fatfs->firstdatasect, fatfs->firstclustsect - 1);

        tsk_fprintf(hFile,
            "** Cluster Area: %" PRIuDADDR " - %" PRIuDADDR "\n",
            fatfs->firstclustsect, (fatfs->firstclustsect + x - 1));

        if ((fatfs->firstclustsect + x - 1) != fs->last_block) {
            tsk_fprintf(hFile,
                "** Non-clustered: %" PRIuDADDR " - %" PRIuDADDR "\n",
                (fatfs->firstclustsect + x), fs->last_block);
        }
    }
    else {
        TSK_LIST *list_seen = NULL;
        TSK_DADDR_T x = fatfs->csize * (fatfs->lastclust - 1);
        TSK_DADDR_T clust, clust_p;

        tsk_fprintf(hFile,
            "** Cluster Area: %" PRIuDADDR " - %" PRIuDADDR "\n",
            fatfs->firstclustsect, (fatfs->firstclustsect + x - 1));


        clust_p = fatfs->rootsect;
        clust = FATFS_SECT_2_CLUST(fatfs, fatfs->rootsect);
        while ((clust) && (0 == FATFS_ISEOF(clust, FATFS_32_MASK))) {
            TSK_DADDR_T nxt;
            clust_p = clust;

            /* Make sure we do not get into an infinite loop */
            if (tsk_list_find(list_seen, clust)) {
                if (tsk_verbose)
                    tsk_fprintf(stderr,
                        "Loop found while determining root directory size\n");
                break;
            }
            if (tsk_list_add(&list_seen, clust)) {
                tsk_list_free(list_seen);
                list_seen = NULL;
                return 1;
            }

            if (fatfs_getFAT(fatfs, clust, &nxt))
                break;
            clust = nxt;
        }
        tsk_list_free(list_seen);
        list_seen = NULL;

        tsk_fprintf(hFile,
            "*** Root Directory: %" PRIuDADDR " - %" PRIuDADDR "\n",
            fatfs->rootsect, (FATFS_CLUST_2_SECT(fatfs, clust_p + 1) - 1));

        if ((fatfs->firstclustsect + x - 1) != fs->last_block) {
            tsk_fprintf(hFile,
                "** Non-clustered: %" PRIuDADDR " - %" PRIuDADDR "\n",
                (fatfs->firstclustsect + x), fs->last_block);
        }
    }


    tsk_fprintf(hFile, "\nMETADATA INFORMATION\n");
    tsk_fprintf(hFile, "--------------------------------------------\n");

    tsk_fprintf(hFile, "Range: %" PRIuINUM " - %" PRIuINUM "\n",
        fs->first_inum, fs->last_inum);
    tsk_fprintf(hFile, "Root Directory: %" PRIuINUM "\n", fs->root_inum);


    tsk_fprintf(hFile, "\nCONTENT INFORMATION\n");
    tsk_fprintf(hFile, "--------------------------------------------\n");
    tsk_fprintf(hFile, "Sector Size: %" PRIu16 "\n", fatfs->ssize);
    tsk_fprintf(hFile, "Cluster Size: %" PRIu32 "\n",
        (uint32_t) fatfs->csize << fatfs->ssize_sh);

    tsk_fprintf(hFile, "Total Cluster Range: 2 - %" PRIuDADDR "\n",
        fatfs->lastclust);


    /* cycle via cluster and look at each cluster in the FAT 
     * for clusters marked as bad */
    cnt = 0;
    for (i = 2; i <= fatfs->lastclust; i++) {
        TSK_DADDR_T entry;
        TSK_DADDR_T sect;
        unsigned int a;

        /* Get the FAT table entry */
        if (fatfs_getFAT(fatfs, i, &entry))
            break;

        if (FATFS_ISBAD(entry, fatfs->mask) == 0) {
            continue;
        }

        if (cnt == 0)
            tsk_fprintf(hFile, "Bad Sectors: ");

        sect = FATFS_CLUST_2_SECT(fatfs, i);
        for (a = 0; a < fatfs->csize; a++) {
            tsk_fprintf(hFile, "%" PRIuDADDR " ", sect + a);
            if ((++cnt % 8) == 0)
                tsk_fprintf(hFile, "\n");
        }
    }
    if ((cnt > 0) && ((cnt % 8) != 0))
        tsk_fprintf(hFile, "\n");

    /* Display the FAT Table */
    tsk_fprintf(hFile, "\nFAT CONTENTS (in sectors)\n");
    tsk_fprintf(hFile, "--------------------------------------------\n");

    /* 'sstart' marks the first sector of the current run to print */
    sstart = fatfs->firstclustsect;

    /* cycle via cluster and look at each cluster in the FAT  to make runs */
    for (i = 2; i <= fatfs->lastclust; i++) {

        /* 'send' marks the end sector of the current run, which will extend
         * when the current cluster continues to the next 
         */
        send = FATFS_CLUST_2_SECT(fatfs, i + 1) - 1;

        /* get the next cluster */
        if (fatfs_getFAT(fatfs, i, &next))
            break;

        snext = FATFS_CLUST_2_SECT(fatfs, next);

        /* we are also using the next sector (clust) */
        if ((next & fatfs->mask) == (i + 1)) {
            continue;
        }

        /* The next clust is either further away or the clust is available,
         * print it if is further away 
         */
        else if ((next & fatfs->mask)) {
            if (FATFS_ISEOF(next, fatfs->mask))
                tsk_fprintf(hFile,
                    "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR
                    ") -> EOF\n", sstart, send, send - sstart + 1);
            else if (FATFS_ISBAD(next, fatfs->mask))
                tsk_fprintf(hFile,
                    "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR
                    ") -> BAD\n", sstart, send, send - sstart + 1);
            else
                tsk_fprintf(hFile,
                    "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR
                    ") -> %" PRIuDADDR "\n", sstart, send,
                    send - sstart + 1, snext);
        }

        /* reset the starting counter */
        sstart = send + 1;
    }

    return 0;
}
Esempio n. 9
0
static TSK_WALK_RET_ENUM
fw_action1(TSK_FS_FILE * a_fs_file, TSK_OFF_T a_off, TSK_DADDR_T a_addr,
    char *a_buf, size_t a_size, TSK_FS_BLOCK_FLAG_ENUM a_flags,
    void *a_ptr)
{
    TSK_OFF_T tmp_off;
    ssize_t cnt;
    size_t tmp_len;
    TSK_FS_INFO *fs = a_fs_file->fs_info;

    // verify teh offset passed is what we expected
    if (a_off != s_off) {
        fprintf(stderr,
            "offset passed in callback (%" PRIuOFF
            ") diff from internal off (%" PRIuOFF ")\n", a_off, s_off);
    }

    /* The first set of tests is for the file_read API.  We seek
     * to a "random" place to move around any caches, adn then read
     * from the same offset that this call is from.  We compare
     * the buffers. */

    // pick a random place and length
    tmp_off = (s_off * 4 + 1372) % s_file2->meta->size;
    if (s_file2->meta->size - tmp_off > fs->block_size)
        tmp_len = fs->block_size;
    else
        tmp_len = s_file2->meta->size - tmp_off;

    cnt =
        tsk_fs_file_read(s_file2, tmp_off, s_buf, tmp_len,
        (TSK_FS_FILE_READ_FLAG_ENUM) 0);
    if (cnt != (ssize_t) tmp_len) {
        fprintf(stderr,
            "Error reading random offset %" PRIuOFF " in file sized %"
            PRIuOFF " (%zd vs %zd)\n", tmp_off, s_file2->meta->size, cnt,
            tmp_len);
        tsk_error_print(stderr);
        return TSK_WALK_ERROR;
    }

    // now read from the real offset and compare with what we were passed
    if (a_size > fs->block_size)
        tmp_len = fs->block_size;
    else
        tmp_len = a_size;

    cnt =
        tsk_fs_file_read(s_file2, s_off, s_buf, tmp_len,
        (TSK_FS_FILE_READ_FLAG_ENUM) 0);
    if (cnt != (ssize_t) tmp_len) {
        fprintf(stderr,
            "Error reading file offset %" PRIuOFF " in file sized %"
            PRIuOFF "\n", s_off, s_file2->meta->size);
        tsk_error_print(stderr);
        return TSK_WALK_ERROR;
    }

    if (memcmp(s_buf, a_buf, a_size)) {
        fprintf(stderr,
            "Buffers at offset %" PRIuOFF " in file %" PRIuINUM
            " are different\n", s_off, s_file2->meta->addr);
        return TSK_WALK_ERROR;
    }
    s_off += a_size;

    /* IF the block we were passed is RAW (not BAD, resident, compressed etc.,
     * then read using the fs_read() API 
     */
    if (a_flags & TSK_FS_BLOCK_FLAG_RAW) {
        tmp_off = (a_addr * 42 + 82) % fs->last_block;

        cnt = tsk_fs_read_block(fs, tmp_off, s_buf, fs->block_size);
        if (cnt != (ssize_t) fs->block_size) {
            fprintf(stderr,
                "Error reading random block %" PRIuOFF " in file system\n",
                tmp_off);
            tsk_error_print(stderr);
            return TSK_WALK_ERROR;
        }

        cnt = tsk_fs_read_block(fs, a_addr, s_buf, fs->block_size);
        if (cnt != (ssize_t) fs->block_size) {
            fprintf(stderr, "Error reading block %" PRIuOFF "\n", a_addr);
            tsk_error_print(stderr);
            return TSK_WALK_ERROR;
        }

        // compare
        if (memcmp(s_buf, a_buf, a_size)) {
            fprintf(stderr,
                "Buffers at block addr %" PRIuOFF " in file %" PRIuINUM
                " are different\n", a_addr, s_file2->meta->addr);
            return TSK_WALK_ERROR;
        }

        /* Now we also read using the img_read() API, just because we can */
        cnt = tsk_fs_read_block(fs, tmp_off, s_buf, fs->block_size);
        if (cnt != (ssize_t) fs->block_size) {
            fprintf(stderr,
                "Error reading random block %" PRIuOFF " in file system\n",
                tmp_off);
            tsk_error_print(stderr);
            return TSK_WALK_ERROR;
        }

        // get the offset into the image
        tmp_off = a_addr * fs->block_size + fs->offset;
        cnt = tsk_img_read(fs->img_info, tmp_off, s_buf, fs->block_size);
        if (cnt != (ssize_t) fs->block_size) {
            fprintf(stderr,
                "Error reading image offset %" PRIuOFF " in image\n",
                tmp_off);
            tsk_error_print(stderr);
            return TSK_WALK_ERROR;
        }

        // compare
        if (memcmp(s_buf, a_buf, a_size)) {
            fprintf(stderr,
                "Buffers at image offset  %" PRIuOFF " in file %" PRIuINUM
                " are different\n", tmp_off, s_file2->meta->addr);
            return TSK_WALK_ERROR;
        }

    }

    return TSK_WALK_CONT;
}