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

afcompare.cpp

/*
 * acompare.cpp:
 *
 * Compare the contents of an ISO file to an AFF file.
 * Optionally, if they are equal, delete the ISO 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 <ctype.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <limits.h>
#include <string.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;

char *progname = "afcompare";

int opt_quiet  = 0;
int opt_all    = 0;
int opt_print_sectors    = 0;
int opt_print_sector_contents    = 0;
int opt_page   = -1;
char *batch_ext = "";

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


void print_title(char *title)
{
    if(title[0]){
      puts(title);
      title[0] = 0;
    }
}

void usage()
{
    printf("afcompare version %s\n",xstr(AFFLIB_VERSION));
    printf("\n");
    printf("usage: %s [options] file1 file2\n",progname);
    printf("       compares file1 with file2\n");
    printf("options:\n");
    printf("       -v        --- just print the version number and exit\n");
    printf("       -q        --- Quiet. No output except for errors\n");
    printf("       -a        --- print what's the same (all)\n");
    printf("       -b        --- print the numbers of differing sectors\n");
    printf("       -c        --- print the contents of differing sectors\n");
    printf("       -p ###    --- Just examine the differences on page ###\n");
    printf("\n");
    printf("Examples: For comparing ISO and AFF files:\n");
    printf("  %s -b img file.aff               --- compare file.aff and file.img\n",
         progname);
    printf("  %s -b img file1.aff file2.aff... --- compare file1.aff, file1.img, etc.\n",
         progname);
    printf("  %s -r Remove the ISO file if the two are equal.\n",progname);
    printf("  %s -q Quiet; no output except for errors.\n",progname);
    printf("\n");
    printf("Examples:\n");
    printf("  %s  file1.aff file2.aff          --- compares file1 & file2\n",progname);
    exit(0);
}

void print_sector(AFFILE *af,unsigned char *buf)
{
    for(unsigned int i=0;i<af->image_sectorsize;i++){
      if(isprint(buf[i])){
          putchar(buf[i]);
      }
      else {
          putchar('.');
      }
      if(i%64==63) putchar('\n');
    }
}


void print_info(char dir,const char *segname,unsigned long arg,size_t len,
            unsigned char *data,int mcr)
{
    printf("    %c %s arg=%lu len=%d\n",dir,segname,arg,(int)len);
    printf("          ");
    if((arg == AF_SEG_QUADWORD) && (len==8)){
      printf("data=%"I64d" as a 64-bit value\n",af_decode_q(data));
      return;
    }
    /* Otherwise, just print some stuff... */
    for(unsigned int i=0;i<len && i<60;i++){
      if(data[i]==' '){
          putchar(' ');
          continue;
      }
      if(!isprint(data[i])){
          putchar('.');
          continue;
      }
      putchar(data[i]);
    }
    putchar('\n');
}

int  compare_aff_metadata_segments(char *title,AFFILE *af1,AFFILE *af2,const char *segname,int mode)
{
    int ret = 0;

    unsigned long arg1;
    size_t data1_len;
    int r1 = af_get_seg(af1,segname,&arg1,0,&data1_len);

    unsigned long arg2;
    size_t data2_len;
    int r2 = af_get_seg(af2,segname,&arg2,0,&data2_len);
    
    if(r1==0 && r2!=0){
      if(mode==1){
          print_title(title);
          printf("   %s  (arg=%lu; len=%d)\n",segname,arg1,(int)data1_len);
      }
      return 1;
    }
    
    if(r1!=0 && r2==0){
      if(mode==2){
          print_title(title);
          printf("   %s  (arg=%lu; len=%d)\n",segname,arg2,(int)data2_len);
      }
      return 1;
    }
    if(mode!=3) return 0;                 // only report differences in mode 3
    /* Get the actual data... */
    unsigned char *data1 = (unsigned char *)malloc(data1_len);
    unsigned char *data2 = (unsigned char *)malloc(data2_len);

    int s1 = af_get_seg(af1,segname,&arg1,data1,&data1_len);
    if(s1!=0) err(1,"Couldn't read data segment %s in %s",segname,af_filename(af1));

    int s2 = af_get_seg(af2,segname,&arg2,data2,&data2_len);
    if(s2!=0) err(1,"Couldn't read data segment %s in %s",segname,af_filename(af2));

    int mcr = 0;

    if(data1_len != data2_len) mcr = 1;
    else mcr = memcmp(data1,data2,data1_len);
    if(arg1!=arg2 || data1_len!=data2_len || mcr!=0){
      print_title(title);
      print_info('<',segname,arg1,data1_len,data1,mcr);
      print_info('>',segname,arg2,data2_len,data2,mcr);
      if(mcr){
          printf("        *** Metadata segment are different ");
          if(strcmp(segname,AF_BADFLAG)==0){
            printf("(bad flags should be different!)");
          }
          putchar('\n');
      }
      putchar('\n');
      ret = 1;
    }
    else {
      if(opt_all){
          print_title(title);
          printf("   %s (same in both) \n",segname);
      }
    }
    free(data1);
    free(data2);
    return ret;
}

int  compare_aff_data_segments(char *title,AFFILE *af1,AFFILE *af2,int64 pagenum,int mode)
{
    int ret = 0;
    char pagename[65];
    sprintf(pagename,AF_PAGE,pagenum);

    char segname[65];
    sprintf(segname,AF_SEG_D,pagenum);

    unsigned long arg1=0;
    size_t data1_len=0;
    int r1 = af_get_seg(af1,pagename,&arg1,0,&data1_len);
    if(r1==-1) r1=af_get_seg(af1,segname,&arg1,0,&data1_len);
    
    unsigned long arg2=0;
    size_t data2_len=0;
    int r2 = af_get_seg(af2,pagename,&arg2,0,&data2_len);
    if(r2 == -1) r2=af_get_seg(af2,segname,&arg2,0,&data2_len);
    
    if(r1<0 && r2<0) return 0;            // no data segment in either file
    if(r1==0 && r2!=0){
      if(mode==1){
          print_title(title);
          printf("   %s  (arg=%lu; len=%d)\n",segname,arg1,(int)data1_len);
      }
      return 1;
    }
    
    if(r2==0 && r1!=0){
      if(mode==2){
          print_title(title);
          printf("   %s  (arg=%lu; len=%d)\n",segname,arg2,(int)data2_len);
      }
      return 1;
    }
    if(mode!=3) return 0;           // only report differences in mode 3

    /* Get the actual data... */
    unsigned char *data1 = (unsigned char *)malloc(af_page_size(af1));
    unsigned char *data2 = (unsigned char *)malloc(af_page_size(af2));

    data1_len = af_page_size(af1);
    data2_len = af_page_size(af2);

    uint64 start_sector_number = (pagenum * data1_len) / af1->image_sectorsize;

    if(af_get_page(af1,pagenum,data1,&data1_len)<0)
      err(1,"Cannot read page %"I64d" from %s\n",pagenum,af_filename(af1));

    if(af_get_page(af2,pagenum,data2,&data2_len)<0)
      err(1,"Cannot read page %"I64d" from %s\n",pagenum,af_filename(af2));

    /* Now look at the pages sector-by-sector. */
    int af1_bad=0;
    int af2_bad=0;
    int matching_bad_blocks = 0;
    int matching_blocks = 0;
    int total_blocks = 0;
    int no_match = 0;
    vector<uint64> different_sectors;

    for(unsigned int offset=0;offset<data1_len;offset+=af1->image_sectorsize){
      uint64 this_sector = start_sector_number + offset/af1->image_sectorsize;
      total_blocks++;
      if(af_is_badblock(af1,data1+offset) &&
         af_is_badblock(af2,data2+offset)){
          matching_bad_blocks++;
          continue;
      }
      if(af_is_badblock(af1,data1+offset)){
          af1_bad++;
          continue;
      }
      if(af_is_badblock(af2,data2+offset)){
          af2_bad++;
          continue;
      }
      if(memcmp(data1+offset,data2+offset,af1->image_sectorsize)==0){
          matching_blocks++;
          continue;
      }
      no_match++;
      different_sectors.push_back(this_sector);
    }

    char outline[256];
    outline[0] = 0;
    if(opt_all || (no_match>0) || af1_bad || af2_bad){
      sprintf(outline,
            "   page%"I64d" blocks:%d  matching:"
            "%d  different:%d",
            pagenum,total_blocks,matching_blocks,no_match);
    }
    if(af1_bad){
      sprintf(outline+strlen(outline),"    bad in 1:%d ",af1_bad);
    }
    if(af2_bad){
      sprintf(outline+strlen(outline),"    bad in 2:%d ",af2_bad);
    }
    if(matching_bad_blocks){
      if(opt_all){
          sprintf(outline+strlen(outline),
                "    bad both:%3d ",matching_bad_blocks);
      }
    }

    if(outline[0]){
      print_title(title);
      puts(outline);
    }
    if(opt_print_sectors && different_sectors.size()>0){
      print_title(title);
      printf("  Sectors with differences:");
      int i=0;
      for(vector<uint64>::iterator j = different_sectors.begin();
          j != different_sectors.end();
          j++){
          if(i==0){
            printf("\n   ");
          }
          printf(" %"I64d,*j);
          i = (i+1) % 10;
      }
      putchar('\n');
      ret = 1;
    }
    if(opt_print_sector_contents && different_sectors.size()>0){
      print_title(title);
      printf("  Sectors with differences:");
      for(vector<uint64>::iterator j = different_sectors.begin();
          j != different_sectors.end(); j++){
          int offset = (*j - start_sector_number)*af1->image_sectorsize;
          char b2[16];
          printf("offset=%d\n",offset);

          memcpy(b2,data1+offset,16);
          b2[15]=0;

          printf("===  sector %"I64d" (offset=%d) ===",*j,offset);
          printf("   %s:\n",af_filename(af1));
          print_sector(af1,data1+offset);
          printf("\n");
          printf("   %s:\n",af_filename(af2));
          print_sector(af2,data2+offset);
          printf("\n\n");
      }
      ret = 1;
    }
    free(data1);
    free(data2);
    return ret;
}

/* Compare two AFF files.
 * Return 0 if they are equal.
 */
int compare_aff_aff(const char *file1,const char *file2)
{
    bool no_data_segments = false;
    int  ret = 0;

    AFFILE *af1 = af_open(file1,O_RDONLY,0);
    AFFILE *af2 = af_open(file2,O_RDONLY,0);

    af_vnode_info vni1,vni2;

    if(af_vstat(af1,&vni1) || af_vstat(af2,&vni2)){
      err(1,"af_vstat failed?");
    }

    if(af1->image_pagesize != af2->image_pagesize){
      fprintf(stderr,"Currently, %s requires that both images have the "
            "same image datsegsize.\n"
            "pagesize(%s)=%ld\n"
            "pagesize(%s)=%ld\n",
            progname,file1,af1->image_pagesize,
            file2,af2->image_pagesize);
      fprintf(stderr,"Data segments will be ignored.\n");
      no_data_segments = true;
    }

    if(af1->image_pagesize != af2->image_pagesize){
      fprintf(stderr,"Currently, %s requires that both images have the "
            "same image sectorsize.\n"
            "sectorsize(%s)=%ld\n"
            "sectorsize(%s)=%ld\n",
            progname,file1,af1->image_sectorsize, file2,af2->image_sectorsize);
      fprintf(stderr,"Data segments will be ignored.\n");
      no_data_segments = true;
    }
    
    /* Compare all of the metadata segments in af1 with a2.
     * Report those that are missing or different. Then report
     * all of the segments in a2 but not in af1
     */

    /* First build a list of the segments in each */

    vector <string> segs_with_dups;

    AFFILE *af[2] = {af1,af2};
    for(int i=0;i<2;i++){
      af_rewind_seg(af[i]);
      char segname[AF_MAX_NAME_LEN];
      while(af_get_next_seg(af[i],segname,sizeof(segname),0,0,0)==0){
          if(segname[0]){
            string s;
            s = segname;
            segs_with_dups.push_back(s);  // may give duplicates
          }
      }
    }
    sort(segs_with_dups.begin(),segs_with_dups.end());
    vector<string>segs;

    /* Make a list of segs without duplicates */
    string last;
    for(vector<string>::iterator i = segs_with_dups.begin();
      i != segs_with_dups.end(); i++){
      if(last != *i){
          segs.push_back(*i);
      }
      last = *i;
    }

    int lowest_page = -1;
    int highest_page = -1;
    /* Scan for the lowest and highest numbers */
    for(vector<string>::iterator i = segs.begin();i != segs.end(); i++){
      int num = af_segname_page_number(i->c_str());
      if(num!=-1){
          if(num<lowest_page ||lowest_page==-1)  lowest_page = num;
          if(num>highest_page||highest_page==-1) highest_page = num;
      }
    }


    if(opt_page != -1){
      lowest_page  = opt_page;
      highest_page = opt_page;
    }


    if(opt_page == -1 && vni1.supports_metadata && vni2.supports_metadata){
      if(opt_all) puts("Inspecting metadata...");
      for(int mode=1;mode<=3;mode++){
          char *title = "Metadata segments ";
          char mode_title[1024];
          switch(mode){
          case 1: sprintf(mode_title,"  %s only in %s:\n",
                      title,af_filename(af1));break;
          case 2: sprintf(mode_title,"  %s only in %s:\n",
                      title,af_filename(af2));break;
          case 3: sprintf(mode_title,"  %s in both files:\n",title);break;
          }
          
          for(vector<string>::iterator i = segs.begin();i != segs.end();i++){
            int num = af_segname_page_number(i->c_str());
            if(num==-1){
                int r = compare_aff_metadata_segments(mode_title, af1,af2,i->c_str(),mode);
                if(r!=0) ret = r;
            }
          }
      }
    }

    if(opt_all) puts("Inspecting data...");
    for(int mode=1;mode<=3;mode++){
      char mode_title[1024];
      switch(mode){
      case 1: sprintf(mode_title,"  Pages only in %s:\n", af_filename(af1));break;
      case 2: sprintf(mode_title,"  Pages only in %s:\n", af_filename(af2));break;
      case 3: sprintf(mode_title,"  Pages in both files:\n");break;
      }
          
      for(int i=lowest_page;i<=highest_page;i++){
          int r = compare_aff_data_segments(mode_title,af1,af2,i,mode);
          if(r!=0) ret = r;
      }
    }
    return ret;
}

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

     bflag = 0;
     while ((ch = getopt(argc, argv, "s:h?rqb:abcn:p:v")) != -1) {
       switch (ch) {
       case 'q':
           opt_quiet++;
           break;
       case 'a':
           opt_all++;
           break;
       case 'b':
           opt_print_sectors=1;
           break;
       case 'c':
           opt_print_sector_contents=1;
           break;
       case 'p':
           opt_page = atoi(optarg);
           break;
       case 'h':
       case '?':
       default:
           usage();
      case 'v':
          printf("%s version %s\n",progname,xstr(AFFLIB_VERSION));
          exit(0);
       }
     }
     argc -= optind;
     argv += optind;

     if(argc!=2){             // if just 2, compare them
       usage();
       exit(0);
     }
     char *file1 = *argv++;
     char *file2 = *argv++;
       
     int e_code = compare_aff_aff(file1,file2);
     exit(e_code);
}

Generated by  Doxygen 1.6.0   Back to index