The purpose of this assignment is to use fsa (file system analysis) to analyze a partition with the ext2. The functions of this tool are -info, -root, -traverse, etc. The only one I currently have running properly is the -info where it outputs general statistics about file systems and its groups. The biggest problem I have right now is that whenever i run the -root function, I would get the information in the right format( due to print statements) but every value would be zero. This is what it looks like: Name: Inode: 0 Entry Length: 0 Name Length: 0 File Type: 0. The traverse function is worse, it just generates an empty file called traverse_output.txt. I am also using a clone of ext2 formatted disk and have been running with the terminal with commands like : ./fsa cmake-build-debug/BlackBoxTesting/ext2_10mb.img -root
I've tried to put in debug statements but the generated file is always blank. No matter how many print statements I put to debug the -root file, it always just shows me the same thing (the 0s). I assume the there is a fault in my logic. here is my fsa.c:
#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <unistd.h>#include <string.h>#include <ext2fs/ext2fs.h>#define EXT2_FT_REG_FILE 1#define EXT2_FT_DIR 2// Function declarationsvoid print_general_info(struct ext2_super_block *sb, FILE *output);void print_group_info(int fd, struct ext2_super_block *sb, FILE *output);void print_root_entries(int fd, struct ext2_super_block *sb, FILE *output);void traverse_directory(int fd, struct ext2_super_block *sb, int inode_num, char *path, FILE *output);void print_file_content(int fd, struct ext2_super_block *sb, char *path, FILE *output);int main(int argc, char *argv[]) { if (argc < 3) { fprintf(stderr, "Usage: %s <diskname> <-option> [file_path]\n", argv[0]); exit(1); } char *diskname = argv[1]; char *option = argv[2]; FILE *output; char output_file[256]; int fd = open(diskname, O_RDONLY); if (fd == -1) { perror("Virtual disk open failed"); exit(1); } struct ext2_super_block sb; if (lseek(fd, 1024, SEEK_SET) != 1024 || read(fd, &sb, sizeof(sb)) != sizeof(sb)) { perror("Failed to read superblock"); exit(1); } sprintf(output_file, "%s_output.txt", option+1); // Build the output filename from option output = fopen(output_file, "w"); if (!output) { perror("Failed to open output file"); close(fd); exit(1); } if (strcmp(option, "-info") == 0) { print_general_info(&sb, output); print_group_info(fd, &sb, output); } else if (strcmp(option, "-root") == 0) { print_root_entries(fd, &sb, output); } else if (strcmp(option, "-traverse") == 0) { traverse_directory(fd, &sb, EXT2_ROOT_INO, "/", output); } else if (strcmp(option, "-file") == 0 && argc == 4) { print_file_content(fd, &sb, argv[3], output); } else { fprintf(stderr, "Invalid command or insufficient arguments\n"); } if (fclose(output) != 0) { perror("Failed to close output file"); } close(fd); return 0;}void print_general_info(struct ext2_super_block *sb, FILE *output) { fprintf(output, "--General File System Information--\n"); fprintf(output, "Block Size in Bytes: %u\n", 1024 << sb->s_log_block_size); fprintf(output, "Total Number of Blocks: %u\n", sb->s_blocks_count); fprintf(output, "Disk Size in Bytes: %llu\n", (unsigned long long)sb->s_blocks_count * (1024 << sb->s_log_block_size)); fprintf(output, "Maximum Number of Blocks Per Group: %u\n", sb->s_blocks_per_group); fprintf(output, "Inode Size in Bytes: %u\n", sb->s_inode_size); fprintf(output, "Number of Inodes Per Group: %u\n", sb->s_inodes_per_group); fprintf(output, "Number of Inode Blocks Per Group: %u\n", (sb->s_inodes_per_group * sb->s_inode_size) / (1024 << sb->s_log_block_size)); fprintf(output, "Number of Groups: %u\n", (sb->s_blocks_count + sb->s_blocks_per_group - 1) / sb->s_blocks_per_group); if (ferror(output)) { perror("Error writing general info to output file"); }}void print_group_info(int fd, struct ext2_super_block *sb, FILE *output) { unsigned int num_groups = (sb->s_blocks_count + sb->s_blocks_per_group - 1) / sb->s_blocks_per_group; struct ext2_group_desc gd; unsigned int group_size = sizeof(struct ext2_group_desc); fprintf(output, "--Group Information--\n"); lseek(fd, 1024 + (1 * sb->s_log_block_size), SEEK_SET); // Seek to the beginning of the group descriptor table for (unsigned int i = 0; i < num_groups; i++) { if (read(fd, &gd, group_size) != group_size) { perror("Error reading group descriptor"); break; } fprintf(output, "Group %u: \n", i); fprintf(output, " Block Bitmap Block ID: %u\n", gd.bg_block_bitmap); fprintf(output, " Inode Bitmap Block ID: %u\n", gd.bg_inode_bitmap); fprintf(output, " Inode Table Block ID: %u\n", gd.bg_inode_table); fprintf(output, " Free Blocks Count: %u\n", gd.bg_free_blocks_count); fprintf(output, " Free Inodes Count: %u\n", gd.bg_free_inodes_count); fprintf(output, " Used Directories Count: %u\n", gd.bg_used_dirs_count); if (ferror(output)) { perror("Error writing group info to output file"); break; } }}void print_root_entries(int fd, struct ext2_super_block *sb, FILE *output) { struct ext2_inode inode; struct ext2_dir_entry_2 *entry; unsigned int root_inode_num = EXT2_ROOT_INO; unsigned int size = sizeof(struct ext2_inode); // Read root inode (inode ID 2) lseek(fd, (sb->s_first_data_block + 1) * sb->s_log_block_size, SEEK_SET); if (read(fd, &inode, size) != size) { perror("Error reading root inode"); return; } // Read root directory block (first data block of the root inode) char block[sb->s_log_block_size]; unsigned int root_block_num = inode.i_block[0]; printf("Root Directory Block: %u\n", root_block_num); // Debug print lseek(fd, root_block_num * sb->s_log_block_size, SEEK_SET); if (read(fd, block, sb->s_log_block_size) != sb->s_log_block_size) { perror("Error reading root directory block"); return; } fprintf(output, "--Root Directory Entries--\n"); int offset = 0; while (offset < sb->s_log_block_size) { entry = (struct ext2_dir_entry_2 *)(block + offset); printf("Inode: %u, Rec_len: %u, Name_len: %u, Type: %u, Name: %.*s\n", entry->inode, entry->rec_len, entry->name_len, entry->file_type, entry->name_len, entry->name); // Debug print // Print directory entry details fprintf(output, "Inode: %u\n", entry->inode); fprintf(output, "Entry Length: %u\n", entry->rec_len); fprintf(output, "Name Length: %u\n", entry->name_len); fprintf(output, "File Type: %u\n", entry->file_type); fprintf(output, "Name: %.*s\n", entry->name_len, entry->name); // Move to the next directory entry offset += entry->rec_len; } if (ferror(output)) { perror("Error writing root entries to output file"); }}void traverse_directory(int fd, struct ext2_super_block *sb, int inode_num, char *path, FILE *output) { struct ext2_inode inode; struct ext2_dir_entry_2 *entry; char block[sb->s_log_block_size]; lseek(fd, ((inode_num - 1) / sb->s_inodes_per_group) * sb->s_log_block_size + ((inode_num - 1) % sb->s_inodes_per_group) * sizeof(struct ext2_inode), SEEK_SET); if (read(fd, &inode, sizeof(struct ext2_inode)) != sizeof(struct ext2_inode)) { perror("Error reading inode"); return; } for (int i = 0; i < 12; i++) { // Only direct blocks are considered if (inode.i_block[i] == 0) continue; lseek(fd, inode.i_block[i] * sb->s_log_block_size, SEEK_SET); if (read(fd, block, sb->s_log_block_size) != sb->s_log_block_size) { perror("Error reading directory block"); continue; } for (int offset = 0; offset < sb->s_log_block_size; offset += entry->rec_len) { entry = (struct ext2_dir_entry_2 *)(block + offset); if (entry->inode != 0) { fprintf(output, "%s/%.*s\n", path, entry->name_len, entry->name); if (ferror(output)) { perror("Error writing directory entry to output file"); return; } if (entry->file_type == EXT2_FT_DIR && entry->name[0] != '.') { // Skip '.' and '..' char new_path[1024]; snprintf(new_path, sizeof(new_path), "%s/%.*s", path, entry->name_len, entry->name); traverse_directory(fd, sb, entry->inode, new_path, output); } } } }}void print_file_content(int fd, struct ext2_super_block *sb, char *path, FILE *output) { // Simplification for brevity: Assume path directly leads to an inode number for demonstration struct ext2_inode inode; char block[sb->s_log_block_size]; int inode_num = atoi(path); // Mock example, replace with actual path resolution logic lseek(fd, ((inode_num - 1) / sb->s_inodes_per_group) * sb->s_log_block_size + ((inode_num - 1) % sb->s_inodes_per_group) * sizeof(struct ext2_inode), SEEK_SET); if (read(fd, &inode, sizeof(struct ext2_inode)) != sizeof(struct ext2_inode)) { perror("Error reading inode"); return; } for (int i = 0; i < 12; i++) { // Only direct blocks are considered if (inode.i_block[i] == 0) continue; lseek(fd, inode.i_block[i] * sb->s_log_block_size, SEEK_SET); ssize_t bytes_read = read(fd, block, sb->s_log_block_size); if (bytes_read < 0) { perror("Error reading file block"); continue; } if (fwrite(block, 1, bytes_read, output) != bytes_read) { perror("Error writing file content to output file"); return; } }}