/** * Creates a new single-user case with a new database and initializes its tables. * Fails if there's already a file at the given path. * * @param path Full path to create new database at. * @returns Pointer to a new TskCaseDb object, NULL on error */ TskCaseDb * TskCaseDb::newDb(const TSK_TCHAR * const path) { TskDb *db = new TskDbSqlite(path, true); // Check if the database already exsists if (db->dbExists()) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_AUTO_DB); tsk_error_set_errstr("Database %" PRIttocTSK " already exists. Must be deleted first.", path); delete(db); return NULL; } // Open the database. if (db->open(true)) { delete(db); return NULL; } return new TskCaseDb(db); }
/** * Opens a single-user case from an existing database. * * @param path Full path to open database from. * @returns Pointer to a new TskCaseDb object, NULL on error */ TskCaseDb * TskCaseDb::openDb(const TSK_TCHAR * path) { TskDb *db = new TskDbSqlite(path, true); // Confirm that database already exsists if (!db->dbExists()) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_AUTO_DB); tsk_error_set_errstr("Database %" PRIttocTSK " does not exist. Must be created first.", path); delete(db); return NULL; } // Open the database. if (db->open(false)) { delete(db); return NULL; } return new TskCaseDb(db); }
/** * \ingroup fslib * * Open a file given its metadata address. This function loads the metadata * and returns a handle that can be used to read and process the file. Note * that the returned TSK_FS_FILE structure will not have the file name set because * it was not used to load the file and this function does not search the * directory structure to find the name that points to the address. In general, * if you know the metadata address of a file, this function is more effecient * then tsk_fs_file_open, which first maps a file name to the metadata address * and then opens the file using this function. * * @param a_fs File system to analyze * @param a_fs_file Structure to store file data in or NULL to have one allocated. * @param a_addr Metadata address of file to lookup * @returns NULL on error */ TSK_FS_FILE * tsk_fs_file_open_meta(TSK_FS_INFO * a_fs, TSK_FS_FILE * a_fs_file, TSK_INUM_T a_addr) { TSK_FS_FILE *fs_file; if ((a_fs == NULL) || (a_fs->tag != TSK_FS_INFO_TAG)) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_file_open_meta: called with NULL or unallocated structures"); return NULL; } fs_file = a_fs_file; if (fs_file == NULL) { if ((fs_file = tsk_fs_file_alloc(a_fs)) == NULL) return NULL; } else { /* if the structure passed has a name structure, free it * because we won't use it. */ if (fs_file->name) { tsk_fs_name_free(fs_file->name); fs_file->name = NULL; } // reset the rest of it tsk_fs_file_reset(fs_file); } if (a_fs->file_add_meta(a_fs, fs_file, a_addr)) { if (a_fs_file == NULL) free(fs_file); return NULL; } return fs_file; }
/** * \internal * Copy resident data to an attribute. * * @param a_fs_attr Attribute to add data to (cannot be NULL) * @param name Name of the attribute to set * @param type Type of the attribute to set * @param id Id of the attribute to set * @param res_data Pointer to where resident data is located (data will * be copied from here into FS_DATA) * @param len Length of resident data * @return 1 on error and 0 on success */ uint8_t tsk_fs_attr_set_str(TSK_FS_FILE * a_fs_file, TSK_FS_ATTR * a_fs_attr, const char *name, TSK_FS_ATTR_TYPE_ENUM type, uint16_t id, void *res_data, size_t len) { if (a_fs_attr == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("Null fs_attr in tsk_fs_attr_set_str"); return 1; } a_fs_attr->fs_file = a_fs_file; a_fs_attr->flags = (TSK_FS_ATTR_INUSE | TSK_FS_ATTR_RES); a_fs_attr->type = type; a_fs_attr->id = id; a_fs_attr->nrd.compsize = 0; if (fs_attr_put_name(a_fs_attr, name)) { return 1; } if (a_fs_attr->rd.buf_size < len) { a_fs_attr->rd.buf = (uint8_t *) tsk_realloc((char *) a_fs_attr->rd.buf, len); if (a_fs_attr->rd.buf == NULL) return 1; a_fs_attr->rd.buf_size = len; } memset(a_fs_attr->rd.buf, 0, a_fs_attr->rd.buf_size); memcpy(a_fs_attr->rd.buf, res_data, len); a_fs_attr->size = len; return 0; }
/** * \internal * Allocates and initializes a new structure. * * @param type The type of attribute to create (Resident or Non-resident) * @returns NULL on error */ TSK_FS_ATTR * tsk_fs_attr_alloc(TSK_FS_ATTR_FLAG_ENUM type) { TSK_FS_ATTR *fs_attr = (TSK_FS_ATTR *) tsk_malloc(sizeof(TSK_FS_ATTR)); if (fs_attr == NULL) { return NULL; } fs_attr->name_size = 128; if ((fs_attr->name = (char *) tsk_malloc(fs_attr->name_size)) == NULL) { free(fs_attr); return NULL; } if (type == TSK_FS_ATTR_NONRES) { fs_attr->flags = (TSK_FS_ATTR_NONRES | TSK_FS_ATTR_INUSE); } else if (type == TSK_FS_ATTR_RES) { fs_attr->rd.buf_size = 1024; fs_attr->rd.buf = (uint8_t *) tsk_malloc(fs_attr->rd.buf_size); if (fs_attr->rd.buf == NULL) { free(fs_attr->name); return NULL; } fs_attr->flags = (TSK_FS_ATTR_RES | TSK_FS_ATTR_INUSE); } else { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("tsk_fs_attr_alloc: Invalid Type: %d\n", type); return NULL; } return fs_attr; }
/** \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 * Processes a resident TSK_FS_ATTR structure and calls the callback with the associated * data. The size of the buffer in the callback will be block_size at max. * * @param a_fs File system being analyzed * @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_res(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; int myflags; int retval; size_t buf_len = 0; TSK_OFF_T off; size_t read_len; TSK_FS_INFO *fs; fs = fs_attr->fs_file->fs_info; if ((fs_attr->flags & TSK_FS_ATTR_RES) == 0) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_file_walk_res: called with non-resident data"); return 1; } /* Allocate a buffer that is at most a block size in length */ buf_len = (size_t) fs_attr->size; if (buf_len > fs->block_size) buf_len = fs->block_size; if ((a_flags & TSK_FS_FILE_WALK_FLAG_AONLY) == 0) { if ((buf = tsk_malloc(buf_len)) == NULL) { return 1; } } myflags = TSK_FS_BLOCK_FLAG_CONT | TSK_FS_BLOCK_FLAG_ALLOC | TSK_FS_BLOCK_FLAG_RES; // Call the callback in (at max) block-sized chunks. retval = TSK_WALK_CONT; for (off = 0; off < fs_attr->size; off += read_len) { if (fs_attr->size - off > buf_len) read_len = buf_len; else read_len = (size_t) (fs_attr->size - off); if (buf) { // wipe rest of buffer if we are not going to read into all of it if (read_len != buf_len) memset(&buf[read_len], 0, buf_len - read_len); memcpy(buf, &fs_attr->rd.buf[off], read_len); } retval = a_action(fs_attr->fs_file, off, 0, buf, read_len, myflags, a_ptr); if (retval != TSK_WALK_CONT) break; } if (buf) free(buf); if (retval == TSK_WALK_ERROR) return 1; else return 0; }
/** * 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; }
/** \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 ffs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir, TSK_INUM_T a_addr) { TSK_OFF_T size; FFS_INFO *ffs = (FFS_INFO *) a_fs; char *dirbuf; int nchnk, cidx; TSK_FS_LOAD_FILE load_file; TSK_FS_DIR *fs_dir; /* If we get corruption in one of the blocks, then continue processing. * retval_final will change when corruption is detected. Errors are * returned immediately. */ TSK_RETVAL_ENUM retval_tmp; TSK_RETVAL_ENUM retval_final = TSK_OK; 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("ffs_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 ("ffs_dir_open_meta: NULL fs_attr argument given"); return TSK_ERR; } if (tsk_verbose) tsk_fprintf(stderr, "ffs_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); } if ((fs_dir->fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_addr)) == NULL) { tsk_error_reset(); tsk_error_errstr2_concat("- ffs_dir_open_meta"); return TSK_COR; } /* make a copy of the directory contents that we can process */ /* round up cause we want the slack space too */ size = roundup(fs_dir->fs_file->meta->size, FFS_DIRBLKSIZ); if ((dirbuf = tsk_malloc((size_t) size)) == NULL) { return TSK_ERR; } load_file.total = load_file.left = (size_t) size; load_file.base = load_file.cur = dirbuf; if (tsk_fs_file_walk(fs_dir->fs_file, TSK_FS_FILE_WALK_FLAG_SLACK, tsk_fs_load_file_action, (void *) &load_file)) { tsk_error_reset(); tsk_error_errstr2_concat("- ffs_dir_open_meta"); free(dirbuf); return TSK_COR; } /* Not all of the directory was copied, so we return */ if (load_file.left > 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_FWALK); tsk_error_set_errstr("ffs_dir_open_meta: Error reading directory %" PRIuINUM, a_addr); free(dirbuf); return TSK_COR; } /* Directory entries are written in chunks of DIRBLKSIZ ** determine how many chunks of this size we have to read to ** get a full block ** ** Entries do not cross over the DIRBLKSIZ boundary */ nchnk = (int) (size) / (FFS_DIRBLKSIZ) + 1; for (cidx = 0; cidx < nchnk && (int64_t) size > 0; cidx++) { int len = (FFS_DIRBLKSIZ < size) ? FFS_DIRBLKSIZ : (int) size; retval_tmp = ffs_dent_parse_block(ffs, fs_dir, (fs_dir->fs_file-> meta->flags & TSK_FS_META_FLAG_UNALLOC) ? 1 : 0, dirbuf + cidx * FFS_DIRBLKSIZ, len); if (retval_tmp == TSK_ERR) { retval_final = TSK_ERR; break; } else if (retval_tmp == TSK_COR) { retval_final = TSK_COR; } size -= len; } free(dirbuf); // 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_final; }
/** \ingroup fslib * Walk the file names in a directory and obtain the details of the files via a callback. * * @param a_fs File system to analyze * @param a_addr Metadata address of the directory to analyze * @param a_flags Flags used during analysis * @param a_action Callback function that is called for each file name * @param a_ptr Pointer to data that is passed to the callback function each time * @returns 1 on error and 0 on success */ uint8_t tsk_fs_dir_walk(TSK_FS_INFO * a_fs, TSK_INUM_T a_addr, TSK_FS_DIR_WALK_FLAG_ENUM a_flags, TSK_FS_DIR_WALK_CB a_action, void *a_ptr) { DENT_DINFO dinfo; TSK_WALK_RET_ENUM retval; if ((a_fs == NULL) || (a_fs->tag != TSK_FS_INFO_TAG)) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_dir_walk: called with NULL or unallocated structures"); return 1; } memset(&dinfo, 0, sizeof(DENT_DINFO)); if ((dinfo.stack_seen = tsk_stack_create()) == NULL) return 1; /* Sanity check on flags -- make sure at least one ALLOC is set */ if (((a_flags & TSK_FS_DIR_WALK_FLAG_ALLOC) == 0) && ((a_flags & TSK_FS_DIR_WALK_FLAG_UNALLOC) == 0)) { a_flags |= (TSK_FS_DIR_WALK_FLAG_ALLOC | TSK_FS_DIR_WALK_FLAG_UNALLOC); } /* if the flags are right, we can collect info that may be needed * for an orphan walk. If the walk fails or stops, the code that * calls the action will clear this stuff. */ tsk_take_lock(&a_fs->list_inum_named_lock); if ((a_fs->list_inum_named == NULL) && (a_addr == a_fs->root_inum) && (a_flags & TSK_FS_DIR_WALK_FLAG_RECURSE)) { dinfo.save_inum_named = 1; } tsk_release_lock(&a_fs->list_inum_named_lock); retval = tsk_fs_dir_walk_lcl(a_fs, &dinfo, a_addr, a_flags, a_action, a_ptr); if (dinfo.save_inum_named == 1) { if (retval != TSK_WALK_CONT) { /* There was an error and we stopped early, so we should get * rid of the partial list we were making. */ tsk_list_free(dinfo.list_inum_named); dinfo.list_inum_named = NULL; } else { /* We finished the dir walk successfully, so reassign * ownership of the dinfo's list_inum_named to the shared * list_inum_named in TSK_FS_INFO, under a lock, if * another thread hasn't already done so. */ tsk_take_lock(&a_fs->list_inum_named_lock); if (a_fs->list_inum_named == NULL) { a_fs->list_inum_named = dinfo.list_inum_named; } else { tsk_list_free(dinfo.list_inum_named); } tsk_release_lock(&a_fs->list_inum_named_lock); dinfo.list_inum_named = NULL; } } tsk_stack_free(dinfo.stack_seen); if (retval == TSK_WALK_ERROR) return 1; else return 0; }
/** * Find the corresponding name at the * 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. * * Note: This routine assumes that &hdb_info->lock is locked by the caller. * * @param hdb_info Data base to get data from. * @param hash MD5 hash value that was searched for * @param offset Byte offset where hash value should be located in db_file * @param flags * @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 hk_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[TSK_HDB_MAXLEN], *ptr = NULL, pname[TSK_HDB_MAXLEN], other[TSK_HDB_MAXLEN]; int found = 0; if (tsk_verbose) fprintf(stderr, "hk_getentry: Lookup up hash %s at offset %" PRIuOFF "\n", hash, offset); if (strlen(hash) != TSK_HDB_HTYPE_MD5_LEN) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_ARG); tsk_error_set_errstr( "hk_getentry: Invalid hash value: %s", hash); return 1; } memset(pname, '0', TSK_HDB_MAXLEN); /* Loop so that we can find multiple 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( "hk_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( "hk_getentry: Error reading database"); return 1; } len = strlen(buf); if (len < TSK_HDB_HTYPE_MD5_LEN) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_CORRUPT); tsk_error_set_errstr( "hk_getentry: Invalid entry in database (too short): %s", buf); return 1; } if (hk_parse_md5(buf, &ptr, name, TSK_HDB_MAXLEN, ((flags & TSK_HDB_FLAG_EXT) ? other : NULL), ((flags & TSK_HDB_FLAG_EXT) ? TSK_HDB_MAXLEN : 0))) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_CORRUPT); tsk_error_set_errstr( "hk_getentry: Invalid entry in database: %s", buf); return 1; } /* Is this the one that we want? */ if (0 != strcasecmp(ptr, hash)) { break; } if (strcmp(name, pname) != 0) { int retval; retval = action(hdb_info, hash, name, cb_ptr); if (retval == TSK_WALK_ERROR) { return 1; } else if (retval == TSK_WALK_STOP) { return 0; } //if (flags & FLAG_EXT) // printf("%s\t%s\t(%s)\n", hash, name, other); // else // printf("%s\t%s\n", hash, name); 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( "hk_getentry: Hash not found in file at offset: %lu", (unsigned long) offset); return 1; } return 0; }
/** * /internal * Parse a buffer containing the contents of a directory and add TSK_FS_NAME * objects for each named file found to the TSK_FS_DIR representation of the * directory. * * @param fatfs File system information structure for file system that * contains the directory. * @param a_fs_dir Directory structure into to which parsed file metadata will * be added. * @param buf Buffer that contains the directory contents. * @param len Length of buffer in bytes (must be a multiple of sector * size). * @param addrs Array where each element is the original address of * the corresponding sector in a_buf (size of array is number of sectors in * the directory). * @return TSK_RETVAL_ENUM */ TSK_RETVAL_ENUM fatxxfs_dent_parse_buf(FATFS_INFO *fatfs, TSK_FS_DIR *a_fs_dir, char *buf, TSK_OFF_T len, TSK_DADDR_T *addrs) { char *func_name = "fatxxfs_dent_parse_buf"; unsigned int idx = 0; unsigned int sidx = 0; int a = 0; int b = 0; TSK_INUM_T ibase = 0; FATXXFS_DENTRY *dep = NULL; TSK_FS_INFO *fs = (TSK_FS_INFO*)&fatfs->fs_info; int sectalloc = 0; TSK_FS_NAME *fs_name = NULL; FATXXFS_LFN lfninfo; int entrySeenCount = 0; int entryInvalidCount = 0; uint8_t isCorruptDir = 0; tsk_error_reset(); if (fatfs_ptr_arg_is_null(fatfs, "fatfs", func_name) || fatfs_ptr_arg_is_null(a_fs_dir, "a_fs_dir", func_name) || fatfs_ptr_arg_is_null(buf, "buf", func_name) || fatfs_ptr_arg_is_null(addrs, "addrs", func_name)) { return TSK_ERR; } assert(len > 0); if (len < 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("%s: invalid buffer length", func_name); return TSK_ERR; } dep = (FATXXFS_DENTRY*)buf; if ((fs_name = tsk_fs_name_alloc(FATFS_MAXNAMLEN_UTF8, 32)) == NULL) { return TSK_ERR; } memset(&lfninfo, 0, sizeof(FATXXFS_LFN)); lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1; /* Loop through the sectors in the buffer. */ for (sidx = 0; sidx < (unsigned int) (len / fatfs->ssize); sidx++) { /* Get the base inode for the current sector */ ibase = FATFS_SECT_2_INODE(fatfs, addrs[sidx]); if (ibase > fs->last_inum) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("fatfs_parse: inode address is too large"); tsk_fs_name_free(fs_name); return TSK_COR; } if (tsk_verbose) tsk_fprintf(stderr, "fatfs_dent_parse_buf: Parsing sector %" PRIuDADDR " for dir %" PRIuINUM "\n", addrs[sidx], a_fs_dir->addr); /* Get the allocation status of the current sector. */ if ((sectalloc = fatfs_is_sectalloc(fatfs, addrs[sidx])) == -1) { if (tsk_verbose) { tsk_fprintf(stderr, "fatfs_dent_parse_buf: Error looking up sector allocation: %" PRIuDADDR "\n", addrs[sidx]); tsk_error_print(stderr); } tsk_error_reset(); continue; } /* Loop through the putative directory entries in the current sector. */ for (idx = 0; idx < fatfs->dentry_cnt_se; idx++, dep++) { FATXXFS_DENTRY *dir; TSK_INUM_T inode; entrySeenCount++; /* Is the current entry a valid entry? */ if (0 == fatxxfs_is_dentry(fatfs, (FATFS_DENTRY*)dep, (FATFS_DATA_UNIT_ALLOC_STATUS_ENUM)sectalloc, ((isCorruptDir == 0) && (sectalloc)) ? 1 : 0)) { if (tsk_verbose) tsk_fprintf(stderr, "fatfs_dent_parse_buf: Entry %u is invalid\n", idx); entryInvalidCount++; /* If we have seen four entries and all of them are corrupt, * then test every remaining entry in this folder -- * even if the sector is allocated. The scenario is one * where we are processing a cluster that is allocated * to a file and we happen to get some data that matches * every now and then. */ if ((entrySeenCount == 4) && (entryInvalidCount == 4)) { isCorruptDir = 1; } continue; } dir = dep; /* Compute the inode address corresponding to this directory entry. */ inode = ibase + idx; if ((dir->attrib & FATFS_ATTR_LFN) == FATFS_ATTR_LFN) { /* The current entry is a long file name entry. */ FATXXFS_DENTRY_LFN *dirl = (FATXXFS_DENTRY_LFN *) dir; /* Store the name in dinfo until we get the 8.3 name * Use the checksum to identify a new sequence. */ if (((dirl->seq & FATXXFS_LFN_SEQ_FIRST) && (dirl->seq != FATXXFS_SLOT_DELETED)) || (dirl->chksum != lfninfo.chk)) { // @@@ Do a partial output here /* This is the last long file name entry in a sequence. * Reset the sequence number, check sum, and next char * address. */ lfninfo.seq = dirl->seq & FATXXFS_LFN_SEQ_MASK; lfninfo.chk = dirl->chksum; lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1; } else if (dirl->seq != lfninfo.seq - 1) { // @@@ Check the sequence number - the checksum is correct though... } /* Copy the UTF16 values starting at end of buffer */ for (a = 3; a >= 0; a--) { if ((lfninfo.start > 0)) lfninfo.name[lfninfo.start--] = dirl->part3[a]; } for (a = 11; a >= 0; a--) { if ((lfninfo.start > 0)) lfninfo.name[lfninfo.start--] = dirl->part2[a]; } for (a = 9; a >= 0; a--) { if ((lfninfo.start > 0)) lfninfo.name[lfninfo.start--] = dirl->part1[a]; } // Skip ahead until we get a new sequence num or the 8.3 name continue; } else if ((dir->attrib & FATFS_ATTR_VOLUME) == FATFS_ATTR_VOLUME) { /* Special case for volume label: name does not have an * extension and we add a note at the end that it is a label */ a = 0; for (b = 0; b < 8; b++) { if ((dir->name[b] >= 0x20) && (dir->name[b] != 0xff)) { fs_name->name[a++] = dir->name[b]; } else { fs_name->name[a++] = '^'; } } for (b = 0; b < 3; b++) { if ((dir->ext[b] >= 0x20) && (dir->ext[b] != 0xff)) { fs_name->name[a++] = dir->ext[b]; } else { fs_name->name[a++] = '^'; } } fs_name->name[a] = '\0'; /* Append a string to show it is a label */ if (a + 22 < FATFS_MAXNAMLEN_UTF8) { const char *volstr = " (Volume Label Entry)"; strncat(fs_name->name, volstr, FATFS_MAXNAMLEN_UTF8 - a); } } else { /* A short (8.3) entry */ char *name_ptr; // The dest location for the short name /* if we have a lfn, copy it into fs_name->name * and put the short name in fs_name->shrt_name */ if (lfninfo.start != FATFS_MAXNAMLEN_UTF8 - 1) { int retVal; /* @@@ Check the checksum */ /* Convert the UTF16 to UTF8 */ UTF16 *name16 = (UTF16 *) ((uintptr_t) & lfninfo. name[lfninfo.start + 1]); UTF8 *name8 = (UTF8 *) fs_name->name; retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16, (UTF16 *) & lfninfo.name[FATFS_MAXNAMLEN_UTF8], &name8, (UTF8 *) ((uintptr_t) name8 + FATFS_MAXNAMLEN_UTF8), TSKlenientConversion); if (retVal != TSKconversionOK) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_UNICODE); tsk_error_set_errstr ("fatfs_parse: Error converting FAT LFN to UTF8: %d", retVal); continue; } /* Make sure it is NULL Terminated */ if ((uintptr_t) name8 > (uintptr_t) fs_name->name + FATFS_MAXNAMLEN_UTF8) fs_name->name[FATFS_MAXNAMLEN_UTF8 - 1] = '\0'; else *name8 = '\0'; lfninfo.start = FATFS_MAXNAMLEN_UTF8 - 1; name_ptr = fs_name->shrt_name; // put 8.3 into shrt_name } /* We don't have a LFN, so put the short name in * fs_name->name */ else { fs_name->shrt_name[0] = '\0'; name_ptr = fs_name->name; // put 8.3 into normal location } /* copy in the short name into the place specified above. * Skip spaces and put in the . */ a = 0; for (b = 0; b < 8; b++) { if ((dir->name[b] != 0) && (dir->name[b] != 0xff) && (dir->name[b] != 0x20)) { if ((b == 0) && (dir->name[0] == FATXXFS_SLOT_DELETED)) { name_ptr[a++] = '_'; } else if ((dir->lowercase & FATXXFS_CASE_LOWER_BASE) && (dir->name[b] >= 'A') && (dir->name[b] <= 'Z')) { name_ptr[a++] = dir->name[b] + 32; } else { name_ptr[a++] = dir->name[b]; } } } for (b = 0; b < 3; b++) { if ((dir->ext[b] != 0) && (dir->ext[b] != 0xff) && (dir->ext[b] != 0x20)) { if (b == 0) name_ptr[a++] = '.'; if ((dir->lowercase & FATXXFS_CASE_LOWER_EXT) && (dir->ext[b] >= 'A') && (dir->ext[b] <= 'Z')) name_ptr[a++] = dir->ext[b] + 32; else name_ptr[a++] = dir->ext[b]; } } name_ptr[a] = '\0'; // make sure that only ASCII is in the short name fatfs_cleanup_ascii(name_ptr); } /* file type: FAT only knows DIR and FILE */ if ((dir->attrib & FATFS_ATTR_DIRECTORY) == FATFS_ATTR_DIRECTORY) fs_name->type = TSK_FS_NAME_TYPE_DIR; else fs_name->type = TSK_FS_NAME_TYPE_REG; /* set the inode */ fs_name->meta_addr = inode; inode = 0; // so that we don't use it anymore -- use only fs_name->meta_addr /* Handle the . and .. entries specially * The current inode 'address' they have is for the current * slot in the cluster, but it needs to refer to the original * slot */ if (TSK_FS_ISDOT(fs_name->name) && (fs_name->type == TSK_FS_NAME_TYPE_DIR) && idx < 2) { if (fs_name->name[1] == '\0') { /* Current directory - "." */ fs_name->meta_addr = a_fs_dir->fs_file->meta->addr; } /* for the parent directory, look up in the list that * is maintained in fafs_info */ else if (fs_name->name[1] == '.') { /* Parent directory - ".." */ uint8_t dir_found = 0; if (fatfs_dir_buf_get(fatfs, a_fs_dir->fs_file->meta->addr, &(fs_name->meta_addr)) == 0) { dir_found = 1; } if ((dir_found == 0) && (addrs[0] == fatfs->firstdatasect)) { /* if we are currently in the root directory, we aren't going to find * a parent. This shouldn't happen, but could result in an infinite loop. */ fs_name->meta_addr = 0; dir_found = 1; } if (dir_found == 0) { if (tsk_verbose) fprintf(stderr, "fatfs_dent_parse_buf: Walking directory to find parent\n"); /* The parent directory is not in the list. We are going to walk * the directory until we hit this directory. This process will * populate the buffer table and we will then rescan it */ if (tsk_fs_dir_walk(fs, fs->root_inum, (TSK_FS_DIR_WALK_FLAG_ENUM)(TSK_FS_DIR_WALK_FLAG_ALLOC | TSK_FS_DIR_WALK_FLAG_UNALLOC | TSK_FS_DIR_WALK_FLAG_RECURSE), fatfs_find_parent_act, (void *) &a_fs_dir->fs_file->meta->addr)) { return TSK_OK; } if (tsk_verbose) fprintf(stderr, "fatfs_dent_parse_buf: Finished walking directory to find parent\n"); if (fatfs_dir_buf_get(fatfs, a_fs_dir->fs_file->meta->addr, &(fs_name->meta_addr)) == 0) { dir_found = 1; } // if we did not find it, then it was probably // from the orphan directory... if (dir_found == 0) fs_name->meta_addr = TSK_FS_ORPHANDIR_INUM(fs); } } } else { /* Save the (non-. or ..) directory to parent directory info to local * structures so that we can later fill into the inode * info for '..' entries */ if (fs_name->type == TSK_FS_NAME_TYPE_DIR) { if (fatfs_dir_buf_add(fatfs, a_fs_dir->fs_file->meta->addr, fs_name->meta_addr)) return TSK_ERR; } } /* The allocation status of an entry is based on the allocation * status of the sector it is in and the flag. Deleted directories * do not always clear the flags of each entry */ if (sectalloc == 1) { if(FATXXFS_IS_DELETED(dep->name, fatfs)){ fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC; } else{ fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; } } else { fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC; } tsk_fs_dir_add(a_fs_dir, fs_name); } } tsk_fs_name_free(fs_name); return TSK_OK; }
TSK_RETVAL_ENUM fatfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir, TSK_INUM_T a_addr) { TSK_OFF_T size, len; FATFS_INFO *fatfs = (FATFS_INFO *) a_fs; char *dirbuf; TSK_DADDR_T *addrbuf; FATFS_LOAD_DIR load; int retval; TSK_FS_DIR *fs_dir; 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("fatfs_dir_open_meta: invalid a_addr value: %" PRIuINUM "\n", a_addr); return TSK_ERR; } else if (a_fs_dir == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("fatfs_dir_open_meta: NULL fs_attr argument given"); return TSK_ERR; } 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("fatfs_dir_open_meta: %" PRIuINUM " is not a valid inode", a_addr); return TSK_COR; } size = fs_dir->fs_file->meta->size; len = roundup(size, fatfs->ssize); if (tsk_verbose) tsk_fprintf(stderr, "fatfs_dir_open_meta: Processing directory %" PRIuINUM "\n", a_addr); if (size == 0) { if (tsk_verbose) tsk_fprintf(stderr, "fatfs_dir_open_meta: directory has 0 size\n"); return TSK_OK; } /* Make a copy of the directory contents using file_walk */ if ((dirbuf = tsk_malloc((size_t) len)) == NULL) { return TSK_ERR; } load.curdirptr = dirbuf; load.dirleft = (size_t) size; /* We are going to save the address of each sector in the directory * in a stack - they are needed to determine the inode address. */ load.addrsize = (size_t) (len / fatfs->ssize); addrbuf = (TSK_DADDR_T *) tsk_malloc(load.addrsize * sizeof(TSK_DADDR_T)); if (addrbuf == NULL) { free(dirbuf); return TSK_ERR; } /* Set the variables that are used during the copy */ load.addridx = 0; load.addrbuf = addrbuf; /* save the directory contents into dirbuf */ if (tsk_fs_file_walk(fs_dir->fs_file, TSK_FS_FILE_WALK_FLAG_SLACK, fatfs_dent_action, (void *) &load)) { tsk_error_errstr2_concat("- fatfs_dir_open_meta"); free(dirbuf); free(addrbuf); return TSK_COR; } /* We did not copy the entire directory, which occurs if an error occured */ if (load.dirleft > 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_FWALK); tsk_error_set_errstr ("fatfs_dir_open_meta: Error reading directory %" PRIuINUM, a_addr); /* Free the local buffers */ free(dirbuf); free(addrbuf); return TSK_COR; } if (tsk_verbose) fprintf(stderr, "fatfs_dir_open_meta: Parsing directory %" PRIuINUM "\n", a_addr); retval = fatfs_dent_parse_buf(fatfs, fs_dir, dirbuf, len, addrbuf); free(dirbuf); free(addrbuf); // if we are listing the root directory, add the Orphan directory and special FAT file entries 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; // MBR Entry strncpy(fs_name->name, FATFS_MBRNAME, fs_name->name_size); fs_name->meta_addr = FATFS_MBRINO(a_fs); fs_name->type = TSK_FS_NAME_TYPE_VIRT; fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; if (tsk_fs_dir_add(fs_dir, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } // FAT1 Entry strncpy(fs_name->name, FATFS_FAT1NAME, fs_name->name_size); fs_name->meta_addr = FATFS_FAT1INO(a_fs); fs_name->type = TSK_FS_NAME_TYPE_VIRT; fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; if (tsk_fs_dir_add(fs_dir, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } // FAT2 Entry strncpy(fs_name->name, FATFS_FAT2NAME, fs_name->name_size); fs_name->meta_addr = FATFS_FAT2INO(a_fs); fs_name->type = TSK_FS_NAME_TYPE_VIRT; fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; if (tsk_fs_dir_add(fs_dir, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } // orphan directory 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; }
/** * \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 * Parses the MBR of an exFAT file system to obtain file system layout * information to add 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_fs_layout(FATFS_INFO *a_fatfs) { const char *func_name = "exfatfs_get_fs_layout"; TSK_FS_INFO *fs = &(a_fatfs->fs_info); EXFATFS_MASTER_BOOT_REC *exfatbs = NULL; uint64_t vol_len_in_sectors = 0; uint64_t last_sector_of_cluster_heap = 0; assert(a_fatfs != NULL); /* Get the size of the volume. It should be non-zero. */ exfatbs = (EXFATFS_MASTER_BOOT_REC*)(&a_fatfs->boot_sector_buffer); vol_len_in_sectors = tsk_getu64(fs->endian, exfatbs->vol_len_in_sectors); if (vol_len_in_sectors == 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr("Not an exFAT file system (invalid volume length)"); if (tsk_verbose) { fprintf(stderr, "%s: Invalid volume length in sectors (%" PRIu64 ")\n", func_name, vol_len_in_sectors); } return FATFS_FAIL; } /* Get the number of FATs. There will be one FAT for regular exFAT and two * FATs for TexFAT (transactional exFAT). */ a_fatfs->numfat = exfatbs->num_fats; if ((a_fatfs->numfat != 1) && (a_fatfs->numfat != 2)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr("Not an exFAT file system (number of FATs)"); if (tsk_verbose) { fprintf(stderr, "%s: Invalid number of FATs (%d)\n", func_name, a_fatfs->numfat); } return FATFS_FAIL; } /* Get the sector address of the first FAT (FAT0). * It should be non-zero and within the boundaries of the volume. * Note that if the file system is TexFAT, FAT1 will be the working copy * of the FAT and FAT0 will be the stable copy of the last known good FAT. * Therefore, the Sleuthkit should use FAT0. */ a_fatfs->firstfatsect = tsk_getu32(fs->endian, exfatbs->fat_offset); if ((a_fatfs->firstfatsect == 0) || ((uint64_t)a_fatfs->firstfatsect >= vol_len_in_sectors)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); tsk_error_set_errstr("Not an exFAT file system (invalid first FAT sector)"); if (tsk_verbose) { fprintf(stderr, "%s: Invalid first FAT sector (%" PRIuDADDR ")\n", func_name, a_fatfs->firstfatsect); } return FATFS_FAIL; } /* Get the sector address of the cluster heap (data area). It should be * after the FATs and within the boundaries of the volume. */ a_fatfs->firstdatasect = tsk_getu32(fs->endian, exfatbs->cluster_heap_offset); if ((a_fatfs->firstdatasect <= (a_fatfs->firstfatsect + (a_fatfs->sectperfat * a_fatfs->numfat) - 1)) || ((uint64_t)a_fatfs->firstdatasect >= vol_len_in_sectors)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); tsk_error_set_errstr("Not an exFAT file system (invalid first data sector"); if (tsk_verbose) { fprintf(stderr, "%s: Invalid first data sector (%" PRIuDADDR ")\n", func_name, a_fatfs->firstdatasect); } return FATFS_FAIL; } /* Unlike FAT12 and FAT16, but like FAT32, the sector address of the first * cluster (cluster #2, there is no cluster #0 or cluster #1) is the same * as the sector address of the cluster heap (data area). */ a_fatfs->firstclustsect = a_fatfs->firstdatasect; /* Get the total number of clusters. It should be non-zero, and should * define a cluster heap (data area) that is within the boundaries of the * volume. */ a_fatfs->clustcnt = tsk_getu32(fs->endian, exfatbs->cluster_cnt); last_sector_of_cluster_heap = a_fatfs->firstdatasect + (a_fatfs->clustcnt * a_fatfs->csize) - 1; if ((a_fatfs->clustcnt == 0) || (last_sector_of_cluster_heap >= vol_len_in_sectors)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); tsk_error_set_errstr("Not an exFAT file system (invalid cluster count)"); if (tsk_verbose) { fprintf(stderr, "%s: Invalid cluster count (%" PRIuDADDR ")\n", func_name, a_fatfs->clustcnt); } return FATFS_FAIL; } /* The first cluster is #2, so the final cluster is: */ a_fatfs->lastclust = 1 + a_fatfs->clustcnt; /* This bit mask is required to make the FATFS_CLUST_2_SECT macro work * for exFAT. It is the same as the FAT32 mask. */ a_fatfs->mask = EXFATFS_MASK; /* Get the sector address of the root directory. It should be within the * cluster heap (data area). */ a_fatfs->rootsect = FATFS_CLUST_2_SECT(a_fatfs, tsk_getu32(fs->endian, exfatbs->root_dir_cluster)); if ((a_fatfs->rootsect < a_fatfs->firstdatasect) || ((uint64_t)a_fatfs->rootsect > last_sector_of_cluster_heap)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); tsk_error_set_errstr("Not an exFAT file system (invalid root directory sector address)"); if (tsk_verbose) { fprintf(stderr, "%s: Invalid root directory sector address (%"PRIuDADDR")\n", func_name, a_fatfs->rootsect); } return FATFS_FAIL; } /* The number of directory entries in the root directory is not specified * in the exFAT boot sector. */ a_fatfs->numroot = 0; return FATFS_OK; }
/* convert HFS+'s UTF16 to UTF8 * replaces null characters with another character (0xfffd) * replaces slashes (permitted by HFS+ but causes problems with TSK) * with colons (generally not allowed by Mac OS X) * note that at least one directory on HFS+ volumes begins with * four nulls, so we do need to handle nulls; also, Apple chooses * to encode nulls as UTF8 \xC0\x80, which is not a valid UTF8 sequence * * @param fs the file system * @param uni the UTF16 string as a sequence of bytes * @param ulen then length of the UTF16 string in characters * @param asc a buffer to hold the UTF8 result * @param alen the length of that buffer * @param flags control some aspects of the conversion * @return 0 on success, 1 on failure; sets up to error string 1 * * HFS_U16U8_FLAG_REPLACE_SLASH if this flag is set, then slashes will be replaced * by colons. Otherwise, they will not be replaced. * * HFS_U16U8_FLAG_REPLACE_CONTROL if this flag is set, then all control characters * will be replaced by the UTF16_NULL_REPLACE character. N.B., always replaces * null characters regardless of this flag. */ uint8_t hfs_UTF16toUTF8(TSK_FS_INFO * fs, uint8_t * uni, int ulen, char *asc, int alen, uint32_t flags) { UTF8 *ptr8; uint8_t *uniclean; UTF16 *ptr16; int i; TSKConversionResult r; // remove nulls from the Unicode string // convert / to : uniclean = (uint8_t *) tsk_malloc(ulen * 2); if (!uniclean) return 1; memcpy(uniclean, uni, ulen * 2); for (i = 0; i < ulen; ++i) { uint16_t uc = tsk_getu16(fs->endian, uniclean + i * 2); int changed = 0; if (uc == UTF16_NULL) { uc = UTF16_NULL_REPLACE; changed = 1; } else if ((flags & HFS_U16U8_FLAG_REPLACE_SLASH) && uc == UTF16_SLASH) { uc = UTF16_COLON; changed = 1; } else if ((flags & HFS_U16U8_FLAG_REPLACE_CONTROL) && uc < UTF16_LEAST_PRINTABLE) { uc = (uint16_t) UTF16_NULL_REPLACE; changed = 1; } if (changed) *((uint16_t *) (uniclean + i * 2)) = tsk_getu16(fs->endian, (uint8_t *) & uc); } // convert to UTF-8 memset(asc, 0, alen); ptr8 = (UTF8 *) asc; ptr16 = (UTF16 *) uniclean; r = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &ptr16, (const UTF16 *) (&uniclean[ulen * 2]), &ptr8, (UTF8 *) & asc[alen], TSKstrictConversion); free(uniclean); if (r != TSKconversionOK) { tsk_error_set_errno(TSK_ERR_FS_UNICODE); tsk_error_set_errstr ("hfs_UTF16toUTF8: unicode conversion failed (%d)", (int) r); return 1; } return 0; }
/** \internal * Process a directory and load up FS_DIR with the entries. If a pointer to * an already allocated FS_DIR struture is given, it will be cleared. If no existing * FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return * value is error or corruption, then the FS_DIR structure could * have entries (depending on when the error occured). * * @param a_fs File system to analyze * @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated * structure or a new structure. * @param a_addr Address of directory to process. * @returns error, corruption, ok etc. */ TSK_RETVAL_ENUM hfs_dir_open_meta(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir, TSK_INUM_T a_addr) { HFS_INFO *hfs = (HFS_INFO *) fs; uint32_t cnid; /* catalog node ID of the entry (= inum) */ TSK_FS_DIR *fs_dir; TSK_FS_NAME *fs_name; HFS_DIR_OPEN_META_INFO info; tsk_error_reset(); cnid = (uint32_t) a_addr; if (tsk_verbose) fprintf(stderr, "hfs_dir_open_meta: called for directory %" PRIu32 "\n", cnid); if (a_addr < fs->first_inum || a_addr > fs->last_inum) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); tsk_error_set_errstr("hfs_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 ("hfs_dir_open_meta: NULL fs_dir argument given"); return TSK_ERR; } if (tsk_verbose) tsk_fprintf(stderr, "hfs_dir_open_meta: Processing directory %" PRIuINUM "\n", a_addr); fs_dir = *a_fs_dir; if (fs_dir) { tsk_fs_dir_reset(fs_dir); fs_dir->addr = a_addr; } else if ((*a_fs_dir = fs_dir = tsk_fs_dir_alloc(fs, a_addr, 128)) == NULL) { return TSK_ERR; } if ((fs_name = tsk_fs_name_alloc(HFS_MAXNAMLEN, 0)) == NULL) { return TSK_ERR; } info.fs_dir = fs_dir; info.fs_name = fs_name; if ((fs_dir->fs_file = tsk_fs_file_open_meta(fs, NULL, a_addr)) == NULL) { tsk_error_errstr2_concat(" - hfs_dir_open_meta"); tsk_fs_name_free(fs_name); return TSK_ERR; } // if we are listing the root directory, add the Orphan directory and special HFS file entries if (a_addr == fs->root_inum) { int i; for (i = 0; i < 6; i++) { switch (i) { case 0: if (!hfs->has_extents_file) continue; strncpy(fs_name->name, HFS_EXTENTS_FILE_NAME, fs_name->name_size); fs_name->meta_addr = HFS_EXTENTS_FILE_ID; break; case 1: strncpy(fs_name->name, HFS_CATALOG_FILE_NAME, fs_name->name_size); fs_name->meta_addr = HFS_CATALOG_FILE_ID; break; case 2: // Note: the Extents file and the BadBlocks file are really the same. if (!hfs->has_extents_file) continue; strncpy(fs_name->name, HFS_BAD_BLOCK_FILE_NAME, fs_name->name_size); fs_name->meta_addr = HFS_BAD_BLOCK_FILE_ID; break; case 3: strncpy(fs_name->name, HFS_ALLOCATION_FILE_NAME, fs_name->name_size); fs_name->meta_addr = HFS_ALLOCATION_FILE_ID; break; case 4: if (!hfs->has_startup_file) continue; strncpy(fs_name->name, HFS_STARTUP_FILE_NAME, fs_name->name_size); fs_name->meta_addr = HFS_STARTUP_FILE_ID; break; case 5: if (!hfs->has_attributes_file) continue; strncpy(fs_name->name, HFS_ATTRIBUTES_FILE_NAME, fs_name->name_size); fs_name->meta_addr = HFS_ATTRIBUTES_FILE_ID; break; /* case 6: strncpy(fs_name->name, HFS_REPAIR_CATALOG_FILE_NAME, fs_name->name_size); fs_name->meta_addr = HFS_REPAIR_CATALOG_FILE_ID; break; case 7: strncpy(fs_name->name, HFS_BOGUS_EXTENT_FILE_NAME, fs_name->name_size); fs_name->meta_addr = HFS_BOGUS_EXTENT_FILE_ID; break; */ } fs_name->type = TSK_FS_NAME_TYPE_REG; fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; if (tsk_fs_dir_add(fs_dir, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } } } if (hfs_cat_traverse(hfs, &cnid, hfs_dir_open_meta_cb, &info)) { tsk_fs_name_free(fs_name); return TSK_ERR; } tsk_fs_name_free(fs_name); return TSK_OK; }
static uint8_t hfs_dir_open_meta_cb(HFS_INFO * hfs, int8_t level_type, const void *targ_data, const hfs_btree_key_cat * cur_key, TSK_OFF_T key_off, void *ptr) { uint32_t *cnid_p = (uint32_t *) targ_data; HFS_DIR_OPEN_META_INFO *info = (HFS_DIR_OPEN_META_INFO *) ptr; TSK_FS_INFO *fs = &hfs->fs_info; if (tsk_verbose) fprintf(stderr, "hfs_dir_open_meta_cb: want %" PRIu32 " vs got %" PRIu32 " (%s node)\n", *cnid_p, tsk_getu32(hfs->fs_info.endian, cur_key->parent_cnid), (level_type == HFS_BT_NODE_TYPE_IDX) ? "Index" : "Leaf"); if (level_type == HFS_BT_NODE_TYPE_IDX) { if (tsk_getu32(hfs->fs_info.endian, cur_key->parent_cnid) < *cnid_p) { return HFS_BTREE_CB_IDX_LT; } else { return HFS_BTREE_CB_IDX_EQGT; } } else { uint8_t *rec_buf = (uint8_t *) cur_key; uint16_t rec_type; size_t rec_off2; if (tsk_getu32(hfs->fs_info.endian, cur_key->parent_cnid) < *cnid_p) { return HFS_BTREE_CB_LEAF_GO; } else if (tsk_getu32(hfs->fs_info.endian, cur_key->parent_cnid) > *cnid_p) { return HFS_BTREE_CB_LEAF_STOP; } rec_off2 = 2 + tsk_getu16(hfs->fs_info.endian, cur_key->key_len); // @@@ NEED TO REPLACE THIS SOMEHOW, but need to figure out the max length /* if (rec_off2 > nodesize) { tsk_error_set_errno(TSK_ERR_FS_GENFS); tsk_error_set_errstr( "hfs_dir_open_meta: offset of record+keylen %d in leaf node %d too large (%zu vs %" PRIu16 ")", rec, cur_node, rec_off2, nodesize); tsk_fs_name_free(fs_name); free(node); return TSK_COR; } */ rec_type = tsk_getu16(hfs->fs_info.endian, &rec_buf[rec_off2]); // Catalog entry is for a file if (rec_type == HFS_FILE_THREAD) { tsk_error_set_errno(TSK_ERR_FS_GENFS); tsk_error_set_errstr("hfs_dir_open_meta: Entry" " is a file, not a folder"); return HFS_BTREE_CB_ERR; } /* This will link the folder to its parent, which is the ".." entry */ else if (rec_type == HFS_FOLDER_THREAD) { hfs_thread *thread = (hfs_thread *) & rec_buf[rec_off2]; strcpy(info->fs_name->name, ".."); info->fs_name->meta_addr = tsk_getu32(hfs->fs_info.endian, thread->parent_cnid); info->fs_name->type = TSK_FS_NAME_TYPE_DIR; info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; } /* This is a folder in the folder */ else if (rec_type == HFS_FOLDER_RECORD) { hfs_folder *folder = (hfs_folder *) & rec_buf[rec_off2]; info->fs_name->meta_addr = tsk_getu32(hfs->fs_info.endian, folder->std.cnid); info->fs_name->type = TSK_FS_NAME_TYPE_DIR; info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; if (hfs_UTF16toUTF8(fs, (uint8_t *) cur_key->name.unicode, tsk_getu16(hfs->fs_info.endian, cur_key->name.length), info->fs_name->name, HFS_MAXNAMLEN + 1, HFS_U16U8_FLAG_REPLACE_SLASH)) { return HFS_BTREE_CB_ERR; } } /* This is a normal file in the folder */ else if (rec_type == HFS_FILE_RECORD) { hfs_file *file = (hfs_file *) & rec_buf[rec_off2]; // This could be a hard link. We need to test this CNID, and follow it if necessary. unsigned char is_err; TSK_INUM_T file_cnid = tsk_getu32(hfs->fs_info.endian, file->std.cnid); TSK_INUM_T target_cnid = hfs_follow_hard_link(hfs, file, &is_err); if (is_err > 1) { error_returned ("hfs_dir_open_meta_cb: trying to follow a possible hard link in the directory"); return HFS_BTREE_CB_ERR; } if (target_cnid != file_cnid) { HFS_ENTRY entry; uint8_t lkup; // lookup result // This is a hard link. We need to fill in the name->type and name->meta_addr from the target info->fs_name->meta_addr = target_cnid; // get the Catalog entry for the target CNID lkup = hfs_cat_file_lookup(hfs, target_cnid, &entry, FALSE); if (lkup != 0) { error_returned ("hfs_dir_open_meta_cb: retrieving the catalog entry for the target of a hard link"); return HFS_BTREE_CB_ERR; } info->fs_name->type = hfsmode2tsknametype(tsk_getu16(hfs->fs_info.endian, entry.cat.std.perm.mode)); } else { // This is NOT a hard link. info->fs_name->meta_addr = tsk_getu32(hfs->fs_info.endian, file->std.cnid); info->fs_name->type = hfsmode2tsknametype(tsk_getu16(hfs->fs_info.endian, file->std.perm.mode)); } info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; if (hfs_UTF16toUTF8(fs, (uint8_t *) cur_key->name.unicode, tsk_getu16(hfs->fs_info.endian, cur_key->name.length), info->fs_name->name, HFS_MAXNAMLEN + 1, HFS_U16U8_FLAG_REPLACE_SLASH)) { return HFS_BTREE_CB_ERR; } } else { tsk_error_set_errno(TSK_ERR_FS_GENFS); // @@@ MAY NEED TO IMPROVE BELOW MESSAGE tsk_error_set_errstr ("hfs_dir_open_meta: Unknown record type %d in leaf node", rec_type); return HFS_BTREE_CB_ERR; } if (tsk_fs_dir_add(info->fs_dir, info->fs_name)) { return HFS_BTREE_CB_ERR; } return HFS_BTREE_CB_LEAF_GO; } }
/** \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; }
TSK_IMG_INFO * ewf_open(int a_num_img, const TSK_TCHAR * const a_images[], unsigned int a_ssize) { #if defined( HAVE_LIBEWF_V2_API ) char error_string[TSK_EWF_ERROR_STRING_SIZE]; libewf_error_t *ewf_error = NULL; int result = 0; #elif !defined( LIBEWF_STRING_DIGEST_HASH_LENGTH_MD5 ) uint8_t md5_hash[16]; #endif IMG_EWF_INFO *ewf_info = NULL; TSK_IMG_INFO *img_info = NULL; #if !defined( HAVE_LIBEWF_V2_API) if (tsk_verbose) libewf_set_notify_values(stderr, 1); #endif if ((ewf_info = (IMG_EWF_INFO *) tsk_img_malloc(sizeof(IMG_EWF_INFO))) == NULL) { return NULL; } img_info = (TSK_IMG_INFO *) ewf_info; // See if they specified only the first of the set... ewf_info->used_ewf_glob = 0; if (a_num_img == 1) { #if defined( HAVE_LIBEWF_V2_API) #ifdef TSK_WIN32 if (libewf_glob_wide(a_images[0], TSTRLEN(a_images[0]), LIBEWF_FORMAT_UNKNOWN, &ewf_info->images, &ewf_info->num_imgs, &ewf_error) == -1) { #else if (libewf_glob(a_images[0], TSTRLEN(a_images[0]), LIBEWF_FORMAT_UNKNOWN, &ewf_info->images, &ewf_info->num_imgs, &ewf_error) == -1) { #endif tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_MAGIC); getError(ewf_error, error_string); tsk_error_set_errstr("ewf_open: Not an E01 glob name (%s)", error_string); libewf_error_free(&ewf_error); tsk_img_free(ewf_info); return NULL; } #else //use v1 #ifdef TSK_WIN32 ewf_info->num_imgs = libewf_glob_wide(a_images[0], TSTRLEN(a_images[0]), LIBEWF_FORMAT_UNKNOWN, &ewf_info->images); #else ewf_info->num_imgs = libewf_glob(a_images[0], TSTRLEN(a_images[0]), LIBEWF_FORMAT_UNKNOWN, &ewf_info->images); #endif if (ewf_info->num_imgs <= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_MAGIC); tsk_error_set_errstr("ewf_open: Not an E01 glob name"); tsk_img_free(ewf_info); return NULL; } #endif // end v1 ewf_info->used_ewf_glob = 1; if (tsk_verbose) tsk_fprintf(stderr, "ewf_open: found %d segment files via libewf_glob\n", ewf_info->num_imgs); } else { int i; ewf_info->num_imgs = a_num_img; if ((ewf_info->images = (TSK_TCHAR **) tsk_malloc(a_num_img * sizeof(TSK_TCHAR *))) == NULL) { tsk_img_free(ewf_info); return NULL; } for (i = 0; i < a_num_img; i++) { if ((ewf_info->images[i] = (TSK_TCHAR *) tsk_malloc((TSTRLEN(a_images[i]) + 1) * sizeof(TSK_TCHAR))) == NULL) { tsk_img_free(ewf_info); return NULL; } TSTRNCPY(ewf_info->images[i], a_images[i], TSTRLEN(a_images[i]) + 1); } } #if defined( HAVE_LIBEWF_V2_API ) // Check the file signature before we call the library open #if defined( TSK_WIN32 ) if (libewf_check_file_signature_wide(a_images[0], &ewf_error) != 1) #else if (libewf_check_file_signature(a_images[0], &ewf_error) != 1) #endif { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_MAGIC); getError(ewf_error, error_string); tsk_error_set_errstr("ewf_open: Not an EWF file (%s)", error_string); libewf_error_free(&ewf_error); tsk_img_free(ewf_info); if (tsk_verbose != 0) { tsk_fprintf(stderr, "Not an EWF file\n"); } return (NULL); } if (libewf_handle_initialize(&(ewf_info->handle), &ewf_error) != 1) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_OPEN); getError(ewf_error, error_string); tsk_error_set_errstr("ewf_open file: %" PRIttocTSK ": Error initializing handle (%s)", a_images[0], error_string); libewf_error_free(&ewf_error); tsk_img_free(ewf_info); if (tsk_verbose != 0) { tsk_fprintf(stderr, "Unable to create EWF handle\n"); } return (NULL); } #if defined( TSK_WIN32 ) if (libewf_handle_open_wide(ewf_info->handle, (wchar_t * const *) ewf_info->images, ewf_info->num_imgs, LIBEWF_OPEN_READ, &ewf_error) != 1) #else if (libewf_handle_open(ewf_info->handle, (char *const *) ewf_info->images, ewf_info->num_imgs, LIBEWF_OPEN_READ, &ewf_error) != 1) #endif { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_OPEN); getError(ewf_error, error_string); tsk_error_set_errstr("ewf_open file: %" PRIttocTSK ": Error opening (%s)", a_images[0], error_string); libewf_error_free(&ewf_error); tsk_img_free(ewf_info); if (tsk_verbose != 0) { tsk_fprintf(stderr, "Error opening EWF file\n"); } return (NULL); } if (libewf_handle_get_media_size(ewf_info->handle, (size64_t *) & (img_info->size), &ewf_error) != 1) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_OPEN); getError(ewf_error, error_string); tsk_error_set_errstr("ewf_open file: %" PRIttocTSK ": Error getting size of image (%s)", a_images[0], error_string); libewf_error_free(&ewf_error); tsk_img_free(ewf_info); if (tsk_verbose != 0) { tsk_fprintf(stderr, "Error getting size of EWF file\n"); } return (NULL); } result = libewf_handle_get_utf8_hash_value_md5(ewf_info->handle, (uint8_t *) ewf_info->md5hash, 33, &ewf_error); if (result == -1) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_OPEN); getError(ewf_error, error_string); tsk_error_set_errstr("ewf_open file: %" PRIttocTSK ": Error getting MD5 of image (%s)", a_images[0], error_string); libewf_error_free(&ewf_error); tsk_img_free(ewf_info); if (tsk_verbose != 0) { tsk_fprintf(stderr, "Error getting size of EWF file\n"); } return (NULL); } ewf_info->md5hash_isset = result; #else // V1 API // Check the file signature before we call the library open #if defined( TSK_WIN32 ) if (libewf_check_file_signature_wide(a_images[0]) != 1) #else if (libewf_check_file_signature(a_images[0]) != 1) #endif { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_MAGIC); tsk_error_set_errstr("ewf_open: Not an EWF file"); tsk_img_free(ewf_info); if (tsk_verbose) tsk_fprintf(stderr, "Not an EWF file\n"); return NULL; } #if defined( TSK_WIN32 ) ewf_info->handle = libewf_open_wide( (wchar_t * const *) ewf_info->images, ewf_info->num_imgs, LIBEWF_OPEN_READ); #else ewf_info->handle = libewf_open( (char *const *) ewf_info->images, ewf_info->num_imgs, LIBEWF_OPEN_READ); #endif if (ewf_info->handle == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_OPEN); tsk_error_set_errstr("ewf_open file: %" PRIttocTSK ": Error opening", ewf_info->images[0]); tsk_img_free(ewf_info); if (tsk_verbose != 0) { tsk_fprintf(stderr, "Error opening EWF file\n"); } return (NULL); } #if defined( LIBEWF_STRING_DIGEST_HASH_LENGTH_MD5 ) // 2007 version img_info->size = libewf_get_media_size(ewf_info->handle); ewf_info->md5hash_isset = libewf_get_stored_md5_hash(ewf_info->handle, ewf_info->md5hash, LIBEWF_STRING_DIGEST_HASH_LENGTH_MD5); #else // libewf-20080322 version if (libewf_get_media_size(ewf_info->handle, (size64_t *) & (img_info->size)) != 1) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_OPEN); tsk_error_set_errstr("ewf_open file: %" PRIttocTSK ": Error getting size of image", ewf_info->images[0]); tsk_img_free(ewf_info); if (tsk_verbose) { tsk_fprintf(stderr, "Error getting size of EWF file\n"); } return (NULL); } if (libewf_get_md5_hash(ewf_info->handle, md5_hash, 16) == 1) { int md5_string_iterator = 0; int md5_hash_iterator = 0; for (md5_hash_iterator = 0; md5_hash_iterator < 16; md5_hash_iterator++) { int digit = md5_hash[md5_hash_iterator] / 16; if (digit <= 9) { ewf_info->md5hash[md5_string_iterator++] = '0' + (char) digit; } else { ewf_info->md5hash[md5_string_iterator++] = 'a' + (char) (digit - 10); } digit = md5_hash[md5_hash_iterator] % 16; if (digit <= 9) { ewf_info->md5hash[md5_string_iterator++] = '0' + (char) digit; } else { ewf_info->md5hash[md5_string_iterator++] = 'a' + (char) (digit - 10); } } ewf_info->md5hash_isset = 1; } #endif /* defined( LIBEWF_STRING_DIGEST_HASH_LENGTH_MD5 ) */ #endif /* defined( HAVE_LIBEWF_V2_API ) */ if (a_ssize != 0) { img_info->sector_size = a_ssize; } else { img_info->sector_size = 512; } img_info->itype = TSK_IMG_TYPE_EWF_EWF; img_info->read = &ewf_image_read; img_info->close = &ewf_image_close; img_info->imgstat = &ewf_image_imgstat; // initialize the read lock tsk_init_lock(&(ewf_info->read_lock)); return (img_info); }
/** * \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; }
/** * 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; }
/** * 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; }
TSK_IMG_INFO * aff_open(const TSK_TCHAR * const images[], unsigned int a_ssize) { IMG_AFF_INFO *aff_info; TSK_IMG_INFO *img_info; int type; char *image; #ifdef TSK_WIN32 // convert wchar_t* image path to char* to conform to // the AFFLIB API UTF16 *utf16 = (UTF16 *)images[0]; size_t ilen = wcslen(utf16); size_t olen = ilen*4 + 1; UTF8 *utf8 = (UTF8 *) tsk_malloc(olen); image = (char *) utf8; if ( image == NULL ) return NULL; TSKConversionResult retval = tsk_UTF16toUTF8_lclorder( (const UTF16 **) &utf16, &utf16[ilen], &utf8, &utf8[olen], TSKlenientConversion ); *utf8 = '\0'; if (retval != TSKconversionOK) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_UNICODE); tsk_error_set_errstr("aff_open file: %" PRIttocTSK ": Error converting path to UTF-8 %d\n", images[0], retval); free(image); return NULL; } utf8 = (UTF8 *) image; while ( *utf8 ) { if ( *utf8 > 127 ) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_UNICODE); tsk_error_set_errstr("aff_open file: %" PRIttocTSK ": Non-Latin paths are not supported for AFF images\n", images[0]); free(image); return NULL; } utf8++; } #else image = (char *) tsk_malloc( strlen(images[0])+1 ); if ( image == NULL ) return NULL; strncpy(image, images[0], strlen(images[0])+1 ); #endif if ((aff_info = (IMG_AFF_INFO *) tsk_img_malloc(sizeof(IMG_AFF_INFO))) == NULL) { free(image); return NULL; } img_info = (TSK_IMG_INFO *) aff_info; img_info->read = aff_read; img_info->close = aff_close; img_info->imgstat = aff_imgstat; img_info->sector_size = 512; if (a_ssize) img_info->sector_size = a_ssize; type = af_identify_file_type(image, 1); if ((type == AF_IDENTIFY_ERR) || (type == AF_IDENTIFY_NOEXIST)) { if (tsk_verbose) { tsk_fprintf(stderr, "aff_open: Error determining type of file: %" PRIttocTSK "\n", images[0]); perror("aff_open"); } tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_OPEN); tsk_error_set_errstr("aff_open file: %" PRIttocTSK ": Error checking type", images[0]); tsk_img_free(aff_info); free(image); return NULL; } else if (type == AF_IDENTIFY_AFF) { img_info->itype = TSK_IMG_TYPE_AFF_AFF; } else if (type == AF_IDENTIFY_AFD) { img_info->itype = TSK_IMG_TYPE_AFF_AFD; } else if (type == AF_IDENTIFY_AFM) { img_info->itype = TSK_IMG_TYPE_AFF_AFM; } else { img_info->itype = TSK_IMG_TYPE_AFF_ANY; } aff_info->af_file = af_open(image, O_RDONLY | O_BINARY, 0); if (!aff_info->af_file) { // @@@ Need to check here if the open failed because of an incorrect password. tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_OPEN); tsk_error_set_errstr("aff_open file: %" PRIttocTSK ": Error opening - %s", images[0], strerror(errno)); tsk_img_free(aff_info); if (tsk_verbose) { tsk_fprintf(stderr, "Error opening AFF/AFD/AFM file\n"); perror("aff_open"); } free(image); return NULL; } // verify that a password was given and we can read encrypted data. if (af_cannot_decrypt(aff_info->af_file)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_PASSWD); tsk_error_set_errstr("aff_open file: %" PRIttocTSK, images[0]); tsk_img_free(aff_info); if (tsk_verbose) { tsk_fprintf(stderr, "Error opening AFF/AFD/AFM file (incorrect password)\n"); } free(image); return NULL; } aff_info->type = type; img_info->size = af_imagesize(aff_info->af_file); af_seek(aff_info->af_file, 0, SEEK_SET); aff_info->seek_pos = 0; free(image); return img_info; }
/** * \internal * Set the needed fields along with an initial run list for a data attribute. To add more * runs, use ...._add_run(). * * @param a_fs File system the run comes from. * @param a_fs_attr The data attribute to initialize and add the run to * @param a_data_run_new The set of runs to add (can be NULL). * @param name Name of the attribute (can be NULL) * @param type Type of attribute to add run to * @param id Id of attribute to add run to * @param size Total size of the attribute (can be larger than length of initial run being added) * @param init_size Number of bytes in attribute that have been initialized (less then or equal to size) * (note that this sets the initialized size for the attribute and it will not be updated as more runs are added). * @param alloc_size Allocated size of the attribute (>= size). Identifies the slack space. * (note that this sets the allocated size for the attribute and it will not be updated as more runs are added). * @param flags Flags about compression, sparse etc. of data * @param compsize Compression unit size (in case it needs to be created) * * @returns 1 on error and 0 on success */ uint8_t tsk_fs_attr_set_run(TSK_FS_FILE * a_fs_file, TSK_FS_ATTR * a_fs_attr, TSK_FS_ATTR_RUN * a_data_run_new, const char *name, TSK_FS_ATTR_TYPE_ENUM type, uint16_t id, TSK_OFF_T size, TSK_OFF_T init_size, TSK_OFF_T alloc_size, TSK_FS_ATTR_FLAG_ENUM flags, uint32_t compsize) { if ((a_fs_file == NULL) || (a_fs_file->meta == NULL)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("Null fs_file in tsk_fs_attr_set_run"); return 1; } if (a_fs_attr == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("Null fs_attr in tsk_fs_attr_set_run"); return 1; } if (alloc_size < size) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("tsk_fs_attr_set_run: alloc_size (%" PRIuOFF ") is less than size (%" PRIuOFF ")", alloc_size, size); return 1; } a_fs_attr->fs_file = a_fs_file; a_fs_attr->flags = (TSK_FS_ATTR_INUSE | TSK_FS_ATTR_NONRES | flags); a_fs_attr->type = type; a_fs_attr->id = id; a_fs_attr->size = size; a_fs_attr->nrd.allocsize = alloc_size; a_fs_attr->nrd.initsize = init_size; a_fs_attr->nrd.compsize = compsize; if (fs_attr_put_name(a_fs_attr, name)) { return 1; } /* Add the a_data_run_new to the attribute. */ /* We support the ODD case where the run is NULL. In this case, * we set the attribute size info, but set everything else to NULL. */ if (a_data_run_new == NULL) { a_fs_attr->nrd.run = NULL; a_fs_attr->nrd.run_end = NULL; return 0; } /* * If this is not in the begining, then we need to make a filler * to account for the cluster numbers we haven't seen yet * * This commonly happens when we process an MFT entry that * is not a base entry and it is referenced in an $ATTR_LIST * * The $DATA attribute in the non-base have a non-zero * a_data_run_new->offset. */ if (a_data_run_new->offset != 0) { TSK_FS_ATTR_RUN *fill_run = tsk_fs_attr_run_alloc(); fill_run->flags = TSK_FS_ATTR_RUN_FLAG_FILLER; fill_run->offset = 0; fill_run->addr = 0; fill_run->len = a_data_run_new->offset; fill_run->next = a_data_run_new; a_data_run_new = fill_run; } a_fs_attr->nrd.run = a_data_run_new; // update the pointer to the end of the list a_fs_attr->nrd.run_end = a_data_run_new; while (a_fs_attr->nrd.run_end->next) { a_fs_attr->nrd.run_end = a_fs_attr->nrd.run_end->next; } return 0; }
/* * 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; }
/** * \internal * Add a set of consecutive runs to an attribute. This will add and remove FILLER entries * as needed and update internal variables. * * @param a_fs File system run is from * @param fs_attr Attribute to add run to * @param a_data_run_new The set of runs to add. * * @returns 1 on error and 0 on succes */ uint8_t tsk_fs_attr_add_run(TSK_FS_INFO * a_fs, TSK_FS_ATTR * a_fs_attr, TSK_FS_ATTR_RUN * a_data_run_new) { TSK_FS_ATTR_RUN *data_run_cur, *data_run_prev; TSK_DADDR_T run_len; tsk_error_reset(); if (a_fs_attr == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_attr_add_run: Error, a_fs_attr is NULL"); return 1; } // we only support the case of a null run if it is the only run... if (a_data_run_new == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_attr_add_run: Error, a_data_run_new is NULL (%" PRIuINUM ")", a_fs_attr->fs_file->meta->addr); return 1; } run_len = 0; data_run_cur = a_data_run_new; while (data_run_cur) { run_len += data_run_cur->len; data_run_cur = data_run_cur->next; } /* First thing, is to check if we can just add it to the end */ if ((a_fs_attr->nrd.run_end) && (a_fs_attr->nrd.run_end->offset + a_fs_attr->nrd.run_end->len == a_data_run_new->offset)) { a_fs_attr->nrd.run_end->next = a_data_run_new; // update the pointer to the end of the list while (a_fs_attr->nrd.run_end->next) a_fs_attr->nrd.run_end = a_fs_attr->nrd.run_end->next; /* return head of a_fs_attr list */ return 0; } // cycle through existing runs and see if we can add this into a filler spot data_run_cur = a_fs_attr->nrd.run; data_run_prev = NULL; while (data_run_cur) { if (tsk_verbose) tsk_fprintf(stderr, "tsk_fs_attr_add: %" PRIuOFF "@%" PRIuOFF " (Filler: %s)\n", data_run_cur->offset, data_run_cur->len, (data_run_cur->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) ? "Yes" : "No"); /* Do we replace this filler spot? */ if (data_run_cur->flags & TSK_FS_ATTR_RUN_FLAG_FILLER) { /* This should never happen because we always add * the filler to start from VCN 0 */ if (data_run_cur->offset > a_data_run_new->offset) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_GENFS); tsk_error_set_errstr ("tsk_fs_attr_add_run: could not add data_run b.c. offset (%" PRIuOFF ") is larger than FILLER (%" PRIuOFF ") (%" PRIuINUM ")", a_data_run_new->offset, data_run_cur->offset, a_fs_attr->fs_file->meta->addr); dump_attr(a_fs_attr); return 1; } /* Check if the new run starts inside of this filler. */ if (data_run_cur->offset + data_run_cur->len > a_data_run_new->offset) { TSK_FS_ATTR_RUN *endrun; /* if the new starts at the same as the filler, * replace the pointer */ if (data_run_cur->offset == a_data_run_new->offset) { if (data_run_prev) data_run_prev->next = a_data_run_new; else a_fs_attr->nrd.run = a_data_run_new; } /* The new run does not start at the begining of * the filler, so make a new start filler */ else { TSK_FS_ATTR_RUN *newfill = tsk_fs_attr_run_alloc(); if (newfill == NULL) return 1; if (data_run_prev) data_run_prev->next = newfill; else a_fs_attr->nrd.run = newfill; newfill->next = a_data_run_new; newfill->len = a_data_run_new->offset - data_run_cur->offset; newfill->offset = data_run_cur->offset; newfill->flags = TSK_FS_ATTR_RUN_FLAG_FILLER; data_run_cur->len -= newfill->len; } /* get to the end of the run that we are trying to add */ endrun = a_data_run_new; while (endrun->next) endrun = endrun->next; /* if the filler is the same size as the * new one, replace it */ if (run_len == data_run_cur->len) { endrun->next = data_run_cur->next; // update the pointer to the end of the list (if we are the end) if (endrun->next == NULL) a_fs_attr->nrd.run_end = endrun; free(data_run_cur); } /* else adjust the last filler entry */ else { endrun->next = data_run_cur; data_run_cur->len -= run_len; data_run_cur->offset = a_data_run_new->offset + a_data_run_new->len; } return 0; } } data_run_prev = data_run_cur; data_run_cur = data_run_cur->next; } /* * There is no filler holding the location of this run, so * we will add it to the end of the list * * we got here because it did not fit in the current list or * because the current list is NULL * * At this point data_run_prev is the end of the existing list or * 0 if there is no list */ /* This is an error condition. * It means that we cycled through the existing runs, * ended at a VCN that is larger than what we are adding, * and never found a filler entry to insert it into... */ if ((data_run_prev) && (data_run_prev->offset + data_run_prev->len > a_data_run_new->offset)) { /* MAYBE this is because of a duplicate entry .. */ if ((data_run_prev->addr == a_data_run_new->addr) && (data_run_prev->len == a_data_run_new->len)) { // @@@ Sould be we freeing this....? What if the caller tries to write to it? tsk_fs_attr_run_free(a_data_run_new); return 0; } tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_GENFS); tsk_error_set_errstr ("fs_attr_add_run: error adding additional run (%" PRIuINUM "): No filler entry for %" PRIuDADDR ". Final: %" PRIuDADDR, a_fs_attr->fs_file->meta->addr, a_data_run_new->offset, data_run_prev->offset + data_run_prev->len); dump_attr(a_fs_attr); return 1; } /* we should add it right here */ else if (((data_run_prev) && (data_run_prev->offset + data_run_prev->len == a_data_run_new->offset)) || (a_data_run_new->offset == 0)) { if (data_run_prev) data_run_prev->next = a_data_run_new; else a_fs_attr->nrd.run = a_data_run_new; } /* we need to make a filler before it */ else { TSK_FS_ATTR_RUN *tmprun = tsk_fs_attr_run_alloc(); if (tmprun == NULL) return 1; if (data_run_prev) { data_run_prev->next = tmprun; tmprun->offset = data_run_prev->offset + data_run_prev->len; } else { a_fs_attr->nrd.run = tmprun; } tmprun->len = a_data_run_new->offset - tmprun->offset; tmprun->flags = TSK_FS_ATTR_RUN_FLAG_FILLER; tmprun->next = a_data_run_new; } // update the pointer to the end of the list a_fs_attr->nrd.run_end = a_data_run_new; while (a_fs_attr->nrd.run_end->next) a_fs_attr->nrd.run_end = a_fs_attr->nrd.run_end->next; return 0; }
static uint8_t ffs_dent_copy(FFS_INFO * ffs, char *ffs_dent, TSK_FS_NAME * fs_name) { TSK_FS_INFO *a_fs = &(ffs->fs_info); /* this one has the type field */ if ((a_fs->ftype == TSK_FS_TYPE_FFS1) || (a_fs->ftype == TSK_FS_TYPE_FFS2)) { ffs_dentry1 *dir = (ffs_dentry1 *) ffs_dent; fs_name->meta_addr = tsk_getu32(a_fs->endian, dir->d_ino); if (fs_name->name_size != FFS_MAXNAMLEN) { if (tsk_fs_name_realloc(fs_name, FFS_MAXNAMLEN)) return 1; } /* ffs null terminates so we can strncpy */ strncpy(fs_name->name, dir->d_name, fs_name->name_size); switch (dir->d_type) { case FFS_DT_REG: fs_name->type = TSK_FS_NAME_TYPE_REG; break; case FFS_DT_DIR: fs_name->type = TSK_FS_NAME_TYPE_DIR; break; case FFS_DT_CHR: fs_name->type = TSK_FS_NAME_TYPE_CHR; break; case FFS_DT_BLK: fs_name->type = TSK_FS_NAME_TYPE_BLK; break; case FFS_DT_FIFO: fs_name->type = TSK_FS_NAME_TYPE_FIFO; break; case FFS_DT_SOCK: fs_name->type = TSK_FS_NAME_TYPE_SOCK; break; case FFS_DT_LNK: fs_name->type = TSK_FS_NAME_TYPE_LNK; break; case FFS_DT_WHT: fs_name->type = TSK_FS_NAME_TYPE_WHT; break; case FFS_DT_UNKNOWN: default: fs_name->type = TSK_FS_NAME_TYPE_UNDEF; break; } } else if (a_fs->ftype == TSK_FS_TYPE_FFS1B) { ffs_dentry2 *dir = (ffs_dentry2 *) ffs_dent; fs_name->meta_addr = tsk_getu32(a_fs->endian, dir->d_ino); if (fs_name->name_size != FFS_MAXNAMLEN) { if (tsk_fs_name_realloc(fs_name, FFS_MAXNAMLEN)) return 1; } /* ffs null terminates so we can strncpy */ strncpy(fs_name->name, dir->d_name, fs_name->name_size); fs_name->type = TSK_FS_NAME_TYPE_UNDEF; } else { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("ffs_dent_copy: Unknown FS type"); return 1; } fs_name->flags = 0; return 0; }
/** * \ingroup fslib * * Find the meta data address for a given file name (UTF-8). * The basic idea of the function is to break the given name into its * subdirectories and start looking for each (starting in the root * directory). * * @param a_fs FS to analyze * @param a_path UTF-8 path of file to search for * @param [out] a_result Meta data address of file * @param [out] a_fs_name Copy of name details (or NULL if details not wanted) * @returns -1 on (system) error, 0 if found, and 1 if not found */ int8_t tsk_fs_path2inum(TSK_FS_INFO * a_fs, const char *a_path, TSK_INUM_T * a_result, TSK_FS_NAME * a_fs_name) { char *cpath; size_t clen; char *cur_dir; // The "current" directory or file we are looking for char *cur_attr; // The "current" attribute of the dir we are looking for TSK_INUM_T next_meta; uint8_t is_done; char *strtok_last; *a_result = 0; // copy path to a buffer that we can modify clen = strlen(a_path) + 1; if ((cpath = (char *) tsk_malloc(clen)) == NULL) { return -1; } strncpy(cpath, a_path, clen); // Get the first part of the directory path. cur_dir = (char *) strtok_r(cpath, "/", &strtok_last); cur_attr = NULL; /* If there is no token, then only a '/' was given */ if (cur_dir == NULL) { free(cpath); *a_result = a_fs->root_inum; // create the dummy entry if needed if (a_fs_name) { a_fs_name->meta_addr = a_fs->root_inum; // Note that we are not filling in meta_seq -- we could, we just aren't a_fs_name->type = TSK_FS_NAME_TYPE_DIR; a_fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; if (a_fs_name->name) a_fs_name->name[0] = '\0'; if (a_fs_name->shrt_name) a_fs_name->shrt_name[0] = '\0'; } return 0; } /* If this is NTFS, separate out the attribute of the current directory */ if (TSK_FS_TYPE_ISNTFS(a_fs->ftype) && ((cur_attr = strchr(cur_dir, ':')) != NULL)) { *(cur_attr) = '\0'; cur_attr++; } if (tsk_verbose) tsk_fprintf(stderr, "Looking for %s\n", cur_dir); // initialize the first place to look, the root dir next_meta = a_fs->root_inum; // we loop until we know the outcome and then exit. // everything should return from inside the loop. is_done = 0; while (is_done == 0) { size_t i; TSK_FS_FILE *fs_file_alloc = NULL; // set to the allocated file that is our target TSK_FS_FILE *fs_file_del = NULL; // set to an unallocated file that matches our criteria TSK_FS_DIR *fs_dir = NULL; // open the next directory in the recursion if ((fs_dir = tsk_fs_dir_open_meta(a_fs, next_meta)) == NULL) { free(cpath); return -1; } /* Verify this is indeed a directory. We had one reported * problem where a file was a disk image and opening it as * a directory found the directory entries inside of the file * and this caused problems... */ if ( !TSK_FS_IS_DIR_META(fs_dir->fs_file->meta->type)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_GENFS); tsk_error_set_errstr("Address %" PRIuINUM " is not for a directory\n", next_meta); free(cpath); return -1; } // cycle through each entry for (i = 0; i < tsk_fs_dir_getsize(fs_dir); i++) { TSK_FS_FILE *fs_file; uint8_t found_name = 0; if ((fs_file = tsk_fs_dir_get(fs_dir, i)) == NULL) { tsk_fs_dir_close(fs_dir); free(cpath); return -1; } /* * Check if this is the name that we are currently looking for, * as identified in 'cur_dir' */ if ((fs_file->name->name) && (a_fs->name_cmp(a_fs, fs_file->name->name, cur_dir) == 0)) { found_name = 1; } else if ((fs_file->name->shrt_name) && (a_fs->name_cmp(a_fs, fs_file->name->shrt_name, cur_dir) == 0)) { found_name = 1; } /* For NTFS, we have to check the attribute name. */ if ((found_name == 1) && (TSK_FS_TYPE_ISNTFS(a_fs->ftype))) { /* ensure we have the right attribute name */ if (cur_attr != NULL) { found_name = 0; if (fs_file->meta) { int cnt, i; // cycle through the attributes cnt = tsk_fs_file_attr_getsize(fs_file); for (i = 0; i < cnt; i++) { const TSK_FS_ATTR *fs_attr = tsk_fs_file_attr_get_idx(fs_file, i); if (!fs_attr) continue; if ((fs_attr->name) && (a_fs->name_cmp(a_fs, fs_attr->name, cur_attr) == 0)) { found_name = 1; break; } } } } } if (found_name) { /* If we found our file and it is allocated, then stop. If * it is unallocated, keep on going to see if we can get * an allocated hit */ if (fs_file->name->flags & TSK_FS_NAME_FLAG_ALLOC) { fs_file_alloc = fs_file; break; } else { // if we already have an unalloc and its addr is 0, then use the new one if ((fs_file_del) && (fs_file_del->name->meta_addr == 0)) { tsk_fs_file_close(fs_file_del); } fs_file_del = fs_file; } } // close the file if we did not save it for future analysis. else { tsk_fs_file_close(fs_file); fs_file = NULL; } } // we found a directory, go into it if ((fs_file_alloc) || (fs_file_del)) { const char *pname; TSK_FS_FILE *fs_file_tmp; // choose the alloc one first (if they both exist) if (fs_file_alloc) fs_file_tmp = fs_file_alloc; else fs_file_tmp = fs_file_del; pname = cur_dir; // save a copy of the current name pointer // advance to the next name cur_dir = (char *) strtok_r(NULL, "/", &(strtok_last)); cur_attr = NULL; if (tsk_verbose) tsk_fprintf(stderr, "Found it (%s), now looking for %s\n", pname, cur_dir); /* That was the last name in the path -- we found the file! */ if (cur_dir == NULL) { *a_result = fs_file_tmp->name->meta_addr; // make a copy if one was requested if (a_fs_name) { tsk_fs_name_copy(a_fs_name, fs_file_tmp->name); } if (fs_file_alloc) tsk_fs_file_close(fs_file_alloc); if (fs_file_del) tsk_fs_file_close(fs_file_del); tsk_fs_dir_close(fs_dir); free(cpath); return 0; } // update the attribute field, if needed if (TSK_FS_TYPE_ISNTFS(a_fs->ftype) && ((cur_attr = strchr(cur_dir, ':')) != NULL)) { *(cur_attr) = '\0'; cur_attr++; } // update the value for the next directory to open next_meta = fs_file_tmp->name->meta_addr; if (fs_file_alloc) { tsk_fs_file_close(fs_file_alloc); fs_file_alloc = NULL; } if (fs_file_del) { tsk_fs_file_close(fs_file_del); fs_file_del = NULL; } } // no hit in directory else { is_done = 1; } tsk_fs_dir_close(fs_dir); fs_dir = NULL; } free(cpath); return 1; }