Files
dvdisaster/rs01-common.c
Stéphane Lesimple 99b27b982a feat: CLI-only version (without GTK)
Modify the build system and the source
files to support building a CLI-only
version with only glib2 as a dependency.
Use CLI_ONLY=1 ./configure, then make clean all.
2020-08-19 21:21:11 +02:00

422 lines
12 KiB
C

/* dvdisaster: Additional error correction for optical media.
* Copyright (C) 2004-2017 Carsten Gnoerlich.
*
* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* dvdisaster is distributed in the hope that it will be useful,
* 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
* along with dvdisaster. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dvdisaster.h"
#include "rs01-includes.h"
/***
*** Recognize a RS01 error correction file
***/
int RS01Recognize(LargeFile *ecc_file, EccHeader **eh)
{ int n;
*eh = g_malloc(sizeof(EccHeader));
LargeSeek(ecc_file, 0);
n = LargeRead(ecc_file, *eh, sizeof(EccHeader));
if(n != sizeof(EccHeader))
{ g_free(*eh);
return ECCFILE_INVALID;
}
if(strncmp((char*)(*eh)->cookie, "*dvdisaster*", 12))
{ g_free(*eh);
return ECCFILE_DEFECTIVE_HEADER;
}
if(!strncmp((char*)(*eh)->method, "RS01", 4))
{
#ifdef HAVE_BIG_ENDIAN
SwapEccHeaderBytes(*eh);
#endif
return ECCFILE_PRESENT;
}
g_free(*eh);
return ECCFILE_WRONG_CODEC;
}
/***
*** Read and buffer CRC information from RS01 file
***/
CrcBuf *RS01GetCrcBuf(Image *image)
{ LargeFile *file = image->eccFile;
CrcBuf *cb;
guint32 *buf;
guint64 image_sectors_from_ecc, image_sectors_with_crc;
guint64 crc_sectors,crc_remainder;
guint64 i,j,sec_idx;
image_sectors_from_ecc = uchar_to_gint64(image->eccFileHeader->sectors);
cb = CreateCrcBuf(image);
buf = cb->crcbuf;
/* Seek to beginning of CRC sums */
if(!LargeSeek(file, (gint64)sizeof(EccHeader)))
Stop(_("Failed skipping the ecc header: %s"),strerror(errno));
/* Read crc sums. A sector of 2048 bytes contains 512 CRC sums. */
if(cb->allSectors < image_sectors_from_ecc)
image_sectors_with_crc = cb->allSectors; // image is truncated
else image_sectors_with_crc = image_sectors_from_ecc; // get all CRC sectors from ECC
crc_sectors = image_sectors_with_crc / 512;
sec_idx = 0;
for(i=0; i<crc_sectors; i++)
{ if(LargeRead(file, buf, 2048) != 2048)
Stop(_("Error reading CRC information: %s"),strerror(errno));
buf += 512;
for(j=0; j<512; j++, sec_idx++)
SetBit(cb->valid, sec_idx);
}
crc_remainder = sizeof(guint32)*(image_sectors_with_crc % 512);
if(crc_remainder)
{ if(LargeRead(file, buf, crc_remainder) != crc_remainder)
Stop(_("Error reading CRC information: %s"),strerror(errno));
for( ; sec_idx<image_sectors_with_crc; sec_idx++)
SetBit(cb->valid, sec_idx);
}
/* Copy the md5sum */
memcpy(cb->dataMD5sum, image->eccFileHeader->mediumSum, 16);
memcpy(cb->imageMD5sum, image->eccFileHeader->mediumSum, 16);
cb->md5State = MD5_COMPLETE;
return cb;
}
/***
*** Internal checksum handling.
***
* Not overly complicated as we just have a global md5sum.
*/
void RS01ResetCksums(Image *image)
{ RS01CksumClosure *csc = (RS01CksumClosure*)image->eccFileMethod->ckSumClosure;
MD5Init(&csc->md5ctxt);
}
void RS01UpdateCksums(Image *image, gint64 sector, unsigned char *buf)
{ RS01CksumClosure *csc = (RS01CksumClosure*)image->eccFileMethod->ckSumClosure;
MD5Update(&csc->md5ctxt, buf, 2048);
}
int RS01FinalizeCksums(Image *image)
{ Method *self = image->eccFileMethod;
RS01CksumClosure *csc = (RS01CksumClosure*)self->ckSumClosure;
guint8 image_fp[16];
int good_fp;
MD5Final(image_fp, &csc->md5ctxt);
good_fp = !(memcmp(image_fp, image->eccFileHeader->mediumSum ,16));
if(good_fp)
return 0;
else return DATA_MD5_BAD;
}
/***
*** Read an image sector from the .iso file.
***
* Two special cases here:
* - Missing sectors (beyond the range recorded in eh->sectors) will be padded with zeros,
* since we need a multiple of ndata sectors for the parity generation.
* - Missing sectors beyond the range recorded in ii->sectors, but before the real end
* as defined above are treated as "dead sectors".
*/
void RS01ReadSector(Image *image, unsigned char *buf, gint64 s)
{ gint64 eh_sectors = uchar_to_gint64(image->eccFileHeader->sectors);
if(s >= image->sectorSize && s < eh_sectors)
{
CreateMissingSector(buf, s, NULL, 0, NULL); /* truncated image */
}
else if(s >= eh_sectors)
{
memset(buf, 0, 2048); /* zero padding for reads past the image */
}
else /* else normal read within the image */
{ int n,expected;
if(!LargeSeek(image->file, (gint64)(2048*s)))
Stop(_("Failed seeking to sector %lld in image: %s"),
s, strerror(errno));
/* Prepare for short reads at the last image sector.
Doesn't happen for CD and DVD media, but perhaps for future media? */
if(s < image->sectorSize-1) expected = 2048;
else
{ memset(buf, 0, 2048);
expected = image->inLast;
}
/* Finally, read the sector */
n = LargeRead(image->file, buf, expected);
if(n != expected)
Stop(_("Failed reading sector %lld in image: %s"),s,strerror(errno));
}
}
/*
* Scan the image for missing blocks.
* If the ecc file is present, also compare the CRC sums.
* If CREATE_CRC is requested, calculate the CRC sums.
*
* Actually this should be usable for all RS01 type ecc files.
* But unless we have more than one codec, we'll label it as
* as RS01 specific method.
*/
#define CRCBUFSIZE (1024*256)
void RS01ScanImage(Method *method, Image* image, struct MD5Context *ecc_ctxt, int mode)
{
#ifndef CLI
RS01Widgets *wl = NULL;
#endif
unsigned char buf[2048];
guint32 *crcbuf = NULL;
int unrecoverable_sectors = 0;
int crcidx = 0;
struct MD5Context image_md5;
gint64 s, first_missing, last_missing;
#ifndef CLI
gint64 prev_missing = 0;
gint64 prev_crc_errors = 0;
#endif
int last_percent,current_missing;
char *msg;
/* Extract widget list from method */
#ifndef CLI
if(method->widgetList)
wl = (RS01Widgets*)method->widgetList;
#endif
/* Position behind the ecc file header,
initialize CRC buffer pointers */
if(image->eccFile)
{ if(!LargeSeek(image->eccFile, (gint64)sizeof(EccHeader)))
Stop(_("Failed skipping the ecc header: %s"),strerror(errno));
crcbuf = g_malloc(sizeof(guint32) * CRCBUFSIZE);
crcidx = (mode & CREATE_CRC) ? 0 : CRCBUFSIZE;
if(mode & CREATE_CRC)
MD5Init(ecc_ctxt); /* md5sum of CRC portion of ecc file */
}
/* Prepare for scanning the image and calculating its md5sum */
MD5Init(&image_md5); /* md5sum of image file itself */
LargeSeek(image->file, 0); /* rewind image file */
if(mode & PRINT_MODE)
msg = _("- testing sectors : %3d%%");
else msg = _("Scanning image sectors: %3d%%");
last_percent = 0;
image->sectorsMissing = 0;
first_missing = last_missing = -1;
/* Go through all sectors and look for the "dead sector marker" */
for(s=0; s<image->sectorSize; s++)
{ int n,percent,err;
/* Check for user interruption */
#ifndef CLI
if(Closure->stopActions)
{ image->sectorsMissing += image->sectorSize - s;
if(crcbuf) g_free(crcbuf);
return;
}
#endif
/* Read the next sector */
n = LargeRead(image->file, buf, 2048);
if(n != 2048)
{ if(s != image->sectorSize - 1 || n != image->inLast)
{ if(crcbuf) g_free(crcbuf);
Stop(_("premature end in image (only %d bytes): %s\n"),n,strerror(errno));
}
else /* Zero unused sectors for CRC generation */
memset(buf+image->inLast, 0, 2048-image->inLast);
}
/* Look for the dead sector marker */
err = CheckForMissingSector(buf, s, image->fpState == 2 ? image->imageFP : NULL,
FINGERPRINT_SECTOR);
if(err != SECTOR_PRESENT)
{ current_missing = TRUE;
ExplainMissingSector(buf, s, err, SOURCE_IMAGE, &unrecoverable_sectors);
}
else current_missing = FALSE;
if(current_missing)
{ if(first_missing < 0) first_missing = s;
last_missing = s;
image->sectorsMissing++;
}
/* Report dead sectors. Combine subsequent missing sectors into one report. */
if(mode & PRINT_MODE)
if(!current_missing || s==image->sectorSize-1)
{ if(first_missing>=0)
{ if(first_missing == last_missing)
PrintCLI(_("* missing sector : %lld\n"), first_missing);
else PrintCLI(_("* missing sectors : %lld - %lld\n"), first_missing, last_missing);
first_missing = -1;
}
}
if(image->eccFile) /* Do something with the CRC portion of the .ecc file */
{
/* If creation of the CRC32 is requested, do that. */
if(mode & CREATE_CRC)
{ crcbuf[crcidx++] = Crc32(buf, 2048);
if(crcidx >= CRCBUFSIZE) /* write out CRC buffer contents */
{ size_t size = CRCBUFSIZE*sizeof(guint32);
MD5Update(ecc_ctxt, (unsigned char*)crcbuf, size);
if(LargeWrite(image->eccFile, crcbuf, size) != size)
{ if(crcbuf) g_free(crcbuf);
Stop(_("Error writing CRC information: %s"),strerror(errno));
}
crcidx = 0;
}
}
/* else do the CRC32 check. Missing sectors are skipped in the CRC report. */
else if(s < image->expectedSectors)
{ guint32 crc = Crc32(buf, 2048);
/* If the CRC buf is exhausted, refill. */
if(crcidx >= CRCBUFSIZE)
{ size_t remain = image->sectorSize-s;
size_t size;
if(remain < CRCBUFSIZE)
size = remain*sizeof(guint32);
else size = CRCBUFSIZE*sizeof(guint32);
if(LargeRead(image->eccFile, crcbuf, size) != size)
{ if(crcbuf) g_free(crcbuf);
Stop(_("Error reading CRC information: %s"),strerror(errno));
}
crcidx = 0;
}
if(crc != crcbuf[crcidx++] && !current_missing)
{ PrintCLI(_("* CRC error, sector: %lld\n"), s);
image->crcErrors++;
}
}
}
MD5Update(&image_md5, buf, n); /* update image md5sum */
#ifndef CLI
if(Closure->guiMode && mode & PRINT_MODE)
percent = (VERIFY_IMAGE_SEGMENTS*(s+1))/image->sectorSize;
else
#endif
percent = (100*(s+1))/image->sectorSize;
if(last_percent != percent)
{ PrintProgress(msg,percent);
#ifndef CLI
if(Closure->guiMode && mode & CREATE_CRC)
SetProgress(wl->encPBar1, percent, 100);
if(Closure->guiMode && mode & PRINT_MODE)
{ RS01AddVerifyValues(method, percent, image->sectorsMissing, image->crcErrors,
image->sectorsMissing - prev_missing,
image->crcErrors - prev_crc_errors);
prev_missing = image->sectorsMissing;
prev_crc_errors = image->crcErrors;
}
#endif
last_percent = percent;
}
}
/*** Flush the rest of the CRC buffer */
if((mode & CREATE_CRC) && crcidx)
{ size_t size = crcidx*sizeof(guint32);
MD5Update(ecc_ctxt, (unsigned char*)crcbuf, size);
if(LargeWrite(image->eccFile, crcbuf, size) != size)
{ if(crcbuf) g_free(crcbuf);
Stop(_("Error writing CRC information: %s"),strerror(errno));
}
}
/*** The image md5sum can only be calculated if all blocks have been successfully read. */
MD5Final(image->mediumSum, &image_md5);
LargeSeek(image->file, 0);
if(crcbuf) g_free(crcbuf);
}
/***
*** Determine expected size of image
***/
guint64 RS01ExpectedImageSize(Image *image)
{ EccHeader *eh = image->eccFileHeader;
if(!eh) return 0;
return uchar_to_gint64(eh->sectors);
}