/* 2of3.c: (ANSI-)C-Programm for the 2-of-3-function It can be used e. g. for safe backups: 1.) Store the same backup on 3 media. 2.) Read the three media; e. g. /dev/sda, /dev/sdb and /dev/sdc, and do the 2-of-3 function of them. The result is error-corrected when not two bits in a row where flipped. Dr. Rolf Freitag 2008, rolf.freitag at email.de This Program is free software; you can redistribute it and/od modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This Program is distributed in the hop that it will be useful, but WITHOUT ANY WARRENTY; without even the implied warrenty of MERCHANTABILITY of 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 this Program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. First version 2008-02: Works. Second version 2008-09: Added a simple error counter, which counts (max.) 8 Byte defect blocks. */ #include #include #include #include #include #include #include #include #include // simple version number #define SOFTWARE_VERSION_NUMBER 0.1 # define mc_MIN(a, b) ((a) < (b) ? (a) : (b)) // 2-of-3-function e. g. for simple ECC where every data (byte or word or ...) is sent three times # define mc_two_of_three(a, b, c) (((a) bitand (b)) bitor ((a) bitand (c)) bitor ((b) bitand (c))) // For error detection you have to check if a, b and c are equal. // It also works if one data of a packet is lost // check if three values are not equal: if a != b or a != c than the check of b != c is useless # define mc_three_unequal(a, b, c) ((a not_eq b) or (a not_eq c)) void sig_handler (int sig) { if ((SIGINT == sig) or (SIGILL == sig) or (SIGKILL == sig) or (SIGSEGV == sig) or (SIGTERM == sig)) { (void) printf ("\a\a\a\a\a\a\a Received signal %d, program exiting... \r\n\a\a\a", sig); exit (-sig); } printf ("Received signal %d.\r\n", sig); return; } int main (const int argc, const char * const argv[]) { FILE *ifp1=NULL; // input-file-pointer1 FILE *ifp2=NULL; // input-file-pointer2 FILE *ifp3=NULL; // input-file-pointer3 FILE *ofp=NULL; // output file pointer int64_t i64errorcount = 0; // simple error counter char inname1[0x3ff] = // input file name1 { '\0' }; char inname2[0x3ff] = // input file name2 { '\0' }; char inname3[0x3ff] = // input file name2 { '\0' }; char outname[0x3ff] = // output file name { '\0' }; int i1 = 0, i2 = 0, i3 = 0, i4 = 0, i_retval=0; // for the chars of ifp1, ifp2, ofp, exit value, counter(s) int i_f1=0, i_f2=0, i_f3=0, i_f4=0, ij=0; union tag { int64_t i64; char c[8]; } // buffer unions u_buffer1 = { 0 } , u_buffer2 = { 0 }, u_buffer3 = { 0 }, u_buffer4 = { 0 }; void *buffer1 = (void *) &u_buffer1; void *buffer2 = (void *) &u_buffer2; void *buffer3 = (void *) &u_buffer3; void *buffer4 = (void *) &u_buffer4; signal (SIGHUP, sig_handler); signal (SIGINT, sig_handler); signal (SIGQUIT, sig_handler); signal (SIGILL, sig_handler); signal (SIGTRAP, sig_handler); signal (SIGIOT, sig_handler); // signal(SIGEMT, sig_handler); signal (SIGFPE, sig_handler); signal (SIGKILL, sig_handler); signal (SIGBUS, sig_handler); signal (SIGSEGV, sig_handler); // signal(SIGSYS, sig_handler); signal (SIGPIPE, sig_handler); signal (SIGALRM, sig_handler); signal (SIGTERM, sig_handler); signal (SIGABRT, sig_handler); if ( (argc>1) and not strncmp (argv[1], "-V", 123) ) { fprintf (stdout, "%s version %.1f\n", argv[0], SOFTWARE_VERSION_NUMBER); exit ( (2 == argc) ? 0 : -1); } if ((argc < 5) || (argc > 5)) { fprintf (stderr, "\aUsage: %s \n", argv[0]); fprintf (stderr, "\aor: %s -V \n", argv[0]); fprintf (stderr, "[] = Optional <> = Parameter Requirement. 1 or 3 parameters required.\n"); exit (-1); } snprintf (inname1, sizeof(inname1)-1, "%s", argv[1]); snprintf (inname2, sizeof(inname2)-1, "%s", argv[2]); snprintf (inname3, sizeof(inname2)-1, "%s", argv[3]); snprintf (outname, sizeof(outname)-1, "%s", argv[4]); ifp1 = fopen (inname1, "r"); ifp2 = fopen (inname2, "r"); ifp3 = fopen (inname3, "r"); if ((ifp1 == NULL) or (ifp2 == NULL) or (ifp3 == NULL)) { fprintf (stderr, "\a input-file-error "); perror("\n error \n"); exit (errno); } ofp = fopen (outname, "w+"); // overwrite existing output file if (ofp == NULL) { fprintf (stderr, "\a\ncan't open %s ", outname); perror("\n error \n"); exit (errno); // missing output file } // loop for 2of3 with 64 bit blocks while (not feof(ifp1) and ((i1 = fread (buffer1, 1, 8, ifp1)) != 0) ) // while the first input file is not completely read { i2 = fread (buffer2, 1, 8, ifp2); i3 = fread (buffer3, 1, 8, ifp3); u_buffer4.i64 = mc_two_of_three(u_buffer1.i64, u_buffer2.i64, u_buffer3.i64); // only for correct error detection: mask what could not be read (old bytes in buffers) if (i1 < 8) { for (ij=7; ij >=i1; ij--) { u_buffer1.c[ij]=0; u_buffer2.c[ij]=0; u_buffer3.c[ij]=0; } } if mc_three_unequal(u_buffer1.i64, u_buffer2.i64, u_buffer3.i64) i64errorcount++; i_f1=ferror(ifp1); i_f2=ferror(ifp2); i_f3=ferror(ifp3); i_f4=ferror(ofp); if (i_f1 or i_f2 or i_f3) // check for file errors { if (i_f1) (void)fprintf(stderr, "input-file1: error %d.\n", i_f1); if (i_f2) (void)fprintf(stderr, "input-file2: error %d.\n", i_f2); if (i_f3) (void)fprintf(stderr, "input-file3: error %d.\n", i_f3); if (i_f3) (void)fprintf(stderr, "output-file: error %d.\n", i_f3); i_retval = -1; } i4 = mc_MIN( mc_MIN(i1, i2), i3); // todo: use i4 for log messages fwrite (buffer4, 1, i1, ofp); // write the corrected byte(s) if (i1 < 8) // A short item count from fread means that the file end is reached. break; } if (i64errorcount) fprintf(stderr, "Error count of (max.) 8 Byte blocks: %lld\n", i64errorcount); fclose (ifp1); fclose (ifp2); fclose (ifp3); fflush (ofp); fclose (ofp); if (ferror (ofp)) { fprintf(stderr, "\a\r\n output-file-error \n"); perror("\n error \n"); exit (errno); } exit (i_retval); } // main