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 #define DSM_VERSION "1.00"
26 #define PSM_VERSION "1.00"
27 
28 /***
29  *** Create an unique marker for missing sectors
30  ***/
31 
32 static void write_missing_sector(unsigned char *out, guint64 sector,
33 				 unsigned char *fingerprint, guint64 fingerprint_sector,
34 				 char *volume_label, char *simulation_hint)
35 {  char *buf = (char*)out;
36    char *end_marker;
37    int end_length;
38 
39    /* Bytefill requested? */
40 
41    if(Closure->fillUnreadable >= 0)
42    {  memset(out, Closure->fillUnreadable, 2048);
43       return;
44    }
45 
46    /* historic words ... ;-) */
47 
48    memset(buf, 0, 2048);
49 
50    g_sprintf(buf,
51  	     "dvdisaster dead sector marker\n"
52 	     "This sector could not be read from the image.\n"
53 	     "Its contents have been substituted by the dvdisaster read routine.\n");
54 
55    end_marker = "dvdisaster dead sector end marker\n";
56    end_length = strlen(end_marker);
57    memcpy(buf+2046-end_length, end_marker, end_length);
58 
59    /* May we use the new marker features? */
60 
61    if(!Closure->dsmVersion)
62       return;
63 
64    /* Yes, add the missing sector attributes */
65 
66    g_sprintf(buf+0x100,"Dead sector marker version");
67    g_sprintf(buf+0x120,"%s",DSM_VERSION);
68    g_sprintf(buf+0x140,"Dead sector number");
69    g_sprintf(buf+0x160,"%lld", (long long)sector);
70    g_sprintf(buf+0x180,"Medium fingerprint");
71    if(fingerprint) memcpy(buf+0x1a0, fingerprint, 16);
72    else            memcpy(buf+0x1b0, "none", 4);
73    g_sprintf(buf+0x1c0,"Medium fingerprint sector");
74    g_sprintf(buf+0x1e0,"%lld", (long long)fingerprint_sector);
75    g_sprintf(buf+0x200,"Volume label (if any)");
76    g_sprintf(buf+0x220,"%s", volume_label ? volume_label : "none");
77 
78    if(simulation_hint)
79    {  g_sprintf(buf+0x240,"Simulation hint");
80       g_sprintf(buf+0x260,"%s", simulation_hint);
81    }
82 }
83 
84 void CreateDebuggingSector(unsigned char *out, guint64 sector,
85 			   unsigned char *fingerprint, guint64 fingerprint_sector,
86 			   char *volume_label, char *simulation_hint)
87 {  write_missing_sector(out, sector, fingerprint, fingerprint_sector, volume_label, simulation_hint);
88 }
89 
90 void CreateMissingSector(unsigned char *out, guint64 sector,
91 			 unsigned char *fingerprint, guint64 fingerprint_sector,
92 			 char *volume_label)
93 {  write_missing_sector(out, sector, fingerprint, fingerprint_sector, volume_label, NULL);
94 }
95 
96 /***
97  *** Create an unique padding sector
98  ***/
99 
100 void CreatePaddingSector(unsigned char *out, guint64 sector,
101 			 unsigned char *fingerprint, guint64 fingerprint_sector)
102 {  char *buf = (char*)out;
103    char *end_marker;
104    int end_length;
105 
106    memset(buf, 0, 2048);
107 
108    g_sprintf(buf,
109  	     "dvdisaster padding sector       "
110 	     "This is a padding sector needed for augmenting the image "
111 	     "with error correction data.");
112 
113    end_marker = "dvdisaster padding sector end marker";
114    end_length = strlen(end_marker);
115    memcpy(buf+2047-end_length, end_marker, end_length);
116 
117    g_sprintf(buf+0x100,"Padding sector marker version");
118    g_sprintf(buf+0x120,"%s",DSM_VERSION);
119    g_sprintf(buf+0x140,"Padding sector number");
120    g_sprintf(buf+0x160,"%lld", (long long)sector);
121    g_sprintf(buf+0x180,"Medium fingerprint");
122    if(fingerprint) memcpy(buf+0x1a0, fingerprint, 16);
123    else            memcpy(buf+0x1b0, "none", 4);
124    g_sprintf(buf+0x1c0,"Medium fingerprint sector");
125    g_sprintf(buf+0x1e0,"%lld", (long long)fingerprint_sector);
126 }
127 
128 /***
129  *** Helper functions
130  ***/
131 
132 static int get_recorded_number(unsigned char *buf, guint64 *number)
133 {
134    if(!strcmp((char*)buf+0x140, "Dead sector number"))
135    {  *number = strtoll((char*)buf+0x160, NULL, 10);
136       return TRUE;
137    }
138 
139    *number = 0;
140    return FALSE;
141 }
142 
143 static char *get_volume_label(unsigned char *buf)
144 {
145    if(!strcmp((char*)buf+0x200, "Volume label (if any)"))
146    {  if(!strcmp((char*)buf+0x220, "none"))
147 	   return NULL;
148       else return g_strdup((char*)buf+0x220);
149    }
150 
151    return NULL;
152 }
153 
154 /*
155  * Used for simulating specific errors
156  */
157 
158 char *GetSimulationHint(unsigned char *buf)
159 {
160    if(!strcmp((char*)buf+0x240, "Simulation hint"))
161       return g_strdup((char*)buf+0x260);
162 
163    return NULL;
164 }
165 
166 /***
167  *** Check whether this is a missing sector
168  ***/
169 
170 int CheckForMissingSector(unsigned char *buf, guint64 sector,
171 			  unsigned char *fingerprint, guint64 fingerprint_sector)
172 {  static char pattern[2048];
173    static char last_pattern = 0;
174    guint64 recorded_number;
175    char *sim_hint;
176 
177    /* Bytefill used as missing sector marker? */
178 
179    if(Closure->fillUnreadable >= 0)
180    {  if(Closure->fillUnreadable != last_pattern)  /* cache the pattern */
181 	 memset(pattern, Closure->fillUnreadable, 2048);
182 
183       if(memcmp(buf, pattern, 2048))
184 	   return SECTOR_PRESENT;
185       else return SECTOR_MISSING;
186    }
187 
188    /* See if it is our dead sector marker */
189 
190    if(strncmp((char*)buf,
191 	     "dvdisaster dead sector marker\n"
192 	     "This sector could not be read from the image.\n"
193 	     "Its contents have been substituted by the dvdisaster read routine.\n",
194 	     143)
195       || strncmp((char*)buf+2046-34, "dvdisaster dead sector end marker\n", 34))
196 
197       return SECTOR_PRESENT;
198 
199    /* New style missing sector marker? */
200 
201    if(strcmp((char*)buf+0x100,"Dead sector marker version"))
202       return SECTOR_MISSING;
203 
204    /*** Evaluate new style sector marker */
205 
206    /* Look for hints on simulated images */
207 
208    sim_hint = GetSimulationHint(buf);
209    if(sim_hint)
210    {  g_free(sim_hint);
211       return SECTOR_WITH_SIMULATION_HINT;
212    }
213 
214    /* Verify sector number */
215 
216    if(get_recorded_number(buf, &recorded_number))
217       if(recorded_number != sector)
218 	 return SECTOR_MISSING_DISPLACED;
219 
220    /* Verify medium fingerprint. If the dead sector was fingerprinted with
221       a different sector, ignore the test. Retrieving the right fingerprint
222       sector is too expensive. */
223 
224    if(fingerprint
225       && !strcmp((char*)buf+0x1c0, "Medium fingerprint sector")
226       &&  memcmp((char*)buf+0x1b0, "none", 4))
227    {  gint64 fps_recorded = strtoll((char*)buf+0x1e0, NULL, 10);
228 
229       if(fps_recorded == fingerprint_sector)
230       {  if(!strcmp((char*)buf+0x180, "Medium fingerprint"))
231 	    if(memcmp((char*)buf+0x1a0, (char*)fingerprint, 16))
232 	       return SECTOR_MISSING_WRONG_FP;
233       }
234    }
235 
236    return SECTOR_MISSING;
237 }
238 
239 int CheckForMissingSectors(unsigned char *buf, guint64 sector,
240 			   unsigned char *fingerprint, guint64 fingerprint_sector,
241 			   int n_sectors, guint64 *first_defect)
242 {  int i,result;
243 
244    for(i=0; i<n_sectors; i++)
245    {  result = CheckForMissingSector(buf, sector, fingerprint, fingerprint_sector);
246 
247       if(result != SECTOR_PRESENT)
248       {  *first_defect = sector;
249 	 return result;
250       }
251 
252       buf += 2048;
253       sector++;
254    }
255 
256    return SECTOR_PRESENT;
257 }
258 
259 /***
260  *** Dialogue for indicating problem with the missing sector
261  ***/
262 
263 static void insert_buttons(GtkDialog *dialog)
264 {
265    gtk_dialog_add_buttons(dialog,
266 			  _utf("Stop reporting these errors"), 1,
267 			  _utf("Continue reporting"), 0, NULL);
268 }
269 
270 void ExplainMissingSector(unsigned char *buf, guint64 sector, int error, int source_type, int *number)
271 {  int answer;
272    guint64 recorded_number;
273    char *vol_label, *label_msg;
274 
275    if(Closure->noMissingWarnings)
276       return;
277 
278    /* Missing sectors should be reported in the following cases:
279       - In an image, normal missing sectors are to be expected.
280         Only displayced sectors and sectors with wrong fingerprint should be reported.
281       - In a medium, all kinds of missing sectors constitute a problem and must be reported.
282       - Within an ecc file, no missing sectors should appear although  these are at least
283         harmless for RS03-type ecc files. Report them all.
284    */
285 
286    if(source_type == SOURCE_IMAGE && error != SECTOR_MISSING_DISPLACED && error != SECTOR_MISSING_WRONG_FP)
287      return;
288 
289    /* In CLI mode, only report the first unrecoverable sector unless verbose is given. */
290 
291    if(!Closure->guiMode && !Closure->verbose && *number > 0)
292    {  if(*number == 1)
293 	 PrintLog(_("* ... more unrecoverable sectors found ...\n"
294 		    "* further messages are suppressed unless the -v option is given.\n"));
295       (*number)++;
296       return;
297    }
298    (*number)++;
299 
300    /* Get some meta data from the dsm */
301 
302    get_recorded_number(buf, &recorded_number);
303 
304    vol_label = get_volume_label(buf);
305    if(vol_label)
306    {   if(Closure->guiMode)
307 	    label_msg = g_strdup_printf(_("\n\nThe label of the original (defective) medium was:\n%s\n\n"), vol_label);
308        else label_msg = g_strdup_printf(_("\n* \n* The label of the original (defective) medium was:\n* \n*  %s\n* "), vol_label);
309        g_free(vol_label);
310    }
311    else label_msg = g_strdup("\n");
312 
313    /* Error was found in an image */
314 
315    if(source_type == SOURCE_IMAGE)
316    {  switch(error)
317       {  case SECTOR_MISSING_DISPLACED:
318 	 {  char *msg = _("Unrecoverable sector found!\n\n"
319 			  "Sector %lld is marked unreadable and annotated to be\n"
320 			  "in a different location (%lld).\n\n"
321 			  "The image was probably mastered from defective content.\n"
322 			  "For example it might contain one or more files which came\n"
323 			  "from a damaged medium which was NOT fully recovered.\n"
324 			  "This means that some files may have been silently corrupted.%s\n"
325 			  "Since the image was already created defective it can not be\n"
326 			  "repaired by dvdisaster. Also it will not be possible to create\n"
327 			  "error correction data for it. Sorry for the bad news.\n");
328 
329 	    if(!Closure->guiMode)
330 	        PrintLogWithAsterisks(msg,sector, recorded_number, label_msg);
331 	    else
332 	    {  answer = ModalDialog(GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE, insert_buttons, msg,
333 				    sector, recorded_number, label_msg);
334 
335 	       if(answer) Closure->noMissingWarnings = TRUE;
336 	    }
337 	 }
338 	 break;
339 
340 	 case SECTOR_MISSING_WRONG_FP:
341 	 {  char *msg = _("Unrecoverable sector found!\n\n"
342 			  "Sector %lld is marked unreadable and seems to come\n"
343 			  "from a different medium.\n\n"
344 			  "The image was probably mastered from defective content.\n"
345 			  "For example it might contain one or more files which came\n"
346 			  "from a damaged medium which was NOT fully recovered.\n"
347 			  "This means that some files may have been silently corrupted.%s\n"
348 			  "Since the image was already created defective it can not be\n"
349 			  "repaired by dvdisaster. Also it will not be possible to create\n"
350 			  "error correction data for it. Sorry for the bad news.\n");
351 
352 	    if(!Closure->guiMode)
353 	         PrintLogWithAsterisks(msg,sector, label_msg);
354 	    else
355 	    {  answer = ModalDialog(GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE, insert_buttons, msg,
356 				    sector, label_msg);
357 	       if(answer) Closure->noMissingWarnings = TRUE;
358 	    }
359 	 }
360 	 break;
361       }
362    }
363 
364    /* Error was found while reading a medium */
365 
366    if(source_type == SOURCE_MEDIUM)
367    {  char *msg = _("Unrecoverable sector found!\n\n"
368 		    "Sector %lld is marked unreadable on the medium.\n\n"
369 		    "The medium was probably mastered from defective content.\n"
370 		    "For example it might contain one or more files which came\n"
371 		    "from a damaged medium which was NOT fully recovered.\n"
372 		    "This means that some files may have been silently corrupted.\n"
373 		    "Since the medium was already created defective it can not be\n"
374 		    "repaired by dvdisaster. Also it will not be possible to create\n"
375 		    "error correction data for it. Sorry for the bad news.\n");
376 
377      if(!Closure->guiMode)
378           PrintLogWithAsterisks(msg, sector);
379      else
380      {  answer = ModalDialog(GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE, insert_buttons, msg,
381 			     sector);
382 
383         if(answer) Closure->noMissingWarnings = TRUE;
384      }
385    }
386 
387    /* Error was found while reading an ecc file */
388 
389    if(source_type == SOURCE_ECCFILE)
390    {  char *msg = _("Unrecoverable sector found!\n\n"
391 		    "Sector %lld is marked unreadable in the ecc file.\n\n"
392 		    "The ecc file was probably taken from a medium which\n"
393 		    "was NOT fully recovered. That means that some sectors\n"
394 		    "in the ecc file are missing and its error correction\n"
395 		    "capacity will be reduced.\n");
396 
397      if(!Closure->guiMode)
398           PrintLogWithAsterisks(msg, sector);
399      else
400      {  answer = ModalDialog(GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE, insert_buttons, msg,
401 			     sector);
402 
403         if(answer) Closure->noMissingWarnings = TRUE;
404      }
405    }
406 
407    g_free(label_msg);
408 }
409