/** \internal * Internal method that the other findFilesInFs can call after they * have opened FS_INFO. * @returns OK, STOP, or ERR (error message will already have been registered) */ TSK_RETVAL_ENUM TskAuto::findFilesInFsInt(TSK_FS_INFO * a_fs_info, TSK_INUM_T a_inum) { // see if the super class wants us to proceed TSK_FILTER_ENUM retval = filterFs(a_fs_info); if ((retval == TSK_FILTER_STOP) || (m_stopAllProcessing)) return TSK_STOP; else if (retval == TSK_FILTER_SKIP) return TSK_OK; /* Walk the files, starting at the given inum */ if (tsk_fs_dir_walk(a_fs_info, a_inum, (TSK_FS_DIR_WALK_FLAG_ENUM) (TSK_FS_DIR_WALK_FLAG_RECURSE | m_fileFilterFlags), dirWalkCb, this)) { tsk_error_set_errstr2( "Error walking directory in file system at offset %" PRIuOFF, a_fs_info->offset); registerError(); return TSK_ERR; } if (m_stopAllProcessing) return TSK_STOP; /* We could do some analysis of unallocated blocks at this point... */ return TSK_OK; }
/** * Starts in a specified byte offset of the opened disk images and looks for a * file system. Will start processing the file system at a specified * file system. Will call processFile() on each file * that is found in that directory. * * @param a_start Byte offset of file system starting location. * @param a_ftype Type of file system that will be analyzed. * @param a_inum inum to start walking files system at. * * @returns 1 if an error occured (messages will have been registered) and 0 on success */ uint8_t TskAuto::findFilesInFs(TSK_OFF_T a_start, TSK_FS_TYPE_ENUM a_ftype, TSK_INUM_T a_inum) { if (!m_img_info) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_AUTO_NOTOPEN); tsk_error_set_errstr("findFilesInFs -- img_info "); registerError(); return 1; } TSK_FS_INFO *fs_info; if ((fs_info = tsk_fs_open_img(m_img_info, a_start, a_ftype)) == NULL) { if (getCurVsPartFlag() & TSK_VS_PART_FLAG_ALLOC) { tsk_error_set_errstr2( "Sector offset: %" PRIuOFF ", Partition Type: %s", a_start / 512, getCurVsPartDescr().c_str()); registerError(); return 1; } else { tsk_error_reset(); return 0; } } findFilesInFsInt(fs_info, a_inum); tsk_fs_close(fs_info); return m_errors.empty() ? 0 : 1; }
/** * Starts in a specified byte offset of the opened disk images and looks for a * file system. Will call processFile() on each file * that is found. Same as findFilesInFs, but gives more detailed return values. * @param a_start Byte offset to start analyzing from. * @param a_ftype File system type. * @returns Error (messages will have been registered), OK, or STOP. */ TSK_RETVAL_ENUM TskAuto::findFilesInFsRet(TSK_OFF_T a_start, TSK_FS_TYPE_ENUM a_ftype) { if (!m_img_info) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_AUTO_NOTOPEN); tsk_error_set_errstr("findFilesInFsRet -- img_info"); registerError(); return TSK_ERR; } TSK_FS_INFO *fs_info; if ((fs_info = tsk_fs_open_img(m_img_info, a_start, a_ftype)) == NULL) { if (getCurVsPartFlag() & TSK_VS_PART_FLAG_ALLOC) { tsk_error_set_errstr2 ("Sector offset: %" PRIuOFF ", Partition Type: %s", a_start/512, getCurVsPartDescr().c_str() ); registerError(); return TSK_ERR; } else { tsk_error_reset(); return TSK_OK; } } TSK_RETVAL_ENUM retval = findFilesInFsInt(fs_info, fs_info->root_inum); tsk_fs_close(fs_info); if (m_errors.empty() == false) return TSK_ERR; else return retval; }
std::string TskAuto::errorRecordToString(error_record &rec) { tsk_error_reset(); tsk_error_set_errno(rec.code); tsk_error_set_errstr("%s", rec.msg1.c_str()); tsk_error_set_errstr2("%s", rec.msg2.c_str()); const char *msg = tsk_error_get(); std::string ret; if (msg != NULL) ret = msg; tsk_error_reset(); return ret; }
/* * This thread sets error variables, updates the semaphore, * waits on the semaphore, and reads them back. */ void * thread_1(void *arg) { xErrorsTestShared * shared = (xErrorsTestShared*) arg; // wait to be told to start. #ifdef __APPLE__ kern_return_t se = semaphore_wait(shared->sync_barrier); if (se != 0) { fprintf(stderr, "sem_wait failed: %d\n", se); shared->failure = true; } #else if (sem_wait(&shared->sync_barrier) != 0) { fprintf(stderr, "sem_wait failed: %s\n", strerror(errno)); shared->failure = true; } #endif tsk_error_set_errno(42); tsk_error_set_errstr("I just set errno to %d.", 42); tsk_error_set_errstr2("Indeed, I just set errno to %d.", 42); #ifdef __APPLE__ se = semaphore_signal(shared->sync_barrier); if (se != 0) { fprintf(stderr, "sem_signal failed: %d\n", se); shared->failure = true; } se = semaphore_wait(shared->sync_barrier); if (se != 0) { fprintf(stderr, "sem_wait failed: %d\n", se); shared->failure = true; } #else sem_post(&shared->sync_barrier); sem_wait(&shared->sync_barrier); #endif shared->errno_check_failed = 42 != tsk_error_get_errno(); char const * s = tsk_error_get_errstr(); shared->errstr_check_failed = 0 != strcmp("I just set errno to 42.", s); s = tsk_error_get_errstr2(); shared->errstr2_check_failed = 0 != strcmp("Indeed, I just set errno to 42.", s); 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; }
/** * Find the corresponding name at a * given offset. The offset was likely determined from the index. * The entries in the DB following the one specified are also processed * if they have the same hash value and their name is different. * The callback is called for each entry. * * @param hdb_info Database to get data from. * @param hash MD5/SHA-1 hash value that was searched for * @param offset Byte offset where hash value should be located in db_file * @param flags (not used) * @param action Callback used for each entry found in lookup * @param cb_ptr Pointer to data passed to callback * * @return 1 on error and 0 on success */ uint8_t nsrl_getentry(TSK_HDB_INFO * hdb_info, const char *hash, TSK_OFF_T offset, TSK_HDB_FLAG_ENUM flags, TSK_HDB_LOOKUP_FN action, void *cb_ptr) { char buf[TSK_HDB_MAXLEN], *name, *cur_hash, pname[TSK_HDB_MAXLEN]; int found = 0; int ver; if (tsk_verbose) fprintf(stderr, "nsrl_getentry: Lookup up hash %s at offset %" PRIuOFF "\n", hash, offset); if ((hdb_info->hash_type == TSK_HDB_HTYPE_MD5_ID) && (strlen(hash) != TSK_HDB_HTYPE_MD5_LEN)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_ARG); tsk_error_set_errstr( "nsrl_getentry: Invalid hash value (expected to be MD5): %s\n", hash); return 1; } else if ((hdb_info->hash_type == TSK_HDB_HTYPE_SHA1_ID) && (strlen(hash) != TSK_HDB_HTYPE_SHA1_LEN)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_ARG); tsk_error_set_errstr( "nsrl_getentry: Invalid hash value (expected to be SHA1): %s\n", hash); return 1; } /* read the header line ... -- this should be done only once... */ fseeko(hdb_info->hDb, 0, SEEK_SET); if (NULL == fgets(buf, TSK_HDB_MAXLEN, hdb_info->hDb)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_READDB); tsk_error_set_errstr( "nsrl_getentry: Error reading NSRLFile.txt header\n"); return 1; } if ((ver = get_format_ver(buf)) == -1) { tsk_error_set_errstr2( "nsrl_getentry"); return 1; } memset(pname, '0', TSK_HDB_MAXLEN); /* Loop so that we can find consecutive occurances of the same hash */ while (1) { size_t len; if (0 != fseeko(hdb_info->hDb, offset, SEEK_SET)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_READDB); tsk_error_set_errstr( "nsrl_getentry: Error seeking to get file name: %lu", (unsigned long) offset); return 1; } if (NULL == fgets(buf, TSK_HDB_MAXLEN, hdb_info->hDb)) { if (feof(hdb_info->hDb)) break; tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_READDB); tsk_error_set_errstr( "nsrl_getentry: Error reading database"); return 1; } len = strlen(buf); if (len < TSK_HDB_HTYPE_SHA1_LEN + 5) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_CORRUPT); tsk_error_set_errstr( "nsrl_getentry: Invalid entry in database (too short): %s", buf); return 1; } /* Which field are we looking for */ if (hdb_info->hash_type == TSK_HDB_HTYPE_SHA1_ID) { if (nsrl_parse_sha1(buf, &cur_hash, &name, ver)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_CORRUPT); tsk_error_set_errstr( "nsrl_getentry: Invalid entry in database: %s", buf); return 1; } } else if (hdb_info->hash_type == TSK_HDB_HTYPE_MD5_ID) { if (nsrl_parse_md5(buf, &cur_hash, &name, ver)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_CORRUPT); tsk_error_set_errstr( "nsrl_getentry: Invalid entry in database: %s", buf); return 1; } } /* Verify that this is the hash we are looking for */ if (0 != strcasecmp(cur_hash, hash)) { break; } /* Check if this is the same name as the previous entry */ if (strcmp(name, pname) != 0) { int retval; retval = action(hdb_info, hash, name, cb_ptr); if (retval == TSK_WALK_STOP) return 0; else if (retval == TSK_WALK_ERROR) return 1; found = 1; strncpy(pname, name, TSK_HDB_MAXLEN); } /* Advance to the next row */ offset += len; } if (found == 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_ARG); tsk_error_set_errstr( "nsrl_getentry: Hash not found in file at offset: %lu", (unsigned long) offset); return 1; } return 0; }
/** * Process the database to create a sorted index of it. Consecutive * entries with the same hash value are not added to the index, but * will be found during lookup. * * @param hdb_info Hash database to make index of. * @param dbtype Type of database * * @return 1 on error and 0 on success. */ uint8_t nsrl_makeindex(TSK_HDB_INFO * hdb_info, TSK_TCHAR * dbtype) { size_t i, len; char buf[TSK_HDB_MAXLEN]; char *hash = NULL, phash[TSK_HDB_HTYPE_SHA1_LEN + 1]; TSK_OFF_T offset = 0; int ver = 0; int db_cnt = 0, idx_cnt = 0, ig_cnt = 0; if (tsk_hdb_idxinitialize(hdb_info, dbtype)) { tsk_error_set_errstr2( "nsrl_makeindex"); return 1; } /* Status */ if (tsk_verbose) TFPRINTF(stderr, _TSK_T("Extracting Data from Database (%s)\n"), hdb_info->db_fname); /* Allocate a buffer for the previous hash value */ memset(phash, '0', TSK_HDB_HTYPE_SHA1_LEN + 1); /* read the file */ fseek(hdb_info->hDb, 0, SEEK_SET); for (i = 0; NULL != fgets(buf, TSK_HDB_MAXLEN, hdb_info->hDb); offset += len, i++) { len = strlen(buf); /* Get the version of the database on the first time around */ if (i == 0) { if ((ver = get_format_ver(buf)) == -1) { return 1; } ig_cnt++; continue; } /* Parse the line */ if (hdb_info->hash_type & TSK_HDB_HTYPE_SHA1_ID) { if (nsrl_parse_sha1(buf, &hash, NULL, ver)) { ig_cnt++; continue; } } else if (hdb_info->hash_type & TSK_HDB_HTYPE_MD5_ID) { if (nsrl_parse_md5(buf, &hash, NULL, ver)) { ig_cnt++; continue; } } db_cnt++; /* We only want to add one of each hash to the index */ if (memcmp(hash, phash, hdb_info->hash_len) == 0) { continue; } /* Add the entry to the index */ if (tsk_hdb_idxaddentry(hdb_info, hash, offset)) { tsk_error_set_errstr2( "nsrl_makeindex"); return 1; } idx_cnt++; /* Set the previous has value */ strncpy(phash, hash, hdb_info->hash_len + 1); } if (idx_cnt > 0) { if (tsk_verbose) { fprintf(stderr, " Valid Database Entries: %d\n", db_cnt); fprintf(stderr, " Invalid Database Entries (headers or errors): %d\n", ig_cnt); fprintf(stderr, " Index File Entries %s: %d\n", (idx_cnt == db_cnt) ? "" : "(optimized)", idx_cnt); } /* Close and sort the index */ if (tsk_hdb_idxfinalize(hdb_info)) { tsk_error_set_errstr2( "nsrl_makeindex"); return 1; } } else { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_CORRUPT); tsk_error_set_errstr( "nsrl_makeindex: No valid entries found in database"); return 1; } return 0; }
/* * Load the primary partition table (MBR) into the internal * data structures in TSK_VS_INFO * * This will automatically call load_ext_table for extended * partitions * * sect_cur is the address of the table to load * * 0 is returned if the load is successful and 1 if error */ static uint8_t dos_load_prim_table(TSK_VS_INFO * vs, uint8_t test) { dos_sect *sect; char *sect_buf; int i, added = 0; char *table_str; ssize_t cnt; TSK_DADDR_T taddr = vs->offset / vs->block_size + DOS_PART_SOFFSET; TSK_DADDR_T max_addr = (vs->img_info->size - vs->offset) / vs->block_size; // max sector if (tsk_verbose) tsk_fprintf(stderr, "dos_load_prim: Table Sector: %" PRIuDADDR "\n", taddr); if ((sect_buf = tsk_malloc(vs->block_size)) == NULL) return 1; sect = (dos_sect *) sect_buf; /* Read the table */ cnt = tsk_vs_read_block (vs, DOS_PART_SOFFSET, sect_buf, vs->block_size); if (cnt != vs->block_size) { if (cnt >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_READ); } tsk_error_set_errstr2("Primary DOS table sector %" PRIuDADDR, taddr); free(sect_buf); return 1; } /* Sanity Check */ if (tsk_vs_guessu16(vs, sect->magic, DOS_MAGIC)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_MAGIC); tsk_error_set_errstr ("File is not a DOS partition (invalid primary magic) (Sector: %" PRIuDADDR ")", taddr); if (tsk_verbose) fprintf(stderr, "File is not a DOS partition (invalid primary magic) (Sector: %" PRIuDADDR ")", taddr); free(sect_buf); return 1; } /* Because FAT and NTFS use the same magic - check for a * standard MS OEM name and sizes. Not a great check, but we can't * really test the table entries. */ if (test) { if (tsk_verbose) tsk_fprintf(stderr, "dos_load_prim_table: Testing FAT/NTFS conditions\n"); if (strncmp("MSDOS", sect->oemname, 5) == 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_MAGIC); tsk_error_set_errstr ("dos_load_prim_table: MSDOS OEM name exists"); if (tsk_verbose) tsk_fprintf(stderr, "dos_load_prim_table: MSDOS OEM name exists\n"); free(sect_buf); return 1; } else if (strncmp("MSWIN", sect->oemname, 5) == 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_MAGIC); tsk_error_set_errstr ("dos_load_prim_table: MSWIN OEM name exists"); if (tsk_verbose) tsk_fprintf(stderr, "dos_load_prim_table: MSWIN OEM name exists\n"); free(sect_buf); return 1; } else if (strncmp("NTFS", sect->oemname, 4) == 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_MAGIC); tsk_error_set_errstr ("dos_load_prim_table: NTFS OEM name exists"); if (tsk_verbose) tsk_fprintf(stderr, "dos_load_prim_table: NTFS OEM name exists\n"); free(sect_buf); return 1; } else if (strncmp("FAT", sect->oemname, 4) == 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_MAGIC); tsk_error_set_errstr ("dos_load_prim_table: FAT OEM name exists"); if (tsk_verbose) tsk_fprintf(stderr, "dos_load_prim_table: FAT OEM name exists\n"); free(sect_buf); return 1; } } /* Add an entry of 1 sector for the table to the internal structure */ if ((table_str = tsk_malloc(32)) == NULL) { free(sect_buf); return 1; } snprintf(table_str, 32, "Primary Table (#0)"); if (NULL == tsk_vs_part_add(vs, DOS_PART_SOFFSET, (TSK_DADDR_T) 1, TSK_VS_PART_FLAG_META, table_str, -1, -1)) { free(sect_buf); return 1; } /* Cycle through the partition table */ for (i = 0; i < 4; i++) { dos_part *part = §->ptable[i]; /* We currently ignore CHS */ uint32_t part_start = tsk_getu32(vs->endian, part->start_sec); uint32_t part_size = tsk_getu32(vs->endian, part->size_sec); if (tsk_verbose) tsk_fprintf(stderr, "load_pri:0:%d Start: %" PRIu32 " Size: %" PRIu32 " Type: %d\n", i, part_start, part_size, part->ptype); if (part_size == 0) continue; // make sure the first couple are in the image bounds if ((i < 2) && (part_start > max_addr)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_BLK_NUM); tsk_error_set_errstr ("dos_load_prim_table: Starting sector too large for image"); if (tsk_verbose) tsk_fprintf(stderr, "Starting sector %" PRIu32 " too large for image\n", part_start); free(sect_buf); return 1; } #if 0 // I'm not sure if this is too strict ... else if ((part_start + part_size) > max_addr) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_BLK_NUM); tsk_error_set_errstr ("dos_load_prim_table: Partition ends after image"); return 1; } #endif added = 1; /* Add the partition to the internal structure * If it is an extended partition, process it now */ if (dos_is_ext(part->ptype)) { if (NULL == tsk_vs_part_add(vs, (TSK_DADDR_T) part_start, (TSK_DADDR_T) part_size, TSK_VS_PART_FLAG_META, dos_get_desc(part->ptype), 0, i)) { free(sect_buf); return 1; } if (dos_load_ext_table(vs, part_start, part_start, 1)) { if (tsk_verbose) { fprintf(stderr, "Error loading extended table, moving on"); tsk_error_print(stderr); } tsk_error_reset(); } } else { if (NULL == tsk_vs_part_add(vs, (TSK_DADDR_T) part_start, (TSK_DADDR_T) part_size, TSK_VS_PART_FLAG_ALLOC, dos_get_desc(part->ptype), 0, i)) { free(sect_buf); return 1; } } } free(sect_buf); if (added == 0) { if (tsk_verbose) tsk_fprintf(stderr, "dos_load_prim: No valid entries\n"); tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_MAGIC); tsk_error_set_errstr ("dos_load_prim_table: No valid entries in primary table"); return 1; } return 0; }
/** * Process the database to create a sorted index of it. Consecutive * entries with the same hash value are not added to the index, but * will be found during lookup. * * @param hdb_info Hash database to make index of * @param dbtype Text of database type (should always be TSK_HDB_DBTYPE_HK_STR) * * @return 1 on error and 0 on success. */ uint8_t hk_makeindex(TSK_HDB_INFO * hdb_info, TSK_TCHAR * dbtype) { int i; size_t len = 0; char buf[TSK_HDB_MAXLEN]; char *hash = NULL, phash[TSK_HDB_HTYPE_MD5_LEN + 1]; TSK_OFF_T offset = 0; int db_cnt = 0, idx_cnt = 0, ig_cnt = 0; if (tsk_hdb_idxinitialize(hdb_info, dbtype)) { tsk_error_set_errstr2( "hk_makeindex"); return 1; } fseek(hdb_info->hDb, 0, SEEK_SET); /* Status */ if (tsk_verbose) TFPRINTF(stderr, _TSK_T("Extracting Data from Database (%s)\n"), hdb_info->db_fname); /* Allocate a buffer to hold the previous hash values */ memset(phash, '0', TSK_HDB_HTYPE_MD5_LEN + 1); /* read each line of the file */ fseek(hdb_info->hDb, 0, SEEK_SET); for (i = 0; NULL != fgets(buf, TSK_HDB_MAXLEN, hdb_info->hDb); offset += (TSK_OFF_T) len, i++) { len = strlen(buf); /* Parse each line to get the MD5 value */ if (hk_parse_md5(buf, &hash, NULL, 0, NULL, 0)) { ig_cnt++; continue; } db_cnt++; /* If this entry is for the same hash value as the last entry, * the skip it -- we'll look for it during lookup */ if (memcmp(hash, phash, TSK_HDB_HTYPE_MD5_LEN) == 0) { continue; } /* Add the entry to the index */ if (tsk_hdb_idxaddentry(hdb_info, hash, offset)) { tsk_error_set_errstr2( "hk_makeindex"); return 1; } idx_cnt++; /* Set the previous hash value */ strncpy(phash, hash, TSK_HDB_HTYPE_MD5_LEN + 1); } if (idx_cnt > 0) { if (tsk_verbose) { fprintf(stderr, " Valid Database Entries: %d\n", db_cnt); fprintf(stderr, " Invalid Database Entries (headers or errors): %d\n", ig_cnt); fprintf(stderr, " Index File Entries %s: %d\n", (idx_cnt == db_cnt) ? "" : "(optimized)", idx_cnt); } /* Finish the index making process */ if (tsk_hdb_idxfinalize(hdb_info)) { tsk_error_set_errstr2( "hk_makeindex"); return 1; } } else { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_CORRUPT); tsk_error_set_errstr( "hk_makeindex: No valid entries found in database"); return 1; } return 0; }
/** * \ingroup fslib * Read the contents of a given attribute using a typical read() type interface. * 0s are returned for missing runs. * * @param a_fs_attr The attribute to read. * @param a_offset The byte offset to start reading from. * @param a_buf The buffer to read the data into. * @param a_len The number of bytes to read from the file. * @param a_flags Flags to use while reading * @returns The number of bytes read or -1 on error (incl if offset is past end of file). */ ssize_t tsk_fs_attr_read(const TSK_FS_ATTR * a_fs_attr, TSK_OFF_T a_offset, char *a_buf, size_t a_len, TSK_FS_FILE_READ_FLAG_ENUM a_flags) { TSK_FS_INFO *fs; if ((a_fs_attr == NULL) || (a_fs_attr->fs_file == NULL) || (a_fs_attr->fs_file->fs_info == NULL)) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_attr_read: Attribute has null pointers."); return -1; } fs = a_fs_attr->fs_file->fs_info; /* for compressed data, call the specialized function */ if (a_fs_attr->flags & TSK_FS_ATTR_COMP) { if (a_fs_attr->r == NULL) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_attr_read: Attribute has compressed type set, but no compressed read function defined"); return -1; } return a_fs_attr->r(a_fs_attr, a_offset, a_buf, a_len); } /* For resident data, copy data from the local buffer */ else if (a_fs_attr->flags & TSK_FS_ATTR_RES) { size_t len_toread; if (a_offset >= a_fs_attr->size) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_READ_OFF); tsk_error_set_errstr("tsk_fs_attr_read - %" PRIuOFF, a_offset); return -1; } len_toread = a_len; if (a_offset + a_len > a_fs_attr->size) { len_toread = (size_t) (a_fs_attr->size - a_offset); memset(&a_buf[len_toread], 0, a_len - len_toread); } memcpy(a_buf, &a_fs_attr->rd.buf[a_offset], len_toread); return (ssize_t) len_toread; } /* For non-resident data, load the needed block and copy the data */ else if (a_fs_attr->flags & TSK_FS_ATTR_NONRES) { TSK_FS_ATTR_RUN *data_run_cur; TSK_DADDR_T blkoffset_toread; // block offset of where we want to start reading from size_t byteoffset_toread; // byte offset in blkoffset_toread of where we want to start reading from size_t len_remain; // length remaining to copy size_t len_toread; // length total to copy if (((a_flags & TSK_FS_FILE_READ_FLAG_SLACK) && (a_offset >= a_fs_attr->nrd.allocsize)) || (!(a_flags & TSK_FS_FILE_READ_FLAG_SLACK) && (a_offset >= a_fs_attr->size))) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_READ_OFF); tsk_error_set_errstr("tsk_fs_attr_read - %" PRIuOFF, a_offset); return -1; } blkoffset_toread = a_offset / fs->block_size; byteoffset_toread = (size_t) (a_offset % fs->block_size); // determine how many bytes we can copy len_toread = a_len; if (a_flags & TSK_FS_FILE_READ_FLAG_SLACK) { if (a_offset + a_len > a_fs_attr->nrd.allocsize) len_toread = (size_t) (a_fs_attr->nrd.allocsize - a_offset); } else { if (a_offset + a_len > a_fs_attr->size) len_toread = (size_t) (a_fs_attr->size - a_offset); } // wipe the buffer we won't read into if (len_toread < a_len) memset(&a_buf[len_toread], 0, a_len - len_toread); len_remain = len_toread; // cycle through the run until we find where we can start to process the clusters for (data_run_cur = a_fs_attr->nrd.run; data_run_cur; data_run_cur = data_run_cur->next) { TSK_DADDR_T blkoffset_inrun; size_t len_inrun; // we are done if (len_remain <= 0) break; // See if this run contains the starting offset they requested if (data_run_cur->offset + data_run_cur->len <= blkoffset_toread) continue; // block offset into this run if (data_run_cur->offset < blkoffset_toread) blkoffset_inrun = blkoffset_toread - data_run_cur->offset; else blkoffset_inrun = 0; // see if we need to read the rest of this run and into the next or if it is all here len_inrun = len_remain; if ((data_run_cur->len - blkoffset_inrun) * fs->block_size - byteoffset_toread < len_remain) len_inrun = (size_t) ((data_run_cur->len - blkoffset_inrun) * fs->block_size - byteoffset_toread); /* sparse files/runs just get 0s */ if (data_run_cur->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE) { memset(&a_buf[len_toread - len_remain], 0, len_inrun); } /* 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 (data_run_cur->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) { memset(&a_buf[len_toread - len_remain], 0, len_inrun); if (tsk_verbose) fprintf(stderr, "tsk_fs_attr_read_type: File %" PRIuINUM " has FILLER entry, using 0s\n", (a_fs_attr->fs_file->meta) ? a_fs_attr->fs_file-> meta->addr : 0); } // we return 0s for reads past the initsize (unless they want slack space) else if (((TSK_OFF_T) ((data_run_cur->offset + blkoffset_inrun) * fs->block_size + byteoffset_toread) >= a_fs_attr->nrd.initsize) && ((a_flags & TSK_FS_FILE_READ_FLAG_SLACK) == 0)) { memset(&a_buf[len_toread - len_remain], 0, len_inrun); if (tsk_verbose) fprintf(stderr, "tsk_fs_attr_read: Returning 0s for read past end of initsize (%" PRIuINUM ")\n", ((a_fs_attr->fs_file) && (a_fs_attr->fs_file->meta)) ? a_fs_attr-> fs_file->meta->addr : 0); } else { TSK_OFF_T fs_offset_b; ssize_t cnt; // calcuate the byte offset in the file system fs_offset_b = (data_run_cur->addr + blkoffset_inrun) * fs->block_size; // add the byte offset in the block fs_offset_b += byteoffset_toread; // reset this in case we need to also read from the next run byteoffset_toread = 0; cnt = tsk_fs_read(fs, fs_offset_b, &a_buf[len_toread - len_remain], len_inrun); if (cnt != len_inrun) { if (cnt >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_READ); } tsk_error_set_errstr2 ("tsk_fs_attr_read_type: offset: %" PRIuOFF " Len: %" PRIuSIZE "", fs_offset_b, len_inrun); return cnt; } // see if part of the data is in the non-initialized space if (((TSK_OFF_T) ((data_run_cur->offset + blkoffset_inrun) * fs->block_size + byteoffset_toread + len_inrun) > a_fs_attr->nrd.initsize) && ((a_flags & TSK_FS_FILE_READ_FLAG_SLACK) == 0)) { size_t uninit_off = a_fs_attr->nrd.initsize - ((data_run_cur->offset + blkoffset_inrun) * fs->block_size + byteoffset_toread); memset(&a_buf[len_toread - len_remain + uninit_off], 0, len_inrun - uninit_off); } } len_remain -= len_inrun; } return (ssize_t) (len_toread - len_remain); } tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("tsk_fs_attr_read: Unknown attribute type: %x", a_fs_attr->flags); return -1; }
/** \internal * Process a directory and load up FS_DIR with the entries. If a pointer to * an already allocated FS_DIR struture is given, it will be cleared. If no existing * FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return * value is error or corruption, then the FS_DIR structure could * have entries (depending on when the error occured). * * @param a_fs File system to analyze * @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated * structure or a new structure. * @param a_addr Address of directory to process. * @returns error, corruption, ok etc. */ TSK_RETVAL_ENUM iso9660_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir, TSK_INUM_T a_addr) { TSK_RETVAL_ENUM retval; TSK_FS_DIR *fs_dir; ssize_t cnt; char *buf; size_t length; if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); tsk_error_set_errstr ("iso9660_dir_open_meta: Invalid inode value: %" PRIuINUM, a_addr); return TSK_ERR; } else if (a_fs_dir == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("iso9660_dir_open_meta: NULL fs_attr argument given"); return TSK_ERR; } if (tsk_verbose) tsk_fprintf(stderr, "iso9660_dir_open_meta: Processing directory %" PRIuINUM "\n", a_addr); fs_dir = *a_fs_dir; if (fs_dir) { tsk_fs_dir_reset(fs_dir); } else { if ((*a_fs_dir = fs_dir = tsk_fs_dir_alloc(a_fs, a_addr, 128)) == NULL) { return TSK_ERR; } } // handle the orphan directory if its contents were requested if (a_addr == TSK_FS_ORPHANDIR_INUM(a_fs)) { return tsk_fs_dir_find_orphans(a_fs, fs_dir); } fs_dir->fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_addr); if (fs_dir->fs_file == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_INODE_NUM); tsk_error_set_errstr("iso9660_dir_open_meta: %" PRIuINUM " is not a valid inode", a_addr); return TSK_COR; } /* read directory extent into memory */ length = (size_t) fs_dir->fs_file->meta->size; if ((buf = tsk_malloc(length)) == NULL) return TSK_ERR; cnt = tsk_fs_file_read(fs_dir->fs_file, 0, buf, length, 0); if (cnt != length) { if (cnt >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_READ); } tsk_error_set_errstr2("iso9660_dir_open_meta"); return TSK_ERR; } // process the contents retval = iso9660_proc_dir(a_fs, fs_dir, buf, length, a_addr, fs_dir->fs_file->meta->attr->head->nrd.run->addr); // if we are listing the root directory, add the Orphan directory entry if (a_addr == a_fs->root_inum) { TSK_FS_NAME *fs_name = tsk_fs_name_alloc(256, 0); if (fs_name == NULL) return TSK_ERR; if (tsk_fs_dir_make_orphan_dir_name(a_fs, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } if (tsk_fs_dir_add(fs_dir, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } tsk_fs_name_free(fs_name); } return retval; }
/* * Process the partition table at the sector address * * This method just finds out if it is sparc or Intel and then * calls the appropriate method * * Return 0 on success and 1 on error */ static uint8_t sun_load_table(TSK_VS_INFO * vs) { sun_dlabel_sparc *dlabel_sp; sun_dlabel_i386 *dlabel_x86; char *buf; ssize_t cnt; TSK_DADDR_T taddr = vs->offset / vs->block_size + SUN_SPARC_PART_SOFFSET; int result = 0; /* Sanity check in case label sizes change */ if ((sizeof(*dlabel_sp) > vs->block_size) || (sizeof(*dlabel_x86) > vs->block_size)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_BUF); tsk_error_set_errstr ("sun_load_table: disk labels bigger than block size"); return 1; } if (tsk_verbose) tsk_fprintf(stderr, "sun_load_table: Trying sector: %" PRIuDADDR "\n", taddr); if ((buf = tsk_malloc(vs->block_size)) == NULL) { goto on_error; } /* Try the given offset */ cnt = tsk_vs_read_block (vs, SUN_SPARC_PART_SOFFSET, buf, vs->block_size); /* If -1 is returned, tsk_errno is already set */ if (cnt != vs->block_size) { if (cnt >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_READ); } tsk_error_set_errstr2("SUN Disk Label in Sector: %" PRIuDADDR, taddr); goto on_error; } /* Check the magic value * Both intel and sparc have the magic value in the same location * * We try both in case someone specifies the exact location of the * intel disk label. * */ dlabel_sp = (sun_dlabel_sparc *) buf; dlabel_x86 = (sun_dlabel_i386 *) buf; if (tsk_vs_guessu16(vs, dlabel_sp->magic, SUN_MAGIC) == 0) { if (tsk_getu32(vs->endian, dlabel_sp->sanity) == SUN_SANITY) { result = sun_load_table_sparc(vs, dlabel_sp); // TODO: I assume based on the existing free that the previous function // does not take ownership of buf. free(buf); return result; } else if (tsk_getu32(vs->endian, dlabel_x86->sanity) == SUN_SANITY) { result = sun_load_table_i386(vs, dlabel_x86); // TODO: I assume based on the existing free that the previous function // does not take ownership of buf. free(buf); return result; } } /* Now try the next sector, which is where the intel * could be stored */ taddr = vs->offset / vs->block_size / SUN_I386_PART_SOFFSET; if (tsk_verbose) tsk_fprintf(stderr, "sun_load_table: Trying sector: %" PRIuDADDR "\n", taddr + 1); cnt = tsk_vs_read_block (vs, SUN_I386_PART_SOFFSET, buf, vs->block_size); if (cnt != vs->block_size) { if (cnt >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_READ); } tsk_error_set_errstr2("SUN (Intel) Disk Label in Sector: %" PRIuDADDR, taddr); goto on_error; } dlabel_x86 = (sun_dlabel_i386 *) buf; if (tsk_vs_guessu16(vs, dlabel_x86->magic, SUN_MAGIC)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_MAGIC); tsk_error_set_errstr("SUN (intel) partition table (Sector: %" PRIuDADDR ") %x", taddr, tsk_getu16(vs->endian, dlabel_sp->magic)); goto on_error; } if (tsk_getu32(vs->endian, dlabel_x86->sanity) != SUN_SANITY) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_MAGIC); tsk_error_set_errstr("SUN (intel) sanity value (Sector: %" PRIuDADDR ") %x", taddr, tsk_getu16(vs->endian, dlabel_sp->magic)); goto on_error; } result = sun_load_table_i386(vs, dlabel_x86); // TODO: I assume based on the existing free that the previous function // does not take ownership of buf. free(buf); return result; on_error: if( buf != NULL ) { free( buf ); } return 1; }
/** * Process the database to create a sorted index of it. Consecutive * entries with the same hash value are not added to the index, but * will be found during lookup. * * @param hdb_info_base Hash database to make index of. * @param dbtype Type of hash database (should always be TSK_HDB_DBTYPE_MD5SUM_STR) * * @return 1 on error and 0 on success. */ uint8_t md5sum_makeindex(TSK_HDB_INFO *hdb_info_base, TSK_TCHAR * dbtype) { TSK_HDB_BINSRCH_INFO *hdb_info = (TSK_HDB_BINSRCH_INFO*)hdb_info_base; int i; char buf[TSK_HDB_MAXLEN]; char *hash = NULL, phash[TSK_HDB_HTYPE_MD5_LEN + 1]; TSK_OFF_T offset = 0; int db_cnt = 0, idx_cnt = 0, ig_cnt = 0; size_t len; /* Initialize the TSK index file */ if (hdb_binsrch_idx_initialize(hdb_info, dbtype)) { tsk_error_set_errstr2( "md5sum_makeindex"); return 1; } /* Status */ if (tsk_verbose) TFPRINTF(stderr, _TSK_T("Extracting Data from Database (%s)\n"), hdb_info->base.db_fname); /* Allocate a buffer for the previous hash value */ memset(phash, '0', TSK_HDB_HTYPE_MD5_LEN + 1); /* read the file and add to the index */ fseek(hdb_info->hDb, 0, SEEK_SET); for (i = 0; NULL != fgets(buf, TSK_HDB_MAXLEN, hdb_info->hDb); offset += (TSK_OFF_T) len, i++) { len = strlen(buf); /* Parse each line */ if (md5sum_parse_md5(buf, &hash, NULL)) { ig_cnt++; continue; } db_cnt++; /* We only want to add one of each hash to the index */ if (memcmp(hash, phash, TSK_HDB_HTYPE_MD5_LEN) == 0) { continue; } /* Add the entry to the index */ if (hdb_binsrch_idx_add_entry_str(hdb_info, hash, offset)) { tsk_error_set_errstr2( "md5sum_makeindex"); return 1; } idx_cnt++; /* Set the previous has value */ strncpy(phash, hash, TSK_HDB_HTYPE_MD5_LEN + 1); } if (idx_cnt > 0) { if (tsk_verbose) { fprintf(stderr, " Valid Database Entries: %d\n", db_cnt); fprintf(stderr, " Invalid Database Entries (headers or errors): %d\n", ig_cnt); fprintf(stderr, " Index File Entries %s: %d\n", (idx_cnt == db_cnt) ? "" : "(optimized)", idx_cnt); } /* Close and sort the index */ if (hdb_binsrch_idx_finalize(hdb_info)) { tsk_error_set_errstr2( "md5sum_makeindex"); return 1; } } else { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_CORRUPT); tsk_error_set_errstr( "md5sum_makeindex: No valid entries found in database"); return 1; } return 0; }
uint8_t fatxxfs_open(FATFS_INFO *fatfs) { const char *func_name = "fatxxfs_open"; TSK_FS_INFO *fs = &(fatfs->fs_info); FATXXFS_SB *fatsb = (FATXXFS_SB*)(&fatfs->boot_sector_buffer); int i = 0; TSK_DADDR_T sectors = 0; TSK_FS_DIR * test_dir1; // Directories used to try opening the root directory // clean up any error messages that are lying around tsk_error_reset(); /* Calculate block sizes and layout info */ // sector size fatfs->ssize = tsk_getu16(fs->endian, fatsb->ssize); if (fatfs->ssize == 512) { fatfs->ssize_sh = 9; } else if (fatfs->ssize == 1024) { fatfs->ssize_sh = 10; } else if (fatfs->ssize == 2048) { fatfs->ssize_sh = 11; } else if (fatfs->ssize == 4096) { fatfs->ssize_sh = 12; } else { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr ("Error: sector size (%d) is not a multiple of device size (%d)\nDo you have a disk image instead of a partition image?", fatfs->ssize, fs->dev_bsize); if (tsk_verbose) fprintf(stderr, "%s: Invalid sector size (%d)\n", func_name, fatfs->ssize); return 1; } // cluster size fatfs->csize = fatsb->csize; if ((fatfs->csize != 0x01) && (fatfs->csize != 0x02) && (fatfs->csize != 0x04) && (fatfs->csize != 0x08) && (fatfs->csize != 0x10) && (fatfs->csize != 0x20) && (fatfs->csize != 0x40) && (fatfs->csize != 0x80)) { if (tsk_verbose) fprintf(stderr, "%s: Invalid cluster size (%d)\n", func_name, fatfs->csize); tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr("Not a FATXX file system (cluster size)"); return 1; } // number of FAT tables fatfs->numfat = fatsb->numfat; if ((fatfs->numfat == 0) || (fatfs->numfat > 8)) { if (tsk_verbose) fprintf(stderr, "%s: Invalid number of FATS (%d)\n", func_name, fatfs->numfat); tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr("Not a FATXX file system (number of FATs)"); return 1; } /* We can't do a sanity check on this b.c. TSK_FS_TYPE_FAT32 has a value of 0 */ /* num of root entries */ fatfs->numroot = tsk_getu16(fs->endian, fatsb->numroot); /* if sectors16 is 0, then the number of sectors is stored in sectors32 */ if (0 == (sectors = tsk_getu16(fs->endian, fatsb->sectors16))) sectors = tsk_getu32(fs->endian, fatsb->sectors32); /* if secperfat16 is 0, then read sectperfat32 */ if (0 == (fatfs->sectperfat = tsk_getu16(fs->endian, fatsb->sectperfat16))) fatfs->sectperfat = tsk_getu32(fs->endian, fatsb->a.f32.sectperfat32); if (fatfs->sectperfat == 0) { if (tsk_verbose) fprintf(stderr, "%s: Invalid number of sectors per FAT (%d)\n", func_name, fatfs->sectperfat); tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr ("Not a FATXX file system (invalid sectors per FAT)"); return 1; } fatfs->firstfatsect = tsk_getu16(fs->endian, fatsb->reserved); if ((fatfs->firstfatsect == 0) || (fatfs->firstfatsect > sectors)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); tsk_error_set_errstr ("Not a FATXX file system (invalid first FAT sector %" PRIuDADDR ")", fatfs->firstfatsect); if (tsk_verbose) fprintf(stderr, "%s: Invalid first FAT (%" PRIuDADDR ")\n", func_name, fatfs->firstfatsect); return 1; } /* Calculate the block info * * The sector of the beginning of the data area - which is * after all of the FATs * * For TSK_FS_TYPE_FAT12 and TSK_FS_TYPE_FAT16, the data area starts with the root * directory entries and then the first cluster. For TSK_FS_TYPE_FAT32, * the data area starts with clusters and the root directory * is somewhere in the data area */ fatfs->firstdatasect = fatfs->firstfatsect + fatfs->sectperfat * fatfs->numfat; /* The sector where the first cluster is located. It will be used * to translate cluster addresses to sector addresses * * For TSK_FS_TYPE_FAT32, the first cluster is the start of the data area and * it is after the root directory for TSK_FS_TYPE_FAT12 and TSK_FS_TYPE_FAT16. At this * point in the program, numroot is set to 0 for TSK_FS_TYPE_FAT32 */ fatfs->firstclustsect = fatfs->firstdatasect + ((fatfs->numroot * 32 + fatfs->ssize - 1) / fatfs->ssize); /* total number of clusters */ fatfs->clustcnt = (sectors - fatfs->firstclustsect) / fatfs->csize; /* the first cluster is #2, so the final cluster is: */ fatfs->lastclust = 1 + fatfs->clustcnt; /* identify the FAT type by the total number of data clusters * this calculation is from the MS FAT Overview Doc * * A FAT file system made by another OS could use different values */ if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT_DETECT) { if (fatfs->clustcnt < 4085) { fatfs->fs_info.ftype = TSK_FS_TYPE_FAT12; } else if (fatfs->clustcnt < 65525) { fatfs->fs_info.ftype = TSK_FS_TYPE_FAT16; } else { fatfs->fs_info.ftype = TSK_FS_TYPE_FAT32; } fatfs->fs_info.ftype = fatfs->fs_info.ftype; } /* Some sanity checks */ else { if ((fatfs->fs_info.ftype == TSK_FS_TYPE_FAT12) && (fatfs->clustcnt >= 4085)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr ("Too many sectors for TSK_FS_TYPE_FAT12: try auto-detect mode"); if (tsk_verbose) fprintf(stderr, "%s: Too many sectors for FAT12\n", func_name); return 1; } } if ((fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32) && (fatfs->numroot != 0)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr ("Invalid TSK_FS_TYPE_FAT32 image (numroot != 0)"); if (tsk_verbose) fprintf(stderr, "%s: numroom != 0 for FAT32\n", func_name); return 1; } if ((fatfs->fs_info.ftype != TSK_FS_TYPE_FAT32) && (fatfs->numroot == 0)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr ("Invalid FAT image (numroot == 0, and not TSK_FS_TYPE_FAT32)"); if (tsk_verbose) fprintf(stderr, "%s: numroom == 0 and not FAT32\n", func_name); return 1; } /* additional sanity checks if we think we are using the backup boot sector. * The scenario to prevent here is if fat_open is called 6 sectors before the real start * of the file system, then we want to detect that it was not a backup that we saw. */ if (fatfs->using_backup_boot_sector) { // only FAT32 has backup boot sectors.. if (fatfs->fs_info.ftype != TSK_FS_TYPE_FAT32) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr ("Invalid FAT image (Used what we thought was a backup boot sector, but it is not TSK_FS_TYPE_FAT32)"); if (tsk_verbose) fprintf(stderr, "%s: Had to use backup boot sector, but this isn't FAT32\n", func_name); return 1; } if (fatfs->numroot > 1) { uint8_t buf1[512]; uint8_t buf2[512]; int i2; int numDiffs; ssize_t cnt = 0; cnt = tsk_fs_read(fs, fatfs->firstfatsect * fatfs->ssize, (char *) buf1, 512); if (cnt != 512) { if (cnt >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_READ); } tsk_error_set_errstr2("%s: FAT1", func_name); fs->tag = 0; return 1; } cnt = tsk_fs_read(fs, (fatfs->firstfatsect + fatfs->sectperfat) * fatfs->ssize, (char *) buf2, 512); if (cnt != 512) { if (cnt >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_READ); } tsk_error_set_errstr2("%s: FAT2", func_name); fs->tag = 0; return 1; } numDiffs = 0; for (i2 = 0; i2 < 512; i2++) { if (buf1[i2] != buf2[i2]) { numDiffs++; } } if (numDiffs > 25) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr ("Invalid FAT image (Too many differences between FATS from guessing (%d diffs))", numDiffs); if (tsk_verbose) fprintf(stderr, "%s: Too many differences in FAT from guessing (%d diffs)\n", func_name, numDiffs); return 1; } } } /* Set the mask to use on the cluster values */ if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT12) { fatfs->mask = FATFS_12_MASK; } else if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT16) { fatfs->mask = FATFS_16_MASK; } else if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32) { fatfs->mask = FATFS_32_MASK; } else { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("Unknown FAT type in %s: %d\n", func_name, fatfs->fs_info.ftype); return 1; } fs->duname = "Sector"; /* the root directories are always after the FAT for TSK_FS_TYPE_FAT12 and TSK_FS_TYPE_FAT16, * but are dynamically located for TSK_FS_TYPE_FAT32 */ if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32) fatfs->rootsect = FATFS_CLUST_2_SECT(fatfs, tsk_getu32(fs->endian, fatsb->a.f32.rootclust)); else fatfs->rootsect = fatfs->firstdatasect; for (i = 0; i < FATFS_FAT_CACHE_N; i++) { fatfs->fatc_addr[i] = 0; fatfs->fatc_ttl[i] = 0; } /* * block calculations : although there are no blocks in fat, we will * use these fields for sector calculations */ fs->first_block = 0; fs->block_count = sectors; fs->last_block = fs->last_block_act = fs->block_count - 1; fs->block_size = fatfs->ssize; // determine the last block we have in this image if ((TSK_DADDR_T) ((fatfs->fs_info.img_info->size - fatfs->fs_info.offset) / fs->block_size) < fs->block_count) fs->last_block_act = (fatfs->fs_info.img_info->size - fatfs->fs_info.offset) / fs->block_size - 1; /* * inode calculations */ /* maximum number of dentries in a sector & cluster */ fatfs->dentry_cnt_se = fatfs->ssize / sizeof(FATXXFS_DENTRY); fatfs->dentry_cnt_cl = fatfs->dentry_cnt_se * fatfs->csize; fs->root_inum = FATFS_ROOTINO; fs->first_inum = FATFS_FIRSTINO; /* Calculate inode addresses for the virtual files (MBR, one or two FATS) * and the virtual orphan files directory. */ fs->last_inum = (FATFS_SECT_2_INODE(fatfs, fs->last_block_act + 1) - 1) + FATFS_NUM_VIRT_FILES(fatfs); fatfs->mbr_virt_inum = fs->last_inum - FATFS_NUM_VIRT_FILES(fatfs) + 1; fatfs->fat1_virt_inum = fatfs->mbr_virt_inum + 1; if (fatfs->numfat == 2) { fatfs->fat2_virt_inum = fatfs->fat1_virt_inum + 1; } else { fatfs->fat2_virt_inum = fatfs->fat1_virt_inum; } /* Calculate the total number of inodes. */ fs->inum_count = fs->last_inum - fs->first_inum + 1; /* Volume ID */ for (fs->fs_id_used = 0; fs->fs_id_used < 4; fs->fs_id_used++) { if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32) fs->fs_id[fs->fs_id_used] = fatsb->a.f32.vol_id[fs->fs_id_used]; else fs->fs_id[fs->fs_id_used] = fatsb->a.f16.vol_id[fs->fs_id_used]; } /* * Set the function pointers */ fs->block_walk = fatfs_block_walk; fs->block_getflags = fatfs_block_getflags; fs->inode_walk = fatfs_inode_walk; fs->istat = fatfs_istat; fs->file_add_meta = fatfs_inode_lookup; fs->get_default_attr_type = fatfs_get_default_attr_type; fs->load_attrs = fatfs_make_data_runs; fs->dir_open_meta = fatfs_dir_open_meta; fs->name_cmp = fatfs_name_cmp; fs->fsstat = fatxxfs_fsstat; fs->fscheck = fatfs_fscheck; fs->close = fatfs_close; fs->jblk_walk = fatfs_jblk_walk; fs->jentry_walk = fatfs_jentry_walk; fs->jopen = fatfs_jopen; fatfs->is_cluster_alloc = fatxxfs_is_cluster_alloc; fatfs->is_dentry = fatxxfs_is_dentry; fatfs->dinode_copy = fatxxfs_dinode_copy; fatfs->inode_lookup = fatxxfs_inode_lookup; fatfs->inode_walk_should_skip_dentry = fatxxfs_inode_walk_should_skip_dentry; fatfs->istat_attr_flags = fatxxfs_istat_attr_flags; fatfs->dent_parse_buf = fatxxfs_dent_parse_buf; // initialize the caches tsk_init_lock(&fatfs->cache_lock); tsk_init_lock(&fatfs->dir_lock); fatfs->inum2par = NULL; // Test to see if this is the odd Android case where the FAT entries have no short name // // If there are no entries found with the normal short name // and we find more entries by removing the short name test for allocated directories, then assume // this is the case where we have no short names fatfs->subtype = TSK_FATFS_SUBTYPE_SPEC; test_dir1 = tsk_fs_dir_open_meta(fs, fs->root_inum); if (test_dir1 != NULL && test_dir1->names_used <= 4){ // At most four automatic directories ($MBR, $FAT1, $FAT1, $OrphanFiles) TSK_FS_DIR * test_dir2; // to see if it's the Android FAT version fatfs->subtype = TSK_FATFS_SUBTYPE_ANDROID_1; test_dir2 = tsk_fs_dir_open_meta(fs, fs->root_inum); if (test_dir2 != NULL && test_dir2->names_used > test_dir1->names_used){ fatfs->subtype = TSK_FATFS_SUBTYPE_ANDROID_1; } else{ fatfs->subtype = TSK_FATFS_SUBTYPE_SPEC; } tsk_fs_dir_close(test_dir2); } tsk_fs_dir_close(test_dir1); return 0; }
/** \internal * * return 1 on error and 0 on success */ uint8_t tsk_fs_nofs_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T a_start_blk, TSK_DADDR_T a_end_blk, TSK_FS_BLOCK_WALK_FLAG_ENUM a_flags, TSK_FS_BLOCK_WALK_CB a_action, void *a_ptr) { TSK_FS_BLOCK *fs_block; TSK_DADDR_T addr; // clean up any error messages that are lying around tsk_error_reset(); /* * Sanity checks. */ if (a_start_blk < fs->first_block || a_start_blk > fs->last_block) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); tsk_error_set_errstr("nofs_block_walk: Start block number: %" PRIuDADDR, a_start_blk); return 1; } if (a_end_blk < fs->first_block || a_end_blk > fs->last_block || a_end_blk < a_start_blk) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); tsk_error_set_errstr("nofs_block_walk: Last block number: %" PRIuDADDR, a_end_blk); return 1; } /* Sanity check on a_flags -- make sure at least one ALLOC is set */ if (((a_flags & TSK_FS_BLOCK_WALK_FLAG_ALLOC) == 0) && ((a_flags & TSK_FS_BLOCK_WALK_FLAG_UNALLOC) == 0)) { a_flags |= (TSK_FS_BLOCK_WALK_FLAG_ALLOC | TSK_FS_BLOCK_WALK_FLAG_UNALLOC); } /* All swap has is allocated blocks... exit if not wanted */ if (!(a_flags & TSK_FS_BLOCK_FLAG_ALLOC)) { return 0; } if ((fs_block = tsk_fs_block_alloc(fs)) == NULL) { return 1; } for (addr = a_start_blk; addr <= a_end_blk; addr++) { int retval; if (tsk_fs_block_get(fs, fs_block, addr) == NULL) { tsk_error_set_errstr2("nofs_block_walk: Block %" PRIuDADDR, addr); tsk_fs_block_free(fs_block); return 1; } retval = a_action(fs_block, a_ptr); if (retval == TSK_WALK_STOP) { break; } else if (retval == TSK_WALK_ERROR) { tsk_fs_block_free(fs_block); return 1; } } /* * Cleanup. */ tsk_fs_block_free(fs_block); 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; }
/** \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 * 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; }
/* * Process the partition table at the sector address * * It is loaded into the internal sorted list */ static uint8_t gpt_load_table(TSK_VS_INFO * vs) { gpt_head *head; gpt_entry *ent; dos_sect *dos_part; unsigned int i, a; uint32_t ent_size; char *safe_str, *head_str, *tab_str, *ent_buf; ssize_t cnt; char *sect_buf; TSK_DADDR_T taddr = vs->offset / vs->block_size + GPT_PART_SOFFSET; TSK_DADDR_T max_addr = (vs->img_info->size - vs->offset) / vs->block_size; // max sector if (tsk_verbose) tsk_fprintf(stderr, "gpt_load_table: Sector: %" PRIuDADDR "\n", taddr); if ((sect_buf = tsk_malloc(vs->block_size)) == NULL) return 1; dos_part = (dos_sect *) sect_buf; cnt = tsk_vs_read_block (vs, GPT_PART_SOFFSET, sect_buf, vs->block_size); /* if -1, then tsk_errno is already set */ if (cnt != vs->block_size) { if (cnt >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_READ); } tsk_error_set_errstr2 ("Error reading DOS safety partition table in Sector: %" PRIuDADDR, taddr); free(sect_buf); return 1; } /* Sanity Check */ if (tsk_vs_guessu16(vs, dos_part->magic, DOS_MAGIC)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_MAGIC); tsk_error_set_errstr ("Missing DOS safety partition (invalid magic) (Sector: %" PRIuDADDR ")", taddr); free(sect_buf); return 1; } if (dos_part->ptable[0].ptype != GPT_DOS_TYPE) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_MAGIC); tsk_error_set_errstr ("Missing DOS safety partition (invalid type in table: %d)", dos_part->ptable[0].ptype); free(sect_buf); return 1; } /* Read the GPT header */ head = (gpt_head *) sect_buf; cnt = tsk_vs_read_block (vs, GPT_PART_SOFFSET + 1, sect_buf, vs->block_size); if (cnt != vs->block_size) { if (cnt >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_READ); } tsk_error_set_errstr2("GPT Header structure in Sector: %" PRIuDADDR, taddr + 1); free(sect_buf); return 1; } if (tsk_getu64(vs->endian, &head->signature) != GPT_HEAD_SIG) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_MAGIC); tsk_error_set_errstr("GPT Header: %" PRIx64, tsk_getu64(vs->endian, &head->signature)); free(sect_buf); return 1; } // now that we checked the sig, lets make the meta entries if ((safe_str = tsk_malloc(16)) == NULL) { free(sect_buf); return 1; } snprintf(safe_str, 16, "Safety Table"); if (NULL == tsk_vs_part_add(vs, (TSK_DADDR_T) 0, (TSK_DADDR_T) 1, TSK_VS_PART_FLAG_META, safe_str, -1, -1)) { free(sect_buf); return 1; } if ((head_str = tsk_malloc(16)) == NULL) { free(sect_buf); return 1; } snprintf(head_str, 16, "GPT Header"); if (NULL == tsk_vs_part_add(vs, (TSK_DADDR_T) 1, (TSK_DADDR_T) ((tsk_getu32(vs->endian, &head->head_size_b) + (vs->block_size-1)) / vs->block_size), TSK_VS_PART_FLAG_META, head_str, -1, -1)) { free(sect_buf); return 1; } /* Allocate a buffer for each table entry */ ent_size = tsk_getu32(vs->endian, &head->tab_size_b); if (ent_size < sizeof(gpt_entry)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_MAGIC); tsk_error_set_errstr("Header reports partition entry size of %" PRIu32 " and not %" PRIuSIZE "", ent_size, sizeof(gpt_entry)); free(sect_buf); return 1; } if ((tab_str = tsk_malloc(20)) == NULL) { free(sect_buf); return 1; } snprintf(tab_str, 20, "Partition Table"); if (NULL == tsk_vs_part_add(vs, (TSK_DADDR_T) tsk_getu64(vs->endian, &head->tab_start_lba), (TSK_DADDR_T) ((ent_size * tsk_getu32(vs->endian, &head->tab_num_ent) + (vs->block_size-1)) / vs->block_size), TSK_VS_PART_FLAG_META, tab_str, -1, -1)) { free(sect_buf); return 1; } /* Process the partition table */ if ((ent_buf = tsk_malloc(vs->block_size)) == NULL) { free(sect_buf); return 1; } i = 0; for (a = 0; i < tsk_getu32(vs->endian, &head->tab_num_ent); a++) { char *name; /* Read a sector */ cnt = tsk_vs_read_block(vs, tsk_getu64(vs->endian, &head->tab_start_lba) + a, ent_buf, vs->block_size); if (cnt != vs->block_size) { if (cnt >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_READ); } tsk_error_set_errstr2 ("Error reading GPT partition table sector : %" PRIuDADDR, tsk_getu64(vs->endian, &head->tab_start_lba) + a); free(ent_buf); free(sect_buf); return 1; } /* Process the sector */ ent = (gpt_entry *) ent_buf; for (; (uintptr_t) ent < (uintptr_t) ent_buf + vs->block_size && i < tsk_getu32(vs->endian, &head->tab_num_ent); i++) { UTF16 *name16; UTF8 *name8; int retVal; if (tsk_verbose) tsk_fprintf(stderr, "gpt_load: %d Starting Sector: %" PRIu64 " End: %" PRIu64 " Flag: %" PRIx64 "\n", i, tsk_getu64(vs->endian, ent->start_lba), tsk_getu64(vs->endian, ent->end_lba), tsk_getu64(vs->endian, ent->flags)); if (tsk_getu64(vs->endian, ent->start_lba) == 0) { ent++; continue; } // make sure the first couple are in the image bounds if ((i < 2) && (tsk_getu64(vs->endian, ent->start_lba) > max_addr)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_BLK_NUM); tsk_error_set_errstr ("gpt_load_table: Starting sector too large for image"); free(sect_buf); free(ent_buf); return 1; } if ((name = tsk_malloc(256)) == NULL) { free(sect_buf); free(ent_buf); return 1; } name16 = (UTF16 *) ((uintptr_t) ent->name); name8 = (UTF8 *) name; retVal = tsk_UTF16toUTF8(vs->endian, (const UTF16 **) &name16, (UTF16 *) ((uintptr_t) name16 + sizeof(ent->name)), &name8, (UTF8 *) ((uintptr_t) name8 + 256), TSKlenientConversion); if (retVal != TSKconversionOK) { if (tsk_verbose) tsk_fprintf(stderr, "gpt_load_table: Error converting name to UTF8: %d\n", retVal); *name = '\0'; } if (NULL == tsk_vs_part_add(vs, (TSK_DADDR_T) tsk_getu64(vs->endian, ent->start_lba), (TSK_DADDR_T) (tsk_getu64(vs->endian, ent->end_lba) - tsk_getu64(vs->endian, ent->start_lba) + 1), TSK_VS_PART_FLAG_ALLOC, name, -1, i)) { free(sect_buf); free(ent_buf); return 1; } ent++; } } free(sect_buf); free(ent_buf); return 0; }
/* * Load an extended partition table into the structure in TSK_VS_INFO. * * sect_cur: The sector where the extended table is located * sect_ext_base: The sector of the primary extended table (this does * not change for recursive calls) * table: a counter that identifies the table depth * (increases by 1 for each recursive call) * * For the primary extended table, sect_cur == sect_ext_base * * Return 1 on error and 0 on success * */ static uint8_t dos_load_ext_table(TSK_VS_INFO * vs, TSK_DADDR_T sect_cur, TSK_DADDR_T sect_ext_base, int table) { dos_sect *sect; char *sect_buf; int i; char *table_str; ssize_t cnt; TSK_DADDR_T max_addr = (vs->img_info->size - vs->offset) / vs->block_size; // max sector if (tsk_verbose) tsk_fprintf(stderr, "dos_load_ext: Table Sector: %" PRIuDADDR ", Primary Base Sector: %" PRIuDADDR "\n", sect_cur, sect_ext_base); if ((sect_buf = tsk_malloc(vs->block_size)) == NULL) return 1; sect = (dos_sect *) sect_buf; /* Read the partition table sector */ cnt = tsk_vs_read_block(vs, sect_cur, sect_buf, vs->block_size); if (cnt != vs->block_size) { if (cnt >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_READ); } tsk_error_set_errstr2("Extended DOS table sector %" PRIuDADDR, sect_cur); free(sect_buf); return 1; } /* Sanity Check */ if (tsk_getu16(vs->endian, sect->magic) != DOS_MAGIC) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_MAGIC); tsk_error_set_errstr("Extended DOS partition table in sector %" PRIuDADDR, sect_cur); free(sect_buf); return 1; } /* Add an entry of 1 length for the table to the internal structure */ if ((table_str = tsk_malloc(32)) == NULL) { free(sect_buf); return 1; } snprintf(table_str, 32, "Extended Table (#%d)", table); if (NULL == tsk_vs_part_add(vs, (TSK_DADDR_T) sect_cur, (TSK_DADDR_T) 1, TSK_VS_PART_FLAG_META, table_str, table, -1)) { free(sect_buf); return 1; } /* Cycle through the four partitions in the table * * When another extended partition is found, it is processed * inside of the loop */ for (i = 0; i < 4; i++) { dos_part *part = §->ptable[i]; /* Get the starting sector and size, we currently * ignore CHS */ uint32_t part_start = tsk_getu32(vs->endian, part->start_sec); uint32_t part_size = tsk_getu32(vs->endian, part->size_sec); if (tsk_verbose) tsk_fprintf(stderr, "load_ext: %d:%d Start: %" PRIu32 " Size: %" PRIu32 " Type: %d\n", table, i, part_start, part_size, part->ptype); if (part_size == 0) continue; /* partitions are addressed differently * in extended partitions */ if (dos_is_ext(part->ptype)) { /* part start is added to the start of the * first extended partition (the primary * extended partition) */ if (NULL == tsk_vs_part_add(vs, (TSK_DADDR_T) (sect_ext_base + part_start), (TSK_DADDR_T) part_size, TSK_VS_PART_FLAG_META, dos_get_desc(part->ptype), table, i)) { free(sect_buf); return 1; } if (sect_ext_base + part_start > max_addr) { if (tsk_verbose) tsk_fprintf(stderr, "Starting sector %" PRIuDADDR " of extended partition too large for image\n", sect_ext_base + part_start); } /* Process the extended partition */ else if (dos_load_ext_table(vs, sect_ext_base + part_start, sect_ext_base, table + 1)) { free(sect_buf); return 1; } } else { /* part_start is added to the start of the * current partition for the actual * starting location */ // we ignore the max_addr checks on extended partitions... if (NULL == tsk_vs_part_add(vs, (TSK_DADDR_T) (sect_cur + part_start), (TSK_DADDR_T) part_size, TSK_VS_PART_FLAG_ALLOC, dos_get_desc(part->ptype), table, i)) { free(sect_buf); return 1; } } } free(sect_buf); return 0; }