1 /*  dvdisaster: Additional error correction for optical media.
2  *  Copyright (C) 2004-2015 Carsten Gnoerlich.
3  *
4  *  Email: carsten@dvdisaster.org  -or-  cgnoerlich@fsfe.org
5  *  Project homepage: http://www.dvdisaster.org
6  *
7  *  This file is part of dvdisaster.
8  *
9  *  dvdisaster is free software: you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation, either version 3 of the License, or
12  *  (at your option) any later version.
13  *
14  *  dvdisaster is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with dvdisaster. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "dvdisaster.h"
24 
25 #include "rs01-includes.h"
26 
27 /***
28  *** Recognize a RS01 error correction file
29  ***/
30 
RS01Recognize(LargeFile * ecc_file,EccHeader ** eh)31 int RS01Recognize(LargeFile *ecc_file, EccHeader **eh)
32 {  int n;
33 
34    *eh = g_malloc(sizeof(EccHeader));
35 
36    LargeSeek(ecc_file, 0);
37    n = LargeRead(ecc_file, *eh, sizeof(EccHeader));
38 
39    if(n != sizeof(EccHeader))
40    {  g_free(*eh);
41       return ECCFILE_INVALID;
42    }
43 
44    if(strncmp((char*)(*eh)->cookie, "*dvdisaster*", 12))
45    {  g_free(*eh);
46       return ECCFILE_DEFECTIVE_HEADER;
47    }
48 
49    if(!strncmp((char*)(*eh)->method, "RS01", 4))
50    {
51 #ifdef HAVE_BIG_ENDIAN
52       SwapEccHeaderBytes(*eh);
53 #endif
54       return ECCFILE_PRESENT;
55    }
56 
57    g_free(*eh);
58    return ECCFILE_WRONG_CODEC;
59 }
60 
61 /***
62  *** Read and buffer CRC information from RS01 file
63  ***/
64 
RS01GetCrcBuf(Image * image)65 CrcBuf *RS01GetCrcBuf(Image *image)
66 {  LargeFile *file = image->eccFile;
67    CrcBuf *cb;
68    guint32 *buf;
69    guint64 image_sectors;
70    guint64 crc_sectors,crc_remainder;
71    guint64 i,j,sec_idx;
72 
73    image_sectors = uchar_to_gint64(image->eccFileHeader->sectors);
74    cb = CreateCrcBuf(image_sectors);
75    buf = cb->crcbuf;
76 
77    /* Seek to beginning of CRC sums */
78 
79    if(!LargeSeek(file, (gint64)sizeof(EccHeader)))
80       Stop(_("Failed skipping the ecc header: %s"),strerror(errno));
81 
82    /* Read crc sums. A sector of 2048 bytes contains 512 CRC sums. */
83 
84    crc_sectors = image_sectors / 512;
85    sec_idx = 0;
86 
87    for(i=0; i<crc_sectors; i++)
88    {  if(LargeRead(file, buf, 2048) != 2048)
89 	 Stop(_("Error reading CRC information: %s"),strerror(errno));
90       buf += 512;
91 
92       for(j=0; j<512; j++, sec_idx++)
93 	 SetBit(cb->valid, sec_idx);
94    }
95 
96    crc_remainder = sizeof(guint32)*(image_sectors % 512);
97    if(crc_remainder)
98    {  if(LargeRead(file, buf, crc_remainder) != crc_remainder)
99 	 Stop(_("Error reading CRC information: %s"),strerror(errno));
100 
101       for( ; sec_idx<image_sectors; sec_idx++)
102 	 SetBit(cb->valid, sec_idx);
103    }
104 
105    return cb;
106 }
107 
108 /***
109  *** Internal checksum handling.
110  ***
111  * Not overly complicated as we just have a global md5sum.
112  */
113 
RS01ResetCksums(Image * image)114 void RS01ResetCksums(Image *image)
115 {  RS01CksumClosure *csc = (RS01CksumClosure*)image->eccFileMethod->ckSumClosure;
116 
117    MD5Init(&csc->md5ctxt);
118 }
119 
RS01UpdateCksums(Image * image,gint64 sector,unsigned char * buf)120 void RS01UpdateCksums(Image *image, gint64 sector, unsigned char *buf)
121 {  RS01CksumClosure *csc = (RS01CksumClosure*)image->eccFileMethod->ckSumClosure;
122 
123    MD5Update(&csc->md5ctxt, buf, 2048);
124 }
125 
RS01FinalizeCksums(Image * image)126 int RS01FinalizeCksums(Image *image)
127 {  Method *self = image->eccFileMethod;
128    RS01CksumClosure *csc = (RS01CksumClosure*)self->ckSumClosure;
129    guint8 image_fp[16];
130    int good_fp;
131 
132    MD5Final(image_fp, &csc->md5ctxt);
133 
134    good_fp = !(memcmp(image_fp, image->eccFileHeader->mediumSum ,16));
135 
136    if(good_fp)
137         return 0;
138    else return DATA_MD5_BAD;
139 }
140 
141 /***
142  *** Read an image sector from the .iso file.
143  ***
144  * Two special cases here:
145  * - Missing sectors (beyond the range recorded in eh->sectors) will be padded with zeros,
146  *   since we need a multiple of ndata sectors for the parity generation.
147  * - Missing sectors beyond the range recorded in ii->sectors, but before the real end
148  *   as defined above are treated as "dead sectors".
149  */
150 
RS01ReadSector(Image * image,unsigned char * buf,gint64 s)151 void RS01ReadSector(Image *image, unsigned char *buf, gint64 s)
152 { gint64 eh_sectors = uchar_to_gint64(image->eccFileHeader->sectors);
153 
154   if(s >= image->sectorSize && s < eh_sectors)
155   {
156      CreateMissingSector(buf, s, NULL, 0, NULL); /* truncated image */
157   }
158   else if(s >= eh_sectors)
159   {
160      memset(buf, 0, 2048);            /* zero padding for reads past the image */
161   }
162   else                                /* else normal read within the image */
163   {  int n,expected;
164 
165      if(!LargeSeek(image->file, (gint64)(2048*s)))
166        Stop(_("Failed seeking to sector %lld in image: %s"),
167 	    s, strerror(errno));
168 
169      /* Prepare for short reads at the last image sector.
170 	Doesn't happen for CD and DVD media, but perhaps for future media? */
171 
172      if(s < image->sectorSize-1) expected = 2048;
173      else
174      {  memset(buf, 0, 2048);
175         expected = image->inLast;
176      }
177 
178      /* Finally, read the sector */
179 
180      n = LargeRead(image->file, buf, expected);
181      if(n != expected)
182        Stop(_("Failed reading sector %lld in image: %s"),s,strerror(errno));
183   }
184 }
185 
186 
187 /*
188  * Scan the image for missing blocks.
189  * If the ecc file is present, also compare the CRC sums.
190  * If CREATE_CRC is requested, calculate the CRC sums.
191  *
192  * Actually this should be usable for all RS01 type ecc files.
193  * But unless we have more than one codec, we'll label it as
194  * as RS01 specific method.
195  */
196 
197 #define CRCBUFSIZE (1024*256)
198 
RS01ScanImage(Method * method,Image * image,struct MD5Context * ecc_ctxt,int mode)199 void RS01ScanImage(Method *method, Image* image, struct MD5Context *ecc_ctxt, int mode)
200 {  RS01Widgets *wl = NULL;
201    unsigned char buf[2048];
202    guint32 *crcbuf = NULL;
203    int unrecoverable_sectors = 0;
204    int crcidx = 0;
205    struct MD5Context image_md5;
206    gint64 s, first_missing, last_missing;
207    gint64 prev_missing = 0;
208    gint64 prev_crc_errors = 0;
209    int last_percent,current_missing;
210    char *msg;
211 
212    /* Extract widget list from method */
213 
214    if(method->widgetList)
215      wl = (RS01Widgets*)method->widgetList;
216 
217    /* Position behind the ecc file header,
218       initialize CRC buffer pointers */
219 
220    if(image->eccFile)
221    {  if(!LargeSeek(image->eccFile, (gint64)sizeof(EccHeader)))
222          Stop(_("Failed skipping the ecc header: %s"),strerror(errno));
223 
224       crcbuf = g_malloc(sizeof(guint32) * CRCBUFSIZE);
225       crcidx = (mode & CREATE_CRC) ? 0 : CRCBUFSIZE;
226       if(mode & CREATE_CRC)
227 	 MD5Init(ecc_ctxt);            /* md5sum of CRC portion of ecc file */
228    }
229 
230    /* Prepare for scanning the image and calculating its md5sum */
231 
232    MD5Init(&image_md5);              /* md5sum of image file itself */
233    LargeSeek(image->file, 0);        /* rewind image file */
234 
235    if(mode & PRINT_MODE)
236         msg = _("- testing sectors  : %3d%%");
237    else msg = _("Scanning image sectors: %3d%%");
238 
239    last_percent = 0;
240    image->sectorsMissing = 0;
241    first_missing = last_missing = -1;
242 
243    /* Go through all sectors and look for the "dead sector marker" */
244 
245    for(s=0; s<image->sectorSize; s++)
246    {  int n,percent,err;
247 
248       /* Check for user interruption */
249 
250       if(Closure->stopActions)
251       {  image->sectorsMissing += image->sectorSize - s;
252 	 if(crcbuf) g_free(crcbuf);
253          return;
254       }
255 
256       /* Read the next sector */
257 
258       n = LargeRead(image->file, buf, 2048);
259       if(n != 2048)
260       {  if(s != image->sectorSize - 1 || n != image->inLast)
261          {  if(crcbuf) g_free(crcbuf);
262 	    Stop(_("premature end in image (only %d bytes): %s\n"),n,strerror(errno));
263          }
264 	 else /* Zero unused sectors for CRC generation */
265 	    memset(buf+image->inLast, 0, 2048-image->inLast);
266       }
267 
268       /* Look for the dead sector marker */
269 
270       err = CheckForMissingSector(buf, s, image->fpState == 2 ? image->imageFP : NULL,
271 				  FINGERPRINT_SECTOR);
272       if(err != SECTOR_PRESENT)
273       {    current_missing = TRUE;
274 	ExplainMissingSector(buf, s, err, SOURCE_IMAGE, &unrecoverable_sectors);
275       }
276       else current_missing = FALSE;
277 
278       if(current_missing)
279       {  if(first_missing < 0) first_missing = s;
280          last_missing = s;
281 	 image->sectorsMissing++;
282       }
283 
284       /* Report dead sectors. Combine subsequent missing sectors into one report. */
285 
286       if(mode & PRINT_MODE)
287 	if(!current_missing || s==image->sectorSize-1)
288 	{  if(first_missing>=0)
289 	    {   if(first_missing == last_missing)
290 		     PrintCLI(_("* missing sector   : %lld\n"), first_missing);
291 		else PrintCLI(_("* missing sectors  : %lld - %lld\n"), first_missing, last_missing);
292 	      first_missing = -1;
293 	   }
294 	}
295 
296       if(image->eccFile) /* Do something with the CRC portion of the .ecc file */
297       {
298 	 /* If creation of the CRC32 is requested, do that. */
299 
300 	 if(mode & CREATE_CRC)
301 	 {  crcbuf[crcidx++] = Crc32(buf, 2048);
302 
303 	    if(crcidx >= CRCBUFSIZE)  /* write out CRC buffer contents */
304 	    {  size_t size = CRCBUFSIZE*sizeof(guint32);
305 
306 	       MD5Update(ecc_ctxt, (unsigned char*)crcbuf, size);
307 	       if(LargeWrite(image->eccFile, crcbuf, size) != size)
308 	       { if(crcbuf) g_free(crcbuf);
309 		 Stop(_("Error writing CRC information: %s"),strerror(errno));
310 	       }
311 	       crcidx = 0;
312 	    }
313 	 }
314 
315 	 /* else do the CRC32 check. Missing sectors are skipped in the CRC report. */
316 
317 	 else if(s < image->expectedSectors)
318 	 {  guint32 crc = Crc32(buf, 2048);
319 
320             /* If the CRC buf is exhausted, refill. */
321 
322             if(crcidx >= CRCBUFSIZE)
323 	    {  size_t remain = image->sectorSize-s;
324 	       size_t size;
325 
326 	       if(remain < CRCBUFSIZE)
327 		    size = remain*sizeof(guint32);
328 	       else size = CRCBUFSIZE*sizeof(guint32);
329 
330 	       if(LargeRead(image->eccFile, crcbuf, size) != size)
331 	       { if(crcbuf) g_free(crcbuf);
332 		 Stop(_("Error reading CRC information: %s"),strerror(errno));
333 	       }
334 	       crcidx = 0;
335 	    }
336 
337 	    if(crc != crcbuf[crcidx++] && !current_missing)
338 	    {  PrintCLI(_("* CRC error, sector: %lld\n"), s);
339 	       image->crcErrors++;
340 	    }
341 	 }
342       }
343 
344       MD5Update(&image_md5, buf, n);  /* update image md5sum */
345 
346       if(Closure->guiMode && mode & PRINT_MODE)
347 	   percent = (VERIFY_IMAGE_SEGMENTS*(s+1))/image->sectorSize;
348       else percent = (100*(s+1))/image->sectorSize;
349       if(last_percent != percent)
350       {  PrintProgress(msg,percent);
351 
352          if(Closure->guiMode && mode & CREATE_CRC)
353 	   SetProgress(wl->encPBar1, percent, 100);
354 
355 	 if(Closure->guiMode && mode & PRINT_MODE)
356  	 {  RS01AddVerifyValues(method, percent, image->sectorsMissing, image->crcErrors,
357 				image->sectorsMissing - prev_missing,
358 				image->crcErrors - prev_crc_errors);
359 
360 	    prev_missing = image->sectorsMissing;
361 	    prev_crc_errors = image->crcErrors;
362 	 }
363 
364 	 last_percent = percent;
365       }
366    }
367 
368    /*** Flush the rest of the CRC buffer */
369 
370    if((mode & CREATE_CRC) && crcidx)
371    {  size_t size = crcidx*sizeof(guint32);
372 
373       MD5Update(ecc_ctxt, (unsigned char*)crcbuf, size);
374       if(LargeWrite(image->eccFile, crcbuf, size) != size)
375       {	if(crcbuf) g_free(crcbuf);
376 	Stop(_("Error writing CRC information: %s"),strerror(errno));
377       }
378    }
379 
380    /*** The image md5sum can only be calculated if all blocks have been successfully read. */
381 
382    MD5Final(image->mediumSum, &image_md5);
383 
384    LargeSeek(image->file, 0);
385    if(crcbuf) g_free(crcbuf);
386 }
387 
388 /***
389  *** Determine expected size of image
390  ***/
391 
RS01ExpectedImageSize(Image * image)392 guint64 RS01ExpectedImageSize(Image *image)
393 {  EccHeader *eh = image->eccFileHeader;
394 
395    if(!eh) return 0;
396 
397    return uchar_to_gint64(eh->sectors);
398 }
399