/* * Parse a V 2.0 USN record. * Returns 0 on success, 1 otherwise */ static uint8_t parse_v2_record(const unsigned char *buf, TSK_USN_RECORD_HEADER *header, TSK_USN_RECORD_V2 *record, TSK_ENDIAN_ENUM endian) { uint64_t timestamp = 0; uint16_t name_offset = 0, name_length = 0; record->refnum = tsk_getu48(endian, &buf[8]); record->refnum_seq = tsk_getu16(endian, &buf[14]); record->parent_refnum = tsk_getu48(endian, &buf[16]); record->parent_refnum_seq = tsk_getu16(endian, &buf[22]); record->usn = tsk_getu64(endian, &buf[24]); /* Convert NT timestamp into Unix */ timestamp = tsk_getu64(endian, &buf[32]); record->time_sec = nt2unixtime(timestamp); record->time_nsec = nt2nano(timestamp); record->reason = tsk_getu32(endian, &buf[40]); record->source_info = tsk_getu32(endian, &buf[44]); record->security = tsk_getu32(endian, &buf[48]); record->attributes = tsk_getu32(endian, &buf[52]); /* Extract file name */ name_length = tsk_getu16(endian, &buf[56]); name_offset = tsk_getu16(endian, &buf[58]); return parse_fname(&buf[name_offset], name_length, record, endian); }
static uint8_t ntfs_dent_copy(NTFS_INFO * ntfs, ntfs_idxentry * idxe, TSK_FS_NAME * fs_name) { ntfs_attr_fname *fname = (ntfs_attr_fname *) & idxe->stream; TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info; UTF16 *name16; UTF8 *name8; int retVal; int i; fs_name->meta_addr = tsk_getu48(fs->endian, idxe->file_ref); fs_name->meta_seq = tsk_getu16(fs->endian, idxe->seq_num); name16 = (UTF16 *) & fname->name; name8 = (UTF8 *) fs_name->name; retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16, (UTF16 *) ((uintptr_t) name16 + fname->nlen * 2), &name8, (UTF8 *) ((uintptr_t) name8 + fs_name->name_size), TSKlenientConversion); if (retVal != TSKconversionOK) { *name8 = '\0'; if (tsk_verbose) tsk_fprintf(stderr, "Error converting NTFS name to UTF8: %d %" PRIuINUM, retVal, fs_name->meta_addr); } /* Make sure it is NULL Terminated */ if ((uintptr_t) name8 > (uintptr_t) fs_name->name + fs_name->name_size) fs_name->name[fs_name->name_size] = '\0'; else *name8 = '\0'; /* Clean up name */ i = 0; while (fs_name->name[i] != '\0') { if (TSK_IS_CNTRL(fs_name->name[i])) fs_name->name[i] = '^'; i++; } if (tsk_getu64(fs->endian, fname->flags) & NTFS_FNAME_FLAGS_DIR) fs_name->type = TSK_FS_NAME_TYPE_DIR; else fs_name->type = TSK_FS_NAME_TYPE_REG; fs_name->flags = 0; return 0; }
/** * \internal * Sets the file system layout members of a FATFS_INFO object for an exFAT file * system. Note that there are no "block" or "inode" concepts in exFAT. So, to * conform to the SleuthKit generic file system model, sectors are treated as * blocks, directory entries are treated as inodes, and inode addresses (inode * numbers) are assigned to every directory-entry-sized chunk of the file * system. This is the same mapping previously established for TSK treatment of * the other FAT file systems. As with those sister file systems, any given * inode address may or may not point to a conceptual exFAT inode. * * @param [in, out] a_fatfs Generic FAT file system info structure. */ static void exfatfs_setup_fs_layout_model(FATFS_INFO *a_fatfs) { TSK_FS_INFO *fs = &(a_fatfs->fs_info); EXFATFS_MASTER_BOOT_REC *exfatbs = NULL; assert(a_fatfs != NULL); fs->duname = "Sector"; fs->block_size = a_fatfs->ssize; exfatbs = (EXFATFS_MASTER_BOOT_REC*)(&a_fatfs->boot_sector_buffer); fs->block_count = tsk_getu64(fs->endian, exfatbs->vol_len_in_sectors); fs->first_block = 0; fs->last_block = fs->last_block_act = fs->block_count - 1; /* Determine the last block actually included in the image, since the * end of the file system could be "cut off." */ if ((TSK_DADDR_T) ((fs->img_info->size - fs->offset) / fs->block_size) < fs->block_count) { fs->last_block_act = (fs->img_info->size - fs->offset) / fs->block_size - 1; } /* Calculate the maximum number of directory entries that will fit in a * sector and a cluster. */ a_fatfs->dentry_cnt_se = a_fatfs->ssize / sizeof(FATFS_DENTRY); a_fatfs->dentry_cnt_cl = a_fatfs->dentry_cnt_se * a_fatfs->csize; /* The first entry in an exFAT FAT is a media type indicator. * The second entry is simply a meaningless 0xFFFFFFFF. * The first inode address is therefore 2. */ fs->first_inum = FATFS_FIRSTINO; fs->root_inum = FATFS_ROOTINO; /* Calculate inode addresses for the virtual files (MBR, one or two FATS) * and the virtual orphan files directory. */ fs->last_inum = (FATFS_SECT_2_INODE(a_fatfs, fs->last_block_act + 1) - 1) + FATFS_NUM_VIRT_FILES(a_fatfs); a_fatfs->mbr_virt_inum = fs->last_inum - FATFS_NUM_VIRT_FILES(a_fatfs) + 1; a_fatfs->fat1_virt_inum = a_fatfs->mbr_virt_inum + 1; if (a_fatfs->numfat == 2) { a_fatfs->fat2_virt_inum = a_fatfs->fat1_virt_inum + 1; } else { a_fatfs->fat2_virt_inum = a_fatfs->fat1_virt_inum; } /* Calculate the total number of inodes. */ fs->inum_count = fs->last_inum - fs->first_inum + 1; }
/** * \internal * Prints file system category data for an exFAT file system to a file * handle. * * @param [in] a_fs Generic file system info structure for the file system. * @param [in] a_hFile The file handle. * @return 0 on success, 1 otherwise, per TSK convention. */ static uint8_t exfatfs_fsstat_fs_info(TSK_FS_INFO *a_fs, FILE *a_hFile) { FATFS_INFO *fatfs = NULL; EXFATFS_MASTER_BOOT_REC *exfatbs = NULL; TSK_FS_FILE *fs_file = NULL; assert(a_fs != NULL); assert(a_hFile != NULL); fatfs = (FATFS_INFO*)a_fs; exfatbs = (EXFATFS_MASTER_BOOT_REC*)&(fatfs->boot_sector_buffer); if ((fs_file = tsk_fs_file_alloc(a_fs)) == NULL) { return FATFS_FAIL; } if ((fs_file->meta = tsk_fs_meta_alloc(FATFS_FILE_CONTENT_LEN)) == NULL) { return FATFS_FAIL; } tsk_fprintf(a_hFile, "FILE SYSTEM INFORMATION\n"); tsk_fprintf(a_hFile, "--------------------------------------------\n"); tsk_fprintf(a_hFile, "File System Type: exFAT\n"); tsk_fprintf(a_hFile, "\nVolume Serial Number: %x%x-%x%x\n", exfatbs->vol_serial_no[3], exfatbs->vol_serial_no[2], exfatbs->vol_serial_no[1], exfatbs->vol_serial_no[0]); if (exfatfs_find_volume_label_dentry(fatfs, fs_file) == 0) { tsk_fprintf(a_hFile, "Volume Label (from root directory): %s\n", fs_file->meta->name2->name); } else { tsk_fprintf(a_hFile, "Volume Label:\n"); } tsk_fprintf(a_hFile, "File System Name (from MBR): %s\n", exfatbs->fs_name); tsk_fprintf(a_hFile, "File System Revision: %x.%x\n", exfatbs->fs_revision[1], exfatbs->fs_revision[0]); tsk_fprintf(a_hFile, "Partition Offset: %" PRIuDADDR "\n", tsk_getu64(a_fs->endian, exfatbs->partition_offset)); tsk_fprintf(a_hFile, "Number of FATs: %d\n", fatfs->numfat); tsk_fs_file_close(fs_file); return FATFS_OK; }
/* * 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; }
// @@@ Should make a_idxe const and use internal pointer in function loop static TSK_RETVAL_ENUM ntfs_proc_idxentry(NTFS_INFO * a_ntfs, TSK_FS_DIR * a_fs_dir, uint8_t a_is_del, ntfs_idxentry * a_idxe, uint32_t a_idxe_len, uint32_t a_used_len) { uintptr_t endaddr, endaddr_alloc; TSK_FS_NAME *fs_name; TSK_FS_INFO *fs = (TSK_FS_INFO *) & a_ntfs->fs_info; if ((fs_name = tsk_fs_name_alloc(NTFS_MAXNAMLEN_UTF8, 0)) == NULL) { return TSK_ERR; } if (tsk_verbose) tsk_fprintf(stderr, "ntfs_proc_idxentry: Processing index entry: %" PRIu64 " Size: %" PRIu32 " Len: %" PRIu32 "\n", (uint64_t) ((uintptr_t) a_idxe), a_idxe_len, a_used_len); /* Sanity check */ if (a_idxe_len < a_used_len) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_ARG; snprintf(tsk_errstr, TSK_ERRSTR_L, "ntfs_proc_idxentry: Allocated length of index entries is larger than buffer length"); return TSK_ERR; } /* where is the end of the buffer */ endaddr = ((uintptr_t) a_idxe + a_idxe_len); /* where is the end of the allocated data */ endaddr_alloc = ((uintptr_t) a_idxe + a_used_len); /* cycle through the index entries, based on provided size */ while (((uintptr_t) & (a_idxe->stream) + sizeof(ntfs_attr_fname)) < endaddr) { ntfs_attr_fname *fname = (ntfs_attr_fname *) & a_idxe->stream; if (tsk_verbose) tsk_fprintf(stderr, "ntfs_proc_idxentry: New IdxEnt: %" PRIu64 " $FILE_NAME Entry: %" PRIu64 " File Ref: %" PRIu64 " IdxEnt Len: %" PRIu16 " StrLen: %" PRIu16 "\n", (uint64_t) ((uintptr_t) a_idxe), (uint64_t) ((uintptr_t) fname), (uint64_t) tsk_getu48(fs->endian, a_idxe->file_ref), tsk_getu16(fs->endian, a_idxe->idxlen), tsk_getu16(fs->endian, a_idxe->strlen)); /* perform some sanity checks on index buffer head * and advance by 4-bytes if invalid */ if ((tsk_getu48(fs->endian, a_idxe->file_ref) > fs->last_inum) || (tsk_getu48(fs->endian, a_idxe->file_ref) < fs->first_inum) || (tsk_getu16(fs->endian, a_idxe->idxlen) <= tsk_getu16(fs->endian, a_idxe->strlen)) || (tsk_getu16(fs->endian, a_idxe->idxlen) % 4) || (tsk_getu16(fs->endian, a_idxe->idxlen) > a_idxe_len)) { a_idxe = (ntfs_idxentry *) ((uintptr_t) a_idxe + 4); continue; } /* do some sanity checks on the deleted entries */ if ((tsk_getu16(fs->endian, a_idxe->strlen) == 0) || (((uintptr_t) a_idxe + tsk_getu16(fs->endian, a_idxe->idxlen)) > endaddr_alloc)) { /* name space checks */ if ((fname->nspace != NTFS_FNAME_POSIX) && (fname->nspace != NTFS_FNAME_WIN32) && (fname->nspace != NTFS_FNAME_DOS) && (fname->nspace != NTFS_FNAME_WINDOS)) { a_idxe = (ntfs_idxentry *) ((uintptr_t) a_idxe + 4); if (tsk_verbose) tsk_fprintf(stderr, "ntfs_proc_idxentry: Skipping because of invalid name space\n"); continue; } if ((tsk_getu64(fs->endian, fname->alloc_fsize) < tsk_getu64(fs->endian, fname->real_fsize)) || (fname->nlen == 0) || (*(uint8_t *) & fname->name == 0)) { a_idxe = (ntfs_idxentry *) ((uintptr_t) a_idxe + 4); if (tsk_verbose) tsk_fprintf(stderr, "ntfs_proc_idxentry: Skipping because of reported file sizes, name length, or NULL name\n"); continue; } if ((is_time(tsk_getu64(fs->endian, fname->crtime)) == 0) || (is_time(tsk_getu64(fs->endian, fname->atime)) == 0) || (is_time(tsk_getu64(fs->endian, fname->mtime)) == 0)) { a_idxe = (ntfs_idxentry *) ((uintptr_t) a_idxe + 4); if (tsk_verbose) tsk_fprintf(stderr, "ntfs_proc_idxentry: Skipping because of invalid times\n"); continue; } } /* For all fname entries, there will exist a DOS style 8.3 * entry. We don't process those because we already processed * them before in their full version. If the type is * full POSIX or WIN32 that does not satisfy DOS, then a * type NTFS_FNAME_DOS will exist. If the name is WIN32, * but already satisfies DOS, then a type NTFS_FNAME_WINDOS * will exist * * Note that we could be missing some info from deleted files * if the windows version was deleted and the DOS wasn't... * * @@@ This should be added to the shrt_name entry of TSK_FS_NAME. The short * name entry typically comes after the long name */ if (fname->nspace == NTFS_FNAME_DOS) { if (tsk_verbose) tsk_fprintf(stderr, "ntfs_proc_idxentry: Skipping because of name space: %d\n", fname->nspace); goto incr_entry; } /* Copy it into the generic form */ if (ntfs_dent_copy(a_ntfs, a_idxe, fs_name)) { if (tsk_verbose) tsk_fprintf(stderr, "ntfs_proc_idxentry: Skipping because error copying dent_entry\n"); goto incr_entry; } /* * Check if this entry is deleted * * The final check is to see if the end of this entry is * within the space that the idxallocbuf claimed was valid OR * if the parent directory is deleted */ if ((a_is_del == 1) || (tsk_getu16(fs->endian, a_idxe->strlen) == 0) || (((uintptr_t) a_idxe + tsk_getu16(fs->endian, a_idxe->idxlen)) > endaddr_alloc)) { fs_name->flags = TSK_FS_NAME_FLAG_UNALLOC; } else { fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; } if (tsk_verbose) tsk_fprintf(stderr, "ntfs_proc_idxentry: Entry Details of %s: Str Len: %" PRIu16 " Len to end after current: %" PRIu64 " flags: %x\n", fs_name->name, tsk_getu16(fs->endian, a_idxe->strlen), (uint64_t) (endaddr_alloc - (uintptr_t) a_idxe - tsk_getu16(fs->endian, a_idxe->idxlen)), fs_name->flags); if (tsk_fs_dir_add(a_fs_dir, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } incr_entry: /* the theory here is that deleted entries have strlen == 0 and * have been found to have idxlen == 16 * * if the strlen is 0, then guess how much the indexlen was * before it was deleted */ /* 16: size of idxentry before stream * 66: size of fname before name * 2*nlen: size of name (in unicode) */ if (tsk_getu16(fs->endian, a_idxe->strlen) == 0) { a_idxe = (ntfs_idxentry *) ((((uintptr_t) a_idxe + 16 + 66 + 2 * fname->nlen + 3) / 4) * 4); } else { a_idxe = (ntfs_idxentry *) ((uintptr_t) a_idxe + tsk_getu16(fs->endian, a_idxe->idxlen)); } } /* end of loop of index entries */ tsk_fs_name_free(fs_name); return TSK_OK; }
/* * Process the partition table at the sector address * * It is loaded into the internal sorted list */ static uint8_t gpt_load_table(TSK_MM_INFO * mm) { 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; DADDR_T taddr = mm->offset / mm->block_size + GPT_PART_SOFFSET; DADDR_T max_addr = (mm->img_info->size - mm->offset) / mm->block_size; // max sector if (tsk_verbose) tsk_fprintf(stderr, "gpt_load_table: Sector: %" PRIuDADDR "\n", taddr); cnt = tsk_mm_read_block_nobuf (mm, (char *) &dos_part, sizeof(dos_part), GPT_PART_SOFFSET); /* if -1, then tsk_errno is already set */ if (cnt != sizeof(dos_part)) { if (cnt != -1) { tsk_error_reset(); tsk_errno = TSK_ERR_MM_READ; } snprintf(tsk_errstr2, TSK_ERRSTR_L, "Error reading DOS safety partition table in Sector: %" PRIuDADDR, taddr); return 1; } /* Sanity Check */ if (tsk_mm_guessu16(mm, dos_part.magic, DOS_MAGIC)) { tsk_error_reset(); tsk_errno = TSK_ERR_MM_MAGIC; snprintf(tsk_errstr, TSK_ERRSTR_L, "Missing DOS safety partition (invalid magic) (Sector: %" PRIuDADDR ")", taddr); return 1; } if (dos_part.ptable[0].ptype != GPT_DOS_TYPE) { tsk_error_reset(); tsk_errno = TSK_ERR_MM_MAGIC; snprintf(tsk_errstr, TSK_ERRSTR_L, "Missing DOS safety partition (invalid type in table: %d)", dos_part.ptable[0].ptype); return 1; } if ((safe_str = tsk_malloc(16)) == NULL) return 1; snprintf(safe_str, 16, "Safety Table"); if (NULL == tsk_mm_part_add(mm, (DADDR_T) 0, (DADDR_T) 1, TSK_MM_PART_TYPE_DESC, safe_str, -1, -1)) return 1; /* Read the GPT header */ cnt = tsk_mm_read_block_nobuf (mm, (char *) &head, sizeof(head), GPT_PART_SOFFSET + 1); if (cnt != sizeof(head)) { if (cnt != -1) { tsk_error_reset(); tsk_errno = TSK_ERR_MM_READ; } snprintf(tsk_errstr2, TSK_ERRSTR_L, "GPT Header structure in Sector: %" PRIuDADDR, taddr + 1); return 1; } if (tsk_getu64(mm->endian, &head.signature) != GPT_HEAD_SIG) { tsk_error_reset(); tsk_errno = TSK_ERR_MM_MAGIC; snprintf(tsk_errstr, TSK_ERRSTR_L, "GPT Header: %" PRIx64, tsk_getu64(mm->endian, &head.signature)); return 1; } if ((head_str = tsk_malloc(16)) == NULL) return 1; snprintf(head_str, 16, "GPT Header"); if (NULL == tsk_mm_part_add(mm, (DADDR_T) 1, (DADDR_T) ((tsk_getu32(mm->endian, &head.head_size_b) + 511) / 512), TSK_MM_PART_TYPE_DESC, head_str, -1, -1)) return 1; /* Allocate a buffer for each table entry */ ent_size = tsk_getu32(mm->endian, &head.tab_size_b); if (ent_size < sizeof(gpt_entry)) { tsk_error_reset(); tsk_errno = TSK_ERR_MM_MAGIC; snprintf(tsk_errstr, TSK_ERRSTR_L, "Header reports partition entry size of %" PRIu32 " and not %zu", ent_size, sizeof(gpt_entry)); return 1; } if ((tab_str = tsk_malloc(20)) == NULL) return 1; snprintf(tab_str, 20, "Partition Table"); if (NULL == tsk_mm_part_add(mm, (DADDR_T) tsk_getu64(mm->endian, &head.tab_start_lba), (DADDR_T) ((ent_size * tsk_getu32(mm->endian, &head.tab_num_ent) + 511) / 512), TSK_MM_PART_TYPE_DESC, tab_str, -1, -1)) return 1; /* Process the partition table */ if ((ent_buf = tsk_malloc(mm->block_size)) == NULL) return 1; i = 0; for (a = 0; i < tsk_getu32(mm->endian, &head.tab_num_ent); a++) { char *name; /* Read a sector */ cnt = tsk_mm_read_block_nobuf(mm, ent_buf, mm->block_size, tsk_getu64(mm->endian, &head.tab_start_lba) + a); if (cnt != mm->block_size) { if (cnt != -1) { tsk_error_reset(); tsk_errno = TSK_ERR_MM_READ; } snprintf(tsk_errstr2, TSK_ERRSTR_L, "Error reading GPT partition table sector : %" PRIuDADDR, tsk_getu64(mm->endian, &head.tab_start_lba) + a); return 1; } /* Process the sector */ ent = (gpt_entry *) ent_buf; for (; (uintptr_t) ent < (uintptr_t) ent_buf + mm->block_size && i < tsk_getu32(mm->endian, &head.tab_num_ent); 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(mm->endian, ent->start_lba), tsk_getu64(mm->endian, ent->end_lba), tsk_getu64(mm->endian, ent->flags)); if (tsk_getu64(mm->endian, ent->start_lba) == 0) continue; if (tsk_getu64(mm->endian, ent->start_lba) > max_addr) { tsk_error_reset(); tsk_errno = TSK_ERR_MM_BLK_NUM; snprintf(tsk_errstr, TSK_ERRSTR_L, "gpt_load_table: Starting sector too large for image"); return 1; } if ((name = tsk_malloc(256)) == NULL) return 1; name16 = (UTF16 *) ((uintptr_t) ent->name); name8 = (UTF8 *) name; retVal = tsk_UTF16toUTF8(mm->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_mm_part_add(mm, (DADDR_T) tsk_getu64(mm->endian, ent->start_lba), (DADDR_T) (tsk_getu64(mm->endian, ent->end_lba) - tsk_getu64(mm->endian, ent->start_lba) + 1), TSK_MM_PART_TYPE_VOL, name, -1, i)) return 1; } } return 0; }
/** * \internal * Searches the root directory of an exFAT file system for an allocation bitmap * directory entry. If the entry is found, data from the entry is saved to a * FATFS_INFO object. * * @param [in, out] a_fatfs Generic FAT file system info structure. * @return 0 on success, 1 otherwise, per TSK convention. */ static uint8_t exfatfs_get_alloc_bitmap(FATFS_INFO *a_fatfs) { const char *func_name = "exfatfs_get_alloc_bitmap"; TSK_FS_INFO *fs = &(a_fatfs->fs_info); TSK_DADDR_T current_sector = 0; TSK_DADDR_T last_sector_of_data_area = 0; char *sector_buf = NULL; EXFATFS_ALLOC_BITMAP_DIR_ENTRY *dentry = NULL; uint64_t i = 0; uint64_t first_sector_of_alloc_bitmap = 0; uint64_t alloc_bitmap_length_in_bytes = 0; uint64_t last_sector_of_alloc_bitmap = 0; assert(a_fatfs != NULL); if ((sector_buf = (char*)tsk_malloc(a_fatfs->ssize)) == NULL) { return FATFS_FAIL; } last_sector_of_data_area = a_fatfs->firstdatasect + (a_fatfs->clustcnt * a_fatfs->csize) - 1; for (current_sector = a_fatfs->rootsect; current_sector < last_sector_of_data_area; current_sector++) { ssize_t bytes_read = 0; /* Read in a sector from the root directory. The allocation bitmap * directory entries will probably be near the beginning of the * directory, probably in the first sector. */ bytes_read = tsk_fs_read_block(fs, current_sector, sector_buf, a_fatfs->ssize); if (bytes_read != a_fatfs->ssize) { if (bytes_read >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_READ); } tsk_error_set_errstr2("%s: sector: %" PRIuDADDR, func_name, current_sector); free(sector_buf); return FATFS_FAIL; } /* Read the directory entries in the sector, looking for allocation * bitmap entries. There will be one entry unless the file system is * TexFAT (transactional exFAT), in which case there will be two. */ for (i = 0; i < a_fatfs->ssize; i += sizeof(FATFS_DENTRY)) { dentry = (EXFATFS_ALLOC_BITMAP_DIR_ENTRY*)&(sector_buf[i]); /* The type of the directory entry is encoded in the first byte * of the entry. See EXFATFS_DIR_ENTRY_TYPE_ENUM. */ if (exfatfs_get_enum_from_type(dentry->entry_type) == EXFATFS_DIR_ENTRY_TYPE_ALLOC_BITMAP) { /* Do an in-depth test. */ if (!exfatfs_is_alloc_bitmap_dentry((FATFS_DENTRY*)dentry, FATFS_DATA_UNIT_ALLOC_STATUS_UNKNOWN, a_fatfs)) { continue; } /* The first bit of the flags byte is 0 for the first * allocation bitmap directory entry and 1 for the second * bitmap directory entry. If TexFAT is in use and there are * two allocation bitmaps, the first bitmap should be the * stable copy of the last known good allocation bitmap. * Therefore, the SleuthKit will use the first bitmap to * determine which clusters are allocated. */ if (~(dentry->flags & 0x01)) { first_sector_of_alloc_bitmap = FATFS_CLUST_2_SECT(a_fatfs, tsk_getu32(fs->endian, dentry->first_cluster_of_bitmap)); alloc_bitmap_length_in_bytes = tsk_getu64(fs->endian, dentry->length_of_alloc_bitmap_in_bytes); last_sector_of_alloc_bitmap = first_sector_of_alloc_bitmap + (roundup(alloc_bitmap_length_in_bytes, a_fatfs->ssize) / a_fatfs->ssize) - 1; /* The allocation bitmap must lie within the boundaries of the data area. * It also must be big enough for the number of clusters reported in the VBR. */ if ((first_sector_of_alloc_bitmap >= a_fatfs->firstdatasect) && (last_sector_of_alloc_bitmap <= last_sector_of_data_area) && (alloc_bitmap_length_in_bytes >= (a_fatfs->clustcnt + 7) / 8)) { a_fatfs->EXFATFS_INFO.first_sector_of_alloc_bitmap = first_sector_of_alloc_bitmap; a_fatfs->EXFATFS_INFO.length_of_alloc_bitmap_in_bytes = alloc_bitmap_length_in_bytes; free(sector_buf); return FATFS_OK; } } } } } free(sector_buf); return FATFS_FAIL; }
/** * \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; }
/** \internal * Read an indirect block and process the contents to make a runlist from the pointers. * * @param fs File system to analyze * @param fs_attr Structure to save run data into * @param fs_attr_indir Structure to save addresses of indirect block pointers in * @param buf Buffers to read block data into (0 is block sized, 1+ are DADDR_T arrays based on FS type) * @param level Indirection level that this will process at (1+) * @param addr Address of block to read * @param length Length of file remaining * * @returns the number of bytes processed during call and -1 if an error occurred */ static TSK_OFF_T unix_make_data_run_indirect(TSK_FS_INFO * fs, TSK_FS_ATTR * fs_attr, TSK_FS_ATTR * fs_attr_indir, char *buf[], int level, TSK_DADDR_T addr, TSK_OFF_T length) { char *myname = "unix_make_data_run_indirect"; size_t addr_cnt = 0; TSK_DADDR_T *myaddrs = (TSK_DADDR_T *) buf[level]; TSK_OFF_T length_remain = length; TSK_OFF_T retval; size_t fs_bufsize; size_t fs_blen; TSK_FS_ATTR_RUN *data_run; if (tsk_verbose) tsk_fprintf(stderr, "%s: level %d block %" PRIuDADDR "\n", myname, level, addr); // block_size is a fragment size in UFS, so we need to maintain length in fragments if (TSK_FS_TYPE_ISFFS(fs->ftype)) { FFS_INFO *ffs = (FFS_INFO *) fs; fs_blen = ffs->ffsbsize_f; fs_bufsize = ffs->ffsbsize_b; } else { fs_blen = 1; fs_bufsize = fs->block_size; } if (addr > fs->last_block) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_INODE_COR); tsk_error_set_errstr("unix: Indirect block address too large: %" PRIuDADDR "", addr); return -1; } // make a non-resident run data_run = tsk_fs_attr_run_alloc(); if (data_run == NULL) return -1; data_run->addr = addr; data_run->len = fs_blen; /* * Read a block of disk addresses. */ // sparse if (addr == 0) { memset(buf[0], 0, fs_bufsize); data_run->flags = TSK_FS_ATTR_RUN_FLAG_SPARSE; } else { ssize_t cnt; // read the data into the scratch buffer cnt = tsk_fs_read_block(fs, addr, buf[0], fs_bufsize); if (cnt != fs_bufsize) { if (cnt >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_READ); } tsk_error_set_errstr2("unix_make_data_run_indir: Block %" PRIuDADDR, addr); return -1; } } // save the run tsk_fs_attr_append_run(fs, fs_attr_indir, data_run); // convert the raw addresses to the correct endian ordering if ((fs->ftype == TSK_FS_TYPE_FFS1) || (fs->ftype == TSK_FS_TYPE_FFS1B) || (TSK_FS_TYPE_ISEXT(fs->ftype))) { size_t n; uint32_t *iaddr = (uint32_t *) buf[0]; addr_cnt = fs_bufsize / sizeof(*iaddr); for (n = 0; n < addr_cnt; n++) { myaddrs[n] = tsk_getu32(fs->endian, (uint8_t *) & iaddr[n]); } } else if (fs->ftype == TSK_FS_TYPE_FFS2) { size_t n; uint64_t *iaddr = (uint64_t *) buf[0]; addr_cnt = fs_bufsize / sizeof(*iaddr); for (n = 0; n < addr_cnt; n++) { myaddrs[n] = tsk_getu64(fs->endian, (uint8_t *) & iaddr[n]); } } // pass the addresses to the next level if (level == 1) { retval = unix_make_data_run_direct(fs, fs_attr, myaddrs, addr_cnt, length_remain); if (retval != -1) { length_remain -= retval; } } else { size_t i; retval = 0; for (i = 0; i < addr_cnt && retval != -1; i++) { retval = unix_make_data_run_indirect(fs, fs_attr, fs_attr_indir, buf, level - 1, myaddrs[i], length_remain); if (retval == -1) { break; } else { length_remain -= retval; } } } if (retval == -1) return -1; else return length - length_remain; }
/* * copy the index (directory) entry into the generic structure * * uses the global variables 'dirs' and 'depth' * * Returns 1 on eror and 0 on success */ static uint8_t ntfs_dent_copy(NTFS_INFO * ntfs, NTFS_DINFO * dinfo, ntfs_idxentry * idxe, TSK_FS_DENT * fs_dent) { ntfs_attr_fname *fname = (ntfs_attr_fname *) & idxe->stream; TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info; UTF16 *name16; UTF8 *name8; int retVal; int i; fs_dent->inode = tsk_getu48(fs->endian, idxe->file_ref); name16 = (UTF16 *) & fname->name; name8 = (UTF8 *) fs_dent->name; retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16, (UTF16 *) ((uintptr_t) name16 + fname->nlen * 2), &name8, (UTF8 *) ((uintptr_t) name8 + fs_dent->name_max), TSKlenientConversion); if (retVal != TSKconversionOK) { *name8 = '\0'; if (tsk_verbose) tsk_fprintf(stderr, "Error converting NTFS name to UTF8: %d %" PRIuINUM, retVal, fs_dent->inode); } /* Make sure it is NULL Terminated */ if ((uintptr_t) name8 > (uintptr_t) fs_dent->name + fs_dent->name_max) fs_dent->name[fs_dent->name_max] = '\0'; else *name8 = '\0'; /* Clean up name */ i = 0; while (fs_dent->name[i] != '\0') { if (TSK_IS_CNTRL(fs_dent->name[i])) fs_dent->name[i] = '^'; i++; } /* copy the path data */ fs_dent->path = dinfo->dirs; fs_dent->pathdepth = dinfo->depth; /* Get the actual inode */ if (fs_dent->fsi != NULL) tsk_fs_inode_free(fs_dent->fsi); if (NULL == (fs_dent->fsi = fs->inode_lookup(fs, fs_dent->inode))) { if (tsk_verbose) { tsk_fprintf(stderr, "ntfs_dent_copy: error looking up inode: %" PRIuINUM "\n", fs_dent->inode); tsk_error_print(stderr); tsk_error_reset(); } } if (tsk_getu64(fs->endian, fname->flags) & NTFS_FNAME_FLAGS_DIR) fs_dent->ent_type = TSK_FS_DENT_TYPE_DIR; else fs_dent->ent_type = TSK_FS_DENT_TYPE_REG; fs_dent->flags = 0; return 0; }
/** * Process a lsit of index entries and call the callback for * each. * * @param list_seen List of directories that have already been analyzed * @param idxe Buffer with index entries to process * @param idxe_len Length of idxe buffer (in bytes) * @param used_len Length of data as reported by idexlist header (everything * after which and less then idxe_len is considered deleted) * @param flags (All we care about is ALLOC and UNALLOC) * @param action Callback * @param ptr Pointer to data to pass to callback * * @returns 1 to stop, 0 on success, and -1 on error */ static int ntfs_dent_idxentry(NTFS_INFO * ntfs, NTFS_DINFO * dinfo, TSK_LIST ** list_seen, ntfs_idxentry * idxe, uint32_t idxe_len, uint32_t used_len, int flags, TSK_FS_DENT_TYPE_WALK_CB action, void *ptr) { uintptr_t endaddr, endaddr_alloc; TSK_FS_DENT *fs_dent; TSK_FS_INFO *fs = (TSK_FS_INFO *) & ntfs->fs_info; if ((fs_dent = tsk_fs_dent_alloc(NTFS_MAXNAMLEN_UTF8, 0)) == NULL) { return -1; } if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dent_idxentry: Processing index entry: %" PRIu64 " Size: %" PRIu32 " Len: %" PRIu32 " Flags: %x\n", (uint64_t) ((uintptr_t) idxe), idxe_len, used_len, flags); /* Sanity check */ if (idxe_len < used_len) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_INT; snprintf(tsk_errstr, TSK_ERRSTR_L, "ntfs_dent_idxentry: Allocated length of index entries is larger than buffer length"); return 1; } /* where is the end of the buffer */ endaddr = ((uintptr_t) idxe + idxe_len); /* where is the end of the allocated data */ endaddr_alloc = ((uintptr_t) idxe + used_len); /* cycle through the index entries, based on provided size */ while (((uintptr_t) & (idxe->stream) + sizeof(ntfs_attr_fname)) < endaddr) { ntfs_attr_fname *fname = (ntfs_attr_fname *) & idxe->stream; if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dent_idxentry: New IdxEnt: %" PRIu64 " $FILE_NAME Entry: %" PRIu64 " File Ref: %" PRIu64 " IdxEnt Len: %" PRIu16 " StrLen: %" PRIu16 "\n", (uint64_t) ((uintptr_t) idxe), (uint64_t) ((uintptr_t) fname), (uint64_t) tsk_getu48(fs->endian, idxe->file_ref), tsk_getu16(fs->endian, idxe->idxlen), tsk_getu16(fs->endian, idxe->strlen)); /* perform some sanity checks on index buffer head * and advance by 4-bytes if invalid */ if ((tsk_getu48(fs->endian, idxe->file_ref) > fs->last_inum) || (tsk_getu48(fs->endian, idxe->file_ref) < fs->first_inum) || (tsk_getu16(fs->endian, idxe->idxlen) <= tsk_getu16(fs->endian, idxe->strlen)) || (tsk_getu16(fs->endian, idxe->idxlen) % 4) || (tsk_getu16(fs->endian, idxe->idxlen) > idxe_len)) { idxe = (ntfs_idxentry *) ((uintptr_t) idxe + 4); continue; } /* do some sanity checks on the deleted entries */ if ((tsk_getu16(fs->endian, idxe->strlen) == 0) || (((uintptr_t) idxe + tsk_getu16(fs->endian, idxe->idxlen)) > endaddr_alloc)) { /* name space checks */ if ((fname->nspace != NTFS_FNAME_POSIX) && (fname->nspace != NTFS_FNAME_WIN32) && (fname->nspace != NTFS_FNAME_DOS) && (fname->nspace != NTFS_FNAME_WINDOS)) { idxe = (ntfs_idxentry *) ((uintptr_t) idxe + 4); if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dent_idxentry: Skipping because of invalid name space\n"); continue; } if ((tsk_getu64(fs->endian, fname->alloc_fsize) < tsk_getu64(fs->endian, fname->real_fsize)) || (fname->nlen == 0) || (*(uint8_t *) & fname->name == 0)) { idxe = (ntfs_idxentry *) ((uintptr_t) idxe + 4); if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dent_idxentry: Skipping because of reported file sizes, name length, or NULL name\n"); continue; } if ((is_time(tsk_getu64(fs->endian, fname->crtime)) == 0) || (is_time(tsk_getu64(fs->endian, fname->atime)) == 0) || (is_time(tsk_getu64(fs->endian, fname->mtime)) == 0)) { idxe = (ntfs_idxentry *) ((uintptr_t) idxe + 4); if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dent_idxentry: Skipping because of invalid times\n"); continue; } } /* For all fname entries, there will exist a DOS style 8.3 * entry. We don't process those because we already processed * them before in their full version. If the type is * full POSIX or WIN32 that does not satisfy DOS, then a * type NTFS_FNAME_DOS will exist. If the name is WIN32, * but already satisfies DOS, then a type NTFS_FNAME_WINDOS * will exist * * Note that we could be missing some info from deleted files * if the windows version was deleted and the DOS wasn't... * * @@@ This should be added to the shrt_name entry of TSK_FS_DENT. The short * name entry typically comes after the long name */ if (fname->nspace == NTFS_FNAME_DOS) { if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dent_idxentry: Skipping because of name space: %d\n", fname->nspace); goto incr_entry; } /* Copy it into the generic form */ if (ntfs_dent_copy(ntfs, dinfo, idxe, fs_dent)) { if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dent_idxentry: Skipping because error copying dent_entry\n"); goto incr_entry; } /* * Check if this entry is deleted * * The final check is to see if the end of this entry is * within the space that the idxallocbuf claimed was valid */ if ((tsk_getu16(fs->endian, idxe->strlen) == 0) || (((uintptr_t) idxe + tsk_getu16(fs->endian, idxe->idxlen)) > endaddr_alloc)) { /* we know deleted entries with an inode of 0 are not legit because * that is the MFT value. Free it so it does not confuse * people with invalid data */ if ((fs_dent->inode == 0) && (fs_dent->fsi)) { tsk_fs_inode_free(fs_dent->fsi); fs_dent->fsi = NULL; } fs_dent->flags = TSK_FS_DENT_FLAG_UNALLOC; } else { fs_dent->flags = TSK_FS_DENT_FLAG_ALLOC; } if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dent_idxentry: Entry Details of %s: Str Len: %" PRIu16 " Len to end after current: %" PRIu64 " flags: %x\n", fs_dent->name, tsk_getu16(fs->endian, idxe->strlen), (uint64_t) (endaddr_alloc - (uintptr_t) idxe - tsk_getu16(fs->endian, idxe->idxlen)), fs_dent->flags); if ((flags & fs_dent->flags) == fs_dent->flags) { int retval = action(fs, fs_dent, ptr); if (retval == TSK_WALK_STOP) { tsk_fs_dent_free(fs_dent); return 1; } else if (retval == TSK_WALK_ERROR) { tsk_fs_dent_free(fs_dent); return -1; } } /* Recurse if we need to */ if ((fs_dent->flags & TSK_FS_DENT_FLAG_ALLOC) && (flags & TSK_FS_DENT_FLAG_RECURSE) && (!TSK_FS_ISDOT(fs_dent->name)) && (fs_dent->fsi) && ((fs_dent->fsi->mode & TSK_FS_INODE_MODE_FMT) == TSK_FS_INODE_MODE_DIR) && (fs_dent->inode)) { int depth_added = 0; /* Make sure we do not get into an infinite loop */ if (0 == tsk_list_find(*list_seen, fs_dent->inode)) { if (tsk_list_add(list_seen, fs_dent->inode)) { tsk_fs_dent_free(fs_dent); return -1; } if ((dinfo->depth < MAX_DEPTH) && (DIR_STRSZ > strlen(dinfo->dirs) + strlen(fs_dent->name))) { dinfo->didx[dinfo->depth] = &dinfo->dirs[strlen(dinfo->dirs)]; strncpy(dinfo->didx[dinfo->depth], fs_dent->name, DIR_STRSZ - strlen(dinfo->dirs)); strncat(dinfo->dirs, "/", DIR_STRSZ); depth_added = 1; } dinfo->depth++; if (ntfs_dent_walk_lcl(&(ntfs->fs_info), dinfo, list_seen, fs_dent->inode, flags, action, ptr)) { if (tsk_verbose) tsk_fprintf(stderr, "Error recursing into directory\n"); tsk_error_reset(); } dinfo->depth--; if (depth_added) *dinfo->didx[dinfo->depth] = '\0'; } } /* end of recurse */ incr_entry: /* the theory here is that deleted entries have strlen == 0 and * have been found to have idxlen == 16 * * if the strlen is 0, then guess how much the indexlen was * before it was deleted */ /* 16: size of idxentry before stream * 66: size of fname before name * 2*nlen: size of name (in unicode) */ if (tsk_getu16(fs->endian, idxe->strlen) == 0) { idxe = (ntfs_idxentry *) ((((uintptr_t) idxe + 16 + 66 + 2 * fname->nlen + 3) / 4) * 4); } else { idxe = (ntfs_idxentry *) ((uintptr_t) idxe + tsk_getu16(fs->endian, idxe->idxlen)); } } /* end of loop of index entries */ tsk_fs_dent_free(fs_dent); return 0; }