/* dvdimagecmp. Copyright (C) Jan Panteltje 2002-always Usage: dvdimagecmp /dev/dvd imagefile dvdimagecmp is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. dvdimagecmp distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Make; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #define VERSION "0.3" #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #define MAX_RETRIES 1 #define BUFFER_SIZE 2048*16 #define DVD_SPEED_1X 11080000 int debug_flag; char *strsave(char *s) /* save string s somewhere */ { char *p; p = malloc(strlen(s) + 1); if(!p) return 0; strcpy(p, s); return p; } /* end function strsave */ int print_usage() { fprintf(stdout, "\ Usage: dvdimagecmp -a file1 -b file [-d] [-h] [-o offset in file 1] [-p offset in file 2]\n\n\ -d debug mode, print functuons and arguments.\n\ -h help (this help)\n\ -a filename of first input file.\n\ -b filename of second input file.\n\ -o offset from start in bytes in first input file (default 0).\n\ -p offset from start in bytes in second input file (default 0).\n\ "); return 1; } /* end function print_usage */ int main(int argc, char **argv) { int a, c, d, i; unsigned long long count; unsigned long long errors; unsigned long long last_error_position; int bytes1, bytes2; unsigned char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE]; unsigned char error_buffer1[BUFFER_SIZE], error_buffer2[BUFFER_SIZE]; FILE *fin1; FILE *fin2; int error_flag; int eof_flag; time_t start_time, stop_time; int bytes; double mega_bytes; double bits_per_second, kilo_bits_per_second; double speed; int block_errors; int retries; int rerror1, rerror2; long unsigned defective_blocks; unsigned long long difference; unsigned long long block; off_t offset1, offset2; char *filename1, *filename2; /* program identification */ fprintf(stdout, "\nPanteltje (c) dvdimagecmp-%s\n\n", VERSION); /* defaults */ debug_flag = 0; offset1 = 0; offset2 = 0; filename1 = 0; filename2 = 0; /* end defaults */ while(1) { a = getopt(argc, argv, "a:b:dho:p:"); if(a == -1) break; switch(a) { case 'a': filename1 = strsave(optarg); if(! filename1) { fprintf(stderr, "Could not allocate space for filename1, aborting.\n"); exit(1); } break; case 'b': filename2 = strsave(optarg); if(! filename2) { fprintf(stderr, "Could not allocate space for filename2, aborting.\n"); exit(1); } break; case 'd': debug_flag = 1; break; case 'h': print_usage(); exit(1); break; case 'o': sscanf(optarg, "%llu", &offset1); break; case 'p': sscanf(optarg, "%llu", &offset2); break; case -1: break; case '?': if (isprint(optopt) ) { fprintf(stdout, "Unknown option `-%c'.\n", optopt); } else { fprintf(stdout, "Unknown option character `\\x%x'.\n",\ optopt); } print_usage(); exit(1); break; default: print_usage(); exit(1); break; }/* end switch a */ }/* end while getopt() */ if(debug_flag) { fprintf(stderr,\ "filename1=%s filename2=%s offset1=%llu offset2=%llu\n",\ filename1, filename2, offset1, offset2); } if(! filename1) { fprintf(stderr, "No filename1 specified, use -a filename, aborting\n"); print_usage(); exit(1); } if(! filename2) { fprintf(stderr, "No filename2 specified, use -b filename, aborting\n"); print_usage(); exit(1); } fin1 = fopen(filename1, "r"); if(! fin1) { fprintf(stderr, "dvdimagecmp(): could not open first input file %s, aborting.\n", filename1); exit(1); } fin2 = fopen(filename2, "r"); if(! fin2) { fprintf(stderr, "dvdimagecmp(): could not open second input file %s, aborting.\n", filename2); exit(1); } if(offset1 != 0) { errno = 0; a = fseeko(fin1, offset1, SEEK_SET); if(a == -1) { perror("seek in file1 "); fprintf(stderr, "fseek to offset %llu failed in %s, aborting.\n", (long long int)offset1, filename1); exit(1); } } if(offset2 != 0) { errno = 0; a = fseeko(fin2, offset2, SEEK_CUR); if(a == -1) { perror("seek in file 2 "); fprintf(stderr, "fseek to offset %llu failed in %s, aborting.\n", (long long int)offset2, filename2); exit(1); } } if(debug_flag) { fprintf(stderr, "real file1 offset = %llu\n", (unsigned long long) ftello(fin1) ); fprintf(stderr, "real file2 offset = %llu\n", (unsigned long long) ftello(fin2) ); } //exit(1); bytes = 0; kilo_bits_per_second = 0; speed = 0; block = 0; last_error_position = 0; defective_blocks = 0; retries = 0; start_time = time(0); eof_flag = 0; count = 0; errors = 0; while(1) { bytes1 = fread(buffer1, 1, BUFFER_SIZE, fin1); if(ferror(fin1) ) { perror("dvdimagecmp(): fread file 1():"); break; } if(feof(fin1) ) { fprintf(stderr, "EOF found in file1 at %llu\n", count); eof_flag = 1; } bytes2 = fread(buffer2, 1, BUFFER_SIZE, fin2); if(ferror(fin2) ) { perror("dvdimagecmp(): fread file 2():"); break; } if(feof(fin2) ) { fprintf(stderr, "EOF found in file2 at %llu\n", count); eof_flag = 1; } /* total MB */ mega_bytes = (double)count / 1000000.0; /* kbps */ stop_time = time(0); if(stop_time - start_time >= 1) { if(stop_time - start_time == 1) { bits_per_second = bytes * 8.0; kilo_bits_per_second = bits_per_second / 1000.0; speed = bits_per_second / DVD_SPEED_1X; } start_time = stop_time; bytes = 0; } /* progress report to stdout */ fprintf(stdout,\ "count=%llu (%.2fMB), speed=%.0fkbps (%.2fx) errors=%llu dblocks=%lu %08llx\033[K\r",\ count, mega_bytes, kilo_bits_per_second, speed,\ errors, defective_blocks, count); if(eof_flag) break; block_errors = 0; error_flag = 0; for(i = 0; i < bytes1; i++) { c = buffer1[i]; d = buffer2[i]; if(c != d) { if(! error_flag) fprintf(stderr, "\n"); error_flag = 1; /* errors to stderr, so we can do ./dvdimagecmp /dev/dvd /video/dvd_images/dvd_image 2> error-log.txt */ fprintf(stderr,\ "DIFFERENCE count=%llu file1=%d (0x%02x) file2=%d (0x%02x) errors=%llu i=%d block=%llu\n",\ count, c, c, d, d, errors, i, block); block_errors++; errors++; } count++; } /* since we do not know if it is a read or write error, try same area again. */ if(error_flag) { fprintf(stderr, "retries=%d read block\n", retries); /* if not first read, compare to previous read */ if(retries != 0) { rerror1 = 0; rerror2 = 0; for(i = 0; i < BUFFER_SIZE; i++) { if(error_buffer1[i] != buffer1[i]) rerror1++; if(error_buffer2[i] != buffer2[i]) rerror2++; } if( (rerror1 == 0) && (rerror2 == 0) ) { difference = count - last_error_position; fprintf(stderr, "\n"); fprintf(stdout, "\n"); if(last_error_position != 0) { fprintf(stderr,\ "High probability WRITE error!!!!! previous at -%llu\n",\ difference); fprintf(stdout,\ "High probability WRITE error!!!!! previous at -%llu\n",\ difference); } else { fprintf(stderr,\ "High probability WRITE error!!!!!\n"); fprintf(stdout,\ "High probability WRITE error!!!!!\n"); } } else { fprintf(stderr, "High probability READ error.\n"); } last_error_position = bytes; } /* end if not first retry */ retries++; if(retries < MAX_RETRIES) { /* move back to beginning of block */ a = fseek(fin1, -BUFFER_SIZE, SEEK_CUR); if(a == -1) { fprintf(stderr, "fseek to previous block failed in file1.\n"); exit(1); } a = fseek(fin2, -BUFFER_SIZE, SEEK_CUR); if(a == -1) { fprintf(stderr, "fseek to previous block failed in file2.\n"); exit(1); } /* correct values for reporting */ bytes -= bytes1; count -= bytes1; errors -= block_errors; /* store buffers */ for(i = 0; i < BUFFER_SIZE; i++) { error_buffer1[i] = buffer1[i]; error_buffer2[i] = buffer2[i]; } /* try read and compare same block again */ continue; } /* end if les then max retries */ defective_blocks++; /* must be write error, or persistent read error, test next block */ retries = 0; } /* end if error in block */ block++; bytes += bytes1; } /* end while read bytes */ fclose(fin1); fclose(fin2); fprintf(stderr,\ "\n\nReady, %llu bytes read, %llu errors found, %lu defective blocks\n",\ count, errors, defective_blocks); if(errors == 0) exit(0); else exit(1); } /* end main */