/***************************************************************** 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; }
/** * find_valid_gpt() - Search disk for valid GPT headers and PTEs * @state * @gpt is a GPT header ptr, filled on return. * @ptes is a PTEs ptr, filled on return. * Description: Returns 1 if valid, 0 on error. * If valid, returns pointers to newly allocated GPT header and PTEs. * Validity depends on PMBR being valid (or being overridden by the * 'gpt' kernel command line option) and finding either the Primary * GPT header and PTEs valid, or the Alternate GPT header and PTEs * valid. If the Primary GPT header is not valid, the Alternate GPT header * is not checked unless the 'gpt' kernel command line option is passed. * This protects against devices which misreport their size, and forces * the user to decide to use the Alternate GPT. */ static int find_valid_gpt(struct parsed_partitions *state, gpt_header **gpt, gpt_entry **ptes) { int good_pgpt = 0, good_agpt = 0, good_pmbr = 0; gpt_header *pgpt = NULL, *agpt = NULL; gpt_entry *pptes = NULL, *aptes = NULL; legacy_mbr *legacymbr; u64 lastlba; if (!ptes) return 0; lastlba = last_lba(state->bdev); if (!force_gpt) { /* This will be added to the EFI Spec. per Intel after v1.02. */ legacymbr = kzalloc(sizeof (*legacymbr), GFP_KERNEL); if (legacymbr) { read_lba(state, 0, (u8 *) legacymbr, sizeof (*legacymbr)); good_pmbr = is_pmbr_valid(legacymbr); kfree(legacymbr); } if (!good_pmbr) goto fail; } good_pgpt = is_gpt_valid(state, GPT_PRIMARY_PARTITION_TABLE_LBA, &pgpt, &pptes); if (good_pgpt) good_agpt = is_gpt_valid(state, le64_to_cpu(pgpt->alternate_lba), &agpt, &aptes); if (!good_agpt && force_gpt) good_agpt = is_gpt_valid(state, lastlba, &agpt, &aptes); /* The obviously unsuccessful case */ if (!good_pgpt && !good_agpt) goto fail; compare_gpts(pgpt, agpt, lastlba); /* The good cases */ if (good_pgpt) { *gpt = pgpt; *ptes = pptes; kfree(agpt); kfree(aptes); if (!good_agpt) { printk(KERN_WARNING "Alternate GPT is invalid, " "using primary GPT.\n"); } return 1; } else if (good_agpt) { *gpt = agpt; *ptes = aptes; kfree(pgpt); kfree(pptes); printk(KERN_WARNING "Primary GPT is invalid, using alternate GPT.\n"); return 1; } fail: kfree(pgpt); kfree(agpt); kfree(pptes); kfree(aptes); *gpt = NULL; *ptes = NULL; return 0; }
/****************************************************************************** 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; }
/****************************************************************************** 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; }
/** * find_valid_gpt() - Search disk for valid GPT headers and PTEs * @bdev * @gpt is a GPT header ptr, filled on return. * @ptes is a PTEs ptr, filled on return. * Description: Returns 1 if valid, 0 on error. * If valid, returns pointers to newly allocated GPT header and PTEs. * Validity depends on finding either the Primary GPT header and PTEs valid, * or the Alternate GPT header and PTEs valid, and the PMBR valid. */ static int find_valid_gpt(struct block_device *bdev, gpt_header **gpt, gpt_entry **ptes) { int good_pgpt = 0, good_agpt = 0, good_pmbr = 0; gpt_header *pgpt = NULL, *agpt = NULL; gpt_entry *pptes = NULL, *aptes = NULL; legacy_mbr *legacymbr = NULL; u64 lastlba; if (!bdev || !gpt || !ptes) return 0; lastlba = last_lba(bdev); good_pgpt = is_gpt_valid(bdev, GPT_PRIMARY_PARTITION_TABLE_LBA, &pgpt, &pptes); if (good_pgpt) { good_agpt = is_gpt_valid(bdev, le64_to_cpu(pgpt->alternate_lba), &agpt, &aptes); if (!good_agpt) { good_agpt = is_gpt_valid(bdev, lastlba, &agpt, &aptes); } } else { good_agpt = is_gpt_valid(bdev, lastlba, &agpt, &aptes); } /* The obviously unsuccessful case */ if (!good_pgpt && !good_agpt) { goto fail; } /* This will be added to the EFI Spec. per Intel after v1.02. */ legacymbr = kmalloc(sizeof (*legacymbr), GFP_KERNEL); if (legacymbr) { memset(legacymbr, 0, sizeof (*legacymbr)); read_lba(bdev, 0, (u8 *) legacymbr, sizeof (*legacymbr)); good_pmbr = is_pmbr_valid(legacymbr); kfree(legacymbr); legacymbr=NULL; } /* Failure due to bad PMBR */ if ((good_pgpt || good_agpt) && !good_pmbr && !force_gpt) { printk(KERN_WARNING " Warning: Disk has a valid GPT signature " "but invalid PMBR.\n"); printk(KERN_WARNING " Assuming this disk is *not* a GPT disk anymore.\n"); printk(KERN_WARNING " Use gpt kernel option to override. " "Use GNU Parted to correct disk.\n"); goto fail; } /* Would fail due to bad PMBR, but force GPT anyhow */ if ((good_pgpt || good_agpt) && !good_pmbr && force_gpt) { printk(KERN_WARNING " Warning: Disk has a valid GPT signature but " "invalid PMBR.\n"); printk(KERN_WARNING " Use GNU Parted to correct disk.\n"); printk(KERN_WARNING " gpt option taken, disk treated as GPT.\n"); } compare_gpts(pgpt, agpt, lastlba); /* The good cases */ if (good_pgpt && (good_pmbr || force_gpt)) { *gpt = pgpt; *ptes = pptes; if (agpt) { kfree(agpt); agpt = NULL; } if (aptes) { kfree(aptes); aptes = NULL; } if (!good_agpt) { printk(KERN_WARNING "Alternate GPT is invalid, " "using primary GPT.\n"); } return 1; } else if (good_agpt && (good_pmbr || force_gpt)) { *gpt = agpt; *ptes = aptes; if (pgpt) { kfree(pgpt); pgpt = NULL; } if (pptes) { kfree(pptes); pptes = NULL; } printk(KERN_WARNING "Primary GPT is invalid, using alternate GPT.\n"); return 1; } fail: if (pgpt) { kfree(pgpt); pgpt=NULL; } if (agpt) { kfree(agpt); agpt=NULL; } if (pptes) { kfree(pptes); pptes=NULL; } if (aptes) { kfree(aptes); aptes=NULL; } *gpt = NULL; *ptes = NULL; 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; }
/** * find_valid_gpt() - Search disk for valid GPT headers and PTEs * @fd is an open file descriptor to the whole disk * @gpt is a GPT header ptr, filled on return. * @ptes is a PTEs ptr, filled on return. * Description: Returns 1 if valid, 0 on error. * If valid, returns pointers to newly allocated GPT header and PTEs. * Validity depends on finding either the Primary GPT header and PTEs valid, * or the Alternate GPT header and PTEs valid, and the PMBR valid. */ static int find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes, int ignore_pmbr_err) { int good_pgpt = 0, good_agpt = 0, good_pmbr = 0; gpt_header *pgpt = NULL, *agpt = NULL; gpt_entry *pptes = NULL, *aptes = NULL; legacy_mbr *legacymbr = NULL; uint64_t lastlba; int ret = -1; errno = EINVAL; if (!gpt || !ptes) return -1; lastlba = last_lba(fd); good_pgpt = is_gpt_valid(fd, GPT_PRIMARY_PARTITION_TABLE_LBA, &pgpt, &pptes); if (good_pgpt) { good_agpt = is_gpt_valid(fd, __le64_to_cpu(pgpt->alternate_lba), &agpt, &aptes); if (!good_agpt) { good_agpt = is_gpt_valid(fd, lastlba, &agpt, &aptes); } } else { good_agpt = is_gpt_valid(fd, lastlba, &agpt, &aptes); } /* The obviously unsuccessful case */ if (!good_pgpt && !good_agpt) { goto fail; } /* This will be added to the EFI Spec. per Intel after v1.02. */ legacymbr = malloc(sizeof (*legacymbr)); if (legacymbr) { memset(legacymbr, 0, sizeof (*legacymbr)); read_lba(fd, 0, (uint8_t *) legacymbr, sizeof (*legacymbr)); good_pmbr = is_pmbr_valid(legacymbr); free(legacymbr); legacymbr=NULL; } /* Failure due to bad PMBR */ if ((good_pgpt || good_agpt) && !good_pmbr && !ignore_pmbr_err) { if (report_errors) fprintf(stderr, "Primary GPT is invalid, using alternate GPT.\n"); goto fail; } /* Would fail due to bad PMBR, but force GPT anyhow */ if ((good_pgpt || good_agpt) && !good_pmbr && ignore_pmbr_err && report_errors) { fprintf(stderr, " Warning: Disk has a valid GPT signature but invalid PMBR.\n" " Use GNU Parted to correct disk.\n" " gpt option taken, disk treated as GPT.\n"); } compare_gpts(pgpt, agpt, lastlba); /* The good cases */ if (good_pgpt && (good_pmbr || ignore_pmbr_err)) { *gpt = pgpt; *ptes = pptes; } else if (good_agpt && (good_pmbr || ignore_pmbr_err)) { *gpt = agpt; *ptes = aptes; } ret = 0; errno = 0; fail: if (pgpt && (pgpt != *gpt || ret < 0)) { free(pgpt); pgpt=NULL; } if (pptes && (pptes != *ptes || ret < 0)) { free(pptes); pptes=NULL; } if (agpt && (agpt != *gpt || ret < 0)) { free(agpt); agpt=NULL; } if (aptes && (aptes != *ptes || ret < 0)) { free(aptes); aptes=NULL; } if (ret < 0) { *gpt = NULL; *ptes = NULL; } return ret; }