Logo Search packages:      
Sourcecode: afflib version File versions  Download package

afinfo.cpp

/*
 * afinfo.cpp:
 *
 * print information about an aff file
 */

/*
 * Copyright (c) 2005
 *    Simson L. Garfinkel and Basis Technology, Inc. 
 *      All rights reserved.
 *
 * This code is derrived from software contributed by
 * Simson L. Garfinkel
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by Simson L. Garfinkel
 *    and Basis Technology Corp.
 * 4. Neither the name of Simson Garfinkel, Basis Technology, or other
 *    contributors to this program may be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY SIMSON GARFINKEL, BASIS TECHNOLOGY,
 * AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL SIMSON GARFINKEL, BAIS TECHNOLOGy,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.  
 */


#include "afflib.h"
#include "afflib_i.h"
#include "quads.h"

#include <ctype.h>
#include <zlib.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
#include <assert.h>

#include <algorithm>
#include <cstdlib>
#include <vector>
#include <string>
using namespace std;

#ifdef UNIX
#include <unistd.h>
#include <term.h>
#define HAS_CURSES
#endif

#ifdef WIN32
#include "unix4win32.h"
#include <malloc.h>
#endif

char *progname = "afinfo";

#define VALIDATE_MD5  0x01
#define VALIDATE_SHA1 0x02

int opt_validate = 0;
int opt_info = 1;
int opt_all  = 0;
int opt_wide = 0;
unsigned int cols = 80;                   // default
int opt_x = 0;
int opt_b = 0;
int opt_identify = 1;
int opt_verbose = 0;
int opt_y = 0;
int opt_hexbuf = AF_HEXBUF_SPACE4 | AF_HEXBUF_UPPERCASE;
int opt_page_validate = 0;

#define xstr(s) str(s)
#define str(s) #s

vector<string> opt_seglist;         // just info these segments


void usage()
{
    printf("%s version %s\n",progname,xstr(AFFLIB_VERSION));
    printf("usage: %s [options] infile\n",progname);
    printf("   -a = print ALL segments (normally data segments are suppressed)\n");
    printf("   -b = print how many bad blocks in each segment (implies -a)\n");
    printf("   -i = identify the files, don't do info on them.\n");
    printf("   -w = wide output; print more than 1 line if necessary.\n");
    printf("   -s segment =   Just print information about 'segment'.\n");
    printf("                    (may be repeated)\n");
    printf("   -x = print binary values in hex (default is ASCII)\n");
    printf("   -m = validate MD5 hash of entire image\n");
    printf("   -S = validate SHA1 hash of entire image\n");
    printf("   -p = validate the hash of each page (if present)\n");
    printf("   -y = don't print segments of lengths 16 and 20 as hex)\n");
    printf("   -v = Just print the version number and exit.\n");
    exit(0);
}


AFFILE *af=0;

void sig_info(int arg)
{
    if(af==0) return;
    printf("Validating %"I64d" of %"I64d"\n", af->pos,af->image_size);
}




void validate(const char *infile)
{
    af = af_open(infile,O_RDONLY,0);
    switch(af_identify(af)){
    case AF_IDENTIFY_AFF:
    case AF_IDENTIFY_AFM:
    case AF_IDENTIFY_AFD:
      break;
    default:
      printf("%s is not an AFF file\n",infile);
      af_close(af);
      return;
    }

    printf("\nValidating ");
    if(opt_validate & VALIDATE_MD5) printf("MD5 ");
    if(opt_validate == (VALIDATE_MD5|VALIDATE_SHA1)) printf("and ");
    if(opt_validate & VALIDATE_SHA1) printf("SHA1 ");
    printf("hash codes.\n");


#ifdef SIGINFO
    signal(SIGINFO,sig_info);
#endif

    /* Get a list of all the segments to see if there is a space */
    af_rewind_seg(af);
    char segname[AF_MAX_NAME_LEN];
    vector <int> pages;
    memset(segname,0,sizeof(segname));
    while(af_get_next_seg(af,segname,sizeof(segname),0,0,0)==0){
      int page_num = af_segname_page_number(segname);
      if(page_num>=0) pages.push_back(page_num);
    }
    sort(pages.begin(),pages.end());
    vector<int>::iterator i = pages.begin();
    int last = *i;
    i++;
    for(; i!= pages.end();i++){
      if(last+1 != *i){
          printf("gap in pages (%d!=%d); %s can't be validated.\n",last+1,*i,infile);
          af_close(af);
          return;
      }
      last = *i;
    }

    /* Set up the hash machinery */
    MD5_CTX md5;
    MD5_Init(&md5);

    SHA_CTX sha;
    SHA1_Init(&sha);
    
    uint64 total_bytes = 0;
    while(!af_eof(af)){
      unsigned char buf[65536];           // a decent size
      size_t bytes = af_read(af,buf,sizeof(buf));
      if(bytes==0) break;           // reached sparse region of file
      total_bytes += bytes;
      if(opt_validate & VALIDATE_MD5) MD5_Update(&md5,buf,bytes);
      if(opt_validate & VALIDATE_SHA1) SHA1_Update(&sha,buf,bytes);
    }
    
    /* Finish the hash calculations and write to the db */

    if(opt_validate & VALIDATE_MD5){
      unsigned char md5_stored[16];
      size_t md5len = sizeof(md5_stored);
      unsigned char md5_computed[16];
      char buf[256];
      
      MD5_Final(md5_computed,&md5);
      printf("computed md5: %s\n",
             af_hexbuf(buf,sizeof(buf),md5_computed,16,opt_hexbuf));
      if(af_get_seg(af,AF_MD5,0,md5_stored,&md5len)==0){
          printf("  stored md5: %s ",
               af_hexbuf(buf,sizeof(buf),md5_stored,16,opt_hexbuf));
          if(md5len==16 && !memcmp((const char *)md5_stored,
                             (const char *)md5_computed,16)){
            printf(" MATCH\n");
          }
          else {
            printf(" NO MATCH!\n");
          }
      }
      else {
          printf("(no MD5 in AFF file)\n");
      }
    }
    

    if(opt_validate & VALIDATE_SHA1){
      unsigned char sha1_stored[20];
      size_t sha1len = sizeof(sha1_stored);
      unsigned char sha1_computed[20];
      char buf[256];
      
      SHA1_Final(sha1_computed,&sha);
      printf("computed sha1: %s \n",af_hexbuf(buf,sizeof(buf),sha1_computed,20,opt_hexbuf));
      if(af_get_seg(af,AF_SHA1,0,sha1_stored,&sha1len)==0){
          printf("  stored sha1: %s ",af_hexbuf(buf,sizeof(buf),sha1_stored,20,opt_hexbuf));
          if(sha1len==20 && !memcmp((const char *)sha1_stored,
                              (const char *)sha1_computed,20)){
            printf(" MATCH\n");
          }
          else {
            printf(" NO MATCH!\n");
          }
      }
      else {
          printf("(no SHA1 in AFF file)\n");
      }
    }
    
    af_close(af);
}

#define OUTLINE_LEN 65536


bool display_as_time(const char *segname)
{
    if(strcmp(segname,AF_ACQUISITION_SECONDS)==0) return true;
    return false;
}

bool display_as_hex(const char *segname,int data_len)
{
    if(strcmp(segname,AF_MD5)==0) return true;
    if(strcmp(segname,AF_SHA1)==0) return true;
    if(data_len==16 && strstr(segname,"md5")) return true;
    if(data_len==20 && strstr(segname,"sha1")) return true;
    if(opt_x) return true;
    return false;
}

void badscan(AFFILE *af,int page_number,size_t data_len)
{
    size_t page_size = af->image_pagesize;
    unsigned char *buf = (unsigned char *)malloc(page_size);
    if(af_get_page(af,page_number,buf,&page_size)){
      err(1,"Could not read page %d",page_number);
    }
    printf("page_size = %d\n",(int)page_size);
    int sectors = 0;
    int bad_sectors = 0;
    int funny_sectors = 0;
    for(unsigned int offset=0;offset<page_size;offset+=af->image_sectorsize){
      sectors++;
      if(af_is_badblock(af,buf+offset)){
          bad_sectors ++;
          continue;
      }
#ifdef __FreeBSD__
      /* Look for the part of the bad flag that we know and love */
      if(strnstr((char *)buf+offset,"BAD SECTOR",af->image_sectorsize)){
          funny_sectors++;
          continue;
      }
#endif
    }
    printf("           sectors scanned: %d    bad: %d   ", sectors,bad_sectors);
    if(funny_sectors){
      printf("suspicious: %d ",funny_sectors);
    }
    printf("\n");
    free(buf);
}


/* print_info:
 * Print the info on a given segment name
 */
void print_info(AFFILE *af,const char *segname)
{
    unsigned long arg;

    /* Check to see if this is a null page. */
    if(segname[0]==0 && opt_all==0){
      return;
    }

    /* Check to see if this is a data page */
    int64 page_num = af_segname_page_number(segname);
    if(page_num>=0 && opt_all==0) return;       // don't print data pages?
      
    size_t data_len = 0;
    /* First find out how big the segment is, then get the data */
    if(af_get_seg(af,segname,&arg,0,&data_len)){
      printf("%-25s  SEGMENT NOT FOUND\n",segname);
      return;
    }
    unsigned char *data = (unsigned char *)malloc(data_len);
    if(af_get_seg(af,segname,0,data,&data_len)){
      warn("af_get_seg_2");
      free(data);
      return;
    }

    char output_line[OUTLINE_LEN];        
    memset(output_line,0,sizeof(output_line));

    /* Now append the arg and the data len */
    sprintf(output_line,"%-20s %6ld   %6ld   ",segname,arg,(long)data_len);

    /* Special handling of values that should be displayed as time */
    if(display_as_time(segname)){
      int hours   = arg / 3600;
      int minutes = (arg / 60) % 60;
      int seconds = arg % 60;
      printf("%s= %02d:%02d:%02d (hh:mm:ss)\n",output_line,hours,minutes,seconds);
      free(data);
      return;
    }

    /* Special handling of quadwords that should be printed as such? */
    if(((arg == AF_SEG_QUADWORD) && (data_len==8)) || display_as_quad(segname)){
      /* Print it as a 64-bit value.
       * The strcmp is there because early AF_IMAGESIZE segs didn't set
       * AF_SEG_QUADWORD...
       */
      switch(data_len){
      case 8:
          printf("%s= %"I64d" (64-bit value)\n",
               output_line,af_decode_q(data));
          break;
      case 0:
          printf("%s= 0 (0-length segment)\n",output_line);
          break;
      default:
          printf("%s= CANNOT DECODE %d byte segment\n",output_line,(int)data_len);
      }
      free(data);
      return;
    }


    /* See if I need to truncate */
    int dots = 0;
    uint display_len = data_len;
    if(opt_wide==0 && data_len>32){ // don't bother showing more than first 32 bytes
      dots = 1;
      display_len = 32;
    }
          
    char *cc = output_line + strlen(output_line);

    if(display_as_hex(segname,display_len)){
      char buf[80];
      sprintf(cc,"%s%s",af_hexbuf(buf,sizeof(buf),data,display_len,opt_hexbuf),
            dots ? "..." : "");
      /* Special code for SHA1 */
      if(!opt_wide && strcmp(segname,AF_SHA1)==0){
          fwrite(output_line,1,78,stdout);
          printf("\n%49s\n",output_line+78);
          free(data);
          return;
      }
    }
    else {
      /* Fill it out with some printable data */
      unsigned int i;
      if(display_len > sizeof(output_line)-strlen(output_line)){
          display_len = sizeof(output_line)-strlen(output_line);
      }
      for(i=0;i<display_len;i++){
          *cc = data[i];
          if(isprint(*cc)==0) *cc='.';
          if(*cc=='\n' || *cc=='\r') *cc=' ';
          cc++;
      }
      *cc = 0;
    }
      
    /* Now print the results... */
    if(!opt_wide){
      if(strlen(output_line)>cols){
          output_line[cols-4] = '.';
          output_line[cols-3] = '.';
          output_line[cols-2] = '.';
          output_line[cols-1] = '\000';
      }
    }
    fputs(output_line,stdout);
    if(page_num>=0 && opt_b){
      badscan(af,page_num,data_len);
    }
    if(opt_page_validate && page_num>=0){
      /* Get the page again; this may involve decompression */
      unsigned char *page_data = (unsigned char *)malloc(af->image_pagesize);
      size_t page_data_len = af->image_pagesize;
      if(af_get_page(af,page_num,page_data,&page_data_len)){
          printf("** COULD NOT READ UNCOMPRESSED PAGE ");
          goto skip1;
      }

      char hash_segname[32];
      unsigned char hash_buf[16];
      unsigned char hash_calc[16];
      size_t hash_len = sizeof(hash_buf);
      snprintf(hash_segname,sizeof(hash_segname),AF_PAGE_MD5,page_num);
      printf("          ");
      if(af_get_seg(af,hash_segname,0,hash_buf,&hash_len)){
          printf("** NO SEGMENT %s ** ",hash_segname);
          goto skip1;
      }
      
      MD5(page_data,page_data_len,hash_calc);
      if(memcmp(hash_buf,hash_calc,sizeof(hash_buf))!=0){
          char hb[32];
          printf("** HASH INVALID **\n%30s Calculated %s\n","",af_hexbuf(hb,sizeof(hb),hash_calc,16,opt_hexbuf));
          printf("%30s Wanted %s ","",af_hexbuf(hb,sizeof(hb),hash_buf,16,opt_hexbuf));
          printf("data_len=%d\n",(int)data_len);
      } else{
          printf("HASH OK ");
      }
      free(page_data);
    }
 skip1:;
    putchar('\n');

    free(data);
}


int info_file(const char *infile)
{
    unsigned long total_segs = 0;
    unsigned long total_pages = 0;
    unsigned long null_segs = 0;


    AFFILE *af = af_open(infile,O_RDONLY,0);
    
    if(!af){
      err(1,"Cannot open %s",infile);
    }

    printf("\n%s\n",af_filename(af));
    if(opt_all==0){
      printf("[skipping data segments]\n");
    }
    printf("                                 data       \n");
    printf("Segment                 arg    length   data\n");
    printf("=======             =======    ======   =====\n");

    /* If a list of segments was specified by the user, just use that list */
    if(opt_seglist.size()>0){
      for(vector<string>::iterator i = opt_seglist.begin(); i != opt_seglist.end(); i++){
          print_info(af,i->c_str());
      }
      af_close(af);
      return 0;
    }

    /* Go through the whole file, get all of the segments, put them in a list */
    vector <string> segments;
    char segname[AF_MAX_NAME_LEN];
    af_rewind_seg(af);              // start at the beginning
    while(af_get_next_seg(af,segname,sizeof(segname),0,0,0)==0){
      int page_num = af_segname_page_number(segname);
      if(page_num>=0) total_pages++;
      segments.push_back(segname);
      if(segname[0]==0) null_segs++;
      total_segs ++;
    }

    /* Now process the segments */
    for(vector<string>::iterator i = segments.begin(); i != segments.end(); i++){
      print_info(af,i->c_str());
    }
    printf("\n");
    printf("Page  segments:    %8lu\n",total_pages);
    printf("Empty segments:    %8lu\n",null_segs);
    printf("Total segments:    %8lu\n", total_segs);
    //printf("Compression ratio: %5.2g\n", total_bytes_compressed / total_bytes_uncompressed
    af_close(af);
    return 0;

}
       

int main(int argc,char **argv)
{
    int ch;
    const char *infile;

    /* Figure out how many cols the screen has... */
#ifdef HAS_CURSES
    setupterm((char *)0,1,(int *)0);
    cols = tgetnum("co");
#endif

    while ((ch = getopt(argc, argv, "abh?s:SmiIwj:pxv")) != -1) {
      switch (ch) {
      case 'a': opt_all++; break;
      case 'b': opt_all ++; opt_b   ++; break;
      case 'i': opt_info=0; opt_identify = 1; break;
      case 'w': opt_wide++; break;
      case 'x': opt_x++; break;
      case 'y': opt_y++; break;
      case 'm': opt_validate |= VALIDATE_MD5; break;
      case 'S': opt_validate |= VALIDATE_SHA1; break;
      case 'p': opt_page_validate = 1;break;

      case 'h':
      case '?':
      default:
          usage();
          break;
      case 's':
          opt_seglist.push_back(optarg); // add to the list of segments to info
          break;
      case 'v':
          printf("%s version %s\n",progname,xstr(AFFLIB_VERSION));
          exit(0);
      }
    }
    argc -= optind;
    argv += optind;

    if(argc<1){
      usage();
    }


    /* Loop through all of the files */
    while(*argv){
      infile = *argv++;       // get the file
      argc--;                       // decrement argument counter
       
      const char *name = af_identify_name(infile);
      if(!name) err(1,infile);

      if(opt_identify) printf("%s is a %s file\n",infile,name);
       
      if(opt_info)       info_file(infile);
      if(opt_validate) validate(infile);
    }
    return 0;
}



Generated by  Doxygen 1.6.0   Back to index