C Source - RootReader

This is a simple little command-line tool for reading the root directory of a Ext2 disk.

/**
 * @file rootReader.c
 * @brief Tracks down first superblock and lists files in the root
 *
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <sys/stat.h>
#include <sys/types.h>

#ifdef __CYGWIN__
#include <cygwin/fs.h>
#include <ext2fs/ext2_fs.h>
#else
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#endif

#ifndef EXT2_S_IFDIR
#define EXT2_S_IFDIR 0x4000
#endif
 
typedef enum {false=0, true=1} bool;
#define BASE_ERR_CODE 489000
#define ERR_INVALID_OPTION 1
#define ERR_NO_EXT2_FILE_OPTION 2
#define ERR_OPEN_EXT2_FILE 3
#define ERR_READ_SUPERBLOCK 4
#define ERR_INVALID_SUPERBLOCK 5
#define ERR_INIT_BLOCK_DESCRIPTOR_TABLE 6
#define ERR_READ_BLOCK_DESCRIPTOR_TABLE 7
#define ERR_INIT_INODE_TABLE 8
#define ERR_READ_INODE_TABLE 9
#define ERR_ROOT_INODE_NOT_DIRECTORY 10
#define ERR_INIT_DIRECTORY_BLOCK 11

#define N_SUPERBLOCK_SIZE 1024
#define N_SUPERBLOCK_OFFSET 1024
#define N_ROOT_OFFSET  sizeof(struct ext2_inode)

#define PROGRAM "rootReader v0.99.001 - Simple Ext2 Raw File Reader"
#define AUTHOR "John 'Ghost' Wicks"
#define COPYRIGHT "Copyright (c) 2012 - "
#define COMPANY "Some Mindless Corporation Inc."
#define STR_COPYRIGHT_MASK "%s\n by %s\n%s%s\n\n"

void display_usage(void);
void display_copyright(const char *program_name);

size_t get_super_block(char *storage, size_t offset, size_t size, int source);
bool is_valid_super_block(const char *storage, int16_t *sb_magic);
inline uint32_t get_block_size(const char *storage);   
inline uint32_t get_blocks_per_group(const char *storage);
bool init_block_group_descriptor_table(char **storage, uint32_t size);
size_t get_block_group_descriptor_table(char *storage, size_t offset, size_t size, int source);
bool init_inode_table(void **storage, uint32_t size);
size_t get_inode_table(void *storage, size_t offset, size_t size, int source);
bool is_directory(void *storage, size_t offset);
void *get_inode(void *storage, size_t offset);
bool init_block(void **storage, uint32_t size);
size_t get_block(void *storage, size_t offset, size_t size, int source);


int main(int argc, char *argv[]){
  int nErr, nOpt, cOpt, retVal, nRead;
  char cmdOpts[] = "h";
  char fileTypes[][18] = {"Unknown File Type\0", "Regular File\0", "Directory File\0", "Character Device\0", "Block Device\0", "Buffer File\0", "Socket File\0", "Symbolic Link\0"};
  char *in_file_name = NULL;
  int infile = -1;
  bool showUsage = false;
  char superBlock[N_SUPERBLOCK_SIZE] = {'\0'};
  char *blockGroupDescriptorTable;
  void *inodeTable;
  void *rootInode;
  void *cur_block;
  int16_t sb_magic_num=0;
  uint32_t block_size=0;
  uint32_t blocks_per_group =0;
  uint32_t block_group_inode_table =0;
  uint32_t inode_table_size = 0;
    
  nErr = nOpt = cOpt = 0;
  
  while((cOpt = getopt(argc, argv, cmdOpts) != EOF)){
    switch(cOpt){
      case 'h':{
        showUsage = true;
        break;
      }
      default:{
        nErr = ERR_INVALID_OPTION;
      }
    }  
  }
  
  if(nErr || showUsage ){
    display_usage();
    retVal = (nErr ? BASE_ERR_CODE + nErr : EXIT_SUCCESS);
  }
  else if(in_file_name == NULL && argc !=2){
    printf("Expects ext2 file system raw file or block device path.\r\n");
    retVal = BASE_ERR_CODE + ERR_NO_EXT2_FILE_OPTION;
  }else{
    in_file_name = argv[1];
    
    if((infile=open(in_file_name, O_RDONLY))==-1){
      printf("Cannot open ext2 file system raw file or block device path.\r\n");
      retVal = BASE_ERR_CODE + ERR_OPEN_EXT2_FILE;
    }
    else{
      nRead = get_super_block( superBlock, N_SUPERBLOCK_OFFSET, N_SUPERBLOCK_SIZE, infile);
      if(nRead != 1){
        retVal = BASE_ERR_CODE + ERR_READ_SUPERBLOCK;
      }else{
        if(is_valid_super_block(superBlock, &sb_magic_num)){
          block_size = get_block_size(superBlock);
          blocks_per_group = get_blocks_per_group(superBlock);
          printf("Block size is %i bytes, and a block group has %i blocks.\r\n", block_size, blocks_per_group);
          if(init_block_group_descriptor_table(&blockGroupDescriptorTable, block_size)){
            if(get_block_group_descriptor_table(blockGroupDescriptorTable, (block_size==1024? 2048: block_size), block_size, infile)){
              block_group_inode_table = ((struct ext2_group_desc *)blockGroupDescriptorTable)->bg_inode_table;
              inode_table_size = ((struct ext2_super_block *)superBlock)->s_inodes_count * ((struct ext2_super_block *)superBlock)->s_inode_size;
              if(init_inode_table(&inodeTable, inode_table_size)){
                if(get_inode_table(inodeTable, (block_size * block_group_inode_table), inode_table_size, infile)){
                  rootInode = (struct ext2_inode*)get_inode(inodeTable, N_ROOT_OFFSET);
                  if(is_directory(rootInode, 0)){
                    printf(((((struct ext2_inode*)rootInode)->i_flags & EXT2_INDEX_FL)?"The root directory is indexed.\r\n":"The root directory is linearly linked.\r\n"));
                    if(init_block(&cur_block, block_size)){
                      /* save current block pointer so we can free later otherwise memory leak/core dump*/
                      void *old_cur_block = cur_block;
                      get_block(cur_block, (((struct ext2_inode*)rootInode)->i_block[0]* block_size), block_size, infile);
                      while(((struct ext2_dir_entry*)cur_block)->inode){
                        printf("The inode number is %d.\r\n", ((struct ext2_dir_entry_2 *)cur_block)->inode);
                        printf("The file name is %-*s.\r\n", ((struct ext2_dir_entry_2 *)cur_block)->name_len, ((struct ext2_dir_entry_2 *)cur_block)->name);
                        printf("The file type is %s.\r\n", fileTypes[((struct ext2_dir_entry_2 *)cur_block)->file_type]);
                        cur_block = (struct ext2_dir_entry_2 *)(((uint8_t *)cur_block)+((struct ext2_dir_entry_2 *)cur_block)->rec_len);
                      }
                      retVal = EXIT_SUCCESS;
                      /* Possible memory leak here, but core dump with free */
                      /*if(cur_block) free(cur_block);*/
                      free(old_cur_block);
                    }else{
                      retVal = BASE_ERR_CODE + ERR_INIT_DIRECTORY_BLOCK;
                    }
                  }else{
                    printf("The root inode does not correspond to a directory.\r\n");
                    retVal = BASE_ERR_CODE + ERR_ROOT_INODE_NOT_DIRECTORY;
                  }
                }else{
                  retVal = BASE_ERR_CODE + ERR_READ_INODE_TABLE;
                }
                if(inodeTable) free(inodeTable);
              }else{
                retVal = BASE_ERR_CODE + ERR_INIT_INODE_TABLE;
              }
            }else{
              retVal = BASE_ERR_CODE + ERR_READ_BLOCK_DESCRIPTOR_TABLE;
            }
            if(blockGroupDescriptorTable) free(blockGroupDescriptorTable);
          }else{
            retVal = BASE_ERR_CODE + ERR_INIT_BLOCK_DESCRIPTOR_TABLE;
          }
        }else{
          printf("Superblock magic number is incorrect: %#.4hx.", sb_magic_num);
          retVal = BASE_ERR_CODE + ERR_INVALID_SUPERBLOCK;
        }
      } 
      close(infile);
    }
  }

  return retVal;
}

void display_usage(void){
  printf("Usage: rootReader [options] filename\r\n");
  printf("\tOptions\r\n");
  printf("\t\t -h : display this usage screen\r\n");
}

void display_copyright(const char *program_name){
  printf(STR_COPYRIGHT_MASK, program_name, AUTHOR, COPYRIGHT, COMPANY);
}

size_t get_super_block(char *storage, size_t offset, size_t size, int source){
  size_t retVal=0;
  long i =-1;
  
  if(source !=-1){
    if((i=lseek(source, offset, SEEK_SET))!=-1){
      if((i=read(source, storage, size))==N_SUPERBLOCK_SIZE){
        retVal =1;
      }
    }
  }
  
  return retVal;
}

bool is_valid_super_block(const char *superblock, int16_t *magic){
  bool retVal = false;
  
  if( ((struct ext2_super_block *)superblock)->s_magic == EXT2_SUPER_MAGIC){
    printf("Superblock found and magic number matched.\r\n");
    retVal = true;
  }else{
    *magic = ((struct ext2_super_block *)superblock)->s_magic;
  }
  return retVal;
} 

uint32_t get_block_size(const char *storage){
  uint32_t retVal = 0;
  retVal = 1024 << ((struct ext2_super_block*)storage)->s_log_block_size;
  return retVal;
}

uint32_t get_blocks_per_group(const char *storage){
  uint32_t retVal =0;
  retVal = ((struct ext2_super_block*)storage)->s_blocks_per_group;
  return retVal;
}

bool init_block_group_descriptor_table(char **storage, uint32_t size){
  bool retVal = false;
  
  void *ptr = malloc( size * sizeof(**storage));
  *storage = (char *)ptr;
  
  if( (*storage) ){
    memset(*storage, '\0', (size * sizeof(**storage)) );
    retVal = true;
  }
  return retVal;
}

size_t get_block_group_descriptor_table(char *storage, size_t offset, size_t size, int source){
  size_t retVal =0;
  long i =-1;
  
  if(source !=-1){
    if((i=lseek(source, offset, SEEK_SET))!=-1){
      if((i=read(source, storage, size))!=-1){
        retVal =1;
      }
    }
  }
  
  return retVal;
}

bool init_inode_table(void **storage, uint32_t size){
  bool retVal = false;
  
  void *ptr = malloc( size );
  *storage = ptr;
  
  if( (*storage) ){
    memset(*storage, '\0', size);
    retVal = true;
  }

  return retVal;
}

size_t get_inode_table(void *storage, size_t offset, size_t size, int source){
  size_t retVal =0;
  long i =-1;
  
  if(source !=-1){
    if((i=lseek(source, offset, SEEK_SET))!=-1){
      if((i=read(source, storage, size))!=-1){
        retVal =1;
      }
    }
  }
  
  return retVal;
}

bool is_directory(void *storage, size_t offset){
  bool retVal = false;
  
  if( S_ISDIR(((struct ext2_inode*)(((uint8_t *)storage)+offset))->i_mode)){
    retVal = true;
  }
  
  return retVal;
}

void *get_inode(void *storage, size_t offset){
  void *retVal;
  retVal = (void *)(((uint8_t *)storage)+offset);
  return retVal;
}

bool init_block(void **storage, uint32_t size){
  bool retVal = false;
  void *ptr = malloc( size );
  *storage = ptr;
  
  if( (*storage) ){
    memset(*storage, '\0', size);
    retVal = true;
  }
  return retVal;
}

size_t get_block(void *storage, size_t offset, size_t size, int source){
  size_t retVal =0;
  long i =-1;
  
  if(source !=-1){
    if((i=lseek(source, offset, SEEK_SET))!=-1){
      if((i=read(source, storage, size))!=-1){
        retVal =1;
      }
    }
  }
  
  return retVal;
}


Written by John Wicks.