/***************************************************************** Start here *****************************************************************/ int main(int np, char **p) { char src_drive[NAME_LENGTH] = "/dev/hda", /* source drive */ dst_drive[NAME_LENGTH] = "/dev/hdb"; /* destination drive */ int help = 0, /* set to true to indicate problem with command line */ ask_for_partitions = 1, /* default is true; set to false if partitions given on command line */ status, /* error return on I/O operations */ i; /* loop index */ static disk_control_block *src_disk,*dst_disk; static off_t lba = 0, /* a sector LBA address usually found as a loop index */ common, /* number of sectors common to source & destination */ diffs = 0, /* count of sectors that don't match */ src_lba, /* address of current sector on source */ dst_lba, /* address of current sector on destination */ byte_diffs = 0, /* count of bytes that differ between src and dst */ match = 0, /* number of matching sectors */ /* zero .. other apply to dst sectors beyond common area */ zero = 0, /* number of zero filled sectors */ sfill = 0, /* number of sectors filled with src fill char */ dfill = 0, /* number of sectors filled with dst fill char */ ofill = 0, /* number of sectors filled with some other fill char */ other = 0; /* number of remaining (unfilled) sectors */ static unsigned long nz, /* number of zero filled bytes in a sector */ nfill; /* number of fill bytes in a sector */ int big_src = 0, /* true if src bigger than dst */ big_dst = 0, /* true if dst bigger than src */ is_diff, boot_track_too = 0; /* include boot track in compare */ int src_status, dst_status; /* I/O error returns */ static unsigned char *src_buff,*dst_buff; /* sector buffers */ static time_t from; /* run start time */ FILE *log; /* log file */ int is_debug = 0, log_diffs = 0; unsigned char src_fill_char, dst_fill_char; /* fill characters */ int fill_char; int src_px,dst_px; /* partition table indices from command line */ off_t src_base, /* LBA address of source partition */ src_n, /* size (number of sectors in) source partition */ dst_base, /* ditto dst */ dst_n; /* range_ptr is used to track a list of ranges. In this case the ranges are disk areas specified in LBA addresses */ range_ptr d_r = create_range_list(), /* common area sectors that don't match */ zf_r = create_range_list(), /* zero filled sectors */ sf_r = create_range_list(), /* sectors with src-fill */ df_r = create_range_list(), /* sectors with dst-fill */ of_r = create_range_list(), /* sectors with some other fill */ o_r = create_range_list(); /* other (unfilled) sectors */ static char comment[NAME_LENGTH] = "", log_name[NAME_LENGTH] = "cmpptlog.txt", access[2] = "a"; /***************************************************************** get start run time and decode command line *****************************************************************/ time(&from); if (np < 8) { printf ("%s: Missing parameters\n",p[0]); print_help(p[0]); return 1; } /***************************************************************** get fill characters from command line *****************************************************************/ sscanf (p[5],"%2x",&fill_char); src_fill_char = fill_char; sscanf (p[7],"%2x",&fill_char); dst_fill_char = fill_char; /***************************************************************** get options from command line *****************************************************************/ for (i = 8; i < np; i++) { if (strcmp (p[i],"-h") == 0) help = 1; /* ask for help */ else if (strcmp (p[i],"-select") == 0) { /* /select src_ix dst_ix */ i = i + 2; if (i >= np) { printf ("%s -select requires two parameters: src partition index\n", p[0]); printf ("and dst partition index\n"); help = 1; } else { sscanf (p[i-1],"%d",&src_px); sscanf (p[i],"%d",&dst_px); ask_for_partitions = 0; /* we got 'em so don't ask */ } } else if (strcmp (p[i],"-new_log")== 0) access[0] = 'w'; else if (strcmp (p[i], "-log_name") == 0) { if(++i >= np) { printf("%s: -log_name option requires a logfile name\n", p[0]); help = 1; } else strncpy(log_name, p[i], NAME_LENGTH - 1); } else if (strcmp (p[i],"-boot")== 0) boot_track_too = 1; else if (strcmp (p[i],"-comment")== 0) { i++; if ( i>=np){ printf ("%s: comment required with -comment\n", p[0]); help = 1; } else strncpy (comment,p[i], NAME_LENGTH - 1); } else { printf("Invalid parameter: %s\n", p[i]); help = 1; } } /***************************************************************** get source and destination drives (in hex) from command line *****************************************************************/ strncpy(src_drive, p[4], NAME_LENGTH - 1); strncpy(dst_drive, p[6], NAME_LENGTH - 1); if (!strcmp(src_drive, dst_drive)) { help = 1; printf ("Source and destination drives must be different\n"); } /***************************************************************** If there is a problem on command line, then print help message *****************************************************************/ if (help) { print_help(p[0]); return 0; } /***************************************************************** Open log file, source disk and destination disk *****************************************************************/ log = log_open (log_name, access, comment, SCCS_ID, np, p); status = setup_disk (src_drive, "Source disk", log, p, ask_for_partitions, src_px, &src_base, &src_n, &src_disk); status = status || setup_disk (dst_drive, "Destination disk", log, p, ask_for_partitions, dst_px, &dst_base, &dst_n, &dst_disk); if (status) return 1; /***************************************************************** get ready to do the compare see which is bigger: src or dst *****************************************************************/ if (src_n != dst_n) { if ( src_n < dst_n) { common = src_n; big_dst = 1; } else { common = dst_n; big_src = 1; } } else common = src_n; printf ("Source disk fill byte %2X\n", src_fill_char); printf ("Destination disk fill byte %2X\n", dst_fill_char); fprintf (log, "Source disk fill byte %2X\n", src_fill_char); fprintf (log, "Destination disk fill byte %2X\n", dst_fill_char); src_lba = src_base; dst_lba = dst_base; if (boot_track_too) { common += 63; src_lba -= 63; dst_lba -= 63; src_base -= 63; dst_base -= 63; src_n += 63; dst_n += 63; } fprintf (log,"Source base sector %llu Destination base sector %llu\n", src_base,dst_base); /***************************************************************** Main compare loop: for each sector in common read src sector read dst sector if match then increment match count else increment different count *****************************************************************/ for (lba = 0; lba < common; lba++) { feedback (from, 0, lba, big_dst ? dst_n : common); /* give progress feedback to user */ is_diff = 0; src_status = read_lba(src_disk, src_lba++, &src_buff); dst_status = read_lba(dst_disk, dst_lba++, &dst_buff); if (src_status || dst_status) { fprintf (log,"read error at sector %llu: src %d dst %d\n", lba, src_status, dst_status); printf ("read error at lba %llu: src %d dst %d\n", lba, src_status, dst_status); return 1; } /***************************************************************** Compare corresponding sectors *****************************************************************/ for (i = 0; i < BYTES_PER_SECTOR; i++) { if (src_buff[i] != dst_buff[i]) { is_diff = 1; byte_diffs++; } } if (is_diff) { diffs++; add_to_range (d_r,lba); if (log_diffs && (diffs <= 50)) { fprintf (log,"%12llu ",lba); if ((diffs%5) == 0) fprintf (log,"\n"); } } else { match++; } } /***************************************************************** Log results for corresponding sectors *****************************************************************/ if (log_diffs && (diffs)) fprintf (log,"\n"); fprintf (log,"Sectors compared: %12llu\n",common); fprintf (log,"Sectors match: %12llu\n",match); fprintf (log,"Sectors differ: %12llu\n",diffs); fprintf (log,"Bytes differ: %12llu\n",byte_diffs); print_range_list(log,"Diffs range: ",d_r); if (big_src) { fprintf (log,"Source (%llu) has %llu more sectors than destination (%llu)\n", src_n, src_n - dst_n, dst_n); } /***************************************************************** If the destination is larger than the source then look at the remainder of the destination *****************************************************************/ else if (big_dst) { fprintf (log,"Source (%llu) has %llu fewer sectors than destination (%llu)\n", src_n, dst_n - src_n, dst_n); zero = 0; ofill = sfill = dfill = 0; other = 0; printf ("Destination larger than source; scanning %llu sectors\n", dst_n-common); for (lba = common; lba < dst_n; is_debug?(lba+=100):lba++){ feedback (from, 0, lba, dst_n); if((dst_status = read_lba(dst_disk, dst_lba++, &dst_buff))) { fprintf (log,"read error at sector %llu: dst %d\n", lba, dst_status); printf ("read error at lba %llu: dst %d\n", lba, dst_status); } nz = 0; nfill = 0; /***************************************************************** classify sector: count zero bytes and fill bytes how to count fill bytes? the rule is: all bytes after byte [23] are the same. i.e., 488 bytes of the sector are the same. We use 480 to give some slack. *****************************************************************/ for (i = 0; i < BYTES_PER_SECTOR; i++) { if ( dst_buff[i] == 0) nz++; else if (dst_buff[i] == dst_buff[BUFF_OFF]) nfill++; } if (nz == BYTES_PER_SECTOR) { zero++; add_to_range(zf_r,lba); } else if (nfill > 480) { if (dst_buff[BUFF_OFF] == src_fill_char) { sfill++; add_to_range(sf_r,lba); } else if (dst_buff[BUFF_OFF] == dst_fill_char) { dfill++; add_to_range(df_r,lba); } else { ofill++; add_to_range(of_r,lba); } } else { other++; add_to_range (o_r,lba); } } /***************************************************************** log results to log file *****************************************************************/ if (log_diffs && (other)) fprintf (log, "\n"); fprintf (log,"Zero fill: %llu\n", zero); fprintf (log,"Src Byte fill (%02X): %llu\n", src_fill_char, sfill); if (src_fill_char == dst_fill_char) fprintf (log, "Dst Fill Byte same as Src Fill Byte\n"); else fprintf (log, "Dst Byte fill (%02X): %lu\n", dst_fill_char, dfill); fprintf (log,"Other fill: %llu\n", ofill); fprintf (log,"Other no fill: %llu\n", other); print_range_list(log,"Zero fill range: ", zf_r); print_range_list(log,"Src fill range: ", sf_r); print_range_list(log,"Dst fill range: ", df_r); print_range_list(log,"Other fill range: ", of_r); print_range_list(log,"Other not filled range: ", o_r); } log_close(log, from); return 0; }
/* * Intersect the current allocated disk ranges (curranges) with the * hashinfo ranges read from the signature file (hfile). * Return the resulting range list. */ struct range * hashmap_compute_delta(struct range *curranges, char *hfile, int infd, uint32_t ssect) { uint32_t gapstart, gapsize, lastdrangeend = 0; unsigned int hashlen; unsigned char *(*hashfunc)(const unsigned char *, size_t, unsigned char *); struct range dummy_head, *range_tail; struct hashregion *hreg, *ereg; char *hashstr; struct hashinfo *hinfo; struct range *drange; int retval, changed, gapcount; /* * No allocated ranges, that was easy! */ if (curranges == NULL) return NULL; /* * First we read the hashfile to get hash ranges and values */ retval = readhashinfo(hfile, &hinfo, ssect); if (retval < 0) { fprintf(stderr, "readhashinfo: failed !\n" " * * * Aborting * * *\n"); exit(1); } /* * Deterimine the hash function */ switch (hinfo->hashtype) { case HASH_TYPE_MD5: default: hashlen = 16; hashfunc = MD5; hashstr = "MD5 digest"; break; case HASH_TYPE_SHA1: hashlen = 20; hashfunc = SHA1; hashstr = "SHA1 digest"; break; } /* * The new range list. Use a dummy element as the head and * keep track of the tail for easy appending. The dummy element * is initialized such that add_to_range() will not coalesce * anything with it and it will remain distinct. */ dummy_head.start = ~0; dummy_head.size = 0; dummy_head.next = 0; range_tail = &dummy_head; /* * Loop through all hash regions, comparing with the currently * allocated disk regions. */ drange = curranges; ereg = hinfo->regions + hinfo->nregions; for (hreg = hinfo->regions; hreg < ereg; hreg++) { assert(drange && drange->size > 0); #ifdef FOLLOW fprintf(stderr, "H: [%u-%u] start\n", hreg->region.start, hreg->region.start + hreg->region.size - 1); fprintf(stderr, " D: [%u-%u] start\n", drange->start, drange->start + drange->size - 1); #endif /* * Any allocated ranges on disk that are before the * hash range are newly allocated, and must be put in the image. */ while (drange && (drange->start + drange->size) <= hreg->region.start) { #ifdef FOLLOW fprintf(stderr, " D: [%u-%u] pre-hreg skip\n", drange->start, drange->start + drange->size - 1); #endif #ifdef HASHSTATS hashstats.cur_allocated += drange->size; hashstats.cur_only += drange->size; #endif if (add_to_range(&range_tail, drange->start, drange->size) < 0) goto error; lastdrangeend = drange->start + drange->size; drange = drange->next; assert(drange == NULL || drange->size > 0); } if (drange == NULL) break; assert(hreg->region.start < (drange->start + drange->size)); #ifdef FOLLOW fprintf(stderr, " D: [%u-%u] after pre-hreg skip\n", drange->start, drange->start + drange->size - 1); #endif /* * Any allocated range in the original image that is below our * first allocated range on the current disk can be ignored. * (The blocks must have been deallocated.) */ if (hreg->region.start + hreg->region.size <= drange->start) { #ifdef HASHSTATS hashstats.orig_only += hreg->region.size; #endif continue; } /* * Otherwise there is some overlap between the current drange * and hreg. To simplfy things, we split drange so that we can * treat the portion of drange before the overlap seperately. * thus aligning with hash boundaries */ assert(hreg->region.start + hreg->region.size > drange->start); assert(hreg->region.start < drange->start + drange->size); /* * Any part of the drange that falls before the hreg is * new data and needs to be in the image. */ if (drange->start < hreg->region.start) { uint32_t before = hreg->region.start - drange->start; #ifdef HASHSTATS hashstats.cur_allocated += before; hashstats.cur_only += before; #endif if (add_to_range(&range_tail, drange->start, before) < 0) goto error; #ifdef FOLLOW fprintf(stderr, " D: [%u-%u]/[%u-%u] drange head split\n", drange->start, drange->start + before - 1, drange->start + before, drange->start + drange->size); #endif /* * Update drange with new start and size to account * for the stuff we've taken off. We continue * processing with this new range. */ drange->start += before; drange->size -= before; } /* * We have now isolated one or more dranges that are "covered" * by the current hreg. Here we might use the hash value * associated with the hreg to determine whether the * corresponding disk contents have changed. If there is a * single drange that exactly matches the hreg, then we * obviously do this. But what if there are gaps in the * coverage, i.e., multiple non-adjacent dranges covered by * the hreg? This implies that not all blocks described by * the original hash are still important in the current image. * In fact there could be as little as a single disk block * still valid for a very large hrange. * * In this case we can either blindly include the dranges * in the merged list (hash_free==0), or we can go ahead and * do the hash over the entire range (hash_free==1) on the * chance that the blocks that are no longer allocated (the * "gaps" between dranges) have not changed content and the * hash will still match and thus we can avoid including the * dranges in the merged list. The latter is valid, but is * it likely to pay off? We will have to see. */ if (hash_free || (drange->start == hreg->region.start && drange->size >= hreg->region.size)) { /* * XXX if there is a fixup, all bets are off * (e.g., they might compare equal now, but not * after the fixup). Just force inclusion of all * data. * * XXX we could do this on a drange by drange basis * below, but I deem it not worth the trouble since * all this code will be changing anyway. */ if (hasfixup(hreg->region.start, hreg->region.size)) { changed = 3; #ifdef FOLLOW fprintf(stderr, " H: [%u-%u] fixup overlap\n", hreg->region.start, hreg->region.start + hreg->region.size-1); #endif } else { TIMEOP( changed = hash_and_cmp(infd, hashfunc, hashlen, hreg, ereg - hreg), time_hash_and_cmp); if (changed < 0) goto error; #ifdef FOLLOW fprintf(stderr, " H: [%u-%u] hash %s\n", hreg->region.start, hreg->region.start + hreg->region.size-1, changed ? "differs" : "matches"); #endif } } else { /* * There is a gap in the dranges covered by the hreg. * Just save all dranges covered by this hreg. */ changed = 2; #ifdef FOLLOW fprintf(stderr, " H: [%u-%u] no compare\n", hreg->region.start, hreg->region.start + hreg->region.size - 1); #endif } #ifdef HASHSTATS hashstats.shared += hreg->region.size; if (!changed) hashstats.unchanged += hreg->region.size; else if (changed > 1) { hashstats.nocompare += hreg->region.size; if (changed == 3) hashstats.fixup += hreg->region.size; } gapstart = hreg->region.start; gapsize = gapcount = 0; #endif /* * Loop through all dranges completely covered by the hreg * and add them or skip them depending on changed. */ assert(drange && drange->start < hreg->region.start + hreg->region.size); while (drange && drange->start < hreg->region.start + hreg->region.size) { uint32_t curstart = drange->start; uint32_t curend = curstart + drange->size; uint32_t hregstart = hreg->region.start; uint32_t hregend = hregstart + hreg->region.size; /* * There may be a final drange which crosses over the * hreg end, in which case we split it, treating the * initial part here, and leaving the rest for the next * iteration. */ if (curend > hregend) { uint32_t after = curend - hregend; #ifdef FOLLOW fprintf(stderr, " D: [%u-%u]/[%u-%u] drange tail split\n", curstart, hregend - 1, hregend, curend - 1); #endif drange->start = hregend; drange->size = after; curend = hregend; } assert(curstart >= hregstart); assert(curend <= hregend); #ifdef FOLLOW fprintf(stderr, " D: [%u-%u] drange covered\n", curstart, curend - 1); #endif #ifdef HASHSTATS /* * Keep track of the gaps */ if (gapstart < curstart) { #ifdef FOLLOW fprintf(stderr, " G: [%u-%u]\n", gapstart, curstart - 1); #endif gapsize += curstart - gapstart; gapcount++; } gapstart = curend; hashstats.cur_allocated += curend - curstart; #endif if (changed) { /* * add the overlapping region. */ if (add_to_range(&range_tail, curstart, curend - curstart) < 0) goto error; } /* * Unless we split the current entry, bump * drange to the next entry. */ if (curstart == drange->start) { lastdrangeend = curend; drange = drange->next; assert(drange == NULL || drange->size > 0); } } #ifdef HASHSTATS /* * Check for an end gap */ if (gapstart < hreg->region.start + hreg->region.size) { uint32_t hregend = hreg->region.start + hreg->region.size; #ifdef FOLLOW fprintf(stderr, " G: [%u-%u]\n", gapstart, hregend - 1); #endif gapsize += hregend - gapstart; gapcount++; } /* * Properly account for gaps. * Earlier we counted the gap as part of the shared * space and as either unchanged or uncompared--adjust * those counts now. */ if (gapcount) { hashstats.gaps++; /* note adjustment of counts set above */ hashstats.shared -= gapsize; hashstats.gapsects += gapsize; if (!changed) { hashstats.unchanged -= gapsize; hashstats.unchangedgaps++; hashstats.gapunchanged += gapsize; } else if (changed > 1) { hashstats.nocompare -= gapsize; if (changed == 3) hashstats.fixup -= gapsize; hashstats.gapnocompare += gapsize; } #ifdef FOLLOW fprintf(stderr, " H: [%u-%u] %d/%d free\n", hreg->region.start, hreg->region.start + hreg->region.size - 1, gapsize, hreg->region.size); #endif } #endif if (drange == NULL) break; assert(drange->start >= hreg->region.start + hreg->region.size); } assert(drange == NULL || hreg == ereg); assert(lastdrangeend > 0); /* * Remaining hash entries are ignored since they are deallocated * space. We do keep stats about them however. */ #ifdef HASHSTATS while (hreg < ereg) { uint32_t size; /* * If we ran out of dranges in the middle of an hreg, * the rest of the hreg is deallocated. */ if (lastdrangeend > 0 && lastdrangeend <= hreg->region.start + hreg->region.size) { size = hreg->region.start + hreg->region.size - lastdrangeend; #ifdef FOLLOW fprintf(stderr, "H: [%u-%u]/[", hreg->region.start, lastdrangeend - 1); if (size) fprintf(stderr, "%u-%u", lastdrangeend, hreg->region.start + hreg->region.size - 1); fprintf(stderr, "] split, tail skipped\n"); #endif } else { size = hreg->region.size; #ifdef FOLLOW fprintf(stderr, "H: [%u-%u] skipped\n", hreg->region.start, hreg->region.start + hreg->region.size - 1); #endif } hashstats.orig_only += size; lastdrangeend = 0; hreg++; } #endif /* * Remaining dranges are added to the changed blocks list. */ while (drange) { assert(hreg == ereg); #ifdef HASHSTATS hashstats.cur_allocated += drange->size; hashstats.cur_only += drange->size; #endif if (add_to_range(&range_tail, drange->start, drange->size) < 0) goto error; drange = drange->next; assert(drange == NULL || drange->size > 0); } return dummy_head.next; error: freeranges(dummy_head.next); return NULL; }
/****************************************************************************** Examine a part of the destination disk that does not correspond to any area on the source disk. This is either (1) the sectors of a destination chunk that do not correspond to the source sectors of the corresponding chunk. or (2) destination chunk of sectors not allocated to a corresponding source chunk. To say this another way, source partitions are compared to destination partitions that are a little larger. Sectors in (1) above are the excess destination sectors. If the source has unallocated (i.e., not in a partition) sectors between two partitions, the area between is treated as a partition. Sectors in (2) are the excess sectors after the last partition on the destination. ******************************************************************************/ void scan_region (FILE *log, /* the log file */ disk_control_ptr dst_disk, /* the destination disk */ off_t common, /* starting sector LBA address */ off_t dst_n, /* number of sectors to scan */ unsigned char src_fill_char, /* the source fill character */ unsigned char dst_fill_char, /* the destination fill character */ time_t start_time, /* time the program started running */ off_t *tz, /* update value: running total of zero fill sectors */ off_t *tnz) /* update value: running total of non-zero sectors */ { range_ptr zf_r = create_range_list(), /* range of zero fill sectors */ sf_r = create_range_list(), /* range of source fill sectors */ df_r = create_range_list(), /* range of destination fill sectors */ of_r = create_range_list(), /* range of other fill sectors */ o_r = create_range_list(); /* range of other sectors */ unsigned char *dst_buff, /* the sector to scan */ other_fill_char = 0; /* last other fill char seen */ int dst_status; /* disk I/O status return */ off_t lba = 0, /* the sector relative to start address */ nz, /* count of zero bytes in a sector */ nfill, /* count of fill bytes in a sector */ dst_lba, /* the absolute lba of sector to examine */ zero = 0, /* number of zero sectors */ sfill = 0, /* number of sectors filled with source byte */ dfill = 0, /* number of sectors filled with dst byte */ ofill = 0, /* number of sectors filled with something else */ other = 0; /* count of other sectors */ int i, /* look index */ other_fill_seen = 0, /* flag indicating fill other than src/dst */ new_fill = 0; printf ("scanning %llu unmatched sectors: %llu--%llu\n",dst_n-common,common,dst_n); fprintf (log,"scanning %llu unmatched sectors: %llu--%llu\n",dst_n-common,common,dst_n); /****************************************************************************** Loop to scan dst_n sectors from common up to common + dst_n ******************************************************************************/ dst_lba = common; for (lba = common; lba < dst_n; lba++) { feedback (start_time,0,dst_lba,n_sectors(dst_disk)); dst_status = read_lba(dst_disk,dst_lba++,&dst_buff); if (dst_status) { fprintf (log,"dst read error 0x%02X on track starting at lba %llu\n",dst_status,dst_lba-1); printf ("dst read error 0x%02X on track starting at lba %llu\n",dst_status,dst_lba-1); exit(1); } nz = 0; nfill = 0; /****************************************************************************** scan the sector, count number of zero bytes and number of fill bytes. To count fill bytes: assume sector is filled (from diskwipe) then ... bytes 1-27 has the sector address and the remaining bytes are the same. so pick byte # 30 and count the number of bytes that match dst_buff[30], if enough match (480) then call it filled. The magic constants 30 and 480 allow some room for diskwipe to be off by a few bytes. ******************************************************************************/ for (i = 0; i < BYTES_PER_SECTOR; i++) { if ( dst_buff[i] == 0) nz++; else if (dst_buff[i] == dst_buff[BUFF_OFF]) nfill++; } if (nz == BYTES_PER_SECTOR) { zero++; add_to_range(zf_r,lba); } /* zero sector */ else if ((nfill > 480) && (dst_buff[BUFF_OFF] != 0x00)) { /* filled sector */ if (dst_buff[BUFF_OFF] == src_fill_char) { /* src fill */ sfill++; add_to_range(sf_r,lba); } else if (dst_buff[BUFF_OFF] == dst_fill_char) { /* dst fill */ dfill++; add_to_range(df_r,lba); } else { /* filled with something other than src or dst!! */ ofill++; add_to_range(of_r,lba); if (other_fill_seen) { if (dst_buff[BUFF_OFF] != other_fill_char) new_fill = 1; } else { /* remember the other fill char */ other_fill_char = dst_buff[BUFF_OFF]; new_fill = 0; } } } else { other++; /* not zero and not filled */ add_to_range (o_r,lba); } } /****************************************************************************** Log results ******************************************************************************/ fprintf (log,"Zero fill: %llu\n",zero); fprintf (log,"Src Byte fill (%02X): %llu\n",src_fill_char,sfill); if (src_fill_char == dst_fill_char ) fprintf (log,"Dst Fill Byte same as Src Fill Byte\n"); else fprintf (log,"Dst Byte fill (%02X): %llu\n",dst_fill_char,dfill); fprintf (log,"Other fill %c(%02X): %llu\n",new_fill?'+':' ', other_fill_char,ofill); fprintf (log,"Other no fill: %llu\n",other); print_range_list (log,"Zero fill range: ",zf_r); print_range_list (log,"Src fill range: ",sf_r); print_range_list (log,"Dst fill range: ",df_r); print_range_list (log,"Other fill range: ",of_r); print_range_list (log,"Other not filled range: ",o_r); /****************************************************************************** Update running totals for summary ******************************************************************************/ *tz = *tz + zero; *tnz = *tnz + sfill + dfill + ofill + other; }
/****************************************************************************** Compare a source chunk to a destination chunk log number of sectors compared, # match, # diff, etc If dst is larger, (almost certain) then run scan_region on excess sectors ******************************************************************************/ int cmp_region (FILE *log, /* the log file */ /* source parameters: disk, chunk description, fill char */ disk_control_ptr src_disk, layout_ptr src, char src_fill, /* destination parameters: disk, chunk description, fill char */ disk_control_ptr dst_disk, layout_ptr dst, char dst_fill, time_t start_time, /* time the program started running (for user feedback) */ totals_ptr t) /* summary totals */ { unsigned char *src_buff, *dst_buff; /* sector read buffers */ int src_status, dst_status; /* disk read status return */ off_t lba = 0, /* index for main loop; relative sector in partition */ common, /* number of sectors with both a src and dst sector */ diffs = 0, /* number of sectors that differ */ src_lba, /* absolute LBA of src sector */ dst_lba, /* absolute LBA of dst sector */ byte_diffs = 0, /* number of bytes that differ */ match = 0; /* number of sectors that match */ int big_src = 0, /* src is bigger than dst */ big_dst = 0, /* dst is bigger than src */ is_diff, /* src and dst do not match */ i; /* loop index */ range_ptr d_r = create_range_list(); /* sectors that do not match */ if (src->n_sectors == dst->n_sectors) common = src->n_sectors; else if (src->n_sectors > dst->n_sectors) { common = dst->n_sectors; big_src = 1; } else { common = src->n_sectors; big_dst = 1; } src_lba = src->lba_start; dst_lba = dst->lba_start; fprintf (log,"Src base %llu Dst base %llu\n", src_lba,dst_lba); for (lba = 0; lba < common;lba++) { /* main loop: scan sectors that correspond */ is_diff = 0; feedback(start_time,0,dst_lba,n_sectors(dst_disk)); src_status = read_lba(src_disk,src_lba++,&src_buff); dst_status = read_lba(dst_disk,dst_lba++,&dst_buff); if (src_status) { fprintf (log,"src read error 0x%02X on track starting at lba %llu\n",src_status,lba); printf ("src read error 0x%02X on track starting at lba %llu\n",src_status,lba); } if (dst_status) { fprintf (log,"dst read error 0x%02X on track starting at lba %llu\n",dst_status,lba); printf ("dst read error 0x%02X on track starting at lba %llu\n",dst_status,lba); } if(src_status || dst_status) return 1; /* scan the sectors; note any diffs */ for (i = 0; i < BYTES_PER_SECTOR; i++) { if (src_buff[i] != dst_buff[i]) { is_diff = 1; byte_diffs++; } } if (is_diff) { /* rats! not a match */ diffs++; add_to_range (d_r,lba); } else { /* the source and dst are the same */ match++; } } /* log results */ fprintf (log,"Sectors compared: %12llu\n",common); fprintf (log,"Sectors match: %12llu\n",match); fprintf (log,"Sectors differ: %12llu\n",diffs); fprintf (log,"Bytes differ: %12llu\n",byte_diffs); print_range_list(log,"Diffs range: ",d_r); if (big_src) { fprintf (log,"Source (%llu) has %llu more sectors than destination (%llu)\n", src->n_sectors,src->n_sectors - dst->n_sectors, dst->n_sectors); } else if (big_dst) { /* dst has more sectors to examine */ fprintf (log,"Source (%llu) has %llu fewer sectors than destination (%llu)\n", src->n_sectors,dst->n_sectors - src->n_sectors, dst->n_sectors); printf ("Source (%llu) has %llu fewer sectors than destination (%llu)\n", src->n_sectors,dst->n_sectors - src->n_sectors, dst->n_sectors); if (dst->chunk_class == CHUNK_UNALLOCATED) /* look at excess sectors in chunk */ scan_region (log, dst_disk,dst_lba,dst->lba_start + dst->n_sectors, src_fill, dst_fill,start_time, &t->excess_zero,&t->excess_non_zero); else scan_region (log, dst_disk,dst_lba,dst->lba_start + dst->n_sectors, src_fill, dst_fill,start_time, &t->fill_zero,&t->fill_non_zero); } /****************************************************************************** Update summary totals ******************************************************************************/ if (dst->chunk_class == CHUNK_PARTITION) { /* partition summary */ t->n_partitions++; t->partition_diffs += diffs; t->n_common += common; } else if (dst->chunk_class == CHUNK_UNALLOCATED) { /* nothing to do for unallocated chunk */ t->n_unalloc++; t->unalloc_diffs += diffs; t->n_common_unalloc += common; } else { /* boot track summary */ t->n_boot_tracks++; t->boot_track_diffs += diffs; } return 0; }
main (int np, char **p) { char src_drive[NAME_LENGTH], dst_drive[NAME_LENGTH]; /* drive devices */ int help = 0, status, i; static disk_control_ptr src_disk, dst_disk; /* disk information */ off_t lba = 0, /* index for looping through disk sectors */ common, /* number of sectors common to source and dst */ diffs = 0, /* number of sectors that do not match */ nz, /* number of zero bytes in current sector */ nfill,/* number of filled bytes in current sector */ src_ns,dst_ns, /* number of sectors on src and dst */ /* counts: sectors that ... */ byte_diffs = 0, match = 0, zero = 0, sfill = 0, dfill = 0, ofill = 0, other = 0; int big_src = 0, big_dst = 0, is_diff, src_status, dst_status; /* read status codes (should be zero) */ static unsigned char *src_buff, *dst_buff; /* current src and dst sector data */ static time_t from; /* program start time */ FILE *log; /* the log file */ int is_debug = 0; unsigned char other_fill_char, src_fill_char, dst_fill_char; /* the fill characters */ int fill_char, other_fill_seen = 0; off_t n_src_err = 0, n_dst_err = 0; /* count of number of read errs */ char comment[NAME_LENGTH] = "", access[2] = "a"; /* the user comment */ char log_name[NAME_LENGTH] = "cmplog.txt"; /* sectors that ... */ range_ptr d_r = create_range_list(), /* ... differ (do not match) */ zf_r = create_range_list(), /* ... zeros filled */ sf_r = create_range_list(),/* ... source filled */ df_r = create_range_list(), /* ... dst filled */ of_r = create_range_list(), /* ... filled with something else */ o_r = create_range_list(); /* ... are not filled */ time(&from); src_disk = dst_disk = NULL; /***************************************************************** Get the command line *****************************************************************/ if (np < 8) { print_help(p[0]); /* not enough parameters */ return 1; } strncpy(src_drive, p[4], NAME_LENGTH - 1); strncpy(dst_drive, p[6], NAME_LENGTH - 1); printf ("Src drive %s dst drive %s\n",src_drive,dst_drive); sscanf (p[5],"%2x",&fill_char); src_fill_char = fill_char; sscanf (p[7],"%2x",&fill_char); dst_fill_char = fill_char; printf ("Src fill 0x%02X dst fill 0x%02X\n",src_fill_char,dst_fill_char); for (i = 8; i < np; i++) { /* optional parameters */ if (strcmp (p[i],"-h") == 0) help = 1; else if (strcmp (p[i],"-debug")== 0) is_debug = 1; else if (strcmp (p[i],"-new_log")== 0) access[0] = 'w'; else if (strcmp (p[i], "-log_name") == 0) { if(++i >= np) { printf("%s: -log_name option requires a logfile name\n", p[0]); help = 1; } else strncpy(log_name, p[i], NAME_LENGTH - 1); } else if (strcmp (p[i],"-comment")== 0) { if (++i >= np) { printf ("%s: comment required with -comment\n", p[0]); help = 1; } else strncpy (comment, p[i], NAME_LENGTH - 1); } else { printf("Invalid parameter: %s\n", p[i]); help = 1; } } if (help) { print_help(p[0]); return 0; } /***************************************************************** Start log file *****************************************************************/ log = log_open(log_name,access,comment,SCCS_ID,np,p); src_disk = open_disk (src_drive,&status); if (status) { printf ("%s could not access src drive %s status code %d\n", p[0],src_drive,status); fprintf (log,"%s could not access src drive %s status code %d\n", p[0],src_drive,status); return 1; } log_disk(log,"Source",src_disk); dst_disk = open_disk (dst_drive,&status); if (status){ printf ("%s could not access dst drive %s status code %d\n", p[0],dst_drive,status); fprintf (log,"%s could not access dst drive %s status code %d\n", p[0],dst_drive,status); return 1; } log_disk(log,"Destination",dst_disk); src_ns = n_sectors(src_disk); dst_ns = n_sectors(dst_disk); if (src_ns != dst_ns){ if ( src_ns < dst_ns){ common = src_ns; big_dst = 1; } else { common = dst_ns; big_src = 1; } } else common = src_ns; /***************************************************************** Main scan loop: read corresponding sectors and compare *****************************************************************/ for (lba = 0; lba < common; is_debug?(lba+=100):lba++){ is_diff = 0; feedback (from,0,lba,big_dst?dst_ns:common); src_status = read_lba(src_disk,lba,&src_buff); dst_status = read_lba(dst_disk,lba,&dst_buff); if (src_status) { /* if bad sectors, keep list of first 10 */ n_src_err++; if (n_src_err < 11) { fprintf (log,"src read error 0x%02X on track starting at lba %llu\n", src_status,lba); printf ("src read error 0x%02X at lba %llu\n",src_status,lba); } else if (n_src_err == 11) { fprintf (log,"... more src read errors\n"); printf ("... more src read errors\n"); } continue; } if (dst_status) { /* if bad sectors, keep list of first 10 */ n_dst_err++; if (n_dst_err < 11) { fprintf (log,"dst read error 0x%02X on track starting at lba %llu\n", dst_status,lba); printf ("dst read error 0x%02X at lba %llu\n",dst_status,lba); } else if (n_dst_err == 11) { fprintf (log,"... more dst read errors\n"); printf ("... more dst read errors\n"); } continue; } for (i = 0; i < BYTES_PER_SECTOR; i++){ /* count bytes different */ if (src_buff[i] != dst_buff[i]){ is_diff = 1; byte_diffs++; } } if (is_diff){/* sectors do not match */ diffs++; add_to_range (d_r,lba); } else { match++; } } /* log results for corresponding sectors */ fprintf (log,"Sectors compared: %8llu\n",common); fprintf (log,"Sectors match: %8llu\n",match); fprintf (log,"Sectors differ: %8llu\n",diffs); if (n_src_err + n_dst_err){ /* note any I/O errors */ fprintf (log, "Sectors skipped: %8llu (due to %llu src & %llu dst I/O errors)\n", n_src_err+n_dst_err,n_src_err,n_dst_err); } fprintf (log,"Bytes differ: %8llu\n",byte_diffs); print_range_list(log,"Diffs range",d_r); if (big_src){ fprintf (log,"Source (%llu) has %llu more sectors than destination (%llu)\n", src_ns,src_ns - dst_ns, dst_ns); } else if (big_dst){ /* examine remainder of a larger destination */ fprintf (log,"Source (%llu) has %llu fewer sectors than destination (%llu)\n", src_ns,dst_ns - src_ns, dst_ns); zero = 0; ofill = sfill = dfill = 0; other = 0; printf ("Destination larger than source; scanning %llu sectors\n", dst_ns-common); for (lba = common; lba < dst_ns; is_debug?(lba+=100):lba++){ feedback (from,0,lba,dst_ns); dst_status = read_lba(dst_disk,lba,&dst_buff); if (dst_status){ n_dst_err++; if (n_dst_err < 11){ fprintf (log,"dst read error 0x%02X on track starting at lba %llu\n", dst_status,lba); printf ("dst read error 0x%02X at lba %llu\n",dst_status,lba); } if (n_dst_err == 11) { fprintf (log,"... more dst read errors\n"); printf ("... more dst read errors\n"); } continue; } nz = 0; nfill = 0; for (i = 0; i < BYTES_PER_SECTOR; i++) { if (dst_buff[i] == 0) nz++; else if (dst_buff[i] == dst_buff[BUFF_OFF]) nfill++; } if (nz == BYTES_PER_SECTOR) {zero++; add_to_range(zf_r,lba);} else if (nfill > 480){ /* filled sector: figure out src, dst or other */ if (dst_buff[BUFF_OFF] == src_fill_char){ sfill++; add_to_range(sf_r,lba); } else if (dst_buff[BUFF_OFF] == dst_fill_char){ dfill++; add_to_range(df_r,lba); } else { ofill++; add_to_range(of_r,lba); other_fill_seen = 1; other_fill_char = dst_buff[BUFF_OFF]; } } else { other++; add_to_range (o_r,lba); } } /* log results of scan of extra dst sectors */ fprintf (log,"Zero fill: %8llu\n",zero); fprintf (log,"Src Byte fill (%02X): %8llu\n",src_fill_char,sfill); if (src_fill_char == dst_fill_char) fprintf (log,"Dst Fill Byte same as Src Fill Byte\n"); else fprintf (log,"Dst Byte fill (%02X): %8llu\n",dst_fill_char,dfill); if (other_fill_seen) fprintf (log,"Other fill (%02X): %8llu\n",other_fill_char,ofill); else fprintf (log,"Other fill: %8llu\n",ofill); fprintf (log,"Other no fill: %8llu\n",other); print_range_list (log,"Zero fill range: ",zf_r); print_range_list (log,"Src fill range: ",sf_r); print_range_list (log,"Dst fill range: ",df_r); print_range_list (log,"Other fill range: ",of_r); print_range_list (log,"Other not filled range: ",o_r); } fprintf (log,"%llu source read errors, %llu destination read errors\n", n_src_err,n_dst_err); log_close(log,from); return 0; }