ds-marker.c 12.9 KB
Newer Older
1
/*  dvdisaster: Additional error correction for optical media.
2
 *  Copyright (C) 2004-2015 Carsten Gnoerlich.
3
 *
4 5 6 7 8 9
 *  Email: carsten@dvdisaster.org  -or-  cgnoerlich@fsfe.org
 *  Project homepage: http://www.dvdisaster.org
 *
 *  This file is part of dvdisaster.
 *
 *  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 13
 *  (at your option) any later version.
 *
14
 *  dvdisaster is distributed in the hope that it will be useful,
15 16 17 18 19
 *  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
20
 *  along with dvdisaster. If not, see <http://www.gnu.org/licenses/>.
21 22 23 24 25
 */

#include "dvdisaster.h"

#define DSM_VERSION "1.00"
26
#define PSM_VERSION "1.00"
27 28 29 30 31

/***
 *** Create an unique marker for missing sectors
 ***/

32 33 34
static void write_missing_sector(unsigned char *out, guint64 sector, 
				 unsigned char *fingerprint, guint64 fingerprint_sector,
				 char *volume_label, char *simulation_hint)
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
{  char *buf = (char*)out;
   char *end_marker;
   int end_length; 

   /* Bytefill requested? */

   if(Closure->fillUnreadable >= 0)
   {  memset(out, Closure->fillUnreadable, 2048);
      return;
   }

   /* historic words ... ;-) */

   memset(buf, 0, 2048);

   g_sprintf(buf,
 	     "dvdisaster dead sector marker\n"
	     "This sector could not be read from the image.\n"
	     "Its contents have been substituted by the dvdisaster read routine.\n");

   end_marker = "dvdisaster dead sector end marker\n";
   end_length = strlen(end_marker);
   memcpy(buf+2046-end_length, end_marker, end_length); 

   /* May we use the new marker features? */

   if(!Closure->dsmVersion)
      return;

64
   /* Yes, add the missing sector attributes */
65 66 67 68 69 70 71 72 73 74 75 76

   g_sprintf(buf+0x100,"Dead sector marker version");
   g_sprintf(buf+0x120,"%s",DSM_VERSION);
   g_sprintf(buf+0x140,"Dead sector number");
   g_sprintf(buf+0x160,"%lld", (long long)sector);
   g_sprintf(buf+0x180,"Medium fingerprint");
   if(fingerprint) memcpy(buf+0x1a0, fingerprint, 16);
   else            memcpy(buf+0x1b0, "none", 4);
   g_sprintf(buf+0x1c0,"Medium fingerprint sector");
   g_sprintf(buf+0x1e0,"%lld", (long long)fingerprint_sector);
   g_sprintf(buf+0x200,"Volume label (if any)");
   g_sprintf(buf+0x220,"%s", volume_label ? volume_label : "none");
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93

   if(simulation_hint)
   {  g_sprintf(buf+0x240,"Simulation hint");
      g_sprintf(buf+0x260,"%s", simulation_hint);
   }
}

void CreateDebuggingSector(unsigned char *out, guint64 sector, 
			   unsigned char *fingerprint, guint64 fingerprint_sector,
			   char *volume_label, char *simulation_hint)
{  write_missing_sector(out, sector, fingerprint, fingerprint_sector, volume_label, simulation_hint);
}

void CreateMissingSector(unsigned char *out, guint64 sector, 
			 unsigned char *fingerprint, guint64 fingerprint_sector,
			 char *volume_label)
{  write_missing_sector(out, sector, fingerprint, fingerprint_sector, volume_label, NULL);
94 95 96
}

/***
97
 *** Create an unique padding sector
98 99
 ***/

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
void CreatePaddingSector(unsigned char *out, guint64 sector, 
			 unsigned char *fingerprint, guint64 fingerprint_sector)
{  char *buf = (char*)out;
   char *end_marker;
   int end_length; 

   memset(buf, 0, 2048);

   g_sprintf(buf,
 	     "dvdisaster padding sector       "
	     "This is a padding sector needed for augmenting the image "
	     "with error correction data.");

   end_marker = "dvdisaster padding sector end marker";
   end_length = strlen(end_marker);
   memcpy(buf+2047-end_length, end_marker, end_length); 

   g_sprintf(buf+0x100,"Padding sector marker version");
   g_sprintf(buf+0x120,"%s",DSM_VERSION);
   g_sprintf(buf+0x140,"Padding sector number");
   g_sprintf(buf+0x160,"%lld", (long long)sector);
   g_sprintf(buf+0x180,"Medium fingerprint");
   if(fingerprint) memcpy(buf+0x1a0, fingerprint, 16);
   else            memcpy(buf+0x1b0, "none", 4);
   g_sprintf(buf+0x1c0,"Medium fingerprint sector");
   g_sprintf(buf+0x1e0,"%lld", (long long)fingerprint_sector);
}

/***
 *** Helper functions
 ***/

static int get_recorded_number(unsigned char *buf, guint64 *number)
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
{
   if(!strcmp((char*)buf+0x140, "Dead sector number"))
   {  *number = strtoll((char*)buf+0x160, NULL, 10);
      return TRUE;
   }

   *number = 0;
   return FALSE;   
}

static char *get_volume_label(unsigned char *buf)
{
   if(!strcmp((char*)buf+0x200, "Volume label (if any)"))
   {  if(!strcmp((char*)buf+0x220, "none"))
	   return NULL;
      else return g_strdup((char*)buf+0x220);
   }

   return NULL;
}

154 155 156 157 158 159 160 161 162 163 164 165
/*
 * Used for simulating specific errors
 */

char *GetSimulationHint(unsigned char *buf)
{
   if(!strcmp((char*)buf+0x240, "Simulation hint"))
      return g_strdup((char*)buf+0x260);

   return NULL;
}

166 167 168 169
/***
 *** Check whether this is a missing sector
 ***/

170 171
int CheckForMissingSector(unsigned char *buf, guint64 sector, 
			  unsigned char *fingerprint, guint64 fingerprint_sector)
172 173
{  static char pattern[2048];
   static char last_pattern = 0;
174 175
   guint64 recorded_number;
   char *sim_hint;
176
   
177 178
   /* Bytefill used as missing sector marker? */

179 180 181 182 183 184 185 186 187 188 189
   if(Closure->fillUnreadable >= 0)
   {  if(Closure->fillUnreadable != last_pattern)  /* cache the pattern */
	 memset(pattern, Closure->fillUnreadable, 2048);

      if(memcmp(buf, pattern, 2048)) 
	   return SECTOR_PRESENT;
      else return SECTOR_MISSING;
   }

   /* See if it is our dead sector marker */

190
   if(strncmp((char*)buf, 
191 192
	     "dvdisaster dead sector marker\n"
	     "This sector could not be read from the image.\n"
193 194 195 196
	     "Its contents have been substituted by the dvdisaster read routine.\n",
	     143)
      || strncmp((char*)buf+2046-34, "dvdisaster dead sector end marker\n", 34))

197 198 199 200 201 202 203 204 205
      return SECTOR_PRESENT;

   /* New style missing sector marker? */

   if(strcmp((char*)buf+0x100,"Dead sector marker version"))
      return SECTOR_MISSING;

   /*** Evaluate new style sector marker */

206 207 208 209 210 211 212 213
   /* Look for hints on simulated images */

   sim_hint = GetSimulationHint(buf);
   if(sim_hint)
   {  g_free(sim_hint);
      return SECTOR_WITH_SIMULATION_HINT;
   }
   
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
   /* Verify sector number */

   if(get_recorded_number(buf, &recorded_number))
      if(recorded_number != sector)
	 return SECTOR_MISSING_DISPLACED;

   /* Verify medium fingerprint. If the dead sector was fingerprinted with 
      a different sector, ignore the test. Retrieving the right fingerprint
      sector is too expensive. */

   if(fingerprint 
      && !strcmp((char*)buf+0x1c0, "Medium fingerprint sector")
      &&  memcmp((char*)buf+0x1b0, "none", 4))
   {  gint64 fps_recorded = strtoll((char*)buf+0x1e0, NULL, 10);

      if(fps_recorded == fingerprint_sector)
      {  if(!strcmp((char*)buf+0x180, "Medium fingerprint"))
	    if(memcmp((char*)buf+0x1a0, (char*)fingerprint, 16))
	       return SECTOR_MISSING_WRONG_FP;
      }
   }

   return SECTOR_MISSING;
}

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
int CheckForMissingSectors(unsigned char *buf, guint64 sector, 
			   unsigned char *fingerprint, guint64 fingerprint_sector,
			   int n_sectors, guint64 *first_defect)
{  int i,result;

   for(i=0; i<n_sectors; i++)
   {  result = CheckForMissingSector(buf, sector, fingerprint, fingerprint_sector);

      if(result != SECTOR_PRESENT)
      {  *first_defect = sector;
	 return result;
      }

      buf += 2048;
      sector++;
   }

   return SECTOR_PRESENT;
}

259 260 261 262 263 264 265 266 267 268 269
/***
 *** Dialogue for indicating problem with the missing sector
 ***/

static void insert_buttons(GtkDialog *dialog)
{  
   gtk_dialog_add_buttons(dialog, 
			  _utf("Stop reporting these errors"), 1,
			  _utf("Continue reporting"), 0, NULL);
}

270
void ExplainMissingSector(unsigned char *buf, guint64 sector, int error, int source_type, int *number)
271
{  int answer;
272
   guint64 recorded_number;
273 274 275 276 277
   char *vol_label, *label_msg;

   if(Closure->noMissingWarnings)
      return;

278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
   /* Missing sectors should be reported in the following cases:
      - In an image, normal missing sectors are to be expected.
        Only displayced sectors and sectors with wrong fingerprint should be reported.
      - In a medium, all kinds of missing sectors constitute a problem and must be reported.
      - Within an ecc file, no missing sectors should appear although  these are at least
        harmless for RS03-type ecc files. Report them all.
   */
   
   if(source_type == SOURCE_IMAGE && error != SECTOR_MISSING_DISPLACED && error != SECTOR_MISSING_WRONG_FP)
     return;
   
   /* In CLI mode, only report the first unrecoverable sector unless verbose is given. */

   if(!Closure->guiMode && !Closure->verbose && *number > 0)
   {  if(*number == 1)
	 PrintLog(_("* ... more unrecoverable sectors found ...\n"
		    "* further messages are suppressed unless the -v option is given.\n"));
      (*number)++;
296 297
      return;
   }
298 299 300 301
   (*number)++;
   
   /* Get some meta data from the dsm */
   
302 303 304 305
   get_recorded_number(buf, &recorded_number);

   vol_label = get_volume_label(buf);
   if(vol_label)
306 307 308 309
   {   if(Closure->guiMode)
	    label_msg = g_strdup_printf(_("\n\nThe label of the original (defective) medium was:\n%s\n\n"), vol_label);
       else label_msg = g_strdup_printf(_("\n* \n* The label of the original (defective) medium was:\n* \n*  %s\n* "), vol_label);
       g_free(vol_label);
310
   }
311
   else label_msg = g_strdup("\n");
312 313 314

   /* Error was found in an image */

315
   if(source_type == SOURCE_IMAGE)
316 317
   {  switch(error)
      {  case SECTOR_MISSING_DISPLACED:
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
	 {  char *msg = _("Unrecoverable sector found!\n\n"
			  "Sector %lld is marked unreadable and annotated to be\n"
			  "in a different location (%lld).\n\n"
			  "The image was probably mastered from defective content.\n"
			  "For example it might contain one or more files which came\n"
			  "from a damaged medium which was NOT fully recovered.\n" 
			  "This means that some files may have been silently corrupted.%s\n"
			  "Since the image was already created defective it can not be\n"
			  "repaired by dvdisaster. Also it will not be possible to create\n"
			  "error correction data for it. Sorry for the bad news.\n");

	    if(!Closure->guiMode)
	        PrintLogWithAsterisks(msg,sector, recorded_number, label_msg);
	    else
	    {  answer = ModalDialog(GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE, insert_buttons, msg,
				    sector, recorded_number, label_msg);
334
   
335 336 337 338
	       if(answer) Closure->noMissingWarnings = TRUE;
	    }
	 }
	 break;
339 340

	 case SECTOR_MISSING_WRONG_FP:
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
	 {  char *msg = _("Unrecoverable sector found!\n\n"
			  "Sector %lld is marked unreadable and seems to come\n"
			  "from a different medium.\n\n"
			  "The image was probably mastered from defective content.\n"
			  "For example it might contain one or more files which came\n"
			  "from a damaged medium which was NOT fully recovered.\n" 
			  "This means that some files may have been silently corrupted.%s\n"
			  "Since the image was already created defective it can not be\n"
			  "repaired by dvdisaster. Also it will not be possible to create\n"
			  "error correction data for it. Sorry for the bad news.\n");

	    if(!Closure->guiMode)
	         PrintLogWithAsterisks(msg,sector, label_msg);
	    else
	    {  answer = ModalDialog(GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE, insert_buttons, msg,
				    sector, label_msg);
	       if(answer) Closure->noMissingWarnings = TRUE;
	    }
	 }
	 break;
361 362 363 364 365
      }
   }

   /* Error was found while reading a medium */

366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
   if(source_type == SOURCE_MEDIUM)
   {  char *msg = _("Unrecoverable sector found!\n\n"
		    "Sector %lld is marked unreadable on the medium.\n\n"
		    "The medium was probably mastered from defective content.\n"
		    "For example it might contain one or more files which came\n"
		    "from a damaged medium which was NOT fully recovered.\n" 
		    "This means that some files may have been silently corrupted.\n"
		    "Since the medium was already created defective it can not be\n"
		    "repaired by dvdisaster. Also it will not be possible to create\n"
		    "error correction data for it. Sorry for the bad news.\n");
			   
     if(!Closure->guiMode)
          PrintLogWithAsterisks(msg, sector);
     else
     {  answer = ModalDialog(GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE, insert_buttons, msg,
			     sector);
      
        if(answer) Closure->noMissingWarnings = TRUE;
     }
   }

   /* Error was found while reading an ecc file */

   if(source_type == SOURCE_ECCFILE)
   {  char *msg = _("Unrecoverable sector found!\n\n"
		    "Sector %lld is marked unreadable in the ecc file.\n\n"
		    "The ecc file was probably taken from a medium which\n"
		    "was NOT fully recovered. That means that some sectors\n"
		    "in the ecc file are missing and its error correction\n"
		    "capacity will be reduced.\n");
			   
     if(!Closure->guiMode)
          PrintLogWithAsterisks(msg, sector);
     else
     {  answer = ModalDialog(GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE, insert_buttons, msg,
			     sector);
402
      
403 404
        if(answer) Closure->noMissingWarnings = TRUE;
     }
405 406 407 408
   }

   g_free(label_msg);
}