/* dvdisaster: Additional error correction for optical media. * Copyright (C) 2004-2009 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 * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA, * or direct your browser at http://www.gnu.org. */ #include "dvdisaster.h" /* * Write the raw dump header */ static void init_defective_sector_file(char *path, RawBuffer *rb, LargeFile **file, DefectiveSectorHeader *dsh) { int n; *file = LargeOpen(path, O_RDWR | O_CREAT, IMG_PERMS); if(!*file) Stop(_("Could not open %s: %s"), path, strerror(errno)); memset(dsh, 0, sizeof(DefectiveSectorHeader)); dsh->lba = rb->lba; dsh->sectorSize = CD_RAW_DUMP_SIZE; if(rb->xaMode) dsh->properties |= DSH_XA_MODE; if(rb->validFP) { memcpy(dsh->mediumFP, rb->mediumFP, 16); dsh->properties |= DSH_HAS_FINGERPRINT; } #ifdef HAVE_BIG_ENDIAN SwapDefectiveHeaderBytes(dsh); #endif n = LargeWrite(*file, dsh, sizeof(DefectiveSectorHeader)); #ifdef HAVE_BIG_ENDIAN SwapDefectiveHeaderBytes(dsh); #endif if(n != sizeof(DefectiveSectorHeader)) Stop(_("Failed writing to defective sector file: %s"), strerror(errno)); } /* * Open raw dump, read the header */ static void open_defective_sector_file(RawBuffer *rb, char *path, LargeFile **file, DefectiveSectorHeader *dsh) { gint64 length; int n; *file = LargeOpen(path, O_RDWR, IMG_PERMS); if(!*file) return; LargeStat(path, &length); n = LargeRead(*file, dsh, sizeof(DefectiveSectorHeader)); if(n != sizeof(DefectiveSectorHeader)) Stop(_("Failed reading from defective sector file: %s"), strerror(errno)); #ifdef HAVE_BIG_ENDIAN SwapDefectiveHeaderBytes(dsh); #endif dsh->nSectors = (length-sizeof(DefectiveSectorHeader))/dsh->sectorSize; if(dsh->nSectors*dsh->sectorSize+sizeof(DefectiveSectorHeader) != length) Stop(_("Defective sector file is truncated")); /* Expand the old non-C2 raw dumps to new size */ if(dsh->sectorSize == 2352) /* old non C2-style raw dump? */ { unsigned char *buf,*ptr; unsigned char zero[296]; int i,n; PrintCLI(" * Expanding raw dump for sector %lld from 2352 to %d bytes *\n", (long long)dsh->lba, MAX_RAW_TRANSFER_SIZE); buf = g_malloc(dsh->sectorSize*dsh->nSectors); for(i=0, ptr=buf; inSectors; i++, ptr+=2352) { int n=LargeRead(*file, ptr, dsh->sectorSize); if(n != dsh->sectorSize) Stop(_("Failed reading from defective sector file: %s"), strerror(errno)); } memset(zero, 0, 296); dsh->sectorSize = MAX_RAW_TRANSFER_SIZE; if(!LargeSeek(*file, 0)) Stop(_("Failed seeking in defective sector file: %s"), strerror(errno)); #ifdef HAVE_BIG_ENDIAN SwapDefectiveHeaderBytes(dsh); #endif n = LargeWrite(*file, dsh, sizeof(DefectiveSectorHeader)); #ifdef HAVE_BIG_ENDIAN SwapDefectiveHeaderBytes(dsh); #endif if(n != sizeof(DefectiveSectorHeader)) Stop(_("Failed writing to defective sector file: %s"), strerror(errno)); for(i=0, ptr=buf; inSectors; i++, ptr+=2352) { n=LargeWrite(*file, ptr, 2352); if(n != 2352) Stop(_("Failed writing to defective sector file: %s"), strerror(errno)); n=LargeWrite(*file, zero, 296); if(n != 296) Stop(_("Failed writing to defective sector file: %s"), strerror(errno)); } if(!LargeSeek(*file, sizeof(DefectiveSectorHeader))) Stop(_("Failed seeking in defective sector file: %s"), strerror(errno)); g_free(buf); } /* If the cache file has no fingerprint, add it now */ if(!(dsh->properties & DSH_HAS_FINGERPRINT) && rb->validFP) { memcpy(dsh->mediumFP, rb->mediumFP, 16); dsh->properties |= DSH_HAS_FINGERPRINT; if(!LargeSeek(*file, 0)) Stop(_("Failed seeking in defective sector file: %s"), strerror(errno)); #ifdef HAVE_BIG_ENDIAN SwapDefectiveHeaderBytes(dsh); #endif n = LargeWrite(*file, dsh, sizeof(DefectiveSectorHeader)); #ifdef HAVE_BIG_ENDIAN SwapDefectiveHeaderBytes(dsh); #endif if(n != sizeof(DefectiveSectorHeader)) Stop(_("Failed writing to defective sector file: %s"), strerror(errno)); } /* Verify cache and medium fingerprint */ if((dsh->properties & DSH_HAS_FINGERPRINT) && rb->validFP) { if(memcmp(dsh->mediumFP, rb->mediumFP, 16)) Stop(_("Fingerprints of medium and defective sector cache do not match!")); } } /* * Append RawBuffer contents to defective sector dump */ int SaveDefectiveSector(RawBuffer *rb, int can_c2_scan) { LargeFile *file; DefectiveSectorHeader *dsh = alloca(sizeof(DefectiveSectorHeader)); unsigned char *cache_sectors = NULL; char *filename; gint64 length,offset; int count=0; int i,j,idx; if(!rb->samplesRead) return 0; /* Nothing to be done */ /* Open cache file */ filename = g_strdup_printf("%s/%s%lld.raw", Closure->dDumpDir, Closure->dDumpPrefix, (long long)rb->lba); if(!LargeStat(filename, &length)) { PrintCLIorLabel(Closure->status,_(" [Creating new cache file %s]\n"), filename); init_defective_sector_file(filename, rb, &file, dsh); } else { open_defective_sector_file(rb, filename, &file, dsh); if(!file) Stop(_("Could not open %s: %s"), filename, strerror(errno)); } /* Read already cached sectors */ if(dsh->nSectors > 0) { if(!LargeSeek(file, sizeof(DefectiveSectorHeader))) Stop(_("Failed seeking in defective sector file: %s"), strerror(errno)); cache_sectors = g_malloc(dsh->sectorSize*dsh->nSectors); for(i=0, idx=0; inSectors; i++, idx+=dsh->sectorSize) { int n=LargeRead(file, cache_sectors+idx, dsh->sectorSize); if(n != dsh->sectorSize) Stop(_("Failed reading from defective sector file: %s"), strerror(errno)); } } /* Store sectors which are not already cached */ offset = sizeof(DefectiveSectorHeader) + dsh->sectorSize*dsh->nSectors; if(!LargeSeek(file, offset)) Stop(_("Failed seeking in defective sector file: %s"), strerror(errno)); for(i=0; isamplesRead; i++) { int new_sector = TRUE; /* See comment below on C2 mask field to understand rb->sampleSize-1 */ if(cache_sectors) /* Sector already in cache? */ { for(j=0, idx=0; jnSectors; j++, idx+=dsh->sectorSize) { if(!memcmp(rb->rawBuf[i], cache_sectors+idx, rb->sampleSize-1)) { new_sector = FALSE; break; } } } for(j=0; jrawBuf[i], rb->rawBuf[j], rb->sampleSize-1)) { new_sector = FALSE; break; } } if(new_sector) /* same sector already in cache */ { int n; /* The C2 mask field is not used; so we put a flag into it to mark raw sectors containing C2 error information. */ if(can_c2_scan) rb->rawBuf[i][CD_RAW_DUMP_SIZE-1] = 1; n=LargeWrite(file, rb->rawBuf[i], dsh->sectorSize); if(n != dsh->sectorSize) Stop(_("Failed writing to defective sector file: %s"), strerror(errno)); count++; } } LargeClose(file); PrintCLIorLabel(Closure->status, _(" [Appended %d/%d sectors to cache file %s; LBA=%lld, ssize=%d, %d sectors]\n"), count, rb->samplesRead, filename, dsh->lba, dsh->sectorSize, dsh->nSectors); g_free(filename); if(cache_sectors) g_free(cache_sectors); return count; } /* * Read sectors from the defective sector dump, * feed them into the raw buffer one by one * and retry recovery. */ int TryDefectiveSectorCache(RawBuffer *rb, unsigned char *outbuf) { DefectiveSectorHeader dsh; LargeFile *file; char *path; int status; int last_sector; int i; path = g_strdup_printf("%s/%s%lld.raw", Closure->dDumpDir, Closure->dDumpPrefix, (long long)rb->lba); open_defective_sector_file(rb, path, &file, &dsh); g_free(path); if(!file) /* No cache file */ return -1; /* skip sectors added in current pass */ last_sector = dsh.nSectors - rb->samplesRead; ReallocRawBuffer(rb, dsh.nSectors); for(i=0; iworkBuf->buf, dsh.sectorSize); if(n != dsh.sectorSize) Stop(_("Failed reading from defective sector file: %s"), strerror(errno)); status = TryCDFrameRecovery(rb, outbuf); if(!status) { PrintCLIorLabel(Closure->status, " [Success after processing cached sector %d]\n", i+1); return status; } } LargeClose(file); return -1; } /* * Read sectors from the defective sector dump */ void ReadDefectiveSectorFile(DefectiveSectorHeader *dsh, RawBuffer *rb, char *path) { LargeFile *file; open_defective_sector_file(rb, path, &file, dsh); if(!file) { Stop(_("Could not open %s: %s"), path, strerror(errno)); return; } rb->lba = dsh->lba; if(dsh->properties & DSH_XA_MODE) rb->dataOffset = 24; else rb->dataOffset = 16; ReallocRawBuffer(rb, dsh->nSectors); for(rb->samplesRead=0; rb->samplesReadnSectors; ) { int n=LargeRead(file, rb->rawBuf[rb->samplesRead], dsh->sectorSize); if(n != dsh->sectorSize) { Stop(_("Failed reading from defective sector file: %s"), strerror(errno)); return; } rb->samplesRead++; UpdateFrameStats(rb); CollectGoodVectors(rb); } LargeClose(file); }