"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "read-linear.c" between
dvdisaster-0.79.3.tar.gz and dvdisaster-0.79.5.tar.gz

About: dvdisaster provides a margin of safety against data loss on CD, DVD and BD media caused by aging or scratches. Development version.

read-linear.c  (dvdisaster-0.79.3):read-linear.c  (dvdisaster-0.79.5)
/* dvdisaster: Additional error correction for optical media. /* dvdisaster: Additional error correction for optical media.
* Copyright (C) 2004-2010 Carsten Gnoerlich. * Copyright (C) 2004-2015 Carsten Gnoerlich.
* Project home page: http://www.dvdisaster.com
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * dvdisaster is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with dvdisaster. If not, see <http://www.gnu.org/licenses/>.
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
* or direct your browser at http://www.gnu.org.
*/ */
#include "dvdisaster.h" #include "dvdisaster.h"
#include "read-linear.h" #include "read-linear.h"
#include "scsi-layer.h" #include "scsi-layer.h"
#include "udf.h" #include "udf.h"
/* /*
* IO buffer states * IO buffer states
skipping to change at line 63 skipping to change at line 64
/* /*
* Cleanup. * Cleanup.
*/ */
static void cleanup(gpointer data) static void cleanup(gpointer data)
{ read_closure *rc = (read_closure*)data; { read_closure *rc = (read_closure*)data;
int full_read = FALSE; int full_read = FALSE;
int aborted = rc->earlyTermination; int aborted = rc->earlyTermination;
int scan_mode = rc->scanMode; int scan_mode = rc->scanMode;
int i; int i;
int renderers_left = TRUE;
/* Make sure that all spiral/curve render idle functions have finished. /* Make sure that all spiral/curve render idle functions have finished.
They depend on some structures we are going to free now. */ They depend on some structures we are going to free now.
If Closure->stopActions == STOP_SHUTDOWN_ALL, the main thread is
waiting for this thread to terminate and will not invoke any more
idle functions. Executing the while loop would create a deadlock
in that case. */
while(renderers_left && Closure->stopActions != STOP_SHUTDOWN_ALL)
{ g_mutex_lock(rc->rendererMutex);
if(rc->activeRenderers<=0)
renderers_left=FALSE;
g_mutex_unlock(rc->rendererMutex);
while(rc->activeRenderers)
g_usleep(G_USEC_PER_SEC/10); g_usleep(G_USEC_PER_SEC/10);
}
/* In reading passes > 1, Closure->sectorSkip is forced to be one. /* In reading passes > 1, Closure->sectorSkip is forced to be one.
Restore the old value now. */ Restore the old value now. */
Closure->sectorSkip = rc->savedSectorSkip; Closure->sectorSkip = rc->savedSectorSkip;
/* Reset temporary ignoring of fatal errors. /* Reset temporary ignoring of fatal errors.
User has to set this in the preferences to make it permanent. */ User has to set this in the preferences to make it permanent. */
if(Closure->ignoreFatalSense == 2) if(Closure->ignoreFatalSense == 2)
skipping to change at line 97 skipping to change at line 109
/* Make sure worker thread exits gracefully */ /* Make sure worker thread exits gracefully */
if(rc->worker && !rc->workerError) if(rc->worker && !rc->workerError)
{ send_eof(rc); { send_eof(rc);
g_thread_join(rc->worker); g_thread_join(rc->worker);
} }
/* Clean up reader thread */ /* Clean up reader thread */
if(rc->dh) if(rc->image)
full_read = (rc->readOK == rc->dh->sectors && !Closure->crcErrors); full_read = (rc->readOK == rc->image->dh->sectors && !Closure->crcErrors);
Closure->cleanupProc = NULL; UnregisterCleanup();
if(Closure->guiMode) if(Closure->guiMode)
{ if(rc->unreportedError) { if(rc->unreportedError)
SwitchAndSetFootline(Closure->readLinearNotebook, 1, Closure->readLinea rFootline, SwitchAndSetFootline(Closure->readLinearNotebook, 1, Closure->readLinea rFootline,
_("<span %s>Aborted by unrecoverable error.</span> %lld sectors read, %lld sectors unreadable/skipped so far."), _("<span %s>Aborted by unrecoverable error.</span> %lld sectors read, %lld sectors unreadable/skipped so far."),
Closure->redMarkup, rc->readOK, Closure->readErrors ); Closure->redMarkup, rc->readOK, Closure->readErrors );
} }
if(rc->readerImage) if(rc->readerImage)
if(!LargeClose(rc->readerImage)) if(!LargeClose(rc->readerImage))
Stop(_("Error closing image file:\n%s"), strerror(errno)); Stop(_("Error closing image file:\n%s"), strerror(errno));
if(rc->writerImage) if(rc->writerImage)
if(!LargeClose(rc->writerImage)) if(!LargeClose(rc->writerImage))
Stop(_("Error closing image file:\n%s"), strerror(errno)); Stop(_("Error closing image file:\n%s"), strerror(errno));
if(rc->dh) CloseDevice(rc->dh);
if(rc->ei) FreeEccInfo(rc->ei);
if(rc->mutex) g_mutex_free(rc->mutex); if(rc->image) CloseImage(rc->image);
if(rc->canRead) g_cond_free(rc->canRead);
if(rc->canWrite) g_cond_free(rc->canWrite); if(rc->mutex)
{ g_mutex_clear(rc->mutex);
g_free(rc->mutex);
}
if(rc->canRead)
{ g_cond_clear(rc->canRead);
g_free(rc->canRead);
}
if(rc->canWrite)
{ g_cond_clear(rc->canWrite);
g_free(rc->canWrite);
}
if(rc->workerError) g_free(rc->workerError); if(rc->workerError) g_free(rc->workerError);
for(i=0; i<READ_BUFFERS; i++) for(i=0; i<READ_BUFFERS; i++)
if(rc->alignedBuf[i]) if(rc->alignedBuf[i])
FreeAlignedBuffer(rc->alignedBuf[i]); FreeAlignedBuffer(rc->alignedBuf[i]);
if(rc->msg) g_free(rc->msg); if(rc->msg) g_free(rc->msg);
if(rc->speedTimer) g_timer_destroy(rc->speedTimer); if(rc->speedTimer) g_timer_destroy(rc->speedTimer);
if(rc->readTimer) g_timer_destroy(rc->readTimer); if(rc->readTimer) g_timer_destroy(rc->readTimer);
if(rc->readMap) FreeBitmap(rc->readMap); if(rc->readMap) FreeBitmap(rc->readMap);
if(rc->crcBuf) FreeCrcBuf(rc->crcBuf); if(rc->crcBuf) FreeCrcBuf(rc->crcBuf);
if(rc->lay) g_free(rc->lay);
if(rc->fingerprint) g_free(rc->fingerprint);
if(rc->volumeLabel) g_free(rc->volumeLabel); if(rc->volumeLabel) g_free(rc->volumeLabel);
if(rc->rendererMutex)
{ g_mutex_clear(rc->rendererMutex);
g_free(rc->rendererMutex);
}
/* trigger failure if some threads are still accessing this */ /* trigger failure if some threads are still accessing this */
memset(rc, sizeof(read_closure), 0xff); memset(rc, sizeof(read_closure), 0xff);
g_free(rc); g_free(rc);
if(Closure->readAndCreate && Closure->guiMode && !strncmp(Closure->methodName ClearCrcCache(); /* cache handling is currently broken. Discard the cache! */
, "RS01", 4)
&& !scan_mode && !aborted) /* Continue with ecc file creation after read */
{ if(!full_read)
{ ModalDialog(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, NULL, if(Closure->readAndCreate && Closure->guiMode && !scan_mode && !aborted) /*
_("Automatic error correction file creation\n" General prerequisites */
"is only possible after a full reading pass.\n")); { if( !strncmp(Closure->methodName, "RS01", 4) /*
AllowActions(TRUE); codec prerequisites */
|| (!strncmp(Closure->methodName, "RS03", 4) && Closure->eccTarget == EC
C_FILE) )
{ if(!full_read)
{ ModalDialog(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, NULL,
_("Automatic error correction file creation\n"
"is only possible after a full reading pass.\n"));
AllowActions(TRUE);
}
else ContinueWithAction(ACTION_CREATE_CONT);
} }
else ContinueWithAction(ACTION_CREATE_CONT); else AllowActions(TRUE);
} }
else else
if(Closure->guiMode) if(Closure->guiMode)
AllowActions(TRUE); AllowActions(TRUE);
if(!full_read && Closure->crcCache) if(!full_read && Closure->crcCache)
ClearCrcCache(); ClearCrcCache();
if(scan_mode) /* we haven't created an image, so throw away the crc sums */ if(scan_mode) /* we haven't created an image, so throw away the crc sums */
ClearCrcCache(); ClearCrcCache();
skipping to change at line 196 skipping to change at line 227
{ RegisterCleanup(_("Reading aborted"), cleanup, rc); { RegisterCleanup(_("Reading aborted"), cleanup, rc);
if(Closure->guiMode) if(Closure->guiMode)
SetLabelText(GTK_LABEL(Closure->readLinearHeadline), SetLabelText(GTK_LABEL(Closure->readLinearHeadline),
"<big>%s</big>\n<i>%s</i>", "<big>%s</big>\n<i>%s</i>",
_("Preparing for reading the medium image."), _("Preparing for reading the medium image."),
_("Medium: not yet determined")); _("Medium: not yet determined"));
} }
} }
/* /*
* See if we have ecc data which belongs to the medium
*/
static void check_for_ecc_data(read_closure *rc)
{
Closure->eccType = ECC_NONE;
/* Compare the fingerprint sectors */
rc->ei = OpenEccFile(READABLE_ECC | PRINT_MODE);
if(rc->ei) /* ECC file */
{ guint8 fingerprint[16];
int fp_read;
fp_read = GetMediumFingerprint(rc->dh, fingerprint, rc->ei->eh->fpSector);
if(fp_read && !memcmp(fingerprint, rc->ei->eh->mediumFP, 16))
{ rc->dataSectors = uchar_to_gint64(rc->ei->eh->sectors);
Closure->eccType = ECC_RS01;
}
else
{ FreeEccInfo(rc->ei);
rc->ei = NULL;
}
}
/* maybe we have an augmented image */
if(rc->dh->rs02Header) /* see if we have RS02 type ecc */
{ rc->dataSectors = uchar_to_gint64(rc->dh->rs02Header->sectors);
Closure->eccType = ECC_RS02;
}
}
/*
* Find out current which mode we are operating in: * Find out current which mode we are operating in:
* 1. Scanning * 1. Scanning
* 2. Creating a new image * 2. Creating a new image
* 3. Completing an existing image * 3. Completing an existing image
* Output respective messages and prepare the image file. * Output respective messages and prepare the image file.
*/ */
static void determine_mode(read_closure *rc) static void determine_mode(read_closure *rc)
{ guint8 medium_fp[16], image_fp[16]; { guint8 medium_fp[16], image_fp[16];
gint64 image_size; guint64 image_size;
unsigned char *buf = rc->alignedBuf[0]->buf; unsigned char *buf = rc->alignedBuf[0]->buf;
int unknown_fingerprint = FALSE; int unknown_fingerprint = FALSE;
/*** In scan mode we simply need to output some messages. */ /*** In scan mode we simply need to output some messages. */
if(rc->scanMode) if(rc->scanMode)
{ {
rc->msg = g_strdup(_("Scanning medium for read errors.")); rc->msg = g_strdup(_("Scanning medium for read errors."));
PrintLog("%s\n", rc->msg); PrintLog("%s\n", rc->msg);
if(Closure->guiMode) if(Closure->guiMode)
{ if(Closure->eccType != ECC_NONE) { if(rc->eccMethod)
SetLabelText(GTK_LABEL(Closure->readLinearHeadline), SetLabelText(GTK_LABEL(Closure->readLinearHeadline),
"<big>%s</big>\n<i>- %s -</i>", rc->msg, "<big>%s</big>\n<i>- %s -</i>", rc->msg,
_("Reading CRC information from ecc file")); _("Reading CRC information"));
else else
SetLabelText(GTK_LABEL(Closure->readLinearHeadline), SetLabelText(GTK_LABEL(Closure->readLinearHeadline),
"<big>%s</big>\n<i>%s</i>", rc->msg, rc->dh->mediumDescr) ; "<big>%s</big>\n<i>%s</i>", rc->msg, rc->image->dh->mediu mDescr);
} }
rc->readMarker = 0; rc->readMarker = 0;
if(Closure->guiMode) if(Closure->guiMode)
InitializeCurve(rc, rc->dh->maxRate, rc->dh->canC2Scan); InitializeCurve(rc, rc->image->dh->maxRate, rc->image->dh->canC2Scan);
return; return;
} }
/*** If no image file exists, open a new one. */ /*** If no image file exists, open a new one. */
reopen_image: reopen_image:
if(!LargeStat(Closure->imageName, &image_size)) if(!LargeStat(Closure->imageName, &image_size))
{ {
if(rc->msg) g_free(rc->msg);
rc->msg = g_strdup(_("Reading new medium image.")); rc->msg = g_strdup(_("Reading new medium image."));
if(!(rc->writerImage = LargeOpen(Closure->imageName, O_WRONLY | O_CREAT, I MG_PERMS))) if(!(rc->writerImage = LargeOpen(Closure->imageName, O_WRONLY | O_CREAT, I MG_PERMS)))
Stop(_("Can't open %s:\n%s"),Closure->imageName,strerror(errno)); Stop(_("Can't open %s:\n%s"),Closure->imageName,strerror(errno));
if(!(rc->readerImage = LargeOpen(Closure->imageName, O_RDONLY, IMG_PERMS)) ) if(!(rc->readerImage = LargeOpen(Closure->imageName, O_RDONLY, IMG_PERMS)) )
Stop(_("Can't open %s:\n%s"),Closure->imageName,strerror(errno)); Stop(_("Can't open %s:\n%s"),Closure->imageName,strerror(errno));
PrintLog(_("Creating new %s image.\n"),Closure->imageName); PrintLog(_("Creating new %s image.\n"),Closure->imageName);
if(Closure->guiMode) if(Closure->guiMode)
{ if(Closure->eccType != ECC_NONE) { if(rc->eccMethod)
SetLabelText(GTK_LABEL(Closure->readLinearHeadline), SetLabelText(GTK_LABEL(Closure->readLinearHeadline),
"<big>%s</big>\n<i>%s</i>", rc->msg, "<big>%s</big>\n<i>%s</i>", rc->msg,
_("Reading CRC information")); _("Reading CRC information"));
else else
SetLabelText(GTK_LABEL(Closure->readLinearHeadline), SetLabelText(GTK_LABEL(Closure->readLinearHeadline),
"<big>%s</big>\n<i>%s</i>", rc->msg, rc->dh->mediumDescr ); "<big>%s</big>\n<i>%s</i>", rc->msg, rc->image->dh->medi umDescr);
} }
rc->rereading = FALSE; rc->rereading = FALSE;
rc->readMarker = 0; rc->readMarker = 0;
if(Closure->guiMode) if(Closure->guiMode)
InitializeCurve(rc, rc->dh->maxRate, rc->dh->canC2Scan); InitializeCurve(rc, rc->image->dh->maxRate, rc->image->dh->canC2Scan);
return; return;
} }
/*** Examine the given image file */ /*** Examine the given image file */
if(rc->msg) g_free(rc->msg);
rc->msg = g_strdup(_("Completing existing medium image.")); rc->msg = g_strdup(_("Completing existing medium image."));
/* Use the existing file as a starting point. /* Use the existing file as a starting point.
Set the read marker at the end of the file Set the read marker at the end of the file
so that the reader looks for "dead_sector" markers so that the reader looks for "dead_sector" markers
and skips already read blocks. */ and skips already read blocks. */
if(!(rc->readerImage = LargeOpen(Closure->imageName, O_RDONLY, IMG_PERMS))) if(!(rc->readerImage = LargeOpen(Closure->imageName, O_RDONLY, IMG_PERMS)))
Stop(_("Can't open %s:\n%s"),Closure->imageName,strerror(errno)); Stop(_("Can't open %s:\n%s"),Closure->imageName,strerror(errno));
if(!(rc->writerImage = LargeOpen(Closure->imageName, O_WRONLY, IMG_PERMS))) if(!(rc->writerImage = LargeOpen(Closure->imageName, O_WRONLY, IMG_PERMS)))
skipping to change at line 330 skipping to change at line 329
unknown_fingerprint = TRUE; unknown_fingerprint = TRUE;
else else
{ struct MD5Context md5ctxt; { struct MD5Context md5ctxt;
int n = LargeRead(rc->readerImage, buf, 2048); int n = LargeRead(rc->readerImage, buf, 2048);
int fp_read; int fp_read;
MD5Init(&md5ctxt); MD5Init(&md5ctxt);
MD5Update(&md5ctxt, buf, 2048); MD5Update(&md5ctxt, buf, 2048);
MD5Final(image_fp, &md5ctxt); MD5Final(image_fp, &md5ctxt);
fp_read = GetMediumFingerprint(rc->dh, medium_fp, FINGERPRINT_SECTOR); fp_read = GetImageFingerprint(rc->image, medium_fp, FINGERPRINT_SECTOR);
if(n != 2048 || !fp_read || (CheckForMissingSector(buf, FINGERPRINT_SECTOR , NULL, 0) != SECTOR_PRESENT)) if(n != 2048 || !fp_read || (CheckForMissingSector(buf, FINGERPRINT_SECTOR , NULL, 0) != SECTOR_PRESENT))
unknown_fingerprint = TRUE; unknown_fingerprint = TRUE;
} }
/* If fingerprints could be read, compare them. */ /* If fingerprints could be read, compare them. */
if(!unknown_fingerprint && memcmp(image_fp, medium_fp, 16)) if(!unknown_fingerprint && memcmp(image_fp, medium_fp, 16))
{ {
if(!Closure->guiMode) if(!Closure->guiMode)
Stop(_("Image file does not match the CD/DVD.")); Stop(_("Image file does not match the optical disc."));
else else
{ int answer = ConfirmImageDeletion(Closure->imageName); { int answer = ConfirmImageDeletion(Closure->imageName);
if(!answer) if(!answer)
{ rc->unreportedError = FALSE; { rc->unreportedError = FALSE;
SwitchAndSetFootline(Closure->readLinearNotebook, 1, Closure->readLin earFootline, SwitchAndSetFootline(Closure->readLinearNotebook, 1, Closure->readLin earFootline,
_("<span %s>Reading aborted.</span> Please selec t a different image file."), _("<span %s>Reading aborted.</span> Please selec t a different image file."),
Closure->redMarkup); Closure->redMarkup);
cleanup((gpointer)rc); cleanup((gpointer)rc);
} }
skipping to change at line 366 skipping to change at line 365
goto reopen_image; goto reopen_image;
} }
} }
} }
/*** If the image is not complete yet, first aim to read the /*** If the image is not complete yet, first aim to read the
unvisited sectors before trying to re-read the missing ones. unvisited sectors before trying to re-read the missing ones.
Exception: We must start from the beginning if multiple reading passes a re requested. */ Exception: We must start from the beginning if multiple reading passes a re requested. */
if(!Closure->readStart && !Closure->readEnd if(!Closure->readStart && !Closure->readEnd
&& rc->readMarker < rc->sectors-1 && Closure->readingPasses <= 1) && rc->readMarker < rc->image->dh->sectors-1 && Closure->readingPasses <= 1)
{ PrintLog(_("Completing image %s. Continuing with sector %lld.\n"), { PrintLog(_("Completing image %s. Continuing with sector %lld.\n"),
Closure->imageName, rc->readMarker); Closure->imageName, rc->readMarker);
rc->firstSector = rc->readMarker; rc->firstSector = rc->readMarker;
Closure->additionalSpiralColor = 0; /* blue */ Closure->additionalSpiralColor = 0; /* blue */
} }
else else
{ PrintLog(_("Completing image %s. Only missing sectors will be read.\n"), C losure->imageName); { PrintLog(_("Completing image %s. Only missing sectors will be read.\n"), C losure->imageName);
Closure->additionalSpiralColor = 3; /* dark green*/ Closure->additionalSpiralColor = 3; /* dark green*/
} }
if(Closure->guiMode) if(Closure->guiMode)
SetLabelText(GTK_LABEL(Closure->readLinearHeadline), SetLabelText(GTK_LABEL(Closure->readLinearHeadline),
"<big>%s</big>\n<i>%s</i>",rc->msg,rc->dh->mediumDescr); "<big>%s</big>\n<i>%s</i>",rc->msg,rc->image->dh->mediumDescr) ;
if(Closure->guiMode) if(Closure->guiMode)
InitializeCurve(rc, rc->dh->maxRate, rc->dh->canC2Scan); InitializeCurve(rc, rc->image->dh->maxRate, rc->image->dh->canC2Scan);
} }
/* /*
* Fill the gap between rc->readMarker and rc->firstSector * Fill the gap between rc->readMarker and rc->firstSector
* with dead sector markers. * with dead sector markers.
*/ */
static void fill_gap(read_closure *rc) static void fill_gap(read_closure *rc)
{ gint64 s; { gint64 s;
skipping to change at line 405 skipping to change at line 404
s = rc->readMarker; s = rc->readMarker;
if(!LargeSeek(rc->writerImage, (gint64)(2048*s))) if(!LargeSeek(rc->writerImage, (gint64)(2048*s)))
Stop(_("Failed seeking to sector %lld in image [%s]: %s"), Stop(_("Failed seeking to sector %lld in image [%s]: %s"),
s, "fill", strerror(errno)); s, "fill", strerror(errno));
while(s < rc->firstSector) while(s < rc->firstSector)
{ int n; { int n;
CreateMissingSector(buf, s, rc->fingerprint, FINGERPRINT_SECTOR, rc->vol umeLabel); CreateMissingSector(buf, s, rc->image->imageFP, FINGERPRINT_SECTOR, rc-> volumeLabel);
n = LargeWrite(rc->writerImage, buf, 2048); n = LargeWrite(rc->writerImage, buf, 2048);
if(n != 2048) if(n != 2048)
Stop(_("Failed writing to sector %lld in image [%s]: %s"), Stop(_("Failed writing to sector %lld in image [%s]: %s"),
s, "fill", strerror(errno)); s, "fill", strerror(errno));
s++; s++;
} }
} }
} }
/* /*
* Allocate memory for CRC32 sums and preload the cache. * Allocate memory for CRC32 sums and preload the cache.
*/ */
static void prepare_crc_cache(read_closure *rc) static void prepare_crc_cache(read_closure *rc)
{ {
Closure->crcAvailable = FALSE;
/*** Memory for the CRC32 sums is needed in two cases /*** Memory for the CRC32 sums is needed in two cases
and comes in two flavors: */ and comes in two flavors: */
/* a) A full image read is attempted. /* a) A full image read is attempted.
The image CRC32 and md5sum are calculated on the fly, The image CRC32 and md5sum are calculated on the fly,
as they may be used for ecc creation later. as they may be used for ecc creation later.
In that case we use the old Closure->crcCache storage. */ In that case we use the old Closure->crcCache storage. */
if( !rc->scanMode && !rc->rereading if( !rc->scanMode && !rc->rereading
&& rc->firstSector == 0 && rc->lastSector == rc->sectors-1) && rc->firstSector == 0 && rc->lastSector == rc->image->dh->sectors-1)
{ if(Closure->crcCache) { if(Closure->crcCache)
g_printf("Internal problem: crcCache not clean\n"); g_printf("Internal problem: crcCache not clean\n");
Closure->crcCache = g_try_malloc(sizeof(guint32) * rc->sectors); Closure->crcCache = g_try_malloc(sizeof(guint32) * rc->image->dh->sectors) ;
if(Closure->crcCache) if(Closure->crcCache)
Closure->crcImageName = g_strdup(Closure->imageName); Closure->crcImageName = g_strdup(Closure->imageName);
rc->doMD5sums = TRUE; if(rc->eccMethod && rc->eccMethod->updateCksums && rc->eccMethod->finalize
Cksums)
rc->doMD5sums = TRUE; /* not all codecs support these actions! */
MD5Init(&rc->md5ctxt); MD5Init(&rc->md5ctxt);
} }
/* b) We have suitable ecc data and want to compare CRC32sums /* b) We have suitable ecc data and want to compare CRC32sums
against it while reading. Note that for augmented images against it while reading. Note that for augmented images
the checksums may be incomplete due to unreadable CRC sectors, the checksums may be incomplete due to unreadable CRC sectors,
so we keep them in the CrcBuf struct which deals with lost so we keep them in the CrcBuf struct which deals with lost
sectors internally. */ sectors internally. */
if(Closure->eccType != ECC_NONE) if(rc->eccMethod)
{ { char method_name[5];
PrintCLI("%s ...",_("Reading CRC information from ecc data"));
strncpy(method_name, rc->eccMethod->name, 4);
method_name[4] = 0;
PrintCLI("%s (%s) ... ",_("Reading CRC information from ecc data"),
method_name);
if(Closure->guiMode) if(Closure->guiMode)
SetLabelText(GTK_LABEL(Closure->readLinearHeadline), SetLabelText(GTK_LABEL(Closure->readLinearHeadline),
"<big>%s</big>\n<i>%s</i>", "<big>%s</big>\n<i>%s</i>",
_("Reading CRC information from ecc data"), _("Reading CRC information from ecc data"),
rc->dh->mediumDescr); rc->image->dh->mediumDescr);
switch(Closure->eccType) if(rc->eccMethod->getCrcBuf)
{ case ECC_RS01: { rc->crcBuf = rc->eccMethod->getCrcBuf(rc->image);
rc->crcBuf = GetCRCFromRS01(rc->ei); Closure->crcAvailable = TRUE;
rc->doMD5sums = TRUE; if(Closure->guiMode)
MD5Init(&rc->md5ctxt); RedrawReadLinearWindow();
break; }
case ECC_RS02: else rc->crcBuf = NULL;
{ EccHeader *eh = rc->dh->rs02Header;
rc->lay = CalcRS02Layout(uchar_to_gint64(eh->sectors), eh->eccBytes); if(rc->eccMethod->resetCksums)
rc->crcBuf = GetCRCFromRS02(rc->lay, rc->dh, rc->readerImage); { rc->doMD5sums = TRUE;
rc->doMD5sums = TRUE; rc->eccMethod->resetCksums(rc->image);
MD5Init(&rc->dataCtxt);
MD5Init(&rc->crcCtxt);
MD5Init(&rc->eccCtxt);
MD5Init(&rc->metaCtxt);
break;
}
default:
rc->crcBuf = NULL;
break;
} }
if(Closure->guiMode) if(Closure->guiMode)
SetLabelText(GTK_LABEL(Closure->readLinearHeadline), SetLabelText(GTK_LABEL(Closure->readLinearHeadline),
"<big>%s</big>\n<i>%s</i>", rc->msg, rc->dh->mediumDescr); "<big>%s</big>\n<i>%s</i>", rc->msg, rc->image->dh->mediumD escr);
PrintCLI(_("done.\n")); PrintCLI(_("done.\n"));
} }
} }
/* /*
* Wait for the drive to spin up and prepare the timer * Wait for the drive to spin up and prepare the timer
*/ */
static void prepare_timer(read_closure *rc) static void prepare_timer(read_closure *rc)
{ {
if(Closure->guiMode && Closure->spinupDelay) if(Closure->guiMode && Closure->spinupDelay)
SwitchAndSetFootline(Closure->readLinearNotebook, 1, Closure->readLinearFoo tline, SwitchAndSetFootline(Closure->readLinearNotebook, 1, Closure->readLinearFoo tline,
_("Waiting %d seconds for drive to spin up...\n"), Clos ure->spinupDelay); _("Waiting %d seconds for drive to spin up...\n"), Clos ure->spinupDelay);
SpinupDevice(rc->dh); SpinupDevice(rc->image->dh);
if(Closure->guiMode && Closure->spinupDelay) if(Closure->guiMode && Closure->spinupDelay)
SwitchAndSetFootline(Closure->readLinearNotebook, 0, Closure->readLinearFoo tline, "ignore"); SwitchAndSetFootline(Closure->readLinearNotebook, 0, Closure->readLinearFoo tline, "ignore");
if(Closure->spinupDelay) /* eliminate initial seek time from timing */ if(Closure->spinupDelay) /* eliminate initial seek time from timing */
ReadSectors(rc->dh, rc->alignedBuf[0]->buf, rc->firstSector, 1); ReadSectors(rc->image->dh, rc->alignedBuf[0]->buf, rc->firstSector, 1);
g_timer_start(rc->speedTimer); g_timer_start(rc->speedTimer);
g_timer_start(rc->readTimer); g_timer_start(rc->readTimer);
} }
/* /*
* Update the various progress indicators * Update the various progress indicators
*/ */
static void show_progress(read_closure *rc) static void show_progress(read_closure *rc)
{ int percent; { int percent;
if(Closure->guiMode && rc->lastErrorsPrinted != Closure->readErrors) if(Closure->guiMode && rc->lastErrorsPrinted != Closure->readErrors)
{ SetLabelText(GTK_LABEL(Closure->readLinearErrors), { SetLabelText(GTK_LABEL(Closure->readLinearErrors),
_("Unreadable / skipped sectors: %lld"), Closure->readErrors); _("Unreadable / skipped sectors: %lld"), Closure->readErrors);
rc->lastErrorsPrinted = Closure->readErrors; rc->lastErrorsPrinted = Closure->readErrors;
} }
if(rc->readPos>rc->readMarker) rc->readMarker=rc->readPos; if(rc->readPos>rc->readMarker) rc->readMarker=rc->readPos;
percent = (1000*rc->readPos)/rc->sectors; percent = (1000*rc->readPos)/rc->image->dh->sectors;
if(rc->lastPercent != percent) if(rc->lastPercent != percent)
{ gulong ignore; { gulong ignore;
int color; int color;
if(Closure->guiMode) if(Closure->guiMode)
ChangeSpiralCursor(Closure->readLinearSpiral, percent); ChangeSpiralCursor(Closure->readLinearSpiral, percent);
if(rc->readOK <= rc->lastReadOK) /* nothing read since last sample? */ if(rc->readOK <= rc->lastReadOK) /* nothing read since last sample? */
{ rc->speed = 0.0; { rc->speed = 0.0;
skipping to change at line 560 skipping to change at line 556
double elapsed = g_timer_elapsed(rc->speedTimer, &ignore); double elapsed = g_timer_elapsed(rc->speedTimer, &ignore);
double kb_sec = kb_read / elapsed; double kb_sec = kb_read / elapsed;
if(Closure->readErrors - rc->previousReadErrors > 0) if(Closure->readErrors - rc->previousReadErrors > 0)
color = 2; color = 2;
else if(Closure->crcErrors - rc->previousCRCErrors > 0) else if(Closure->crcErrors - rc->previousCRCErrors > 0)
color = 4; color = 4;
else color = 1; else color = 1;
if(rc->firstSpeedValue) if(rc->firstSpeedValue)
{ rc->speed = kb_sec / rc->dh->singleRate; { rc->speed = kb_sec / rc->image->dh->singleRate;
if(Closure->guiMode) if(Closure->guiMode)
{ AddCurveValues(rc, rc->lastPercent, color, rc->maxC2); { AddCurveValues(rc, rc->lastPercent, color, rc->maxC2);
AddCurveValues(rc, percent, color, rc->maxC2); AddCurveValues(rc, percent, color, rc->maxC2);
} }
rc->firstSpeedValue = FALSE; rc->firstSpeedValue = FALSE;
rc->lastPercent = percent; rc->lastPercent = percent;
rc->lastSpeed = rc->speed; rc->lastSpeed = rc->speed;
rc->previousReadErrors = Closure->readErrors; rc->previousReadErrors = Closure->readErrors;
rc->previousCRCErrors = Closure->crcErrors; rc->previousCRCErrors = Closure->crcErrors;
rc->lastReadOK = rc->readOK; rc->lastReadOK = rc->readOK;
} }
else else
{ static int cut_peaks = 3; { static int cut_peaks = 3;
/* If reading is interrupted by a requester, following /* If reading is interrupted by a requester, following
reads might be extremely fast due to read-ahead in reads might be extremely fast due to read-ahead in
the kernel. Try to mask these out. */ the kernel. Try to mask these out. */
if(kb_sec / rc->dh->singleRate > 100.0 && cut_peaks) if(kb_sec / rc->image->dh->singleRate > 100.0 && cut_peaks)
cut_peaks--; cut_peaks--;
else else
{ rc->speed = (rc->speed + kb_sec / rc->dh->singleRate) / 2.0; { rc->speed = (rc->speed + kb_sec / rc->image->dh->singleRate) / 2.0 ;
cut_peaks=3; cut_peaks=3;
} }
if(Closure->guiMode) if(Closure->guiMode)
AddCurveValues(rc, percent, color, rc->maxC2); AddCurveValues(rc, percent, color, rc->maxC2);
if(Closure->speedWarning && rc->lastSpeed > 0.5) if(Closure->speedWarning && rc->lastSpeed > 0.5)
{ double delta = rc->speed - rc->lastSpeed; { double delta = rc->speed - rc->lastSpeed;
double abs_delta = fabs(delta); double abs_delta = fabs(delta);
double sp = (100.0*abs_delta) / rc->lastSpeed; double sp = (100.0*abs_delta) / rc->lastSpeed;
skipping to change at line 606 skipping to change at line 602
if(sp >= Closure->speedWarning) if(sp >= Closure->speedWarning)
{ if(delta > 0.0) { if(delta > 0.0)
PrintCLI(_("Sector %lld: Speed increased to %4.1fx\n"), PrintCLI(_("Sector %lld: Speed increased to %4.1fx\n"),
rc->readPos, fabs(rc->speed)); rc->readPos, fabs(rc->speed));
else else
PrintCLI(_("Sector %lld: Speed dropped to %4.1fx\n"), PrintCLI(_("Sector %lld: Speed dropped to %4.1fx\n"),
rc->readPos, fabs(rc->speed)); rc->readPos, fabs(rc->speed));
} }
} }
PrintProgress(_("Read position: %3d.%1d%% (%4.1fx)"), if(Closure->fixedSpeedValues)
percent/10,percent%10,rc->speed); PrintProgress(_("Read position: %3d.%1d%% (nn.nx)"),
percent/10,percent%10);
else PrintProgress(_("Read position: %3d.%1d%% (%4.1fx)"),
percent/10,percent%10,rc->speed);
rc->lastPercent = percent; rc->lastPercent = percent;
rc->lastSpeed = rc->speed; rc->lastSpeed = rc->speed;
rc->previousReadErrors = Closure->readErrors; rc->previousReadErrors = Closure->readErrors;
rc->previousCRCErrors = Closure->crcErrors; rc->previousCRCErrors = Closure->crcErrors;
rc->lastReadOK = rc->readOK; rc->lastReadOK = rc->readOK;
g_timer_start(rc->speedTimer); g_timer_start(rc->speedTimer);
} }
} }
rc->maxC2 = 0; rc->maxC2 = 0;
skipping to change at line 679 skipping to change at line 678
} }
/* On-the-fly CRC calculation */ /* On-the-fly CRC calculation */
if(Closure->crcCache) if(Closure->crcCache)
{ for(i=0; i<nsectors; i++) { for(i=0; i<nsectors; i++)
Closure->crcCache[s+i] = Crc32(rc->alignedBuf[rc->writePtr]->buf+2 048*i, 2048); Closure->crcCache[s+i] = Crc32(rc->alignedBuf[rc->writePtr]->buf+2 048*i, 2048);
} }
} }
/* Create the full-image md5sum */
if(rc->doMD5sums)
MD5Update(&rc->md5ctxt, rc->alignedBuf[rc->writePtr]->buf, 2048*nsectors
);
/* Do on-the-fly CRC / md5sum testing. This is the only action carried out /* Do on-the-fly CRC / md5sum testing. This is the only action carried out
in scan mode, but also done while reading. */ in scan mode, but also done while reading. */
if(Closure->eccType && rc->bufState[rc->writePtr] != BUF_DEAD) if(rc->eccMethod && rc->bufState[rc->writePtr] != BUF_DEAD)
{ {
for(i=0; i<nsectors; i++) for(i=0; i<nsectors; i++)
{ unsigned char *buf = rc->alignedBuf[rc->writePtr]->buf+2048*i; { unsigned char *buf = rc->alignedBuf[rc->writePtr]->buf+2048*i;
gint64 sector = s+i; gint64 sector = s+i;
switch(Closure->eccType) /* Have the codec update its internal checksums */
{ case ECC_RS02:
/* md5sum the data portion */ if(rc->doMD5sums)
if(rc->doMD5sums && sector < rc->lay->dataSectors) rc->eccMethod->updateCksums(rc->image, sector, buf);
{ if(sector < rc->lay->dataSectors - 1)
MD5Update(&rc->dataCtxt, buf, 2048); /* Check against CRCs in the ecc data */
else MD5Update(&rc->dataCtxt, buf, rc->dh->rs02Header->inLast
); if(rc->crcBuf && sector < rc->crcBuf->size)
} { switch(CheckAgainstCrcBuffer(rc->crcBuf, sector, buf))
{ case CRC_BAD:
/* md5sum the crc portion */ ClearProgress();
if(rc->doMD5sums && sector >= rc->lay->dataSectors+2 && sector < PrintCLI(_("* CRC error, sector: %lld\n"), (long long int)s+i)
rc->lay->protectedSectors) ;
MD5Update(&rc->crcCtxt, buf, 2048); Closure->crcErrors++;
if(rc->readMap) /* trigger re-read FIXME*/
/* md5sum the ecc layers */ ClearBit(rc->readMap, sector);
if(rc->doMD5sums && sector >= rc->lay->protectedSectors) break;
{ gint64 layer, n;
case CRC_UNKNOWN: /* CRC data missing or detected as defective
RS02SliceIndex(rc->lay, sector, &layer, &n); */
if(layer != -1) /* not an ecc header? */ rc->crcIncomplete = TRUE;
{ if(n < rc->lay->sectorsPerLayer-1) /* not at layer end? * break;
/ }
MD5Update(&rc->eccCtxt, buf, 2048);
else /* layer end; update meta md5 and skip to next layer
*/
{ guint8 sum[16];
MD5Update(&rc->eccCtxt, buf, 2048);
MD5Final(sum, &rc->eccCtxt);
MD5Update(&rc->metaCtxt, sum, 16);
MD5Init(&rc->eccCtxt);
}
}
/* maybe add ...else { check ecc header } */
}
/* fall through! */
case ECC_RS01:
if(sector < rc->dataSectors) /* FIXME: not okay for RS03 */
{ if( rc->crcBuf
&& CheckAgainstCrcBuffer(rc->crcBuf, sector, buf) == CRC_B
AD)
{ PrintCLI(_("* CRC error, sector: %lld\n"), (long long int)
s+i);
Closure->crcErrors++;
if(rc->readMap) /* trigger re-read FIXME*/
ClearBit(rc->readMap, sector);
}
}
break;
} }
} }
} }
/* Release this buffer */ /* Release this buffer */
update_mutex: update_mutex:
g_mutex_lock(rc->mutex); g_mutex_lock(rc->mutex);
rc->bufState[rc->writePtr] = BUF_EMPTY; rc->bufState[rc->writePtr] = BUF_EMPTY;
rc->writePtr++; rc->writePtr++;
skipping to change at line 774 skipping to change at line 744
static void insert_buttons(GtkDialog *dialog) static void insert_buttons(GtkDialog *dialog)
{ {
gtk_dialog_add_buttons(dialog, gtk_dialog_add_buttons(dialog,
_utf("Ignore once"), 1, _utf("Ignore once"), 1,
_utf("Ignore always"), 2, _utf("Ignore always"), 2,
_utf("Abort"), 0, NULL); _utf("Abort"), 0, NULL);
} }
void ReadMediumLinear(gpointer data) void ReadMediumLinear(gpointer data)
{ read_closure *rc = g_malloc0(sizeof(read_closure)); { read_closure *rc = g_malloc0(sizeof(read_closure));
unsigned char fp[16]; int md5_failure = 0;
guint8 data_md5[16]; int unrecoverable_sectors = 0;
guint8 crc_md5[16];
guint8 meta_md5[16];
char *md5_failure = NULL;
GError *err = NULL; GError *err = NULL;
int nsectors; int nsectors;
char *t; char *t = NULL;
int status,n; int status,n;
int tao_tail; int tao_tail;
int i; int i;
/*** This value might be temporarily changed later. */ /*** This value might be temporarily changed later. */
rc->savedSectorSkip = Closure->sectorSkip; rc->savedSectorSkip = Closure->sectorSkip;
/*** Register the cleanup procedure so that Stop() can abort us properly. */ /*** Register the cleanup procedure so that Stop() can abort us properly. */
skipping to change at line 805 skipping to change at line 772
/* Register with different labels depending on rc->scanMode */ /* Register with different labels depending on rc->scanMode */
register_reader(rc); register_reader(rc);
/*** Timer setup */ /*** Timer setup */
rc->speedTimer = g_timer_new(); rc->speedTimer = g_timer_new();
rc->readTimer = g_timer_new(); rc->readTimer = g_timer_new();
/*** Create the aligned buffers. */ /*** Mutex for spiral renderer synchronization */
rc->rendererMutex = g_malloc(sizeof(GMutex));
g_mutex_init(rc->rendererMutex);
/*** Create the aligned buffers. */
for(i=0; i<READ_BUFFERS; i++) for(i=0; i<READ_BUFFERS; i++)
rc->alignedBuf[i] = CreateAlignedBuffer(MAX_CLUSTER_SIZE); rc->alignedBuf[i] = CreateAlignedBuffer(MAX_CLUSTER_SIZE);
/*** Open Device and query medium properties */ /*** Open Device and query medium properties:
rc->image will point to the optical medium,
and possibly the repsective ecc file.
The on disk image is maintained in rc->reader|writerImage. */
rc->dh = OpenAndQueryDevice(Closure->device); rc->image = OpenImageFromDevice(Closure->device);
rc->sectors = rc->dh->sectors;
Closure->readErrors = Closure->crcErrors = rc->readOK = 0; Closure->readErrors = Closure->crcErrors = rc->readOK = 0;
/*** Save some useful information for the missing sector marker */ /*** Save some useful information for the missing sector marker */
if(GetMediumFingerprint(rc->dh, fp, FINGERPRINT_SECTOR)) if(rc->image->isoInfo && rc->image->isoInfo->volumeLabel[0])
{ rc->fingerprint = g_malloc(16); rc->volumeLabel = g_strdup(rc->image->isoInfo->volumeLabel);
memcpy(rc->fingerprint, fp, 16);
/*** See if we have an ecc file which belongs to the medium,
and attach it with the Image struct. */
OpenEccFileForImage(rc->image, Closure->eccName, O_RDONLY, IMG_PERMS);
/*** If ecc information is available in both the image and the
ecc file, the ecc file gets precedence. */
if(rc->image->eccFileMethod)
{ rc->eccMethod = rc->image->eccFileMethod;
rc->eccHeader = rc->image->eccFileHeader;
} }
if(rc->dh->isoInfo && rc->dh->isoInfo->volumeLabel[0]) else if(rc->image->eccMethod)
rc->volumeLabel = g_strdup(rc->dh->isoInfo->volumeLabel); { rc->eccMethod = rc->image->eccMethod;
rc->eccHeader = rc->image->eccHeader;
}
/*** Some very old versions set eh->inLast to 0 when the last sector
is completely filled. Set it to 2048 to avoid confusion. */
if(rc->eccHeader && rc->eccHeader->inLast == 0)
rc->eccHeader->inLast = 2048;
/*** Make sure we are compatible with the ecc data */
if(rc->eccHeader && (Closure->version < rc->eccHeader->neededVersion))
{ if(Closure->guiMode)
{ int answer;
/*** See if we have an ecc file which belongs to the medium */ if(rc->image->eccFileState == ECCFILE_PRESENT)
answer = ModalWarning(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL, NULL
,
_("This ecc file requires dvdisaster-%d.%d!\n\n"
"Proceeding could trigger incorrect behaviour.
\n"
"Please read the image without using this ecc
file\n"
"or visit http://www.dvdisaster.org for an upg
rade.\n\n"),
rc->eccHeader->neededVersion/10000,
(rc->eccHeader->neededVersion%10000)/100);
else
answer = ModalWarning(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL, NULL
,
_("This image requires dvdisaster-%d.%d!\n\n"
"Proceeding could trigger incorrect behaviour.
\n"
"Please visit http://www.dvdisaster.org for an
upgrade.\n\n"),
rc->eccHeader->neededVersion/10000,
(rc->eccHeader->neededVersion%10000)/100);
check_for_ecc_data(rc); if(!answer)
{ SwitchAndSetFootline(Closure->readLinearNotebook, 1, Closure->readLin
earFootline,
_("<span %s>Aborted by user request!</span>"),
Closure->redMarkup, rc->readOK,Closure->readErro
rs);
rc->unreportedError = FALSE; /* suppress respective error message */
goto terminate;
}
}
else
{ if(rc->image->eccFileState == ECCFILE_PRESENT)
PrintCLI(_("* Warning: This ecc file requires dvdisaster-%d.%d!\n"
"* Proceeding could trigger incorrect behaviour.\n"
"* Please read the image without using this ecc file
\n"
"* or visit http://www.dvdisaster.org for an upgrade
.\n\n"),
rc->eccHeader->neededVersion/10000,
(rc->eccHeader->neededVersion%10000)/100);
else
PrintCLI(_("* Warning: This image requires dvdisaster-%d.%d!\n"
"* Proceeding could trigger incorrect behaviour.\n"
"* Please visit http://www.dvdisaster.org for an upg
rade.\n\n"),
rc->eccHeader->neededVersion/10000,
(rc->eccHeader->neededVersion%10000)/100);
}
}
/*** See if user wants to limit the read range. */ /*** See if user wants to limit the read range. */
GetReadingRange(rc->sectors, &rc->firstSector, &rc->lastSector); GetReadingRange(rc->image->dh->sectors, &rc->firstSector, &rc->lastSector);
if(rc->firstSector > 0) /* Mark skipped sectors */ if(rc->firstSector > 0) /* Mark skipped sectors */
Closure->additionalSpiralColor = 0; /* blue */ Closure->additionalSpiralColor = 0; /* blue */
/*** Determine the reading mode. There are three possibilities: /*** Determine the reading mode. There are three possibilities:
1. scanning (rc->scanMode == TRUE) 1. scanning (rc->scanMode == TRUE)
2. reading into a new image file. 2. reading into a new image file.
3. completing an existing image file. 3. completing an existing image file.
After this function, files are prepared After this function, files are prepared
and respective UI messages have been output. */ and respective UI messages have been output. */
skipping to change at line 855 skipping to change at line 891
fill_gap(rc); fill_gap(rc);
/*** Allocate and preload the CRC sum cache if necessary */ /*** Allocate and preload the CRC sum cache if necessary */
prepare_crc_cache(rc); prepare_crc_cache(rc);
/*** Allocate a bitmap of read sectors to speed up multiple reading passes */ /*** Allocate a bitmap of read sectors to speed up multiple reading passes */
if(Closure->readingPasses > 1) if(Closure->readingPasses > 1)
rc->readMap = CreateBitmap0(rc->sectors); rc->readMap = CreateBitmap0(rc->image->dh->sectors);
/*** Start the worker thread. We concentrate on reading from the drive here; /*** Start the worker thread. We concentrate on reading from the drive here;
writing the image file and calculating the checksums is done in a writing the image file and calculating the checksums is done in a
concurrent thread. */ concurrent thread. */
rc->mutex = g_mutex_new(); rc->mutex = g_malloc(sizeof(GMutex));
rc->canRead = g_cond_new(); g_mutex_init(rc->mutex);
rc->canWrite = g_cond_new(); rc->canRead = g_malloc(sizeof(GCond));
rc->worker = g_thread_create((GThreadFunc)worker_thread, (gpointer)rc, TRUE, g_cond_init(rc->canRead);
&err); rc->canWrite = g_malloc(sizeof(GCond));
g_cond_init(rc->canWrite);
rc->worker = g_thread_try_new("readlinear_worker", (GThreadFunc)worker_thread
, (gpointer)rc, &err);
if(!rc->worker) if(!rc->worker)
Stop("Could not create worker thread: %s", err->message); Stop("Could not create worker thread: %s", err->message);
/*** Prepare the speed timing */ /*** Prepare the speed timing */
prepare_timer(rc); prepare_timer(rc);
/*** Reset for the next reading pass */ /*** Reset for the next reading pass */
rc->lastReadOK = 0; /* keep between passes */ rc->lastReadOK = 0; /* keep between passes */
rc->lastPercent = (1000*rc->readPos)/rc->sectors; rc->lastPercent = (1000*rc->readPos)/rc->image->dh->sectors;
next_reading_pass: next_reading_pass:
if(rc->pass > 0) if(rc->pass > 0)
{ Closure->readErrors = Closure->crcErrors = 0; { Closure->readErrors = Closure->crcErrors = 0;
switch(rc->dh->mainType) switch(rc->image->dh->mainType)
{ case BD: { case BD:
if(Closure->sectorSkip > 32) if(Closure->sectorSkip > 32)
Closure->sectorSkip = 32; Closure->sectorSkip = 32;
break; break;
case DVD: case DVD:
if(Closure->sectorSkip > 16) if(Closure->sectorSkip > 16)
Closure->sectorSkip = 16; Closure->sectorSkip = 16;
break; break;
default: default:
Closure->sectorSkip = 0; Closure->sectorSkip = 0;
skipping to change at line 910 skipping to change at line 950
rc->readPos = rc->firstSector; rc->readPos = rc->firstSector;
rc->lastErrorsPrinted = 0; rc->lastErrorsPrinted = 0;
rc->previousReadErrors = rc->previousCRCErrors = 0; rc->previousReadErrors = rc->previousCRCErrors = 0;
rc->speed = 0; rc->speed = 0;
rc->lastSpeed = -1.0; rc->lastSpeed = -1.0;
rc->firstSpeedValue = TRUE; rc->firstSpeedValue = TRUE;
tao_tail = 0; tao_tail = 0;
while(rc->readPos<=rc->lastSector) while(rc->readPos<=rc->lastSector)
{ int cluster_mask = rc->dh->clusterSize-1; { int cluster_mask = rc->image->dh->clusterSize-1;
if(Closure->stopActions) /* somebody hit the Stop button */ if(Closure->stopActions) /* somebody hit the Stop button */
{ {
SwitchAndSetFootline(Closure->readLinearNotebook, 1, Closure->readLinearF if(Closure->stopActions == STOP_CURRENT_ACTION) /* suppress memleak warn
ootline, ing when closing window */
_("<span %s>Aborted by user request!</span> %lld sec { SwitchAndSetFootline(Closure->readLinearNotebook, 1, Closure->readLi
tors read, %lld sectors unreadable/skipped so far."), nearFootline,
Closure->redMarkup, rc->readOK,Closure->readErrors); _("<span %s>Aborted by user request!</span> %ll
rc->unreportedError = FALSE; /* suppress respective error message */ d sectors read, %lld sectors unreadable/skipped so far."),
goto terminate; Closure->redMarkup, rc->readOK,Closure->readErr
ors);
}
rc->unreportedError = FALSE; /* suppress respective error message */
goto terminate;
} }
/*** Decide between reading in fast mode (dh->clusterSize sectors at once) /*** Decide between reading in fast mode (dh->clusterSize sectors at once)
or reading one sector at a time. or reading one sector at a time.
Fast mode gains some reading speed due to transfering fewer Fast mode gains some reading speed due to transfering fewer
but larger data blocks from the device. but larger data blocks from the device.
Switching to fast mode is done only on cluster boundaries Switching to fast mode is done only on cluster boundaries
-- this matches the internal structure of DVD and later media better. -- this matches the internal structure of DVD and later media better.
In order to treat the 2 read errors at the end of TAO discs correctly , In order to treat the 2 read errors at the end of TAO discs correctly ,
we switch back to per sector reading at the end of the medium. */ we switch back to per sector reading at the end of the medium. */
if( rc->readPos & cluster_mask if( rc->readPos & cluster_mask
|| rc->readPos >= ((rc->sectors - 2) & ~cluster_mask) ) || rc->readPos >= ((rc->image->dh->sectors - 2) & ~cluster_mask) )
nsectors = 1; nsectors = 1;
else nsectors = rc->dh->clusterSize; else nsectors = rc->image->dh->clusterSize;
if(rc->readPos+nsectors > rc->lastSector) /* don't read past the (CD) med ia end */ if(rc->readPos+nsectors > rc->lastSector) /* don't read past the (CD) med ia end */
nsectors = rc->lastSector-rc->readPos+1; nsectors = rc->lastSector-rc->readPos+1;
/*** If rc->readPos is lower than the read marker, /*** If rc->readPos is lower than the read marker,
check if the sector has already been read check if the sector has already been read
in a previous session. */ in a previous session. */
reread: reread:
if(!rc->scanMode && rc->readPos < rc->readMarker) if(!rc->scanMode && rc->readPos < rc->readMarker)
skipping to change at line 972 skipping to change at line 1014
if(rc->readPos+nsectors > rc->readMarker) if(rc->readPos+nsectors > rc->readMarker)
num_compare = rc->readMarker-rc->readPos; num_compare = rc->readMarker-rc->readPos;
for(i=0; i<num_compare; i++) for(i=0; i<num_compare; i++)
{ unsigned char sector_buf[2048]; { unsigned char sector_buf[2048];
int err; int err;
n = LargeRead(rc->readerImage, sector_buf, 2048); n = LargeRead(rc->readerImage, sector_buf, 2048);
if(n != 2048) if(n != 2048)
Stop(_("unexpected read error in image for sector %lld"),rc->re adPos); Stop(_("unexpected read error in image for sector %lld"),rc->re adPos);
err = CheckForMissingSector(sector_buf, rc->readPos+i, NULL, 0); err = CheckForMissingSector(sector_buf, rc->readPos+i,
rc->image->fpState == 2 ? rc->image->i
mageFP : NULL,
rc->image->fpSector);
if(err != SECTOR_PRESENT) if(err != SECTOR_PRESENT)
ExplainMissingSector(sector_buf, rc->readPos+i, err, TRUE); ExplainMissingSector(sector_buf, rc->readPos+i, err, SOURCE_IMAG E, &unrecoverable_sectors);
else else
{ if(!rc->crcBuf { if(!rc->crcBuf
|| CheckAgainstCrcBuffer(rc->crcBuf, rc->readPos+i, sector_b uf) != CRC_BAD) || CheckAgainstCrcBuffer(rc->crcBuf, rc->readPos+i, sector_b uf) != CRC_BAD)
{ ok++; /* CRC unavailable or good */ { ok++; /* CRC unavailable or good */
if(rc->readMap) if(rc->readMap)
SetBit(rc->readMap, rc->readPos+i); SetBit(rc->readMap, rc->readPos+i);
} }
} }
} }
} }
skipping to change at line 1011 skipping to change at line 1055
g_mutex_lock(rc->mutex); g_mutex_lock(rc->mutex);
if(rc->workerError) /* something went wrong in the worker thread */ if(rc->workerError) /* something went wrong in the worker thread */
{ g_mutex_unlock(rc->mutex); { g_mutex_unlock(rc->mutex);
Stop(rc->workerError); Stop(rc->workerError);
} }
while(rc->bufState[rc->readPtr] != BUF_EMPTY) while(rc->bufState[rc->readPtr] != BUF_EMPTY)
{ g_cond_wait(rc->canRead, rc->mutex); { g_cond_wait(rc->canRead, rc->mutex);
} }
g_mutex_unlock(rc->mutex); g_mutex_unlock(rc->mutex);
status = ReadSectors(rc->dh, rc->alignedBuf[rc->readPtr]->buf, rc->readPos , nsectors); status = ReadSectors(rc->image->dh, rc->alignedBuf[rc->readPtr]->buf, rc-> readPos, nsectors);
/*** Medium Error (3) and Illegal Request (5) may result from /*** Medium Error (3) and Illegal Request (5) may result from
a medium read problem, but other errors are regarded as fatal. */ a medium read problem, but other errors are regarded as fatal. */
if(status && !Closure->ignoreFatalSense if(status && !Closure->ignoreFatalSense
&& rc->dh->sense.sense_key && rc->dh->sense.sense_key != 3 && rc->dh->se nse.sense_key != 5) && rc->image->dh->sense.sense_key && rc->image->dh->sense.sense_key != 3 && rc->image->dh->sense.sense_key != 5)
{ int answer; { int answer;
if(!Closure->guiMode) if(!Closure->guiMode)
Stop(_("Sector %lld: %s\nCan not recover from above error.\n" Stop(_("Sector %lld: %s\nCan not recover from above error.\n"
"Use the --ignore-fatal-sense option to override."), "Use the --ignore-fatal-sense option to override."),
rc->readPos, GetLastSenseString(FALSE)); rc->readPos, GetLastSenseString(FALSE));
answer = ModalDialog(GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, insert_butt ons, answer = ModalDialog(GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, insert_butt ons,
_("Sector %lld: %s\n\n" _("Sector %lld: %s\n\n"
"It may not be possible to recover from this erro r.\n" "It may not be possible to recover from this erro r.\n"
skipping to change at line 1045 skipping to change at line 1089
{ SwitchAndSetFootline(Closure->readLinearNotebook, 1, Closure->readLin earFootline, { SwitchAndSetFootline(Closure->readLinearNotebook, 1, Closure->readLin earFootline,
_("<span %s>Aborted by user request!</span> %lld sectors read, %lld sectors unreadable/skipped so far."), _("<span %s>Aborted by user request!</span> %lld sectors read, %lld sectors unreadable/skipped so far."),
Closure->redMarkup, rc->readOK,Closure->readErro rs); Closure->redMarkup, rc->readOK,Closure->readErro rs);
rc->unreportedError = FALSE; /* suppress respective error message */ rc->unreportedError = FALSE; /* suppress respective error message */
goto terminate; goto terminate;
} }
} }
/*** Evaluate C2 scan results */ /*** Evaluate C2 scan results */
if(rc->dh->canC2Scan) if(rc->image->dh->canC2Scan)
{ int i; { int i;
for(i=0; i<nsectors; i++) for(i=0; i<nsectors; i++)
{ if(rc->dh->c2[i]) { if(rc->image->dh->c2[i])
{ if(!status) /* Do not print C2 and error messages together */ { if(!status) /* Do not print C2 and error messages together */
PrintCLI(_("Sector %lld: %3d C2 errors.%s\n"), PrintCLI(_("Sector %lld: %3d C2 errors.%s\n"),
rc->readPos+i, rc->dh->c2[i], " "); rc->readPos+i, rc->image->dh->c2[i], " ") ;
if(rc->dh->c2[i] > rc->maxC2) /* remember highest value */ if(rc->image->dh->c2[i] > rc->maxC2) /* remember highest value */
rc->maxC2 = rc->dh->c2[i]; rc->maxC2 = rc->image->dh->c2[i];
} }
} }
} }
/*** Warn the user if we see dead sector markers on the image. */ /*** Warn the user if we see dead sector markers on the image. */
if(!status) if(!status)
{ for(i=0; i<nsectors; i++) { for(i=0; i<nsectors; i++)
{ unsigned char *sector_buf = rc->alignedBuf[rc->readPtr]->buf; { unsigned char *sector_buf = rc->alignedBuf[rc->readPtr]->buf;
int err; int err;
/* Note: providing the fingerprint is not necessary as any /* Note: providing the fingerprint is not necessary as any
incoming missing sector marker indicates a huge problem. */ incoming missing sector marker indicates a huge problem. */
err = CheckForMissingSector(sector_buf+i*2048, rc->readPos+i, NULL, 0 err = CheckForMissingSector(sector_buf+i*2048, rc->readPos+i,
); rc->image->fpState == 2 ? rc->image->imag
eFP : NULL,
rc->image->fpSector);
if(err != SECTOR_PRESENT) if(err != SECTOR_PRESENT)
ExplainMissingSector(sector_buf+i*2048, rc->readPos+i, err, FALSE) ; ExplainMissingSector(sector_buf+i*2048, rc->readPos+i, err, SOURCE_ MEDIUM, &unrecoverable_sectors);
} }
} }
/*** Pass sector(s) to the worker thread (if reading succeeded) */ /*** Pass sector(s) to the worker thread (if reading succeeded) */
if(!status) if(!status)
{ gint64 sidx; { gint64 sidx;
/* Mark the sectors as read (preliminary). /* Mark the sectors as read (preliminary).
The worker thread may later reset the bit if it finds The worker thread may later reset the bit if it finds
skipping to change at line 1125 skipping to change at line 1171
if(Closure->crcCache) if(Closure->crcCache)
rc->doMD5sums = FALSE; rc->doMD5sums = FALSE;
/* Determine number of sectors to skip forward. /* Determine number of sectors to skip forward.
Make sure not to skip past the media end Make sure not to skip past the media end
and to land at a multiple of dh->clusterSize. */ and to land at a multiple of dh->clusterSize. */
if(nsectors>=Closure->sectorSkip) nfill = nsectors; if(nsectors>=Closure->sectorSkip) nfill = nsectors;
else else
{ int skip = rc->dh->clusterSize > Closure->sectorSkip ? rc->dh->cluste rSize : Closure->sectorSkip; { int skip = rc->image->dh->clusterSize > Closure->sectorSkip ? rc->ima ge->dh->clusterSize : Closure->sectorSkip;
if(rc->readPos+skip > rc->lastSector) nfill = rc->lastSector-rc->read Pos+1; if(rc->readPos+skip > rc->lastSector) nfill = rc->lastSector-rc->read Pos+1;
else nfill = skip - ((rc->readPos + skip) & cluster_mask); else nfill = skip - ((rc->readPos + skip) & cluster_mask);
} }
/* If we are reading past the read marker we must take care /* If we are reading past the read marker we must take care
to fill up any holes with dead sector markers before skipping forwar d. to fill up any holes with dead sector markers before skipping forwar d.
When sectorSkip is 0 and nsectors > 1, we will re-read all these sect ors When sectorSkip is 0 and nsectors > 1, we will re-read all these sect ors
again one by one, so we catch this case in order not to write the mar kers twice. */ again one by one, so we catch this case in order not to write the mar kers twice. */
if(!rc->scanMode && rc->readPos+nfill > rc->readMarker if(!rc->scanMode && rc->readPos+nfill > rc->readMarker
skipping to change at line 1155 skipping to change at line 1201
if(rc->workerError) /* something went wrong in the worker th read */ if(rc->workerError) /* something went wrong in the worker th read */
{ g_mutex_unlock(rc->mutex); { g_mutex_unlock(rc->mutex);
Stop(rc->workerError); Stop(rc->workerError);
} }
while(rc->bufState[rc->readPtr] != BUF_EMPTY) while(rc->bufState[rc->readPtr] != BUF_EMPTY)
{ g_cond_wait(rc->canRead, rc->mutex); { g_cond_wait(rc->canRead, rc->mutex);
} }
CreateMissingSector(rc->alignedBuf[rc->readPtr]->buf, rc->readPos+ i, CreateMissingSector(rc->alignedBuf[rc->readPtr]->buf, rc->readPos+ i,
rc->fingerprint, FINGERPRINT_SECTOR, rc->volum eLabel); rc->image->imageFP, FINGERPRINT_SECTOR, rc->vo lumeLabel);
rc->bufferedSector[rc->readPtr] = rc->readPos+i; rc->bufferedSector[rc->readPtr] = rc->readPos+i;
rc->nSectors[rc->readPtr] = 1; rc->nSectors[rc->readPtr] = 1;
rc->bufState[rc->readPtr] = BUF_DEAD; rc->bufState[rc->readPtr] = BUF_DEAD;
rc->readPtr++; rc->readPtr++;
if(rc->readPtr >= READ_BUFFERS) if(rc->readPtr >= READ_BUFFERS)
rc->readPtr = 0; rc->readPtr = 0;
g_cond_signal(rc->canWrite); g_cond_signal(rc->canWrite);
g_mutex_unlock(rc->mutex); g_mutex_unlock(rc->mutex);
} }
skipping to change at line 1209 skipping to change at line 1255
else else
{ {
if(nsectors > 1) if(nsectors > 1)
{ nsectors = 1; { nsectors = 1;
goto reread; goto reread;
} }
else else
{ PrintCLIorLabel(Closure->status, { PrintCLIorLabel(Closure->status,
_("Sector %lld: %s\n"), _("Sector %lld: %s\n"),
rc->readPos, GetLastSenseString(FALSE)); rc->readPos, GetLastSenseString(FALSE));
if(rc->readPos >= rc->sectors - 2) tao_tail++; if(rc->readPos >= rc->image->dh->sectors - 2) tao_tail++;
Closure->readErrors++; Closure->readErrors++;
} }
} }
} }
/*** Step the progress counter */ /*** Step the progress counter */
step_counter: step_counter:
rc->readPos += nsectors; /* advance the reading position */ rc->readPos += nsectors; /* advance the reading position */
skipping to change at line 1233 skipping to change at line 1279
/*** If multiple reading passes are allowed, see if we need another pass. /*** If multiple reading passes are allowed, see if we need another pass.
Note: Checksum errors do not trigger another pass as only sectors Note: Checksum errors do not trigger another pass as only sectors
marked dead are tried on a re-read. This does not hurt as being able marked dead are tried on a re-read. This does not hurt as being able
to checksum means we have ecc data - we can fix the image using ecc to checksum means we have ecc data - we can fix the image using ecc
rather than by re-reading it. */ rather than by re-reading it. */
if(Closure->guiMode) if(Closure->guiMode)
ChangeSpiralCursor(Closure->readLinearSpiral, -1); /* switch cursor off */ ChangeSpiralCursor(Closure->readLinearSpiral, -1); /* switch cursor off */
rc->pass++; rc->pass++;
rc->image->dh->pass = rc->pass;
if( !rc->scanMode if( !rc->scanMode
&& (Closure->readErrors || Closure->crcErrors) && (Closure->readErrors || Closure->crcErrors)
&& rc->pass < Closure->readingPasses) && rc->pass < Closure->readingPasses)
{ if(Closure->guiMode) { int renderers_left = TRUE;
if(Closure->guiMode)
SetLabelText(GTK_LABEL(Closure->readLinearHeadline), SetLabelText(GTK_LABEL(Closure->readLinearHeadline),
_("<big>Trying to complete image, reading pass %d of %d.< /big>\n%s"), _("<big>Trying to complete image, reading pass %d of %d.< /big>\n%s"),
rc->pass+1, Closure->readingPasses, rc->dh->mediumDescr); rc->pass+1, Closure->readingPasses, rc->image->dh->medium Descr);
else PrintCLI(_("\nTrying to complete image, reading pass %d of %d.\n"), else PrintCLI(_("\nTrying to complete image, reading pass %d of %d.\n"),
rc->pass+1, Closure->readingPasses); rc->pass+1, Closure->readingPasses);
goto next_reading_pass; /* Make sure that all spiral renderers have finished before resetting
and starting the next iteration. */
while(renderers_left)
{ g_mutex_lock(rc->rendererMutex);
if(rc->activeRenderers<=0)
renderers_left=FALSE;
g_mutex_unlock(rc->rendererMutex);
g_usleep(G_USEC_PER_SEC/10);
}
goto next_reading_pass;
} }
/*** Signal EOF to writer thread; wait for it to finish */ /*** Signal EOF to writer thread; wait for it to finish */
send_eof(rc); send_eof(rc);
g_thread_join(rc->worker); g_thread_join(rc->worker);
rc->worker = NULL; rc->worker = NULL;
/*** Finalize on-the-fly checksum calculation */ /*** Finalize on-the-fly checksum calculation */
if(rc->doMD5sums) if(rc->doMD5sums && rc->eccMethod)
MD5Final(Closure->md5Cache, &rc->md5ctxt); md5_failure = rc->eccMethod->finalizeCksums(rc->image);
else ClearCrcCache(); /* deferred until here to avoid race condition */ else ClearCrcCache(); /* deferred until here to avoid race condition */
if(rc->doMD5sums && Closure->eccType == ECC_RS02) Verbose("CRC %s.\n", Closure->crcCache ? "cached" : "NOT created");
{ MD5Final(data_md5, &rc->dataCtxt);
MD5Final(crc_md5, &rc->crcCtxt);
MD5Final(meta_md5, &rc->metaCtxt);
if(memcmp(meta_md5, rc->dh->rs02Header->eccSum, 16))
{ md5_failure = g_strdup(_("but wrong ecc md5sum"));
Verbose("BAD ECC md5sum\n");
}
else Verbose("GOOD ECC md5sum\n");
if(memcmp(crc_md5, rc->dh->rs02Header->crcSum, 16))
{ if(md5_failure) g_free(md5_failure);
md5_failure = g_strdup(_("but wrong crc md5sum"));
Verbose("BAD CRC md5sum\n");
}
else Verbose("GOOD CRC md5sum\n");
if(memcmp(data_md5, rc->dh->rs02Header->mediumSum, 16)) /*** Print summary */
{ if(md5_failure) g_free(md5_failure);
md5_failure = g_strdup(_("but wrong data md5sum"));
Verbose("BAD Data md5sum\n");
}
else Verbose("GOOD Data md5sum\n");
}
Verbose("CRC %s.\n", Closure->crcCache ? "cached" : "NOT created."); t = NULL;
Verbose("md5sum %s.\n", rc->doMD5sums ? "cached" : "NOT created.");
/*** Print summary */ /* We were re-reading an incomplete image */
if(rc->rereading) if(rc->rereading)
{ if(!Closure->readErrors) t = g_strdup_printf(_("%lld sectors read. "), rc->readOK); { if(!Closure->readErrors) t = g_strdup_printf(_("%lld sectors read. "), rc->readOK);
else t = g_strdup_printf(_("%lld sectors read; %lld un readable sectors."),rc->readOK,Closure->readErrors); else t = g_strdup_printf(_("%lld sectors read; %lld un readable sectors."),rc->readOK,Closure->readErrors);
} }
else
{ if(!Closure->readErrors && !Closure->crcErrors) /* We were reading the image for the first time */
else /* not rereading */
{ /* Image was fully readable and had no CRC errors (if CRC was available at
all!).
So we had no faulty sectors, but ... */
if(!Closure->readErrors && !Closure->crcErrors)
{ {
switch(Closure->eccType) if(rc->image->eccFile) /* ...maybe wrong image size? */
{ case ECC_RS01: { if(rc->image->dh->sectors != rc->image->expectedSectors)
if(rc->dh->sectors != rc->ei->sectors) t = g_strdup_printf(_("All sectors successfully read, but wrong im
t = g_strdup_printf(_("All sectors successfully read, but wrong age length (%lld sectors difference)"), rc->image->dh->sectors - rc->image->expe
image length (%lld sectors difference)"), rc->dh->sectors - rc->ei->sectors); ctedSectors);
else if( rc->readOK == rc->sectors /* no user limited range */ }
&& rc->pass == 1 /* md5sum invalid after firs /* ...or bad ecc md5 sum (theoretically impossible by n
t pass */ ow)? */
&& memcmp(rc->ei->eh->mediumSum, Closure->md5Cache, 16)) if( rc->readOK == rc->image->dh->sectors /* no user limited range */
t = g_strdup_printf(_("All sectors successfully read, but wrong && rc->pass == 1 /* md5sum invalid after first reading p
image checksum.")); ass */
else t = g_strdup_printf(_("All sectors successfully read. Checksu && md5_failure && !t) /* and we're signalled a mismatch */
ms match.")); { t = g_strdup(_("All sectors successfully read, but wrong ecc md5sum."
break; ));
case ECC_RS02: }
if(!md5_failure)
t = g_strdup_printf(_("All sectors successfully read. Checksu if(!t)
ms match.")); { if(rc->eccMethod) /* we had CRC sums to compare against */
else { if(rc->crcIncomplete)
{ t = g_strdup_printf(_("All sectors successfully read, %s!"), t = g_strdup(_("All sectors successfully read, but incomple
md5_failure); te or damaged checksums."));
g_free(md5_failure); md5_failure=NULL; else t = g_strdup(_("All sectors successfully read. Checksums ma
} tch."));
break; }
default: else t = g_strdup(_("All sectors successfully read."));
t = g_strdup_printf(_("All sectors successfully read."));
break;
} }
} }
else else /* we have unreadable or damaged sectors */
{ if(Closure->readErrors && !Closure->crcErrors) { if(Closure->readErrors && !Closure->crcErrors)
t = g_strdup_printf(_("%lld unreadable sectors."),Closure->readErro rs); t = g_strdup_printf(_("%lld unreadable sectors."),Closure->readErro rs);
else if(!Closure->readErrors && Closure->crcErrors) else if(!Closure->readErrors && Closure->crcErrors)
t = g_strdup_printf(_("%lld CRC errors."),Closure->crcErrors); { if(md5_failure & CRC_MD5_BAD)
else t = g_strdup_printf(_("%lld CRC errors, %lld unreadable sectors."), t = g_strdup_printf(_("%lld CRC errors and a md5sum mismatch in th
Closure->crcErrors, Closure->readErrors); e CRC section."),Closure->crcErrors);
else
t = g_strdup_printf(_("%lld CRC errors."),Closure->crcErrors);
}
else t = g_strdup_printf(_("%lld CRC errors, %lld unreadable sectors."),
Closure->crcErrors, Closure->readErrors);
} }
} }
PrintLog("\n%s\n",t); PrintLog("\n%s\n",t);
if(Closure->guiMode) if(Closure->guiMode)
{ if(rc->scanMode) SwitchAndSetFootline(Closure->readLinearNotebook, 1, Clos ure->readLinearFootline, { if(rc->scanMode) SwitchAndSetFootline(Closure->readLinearNotebook, 1, Clos ure->readLinearFootline,
"%s%s",_("Scanning finished: "),t); "%s%s",_("Scanning finished: "),t);
else SwitchAndSetFootline(Closure->readLinearNotebook, 1, Clos ure->readLinearFootline, else SwitchAndSetFootline(Closure->readLinearNotebook, 1, Clos ure->readLinearFootline,
"%s%s",_("Reading finished: "),t); "%s%s",_("Reading finished: "),t);
} }
g_free(t); if(t) g_free(t);
PrintTimeToLog(rc->readTimer, "for reading/scanning.\n"); if(!Closure->fixedSpeedValues)
PrintTimeToLog(rc->readTimer, "for reading/scanning.\n");
if(rc->dh->mainType == CD && tao_tail && tao_tail == Closure->readErrors && ! Closure->noTruncate) if(rc->image->dh->mainType == CD && tao_tail && tao_tail == Closure->readErro rs && !Closure->noTruncate)
{ int answer; { int answer;
if(Closure->guiMode) if(Closure->guiMode)
answer = ModalWarning(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL, NULL, answer = ModalWarning(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL, NULL,
_("%d sectors missing at the end of the disc.\n" _("%d sectors missing at the end of the disc.\n"
"This is okay if the CD was written in TAO (track at once) mode.\n" "This is okay if the CD was written in TAO (track at once) mode.\n"
"The Image will be truncated accordingly. See the manual for details.\n"), "The Image will be truncated accordingly. See the manual for details.\n"),
tao_tail); tao_tail);
else else
answer = ModalWarning(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL, NULL, answer = ModalWarning(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL, NULL,
_("%d sectors missing at the end of the disc.\n" _("%d sectors missing at the end of the disc.\n"
"This is okay if the CD was written in TAO (track at once) mode.\n" "This is okay if the CD was written in TAO (track at once) mode.\n"
"The Image will be truncated accordingly. See the manual for details.\n" "The Image will be truncated accordingly. See the manual for details.\n"
"Use the --dao option to disable image truncating .\n"), "Use the --dao option to disable image truncating .\n"),
tao_tail); tao_tail);
rc->sectors -= tao_tail;
if(!rc->scanMode && answer) if(!rc->scanMode && answer)
if(!LargeTruncate(rc->writerImage, (gint64)(2048*rc->sectors))) if(!LargeTruncate(rc->writerImage, (gint64)(2048*(rc->image->dh->sectors -tao_tail))))
Stop(_("Could not truncate %s: %s\n"),Closure->imageName,strerror(errno )); Stop(_("Could not truncate %s: %s\n"),Closure->imageName,strerror(errno ));
} }
else if(Closure->readErrors) exitCode = EXIT_FAILURE; else if(Closure->readErrors) exitCode = EXIT_FAILURE;
/*** Eject medium */ /*** Eject medium */
if(Closure->eject && !Closure->readErrors) if(Closure->eject && !Closure->readErrors)
LoadMedium(rc->dh, FALSE); LoadMedium(rc->image->dh, FALSE);
/*** Close and clean up */ /*** Close and clean up */
rc->unreportedError = FALSE; rc->unreportedError = FALSE;
rc->earlyTermination = FALSE; rc->earlyTermination = FALSE;
terminate: terminate:
cleanup((gpointer)rc); cleanup((gpointer)rc);
} }
 End of changes. 105 change blocks. 
295 lines changed or deleted 355 lines changed or added

Home  |  About  |  All  |  Newest  |  Fossies Dox  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTPS