/* Used to process orphan directories and make sure that their contents * are now marked as reachable */ static TSK_WALK_RET_ENUM load_orphan_dir_walk_cb(TSK_FS_FILE * a_fs_file, const char *a_path, void *a_ptr) { FIND_ORPHAN_DATA *data = (FIND_ORPHAN_DATA *) a_ptr; // ignore DOT entries if ((a_fs_file->name) && (a_fs_file->name->name) && (TSK_FS_ISDOT(a_fs_file->name->name))) return TSK_WALK_CONT; // add this entry to the orphan list if (a_fs_file->meta) { tsk_list_add(&data->orphan_subdir_list, a_fs_file->meta->addr); /* FAT file systems spend a lot of time hunting for parent * directory addresses, so we put this code in here to save * the info when we have it. */ if ((a_fs_file->meta->type == TSK_FS_META_TYPE_DIR) && (TSK_FS_TYPE_ISFAT(a_fs_file->fs_info->ftype))) { if (fatfs_dir_buf_add((FATFS_INFO *) a_fs_file->fs_info, a_fs_file->name->par_addr, a_fs_file->meta->addr)) return TSK_WALK_ERROR; } } return TSK_WALK_CONT; }
/* Used to process orphan directories and make sure that their contents * are now marked as reachable */ static TSK_WALK_RET_ENUM load_orphan_dir_walk_cb(TSK_FS_FILE * a_fs_file, const char *a_path, void *a_ptr) { FIND_ORPHAN_DATA *data = (FIND_ORPHAN_DATA *) a_ptr; // ignore DOT entries if ((a_fs_file->name) && (a_fs_file->name->name) && (TSK_FS_ISDOT(a_fs_file->name->name))) return TSK_WALK_CONT; // add this entry to the orphan list if (a_fs_file->meta) { /* Stop if we hit an allocated entry. We shouldn't get these, but did * have some trouble images that went into allocated clusters on * a FAT file system. */ if (a_fs_file->meta->flags & TSK_FS_META_FLAG_ALLOC) { if (tsk_verbose) { tsk_fprintf(stderr, "load_orphan_dir_walk_cb: Skipping an allocated file (ID: %" PRIuINUM ")\n", a_fs_file->meta->addr); } return TSK_WALK_STOP; } /* check if we have already added it as an orphan (in a subdirectory) * Not entirely sure how possible this is, but it was added while * debugging an infinite loop problem. */ if (tsk_list_find(data->orphan_subdir_list, a_fs_file->meta->addr)) { if (tsk_verbose) fprintf(stderr, "load_orphan_dir_walk_cb: Detected loop with address %" PRIuINUM, a_fs_file->meta->addr); return TSK_WALK_STOP; } tsk_list_add(&data->orphan_subdir_list, a_fs_file->meta->addr); /* FAT file systems spend a lot of time hunting for parent * directory addresses, so we put this code in here to save * the info when we have it. */ if ((a_fs_file->meta->type == TSK_FS_META_TYPE_DIR) && (TSK_FS_TYPE_ISFAT(a_fs_file->fs_info->ftype))) { if (fatfs_dir_buf_add((FATFS_INFO *) a_fs_file->fs_info, a_fs_file->name->par_addr, a_fs_file->meta->addr)) return TSK_WALK_ERROR; } } return TSK_WALK_CONT; }
/* dir_walk local function that is used for recursive calls. Callers * should initially call the non-local version. */ static TSK_WALK_RET_ENUM tsk_fs_dir_walk_lcl(TSK_FS_INFO * a_fs, DENT_DINFO * a_dinfo, TSK_INUM_T a_addr, TSK_FS_DIR_WALK_FLAG_ENUM a_flags, TSK_FS_DIR_WALK_CB a_action, void *a_ptr) { TSK_FS_DIR *fs_dir; TSK_FS_FILE *fs_file; size_t i; // get the list of entries in the directory if ((fs_dir = tsk_fs_dir_open_meta(a_fs, a_addr)) == NULL) { return TSK_WALK_ERROR; } /* Allocate a file structure for the callbacks. We * will allocate fs_meta structures as needed and * point into the fs_dir structure for the names. */ if ((fs_file = tsk_fs_file_alloc(a_fs)) == NULL) { tsk_fs_dir_close(fs_dir); return TSK_WALK_ERROR; } for (i = 0; i < fs_dir->names_used; i++) { TSK_WALK_RET_ENUM retval; /* Point name to the buffer of names. We need to be * careful about resetting this before we free fs_file */ fs_file->name = (TSK_FS_NAME *) & fs_dir->names[i]; /* load the fs_meta structure if possible. * Must have non-zero inode addr or have allocated name (if inode is 0) */ if (((fs_file->name->meta_addr) || (fs_file->name->flags & TSK_FS_NAME_FLAG_ALLOC))) { if (a_fs->file_add_meta(a_fs, fs_file, fs_file->name->meta_addr)) { if (tsk_verbose) tsk_error_print(stderr); tsk_error_reset(); } } // call the action if we have the right flags. if ((fs_file->name->flags & a_flags) == fs_file->name->flags) { retval = a_action(fs_file, a_dinfo->dirs, a_ptr); if (retval == TSK_WALK_STOP) { tsk_fs_dir_close(fs_dir); fs_file->name = NULL; tsk_fs_file_close(fs_file); /* free the list -- fs_dir_walk has no way * of knowing that we stopped early w/out error. */ if (a_dinfo->save_inum_named) { tsk_list_free(a_dinfo->list_inum_named); a_dinfo->list_inum_named = NULL; a_dinfo->save_inum_named = 0; } return TSK_WALK_STOP; } else if (retval == TSK_WALK_ERROR) { tsk_fs_dir_close(fs_dir); fs_file->name = NULL; tsk_fs_file_close(fs_file); return TSK_WALK_ERROR; } } // save the inode info for orphan finding - if requested if ((a_dinfo->save_inum_named) && (fs_file->meta) && (fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC)) { if (tsk_list_add(&a_dinfo->list_inum_named, fs_file->meta->addr)) { // if there is an error, then clear the list tsk_list_free(a_dinfo->list_inum_named); a_dinfo->list_inum_named = NULL; a_dinfo->save_inum_named = 0; } } /* Recurse into a directory if: * - Both dir entry and inode have DIR type (or name is undefined) * - Recurse flag is set * - dir entry is allocated OR both are unallocated * - not one of the '.' or '..' entries * - A Non-Orphan Dir or the Orphan Dir with the NOORPHAN flag not set. */ if (((fs_file->name->type == TSK_FS_NAME_TYPE_DIR) || (fs_file->name->type == TSK_FS_NAME_TYPE_UNDEF)) && (fs_file->meta) && (fs_file->meta->type == TSK_FS_META_TYPE_DIR) && (a_flags & TSK_FS_DIR_WALK_FLAG_RECURSE) && ((fs_file->name->flags & TSK_FS_NAME_FLAG_ALLOC) || ((fs_file->name->flags & TSK_FS_NAME_FLAG_UNALLOC) && (fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC)) ) && (!TSK_FS_ISDOT(fs_file->name->name)) && ((fs_file->name->meta_addr != TSK_FS_ORPHANDIR_INUM(a_fs)) || ((a_flags & TSK_FS_DIR_WALK_FLAG_NOORPHAN) == 0)) ) { /* Make sure we do not get into an infinite loop */ if (0 == tsk_stack_find(a_dinfo->stack_seen, fs_file->name->meta_addr)) { int depth_added = 0; uint8_t save_bak = 0; if (tsk_stack_push(a_dinfo->stack_seen, fs_file->name->meta_addr)) { tsk_fs_dir_close(fs_dir); fs_file->name = NULL; tsk_fs_file_close(fs_file); return TSK_WALK_ERROR; } if ((a_dinfo->depth < MAX_DEPTH) && (DIR_STRSZ > strlen(a_dinfo->dirs) + strlen(fs_file->name->name))) { a_dinfo->didx[a_dinfo->depth] = &a_dinfo->dirs[strlen(a_dinfo->dirs)]; strncpy(a_dinfo->didx[a_dinfo->depth], fs_file->name->name, DIR_STRSZ - strlen(a_dinfo->dirs)); strncat(a_dinfo->dirs, "/", DIR_STRSZ); depth_added = 1; } a_dinfo->depth++; /* We do not want to save info about named unalloc files * when we go into the Orphan directory (because then we have * no orphans). So, disable it for this recursion. */ if (fs_file->name->meta_addr == TSK_FS_ORPHANDIR_INUM(a_fs)) { save_bak = a_dinfo->save_inum_named; a_dinfo->save_inum_named = 0; } retval = tsk_fs_dir_walk_lcl(a_fs, a_dinfo, fs_file->name->meta_addr, a_flags, a_action, a_ptr); if (retval == TSK_WALK_ERROR) { /* If this fails because the directory could not be * loaded, then we still continue */ if (tsk_verbose) { tsk_fprintf(stderr, "tsk_fs_dir_walk_lcl: error reading directory: %" PRIuINUM "\n", fs_file->name->meta_addr); tsk_error_print(stderr); } tsk_error_reset(); } else if (retval == TSK_WALK_STOP) { tsk_fs_dir_close(fs_dir); fs_file->name = NULL; tsk_fs_file_close(fs_file); return TSK_WALK_STOP; } // reset the save status if (fs_file->name->meta_addr == TSK_FS_ORPHANDIR_INUM(a_fs)) { a_dinfo->save_inum_named = save_bak; } tsk_stack_pop(a_dinfo->stack_seen); a_dinfo->depth--; if (depth_added) *a_dinfo->didx[a_dinfo->depth] = '\0'; } else { if (tsk_verbose) fprintf(stderr, "tsk_fs_dir_walk_lcl: Loop detected with address %" PRIuINUM, fs_file->name->meta_addr); } } // remove the pointer to name buffer fs_file->name = NULL; // free the metadata if we allocated it if (fs_file->meta) { tsk_fs_meta_close(fs_file->meta); fs_file->meta = NULL; } } tsk_fs_dir_close(fs_dir); fs_file->name = NULL; tsk_fs_file_close(fs_file); return TSK_WALK_CONT; }
/* ** ** Read contents of directory block ** ** if entry is active call action with myflags set to TSK_FS_DENT_FLAG_ALLOC, if ** it is deleted then call action with TSK_FS_DENT_FLAG_UNALLOC. ** len is the size of buf ** ** return 1 to stop, 0 on success, and -1 on error */ static int ext2fs_dent_parse_block(EXT2FS_INFO * ext2fs, EXT2FS_DINFO * dinfo, TSK_LIST ** list_seen, char *buf, int len, int flags, TSK_FS_DENT_TYPE_WALK_CB action, void *ptr) { TSK_FS_INFO *fs = &(ext2fs->fs_info); int dellen = 0; int idx; uint16_t reclen; uint32_t inode; char *dirPtr; TSK_FS_DENT *fs_dent; int minreclen = 4; if ((fs_dent = tsk_fs_dent_alloc(EXT2FS_MAXNAMLEN + 1, 0)) == NULL) return -1; /* update each time by the actual length instead of the ** recorded length so we can view the deleted entries */ for (idx = 0; idx <= len - EXT2FS_DIRSIZ_lcl(1); idx += minreclen) { unsigned int namelen; dirPtr = &buf[idx]; if (ext2fs->deentry_type == EXT2_DE_V1) { ext2fs_dentry1 *dir = (ext2fs_dentry1 *) dirPtr; inode = tsk_getu32(fs->endian, dir->inode); namelen = tsk_getu16(fs->endian, dir->name_len); reclen = tsk_getu16(fs->endian, dir->rec_len); } else { ext2fs_dentry2 *dir = (ext2fs_dentry2 *) dirPtr; inode = tsk_getu32(fs->endian, dir->inode); namelen = dir->name_len; reclen = tsk_getu16(fs->endian, dir->rec_len); } minreclen = EXT2FS_DIRSIZ_lcl(namelen); /* ** Check if we may have a valid directory entry. If we don't, ** then increment to the next word and try again. */ if ((inode > fs->last_inum) || (inode < 0) || (namelen > EXT2FS_MAXNAMLEN) || (namelen <= 0) || (reclen < minreclen) || (reclen % 4) || (idx + reclen > len)) { minreclen = 4; if (dellen > 0) dellen -= 4; continue; } /* Before we process an entry in unallocated space, make * sure that it also ends in the unalloc space */ if ((dellen) && (dellen < minreclen)) { minreclen = 4; if (dellen > 0) dellen -= 4; continue; } if (ext2fs_dent_copy(ext2fs, dinfo, dirPtr, fs_dent)) { tsk_fs_dent_free(fs_dent); return -1; } /* Do we have a deleted entry? */ if ((dellen > 0) || (inode == 0)) { fs_dent->flags = TSK_FS_DENT_FLAG_UNALLOC; if (dellen > 0) dellen -= minreclen; if (flags & TSK_FS_DENT_FLAG_UNALLOC) { int retval; 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; } } } /* We have a non-deleted entry */ else { fs_dent->flags = TSK_FS_DENT_FLAG_ALLOC; if (flags & TSK_FS_DENT_FLAG_ALLOC) { int retval; 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; } } } /* If the actual length is shorter then the ** recorded length, then the next entry(ies) have been ** deleted. Set dellen to the length of data that ** has been deleted ** ** Because we aren't guaranteed with Ext2FS that the next ** entry begins right after this one, we will check to ** see if the difference is less than a possible entry ** before we waste time searching it */ if ((reclen - minreclen >= EXT2FS_DIRSIZ_lcl(1)) && (dellen <= 0)) dellen = reclen - minreclen; /* we will be recursing directories */ if ((fs_dent->flags & TSK_FS_DENT_FLAG_ALLOC) && (flags & TSK_FS_DENT_FLAG_RECURSE) && (!TSK_FS_ISDOT(fs_dent->name)) && ((fs_dent->fsi->mode & TSK_FS_INODE_MODE_FMT) == TSK_FS_INODE_MODE_DIR)) { 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 (ext2fs_dent_walk_lcl(&(ext2fs->fs_info), dinfo, list_seen, fs_dent->inode, flags, action, ptr)) { /* If this fails because the directory could not be * loaded, then we still continue */ if (tsk_verbose) { tsk_fprintf(stderr, "ffs_dent_parse_block: error reading directory: %" PRIuINUM "\n", fs_dent->inode); tsk_error_print(stderr); } tsk_error_reset(); } dinfo->depth--; if (depth_added) *dinfo->didx[dinfo->depth] = '\0'; } } } tsk_fs_dent_free(fs_dent); return 0; } /* end ext2fs_dent_parse_block() */
/** * \internal * Prints file system layout 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_layout_info(TSK_FS_INFO *a_fs, FILE *a_hFile) { const char *func_name = "exfatfs_fsstat_fs_layout_info"; FATFS_INFO *fatfs = NULL; uint64_t i = 0; TSK_DADDR_T fat_base_sect = 0; TSK_DADDR_T clust_heap_len = 0; TSK_LIST *root_dir_clusters_seen = NULL; TSK_DADDR_T current_cluster; TSK_DADDR_T next_cluster = 0; assert(a_fs != NULL); assert(a_hFile != NULL); fatfs = (FATFS_INFO*)a_fs; tsk_fprintf(a_hFile, "\nFile System Layout (in sectors):\n"); tsk_fprintf(a_hFile, "Range: %" PRIuDADDR " - %" PRIuDADDR "\n", a_fs->first_block, a_fs->last_block); if (a_fs->last_block != a_fs->last_block_act) tsk_fprintf(a_hFile, "Range in Image: %" PRIuDADDR " - %" PRIuDADDR "\n", a_fs->first_block, a_fs->last_block_act); tsk_fprintf(a_hFile, "* Reserved: 0 - %" PRIuDADDR "\n", fatfs->firstfatsect - 1); tsk_fprintf(a_hFile, "** Volume Boot Record (VBR): 0 - 11\n"); tsk_fprintf(a_hFile, "*** Boot Sector (MBR): 0\n"); tsk_fprintf(a_hFile, "** Backup Volume Boot Record (VBR): 12 - 23\n"); tsk_fprintf(a_hFile, "*** Backup Boot Sector (MBR): 12\n"); tsk_fprintf(a_hFile, "** FAT alignment space: 24 - %" PRIuDADDR "\n", fatfs->firstfatsect - 1); for (i = 0; i < fatfs->numfat; i++) { fat_base_sect = fatfs->firstfatsect + i * (fatfs->sectperfat); tsk_fprintf(a_hFile, "* FAT %" PRIuDADDR ": %" PRIuDADDR " - %" PRIuDADDR "\n", i + 1, fat_base_sect, (fat_base_sect + fatfs->sectperfat - 1)); } if (fat_base_sect + fatfs->sectperfat < fatfs->firstdatasect) { tsk_fprintf(a_hFile, "* Data Area alignment space: %" PRIuDADDR " - %" PRIuDADDR "\n", fat_base_sect + fatfs->sectperfat, fatfs->firstdatasect - 1); } tsk_fprintf(a_hFile, "* Data Area: %" PRIuDADDR " - %" PRIuDADDR "\n", fatfs->firstdatasect, a_fs->last_block); clust_heap_len = fatfs->csize * (fatfs->lastclust - 1); tsk_fprintf(a_hFile, "** Cluster Heap: %" PRIuDADDR " - %" PRIuDADDR "\n", fatfs->firstclustsect, (fatfs->firstclustsect + clust_heap_len - 1)); /* Walk the FAT chain for the root directory. */ current_cluster = fatfs->rootsect; next_cluster = FATFS_SECT_2_CLUST(fatfs, fatfs->rootsect); while ((next_cluster) && (0 == FATFS_ISEOF(next_cluster, FATFS_32_MASK))) { TSK_DADDR_T nxt; current_cluster = next_cluster; /* Make sure we do not get into an infinite loop */ if (tsk_list_find(root_dir_clusters_seen, next_cluster)) { if (tsk_verbose) { tsk_fprintf(stderr, "%s : Loop found while determining root directory size\n", func_name); } break; } if (tsk_list_add(&root_dir_clusters_seen, next_cluster)) { tsk_list_free(root_dir_clusters_seen); root_dir_clusters_seen = NULL; return FATFS_FAIL; } if (fatfs_getFAT(fatfs, next_cluster, &nxt)) { break; } next_cluster = nxt; } tsk_list_free(root_dir_clusters_seen); root_dir_clusters_seen = NULL; tsk_fprintf(a_hFile, "*** Root Directory: %" PRIuDADDR " - %" PRIuDADDR "\n", fatfs->rootsect, (FATFS_CLUST_2_SECT(fatfs, current_cluster + 1) - 1)); if ((fatfs->firstclustsect + clust_heap_len - 1) != a_fs->last_block) { tsk_fprintf(a_hFile, "** Non-clustered: %" PRIuDADDR " - %" PRIuDADDR "\n", (fatfs->firstclustsect + clust_heap_len), a_fs->last_block); } return FATFS_OK; }
/** * Print details about the file system to a file handle. * * @param fs File system to print details on * @param hFile File handle to print text to * * @returns 1 on error and 0 on success */ static uint8_t fatxxfs_fsstat(TSK_FS_INFO * fs, FILE * hFile) { unsigned int i; TSK_DADDR_T next, snext, sstart, send; FATFS_INFO *fatfs = (FATFS_INFO *) fs; FATXXFS_SB *sb = (FATXXFS_SB*)fatfs->boot_sector_buffer; char *data_buf; FATXXFS_DENTRY *vol_label_dentry = NULL; ssize_t cnt; // clean up any error messages that are lying around tsk_error_reset(); if ((data_buf = (char *) tsk_malloc(fs->block_size)) == NULL) { return 1; } /* Read the root directory sector so that we can get the volume * label from it */ cnt = tsk_fs_read_block(fs, fatfs->rootsect, data_buf, fs->block_size); if (cnt != fs->block_size) { if (cnt >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_READ); } tsk_error_set_errstr2("fatxxfs_fsstat: root directory: %" PRIuDADDR, fatfs->rootsect); free(data_buf); return 1; } /* Find the dentry that is set as the volume label */ vol_label_dentry = NULL; if (fatfs->ssize <= fs->block_size) { FATXXFS_DENTRY *current_entry = (FATXXFS_DENTRY *) data_buf; for (i = 0; i < fatfs->ssize; i += sizeof(*current_entry)) { if (current_entry->attrib == FATFS_ATTR_VOLUME) { vol_label_dentry = current_entry; break; } current_entry++; } } /* Print the general file system information */ tsk_fprintf(hFile, "FILE SYSTEM INFORMATION\n"); tsk_fprintf(hFile, "--------------------------------------------\n"); tsk_fprintf(hFile, "File System Type: FAT"); if (fs->ftype == TSK_FS_TYPE_FAT12) tsk_fprintf(hFile, "12\n"); else if (fs->ftype == TSK_FS_TYPE_FAT16) tsk_fprintf(hFile, "16\n"); else if (fs->ftype == TSK_FS_TYPE_FAT32) tsk_fprintf(hFile, "32\n"); else tsk_fprintf(hFile, "\n"); tsk_fprintf(hFile, "\nOEM Name: %c%c%c%c%c%c%c%c\n", sb->oemname[0], sb->oemname[1], sb->oemname[2], sb->oemname[3], sb->oemname[4], sb->oemname[5], sb->oemname[6], sb->oemname[7]); if (fatfs->fs_info.ftype != TSK_FS_TYPE_FAT32) { tsk_fprintf(hFile, "Volume ID: 0x%" PRIx32 "\n", tsk_getu32(fs->endian, sb->a.f16.vol_id)); tsk_fprintf(hFile, "Volume Label (Boot Sector): %c%c%c%c%c%c%c%c%c%c%c\n", sb->a.f16.vol_lab[0], sb->a.f16.vol_lab[1], sb->a.f16.vol_lab[2], sb->a.f16.vol_lab[3], sb->a.f16.vol_lab[4], sb->a.f16.vol_lab[5], sb->a.f16.vol_lab[6], sb->a.f16.vol_lab[7], sb->a.f16.vol_lab[8], sb->a.f16.vol_lab[9], sb->a.f16.vol_lab[10]); if ((vol_label_dentry) && (vol_label_dentry->name[0])) { tsk_fprintf(hFile, "Volume Label (Root Directory): %c%c%c%c%c%c%c%c%c%c%c\n", vol_label_dentry->name[0], vol_label_dentry->name[1], vol_label_dentry->name[2], vol_label_dentry->name[3], vol_label_dentry->name[4], vol_label_dentry->name[5], vol_label_dentry->name[6], vol_label_dentry->name[7], vol_label_dentry->ext[0], vol_label_dentry->ext[1], vol_label_dentry->ext[2]); } else { tsk_fprintf(hFile, "Volume Label (Root Directory):\n"); } tsk_fprintf(hFile, "File System Type Label: %c%c%c%c%c%c%c%c\n", sb->a.f16.fs_type[0], sb->a.f16.fs_type[1], sb->a.f16.fs_type[2], sb->a.f16.fs_type[3], sb->a.f16.fs_type[4], sb->a.f16.fs_type[5], sb->a.f16.fs_type[6], sb->a.f16.fs_type[7]); } else { char *fat_fsinfo_buf; if ((fat_fsinfo_buf = (char *) tsk_malloc(sizeof(FATXXFS_FSINFO))) == NULL) { free(data_buf); return 1; } tsk_fprintf(hFile, "Volume ID: 0x%" PRIx32 "\n", tsk_getu32(fs->endian, sb->a.f32.vol_id)); tsk_fprintf(hFile, "Volume Label (Boot Sector): %c%c%c%c%c%c%c%c%c%c%c\n", sb->a.f32.vol_lab[0], sb->a.f32.vol_lab[1], sb->a.f32.vol_lab[2], sb->a.f32.vol_lab[3], sb->a.f32.vol_lab[4], sb->a.f32.vol_lab[5], sb->a.f32.vol_lab[6], sb->a.f32.vol_lab[7], sb->a.f32.vol_lab[8], sb->a.f32.vol_lab[9], sb->a.f32.vol_lab[10]); if ((vol_label_dentry) && (vol_label_dentry->name[0])) { tsk_fprintf(hFile, "Volume Label (Root Directory): %c%c%c%c%c%c%c%c%c%c%c\n", vol_label_dentry->name[0], vol_label_dentry->name[1], vol_label_dentry->name[2], vol_label_dentry->name[3], vol_label_dentry->name[4], vol_label_dentry->name[5], vol_label_dentry->name[6], vol_label_dentry->name[7], vol_label_dentry->ext[0], vol_label_dentry->ext[1], vol_label_dentry->ext[2]); } else { tsk_fprintf(hFile, "Volume Label (Root Directory):\n"); } tsk_fprintf(hFile, "File System Type Label: %c%c%c%c%c%c%c%c\n", sb->a.f32.fs_type[0], sb->a.f32.fs_type[1], sb->a.f32.fs_type[2], sb->a.f32.fs_type[3], sb->a.f32.fs_type[4], sb->a.f32.fs_type[5], sb->a.f32.fs_type[6], sb->a.f32.fs_type[7]); /* Process the FS info */ if (tsk_getu16(fs->endian, sb->a.f32.fsinfo)) { FATXXFS_FSINFO *fat_info; cnt = tsk_fs_read(fs, (TSK_DADDR_T) tsk_getu16(fs->endian, sb->a.f32.fsinfo) * fs->block_size, fat_fsinfo_buf, sizeof(FATXXFS_FSINFO)); if (cnt != sizeof(FATXXFS_FSINFO)) { if (cnt >= 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_READ); } tsk_error_set_errstr2 ("fatxxfs_fsstat: TSK_FS_TYPE_FAT32 FSINFO block: %" PRIuDADDR, (TSK_DADDR_T) tsk_getu16(fs->endian, sb->a.f32.fsinfo)); free(data_buf); free(fat_fsinfo_buf); return 1; } fat_info = (FATXXFS_FSINFO *) fat_fsinfo_buf; tsk_fprintf(hFile, "Next Free Sector (FS Info): %" PRIuDADDR "\n", FATFS_CLUST_2_SECT(fatfs, tsk_getu32(fs->endian, fat_info->nextfree))); tsk_fprintf(hFile, "Free Sector Count (FS Info): %" PRIu32 "\n", (tsk_getu32(fs->endian, fat_info->freecnt) * fatfs->csize)); free(fat_fsinfo_buf); } } free(data_buf); tsk_fprintf(hFile, "\nSectors before file system: %" PRIu32 "\n", tsk_getu32(fs->endian, sb->prevsect)); tsk_fprintf(hFile, "\nFile System Layout (in sectors)\n"); tsk_fprintf(hFile, "Total Range: %" PRIuDADDR " - %" PRIuDADDR "\n", fs->first_block, fs->last_block); if (fs->last_block != fs->last_block_act) tsk_fprintf(hFile, "Total Range in Image: %" PRIuDADDR " - %" PRIuDADDR "\n", fs->first_block, fs->last_block_act); tsk_fprintf(hFile, "* Reserved: 0 - %" PRIuDADDR "\n", fatfs->firstfatsect - 1); tsk_fprintf(hFile, "** Boot Sector: 0\n"); if (fatfs->fs_info.ftype == TSK_FS_TYPE_FAT32) { tsk_fprintf(hFile, "** FS Info Sector: %" PRIu16 "\n", tsk_getu16(fs->endian, sb->a.f32.fsinfo)); tsk_fprintf(hFile, "** Backup Boot Sector: %" PRIu16 "\n", tsk_getu16(fs->endian, sb->a.f32.bs_backup)); } for (i = 0; i < fatfs->numfat; i++) { TSK_DADDR_T base = fatfs->firstfatsect + i * (fatfs->sectperfat); tsk_fprintf(hFile, "* FAT %d: %" PRIuDADDR " - %" PRIuDADDR "\n", i, base, (base + fatfs->sectperfat - 1)); } tsk_fprintf(hFile, "* Data Area: %" PRIuDADDR " - %" PRIuDADDR "\n", fatfs->firstdatasect, fs->last_block); if (fatfs->fs_info.ftype != TSK_FS_TYPE_FAT32) { TSK_DADDR_T x = fatfs->csize * fatfs->clustcnt; tsk_fprintf(hFile, "** Root Directory: %" PRIuDADDR " - %" PRIuDADDR "\n", fatfs->firstdatasect, fatfs->firstclustsect - 1); tsk_fprintf(hFile, "** Cluster Area: %" PRIuDADDR " - %" PRIuDADDR "\n", fatfs->firstclustsect, (fatfs->firstclustsect + x - 1)); if ((fatfs->firstclustsect + x - 1) != fs->last_block) { tsk_fprintf(hFile, "** Non-clustered: %" PRIuDADDR " - %" PRIuDADDR "\n", (fatfs->firstclustsect + x), fs->last_block); } } else { TSK_LIST *list_seen = NULL; TSK_DADDR_T x = fatfs->csize * (fatfs->lastclust - 1); TSK_DADDR_T clust, clust_p; tsk_fprintf(hFile, "** Cluster Area: %" PRIuDADDR " - %" PRIuDADDR "\n", fatfs->firstclustsect, (fatfs->firstclustsect + x - 1)); clust_p = fatfs->rootsect; clust = FATFS_SECT_2_CLUST(fatfs, fatfs->rootsect); while ((clust) && (0 == FATFS_ISEOF(clust, FATFS_32_MASK))) { TSK_DADDR_T nxt; clust_p = clust; /* Make sure we do not get into an infinite loop */ if (tsk_list_find(list_seen, clust)) { if (tsk_verbose) tsk_fprintf(stderr, "Loop found while determining root directory size\n"); break; } if (tsk_list_add(&list_seen, clust)) { tsk_list_free(list_seen); list_seen = NULL; return 1; } if (fatfs_getFAT(fatfs, clust, &nxt)) break; clust = nxt; } tsk_list_free(list_seen); list_seen = NULL; tsk_fprintf(hFile, "*** Root Directory: %" PRIuDADDR " - %" PRIuDADDR "\n", fatfs->rootsect, (FATFS_CLUST_2_SECT(fatfs, clust_p + 1) - 1)); if ((fatfs->firstclustsect + x - 1) != fs->last_block) { tsk_fprintf(hFile, "** Non-clustered: %" PRIuDADDR " - %" PRIuDADDR "\n", (fatfs->firstclustsect + x), fs->last_block); } } tsk_fprintf(hFile, "\nMETADATA INFORMATION\n"); tsk_fprintf(hFile, "--------------------------------------------\n"); tsk_fprintf(hFile, "Range: %" PRIuINUM " - %" PRIuINUM "\n", fs->first_inum, fs->last_inum); tsk_fprintf(hFile, "Root Directory: %" PRIuINUM "\n", fs->root_inum); tsk_fprintf(hFile, "\nCONTENT INFORMATION\n"); tsk_fprintf(hFile, "--------------------------------------------\n"); tsk_fprintf(hFile, "Sector Size: %" PRIu16 "\n", fatfs->ssize); tsk_fprintf(hFile, "Cluster Size: %" PRIu32 "\n", (uint32_t) fatfs->csize << fatfs->ssize_sh); tsk_fprintf(hFile, "Total Cluster Range: 2 - %" PRIuDADDR "\n", fatfs->lastclust); /* cycle via cluster and look at each cluster in the FAT * for clusters marked as bad */ cnt = 0; for (i = 2; i <= fatfs->lastclust; i++) { TSK_DADDR_T entry; TSK_DADDR_T sect; unsigned int a; /* Get the FAT table entry */ if (fatfs_getFAT(fatfs, i, &entry)) break; if (FATFS_ISBAD(entry, fatfs->mask) == 0) { continue; } if (cnt == 0) tsk_fprintf(hFile, "Bad Sectors: "); sect = FATFS_CLUST_2_SECT(fatfs, i); for (a = 0; a < fatfs->csize; a++) { tsk_fprintf(hFile, "%" PRIuDADDR " ", sect + a); if ((++cnt % 8) == 0) tsk_fprintf(hFile, "\n"); } } if ((cnt > 0) && ((cnt % 8) != 0)) tsk_fprintf(hFile, "\n"); /* Display the FAT Table */ tsk_fprintf(hFile, "\nFAT CONTENTS (in sectors)\n"); tsk_fprintf(hFile, "--------------------------------------------\n"); /* 'sstart' marks the first sector of the current run to print */ sstart = fatfs->firstclustsect; /* cycle via cluster and look at each cluster in the FAT to make runs */ for (i = 2; i <= fatfs->lastclust; i++) { /* 'send' marks the end sector of the current run, which will extend * when the current cluster continues to the next */ send = FATFS_CLUST_2_SECT(fatfs, i + 1) - 1; /* get the next cluster */ if (fatfs_getFAT(fatfs, i, &next)) break; snext = FATFS_CLUST_2_SECT(fatfs, next); /* we are also using the next sector (clust) */ if ((next & fatfs->mask) == (i + 1)) { continue; } /* The next clust is either further away or the clust is available, * print it if is further away */ else if ((next & fatfs->mask)) { if (FATFS_ISEOF(next, fatfs->mask)) tsk_fprintf(hFile, "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR ") -> EOF\n", sstart, send, send - sstart + 1); else if (FATFS_ISBAD(next, fatfs->mask)) tsk_fprintf(hFile, "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR ") -> BAD\n", sstart, send, send - sstart + 1); else tsk_fprintf(hFile, "%" PRIuDADDR "-%" PRIuDADDR " (%" PRIuDADDR ") -> %" PRIuDADDR "\n", sstart, send, send - sstart + 1, snext); } /* reset the starting counter */ sstart = send + 1; } return 0; }
/** * 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; }