/* * 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; }
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; }
/** \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; }
/** * \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; }
/** * \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; }
/** \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; }
/** * 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; }
/** * 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; }
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; }