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