/* dvdisaster: Additional error correction for optical media. * Copyright (C) 2004-2015 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 . */ #include "dvdisaster.h" #include "rs02-includes.h" #include "rs03-includes.h" #include "udf.h" #include /*** *** Debugging functions. ***/ /* * Debugging function to seed the image with random correctable errors */ /* RS01-style files */ static void random_error1(Image *image, char *arg) { EccHeader *eh; gint64 block_idx[255]; gint64 s,si; int block_sel[255]; int i,percent,last_percent = 0; int n_data,n_errors; double eras_scale, blk_scale; SRandom(Closure->randomSeed); eh = image->eccFileHeader; n_errors = atoi(arg); if(n_errors < 1|| n_errors > eh->eccBytes) Stop(_("Number of erasures must be > 0 and <= %d\n"), eh->eccBytes); n_data = 255-eh->eccBytes; eras_scale = (n_errors+1)/((double)MY_RAND_MAX+1.0); blk_scale = (double)n_data/((double)MY_RAND_MAX+1.0); /*** Setup block pointers */ s = (image->sectorSize+n_data-1)/n_data; for(si=0, i=0; ieccBytes, n_errors); /*** Randomly delete the blocks */ for(si=0; sisectorSize) { if(!LargeSeek(image->file, (gint64)(2048*block_idx[i]))) Stop(_("Failed seeking to sector %lld in image: %s"),block_idx[i],strerror(errno)); CreateMissingSector(missing, block_idx[i], image->imageFP, FINGERPRINT_SECTOR, NULL); if(block_idx[i] == image->sectorSize - 1 && image->inLast < 2048) write_size = image->inLast; if(LargeWrite(image->file, missing, write_size) != write_size) Stop(_("Failed writing to sector %lld in image: %s"),block_idx[i],strerror(errno)); } block_idx[i]++; } percent = (100*si)/s; if(last_percent != percent) { PrintProgress(_("Progress: %3d%%"),percent); last_percent = percent; } } PrintProgress(_("Progress: 100%%\n" "Recover the image using the --fix option before doing another --random-errors run.\n" "Otherwise you'll accumulate >= %d erasures/ECC block and the image will be lost.\n"), n_errors); } /* RS02 ecc images */ static void random_error2(Image *image, char *arg) { EccHeader *eh = image->eccHeader; RS02Layout *lay; gint64 si; guint64 hpos; guint64 end; guint64 header[42]; int block_sel[255]; int i,percent,last_percent = 0; int hidx,n_errors,erase_max = 0; double eras_scale, blk_scale, hdr_scale; SRandom(Closure->randomSeed); lay = RS02LayoutFromImage(image); n_errors = atoi(arg); if(n_errors < 0) { erase_max = 1; n_errors = -n_errors; } if(n_errors <= 0 || n_errors > eh->eccBytes) Stop(_("Number of erasures must be > 0 and <= %d\n"), eh->eccBytes); eras_scale = (n_errors+1)/((double)MY_RAND_MAX+1.0); blk_scale = (double)255.0/((double)MY_RAND_MAX+1.0); PrintLog(_("\nGenerating random correctable erasures (%s; for %d roots, max erasures = %d).\n"), "RS02", eh->eccBytes, n_errors); /*** Randomly delete some ecc headers */ header[0] = lay->firstEccHeader; hidx = 1; hpos = (lay->protectedSectors + lay->headerModulo - 1) / lay->headerModulo; hpos *= lay->headerModulo; end = lay->eccSectors+lay->dataSectors-2; while(hpos < end) /* Calculate positions of all headers */ { header[hidx++] = hpos; hpos += lay->headerModulo; } /* Pick one header to remain intact. Currently this must be one of the repeated headers */ hdr_scale = (double)(hidx-1)/((double)MY_RAND_MAX+1.0); header[(int)(hdr_scale*(double)Random())+1] = 0; for(i=0; i0) { unsigned char missing[2048]; if(!LargeSeek(image->file, (gint64)(2048*s))) Stop(_("Failed seeking to sector %lld in image: %s"), s, strerror(errno)); CreateMissingSector(missing, s, image->imageFP, image->fpSector, NULL); if(LargeWrite(image->file, missing, 2048) != 2048) Stop(_("Failed writing to sector %lld in image: %s"), s, strerror(errno)); } } /*** Randomly delete the blocks */ for(si=0; sisectorsPerLayer; si++) { int n_erasures = (int)(eras_scale*(double)Random()); if(erase_max) n_erasures = n_errors; /* Reset the block selector */ for(i=0; i<255; i++) block_sel[i] = 0; /* Randomly pick n blocks */ for(i=0; idataBytes) { s = si + i * lay->sectorsPerLayer; if(s >= lay->protectedSectors) /* exclude the padding area */ continue; /* respective sectors do not exist */ } else s = RS02EccSectorIndex(lay, i-eh->dataBytes, si); if(!LargeSeek(image->file, (gint64)(2048*s))) Stop(_("Failed seeking to sector %lld in image: %s"), s, strerror(errno)); CreateMissingSector(missing, s, image->imageFP, image->fpSector, NULL); if(LargeWrite(image->file, missing, 2048) != 2048) Stop(_("Failed writing to sector %lld in image: %s"), s, strerror(errno)); } } percent = (100*si)/lay->sectorsPerLayer; if(last_percent != percent) { PrintProgress(_("Progress: %3d%%"),percent); last_percent = percent; } } PrintProgress(_("Progress: 100%%\n" "Recover the image using the --fix option before doing another --random-errors run.\n" "Otherwise you'll accumulate >= %d erasures/ECC block and the image will be lost.\n"), n_errors); g_free(lay); } /* RS03 ecc images */ static void random_error3(Image *image, char *arg) { EccHeader *eh; RS03Layout *lay; gint64 si; int block_sel[255]; int i,percent,last_percent = 0; int n_errors,erase_max = 0; double eras_scale, blk_scale; SRandom(Closure->randomSeed); /*** Calculate the layout */ if(image->eccFileState == ECCFILE_PRESENT) { eh = image->eccFileHeader; lay = CalcRS03Layout(image, ECC_FILE); } else { eh = image->eccHeader; lay = CalcRS03Layout(image, ECC_IMAGE); } n_errors = atoi(arg); if(n_errors < 0) { erase_max = 1; n_errors = -n_errors; } if(n_errors <= 0 || n_errors > eh->eccBytes) Stop(_("Number of erasures must be > 0 and <= %d\n"), eh->eccBytes); eras_scale = (n_errors+1)/((double)MY_RAND_MAX+1.0); blk_scale = (double)255.0/((double)MY_RAND_MAX+1.0); if(lay->target == ECC_FILE) PrintLog(_("\nRS03 error correction file with %d roots.\n"), eh->eccBytes); else PrintLog(_("\nRS03 augmented image with %d roots.\n"), eh->eccBytes); PrintLog(_("Generating at most %d random correctable erasures.\n"), n_errors); /*** Randomly delete the blocks */ for(si=0; sisectorsPerLayer; si++) { int n_erasures = (int)(eras_scale*(double)Random()); if(erase_max) n_erasures = n_errors; /* Reset the block selector */ for(i=0; i<255; i++) block_sel[i] = 0; /* Randomly pick n blocks */ for(i=0; ieccHeaderPos || s == lay->eccHeaderPos+1) continue; /* FIXME: not implemented */ /* Do not write out the virtual padding sectors in ecc file case */ if(lay->target == ECC_FILE && i<=lay->ndata-1 && s>=lay->dataSectors) continue; if(lay->target == ECC_FILE && i>=lay->ndata-1) { file = image->eccFile; if(i == lay->ndata-1) file_s = lay->firstCrcPos + si; else file_s = lay->firstEccPos + (i-lay->ndata)*lay->sectorsPerLayer + si; } else { file = image->file; file_s = s; } if(!LargeSeek(file, (gint64)(2048*file_s))) // FIXME: wrong for ecc files Stop(_("Failed seeking to sector %lld in image: %s"), s, strerror(errno)); CreateMissingSector(missing, s, image->imageFP, image->fpSector, NULL); if(LargeWrite(file, missing, 2048) != 2048) Stop(_("Failed writing to sector %lld in image: %s"), s, strerror(errno)); } } percent = (100*si)/lay->sectorsPerLayer; if(last_percent != percent) { PrintProgress(_("Progress: %3d%%"),percent); last_percent = percent; } } PrintProgress(_("Progress: 100%%\n" "Recover the image using the --fix option before doing another --random-errors run.\n" "Otherwise you'll accumulate >= %d erasures/ECC block and the image will be lost.\n"), n_errors); g_free(lay); } void RandomError(char *arg) { Image *image; Method *method = NULL; char buf[5]; image = OpenImageFromFile(Closure->imageName, O_RDWR, IMG_PERMS); image = OpenEccFileForImage(image, Closure->eccName, O_RDWR, IMG_PERMS); ReportImageEccInconsistencies(image); /* Determine method. Ecc files win over augmented ecc. */ if(image && image->eccFileMethod) method = image->eccFileMethod; else if(image && image->eccMethod) method = image->eccMethod; else Stop("Internal error: No ecc method identified."); if(!strncmp(method->name, "RS01", 4)) { random_error1(image, arg); CloseImage(image); return; } if(!strncmp(method->name, "RS02", 4)) { random_error2(image, arg); CloseImage(image); return; } if(!strncmp(method->name, "RS03", 4)) { random_error3(image, arg); CloseImage(image); return; } CloseImage(image); strncpy(buf, method->name, 4); buf[4] = 0; Stop("Don't know how to handle codec %s\n", buf); } /* * Debugging function to simulate images with single * byte errors (except for faulty cabling and/or controllers, * this should never happen) */ void Byteset(char *arg) { Image *image; gint64 s; int i,byte; char *cpos = NULL; unsigned char buf[1]; /*** Open the image file */ image = OpenImageFromFile(Closure->imageName, O_RDWR, IMG_PERMS); if(!image) Stop(_("Can't open %s:\n%s"), Closure->imageName, strerror(errno)); /*** See which byte to set */ cpos = strchr(arg,','); if(!cpos) Stop(_("2nd argument is missing")); *cpos = 0; s = atoi(arg); arg = cpos+1; cpos = strchr(arg,','); if(!cpos) Stop(_("3rd argument is missing")); *cpos = 0; i = atoi(arg); byte = atoi(cpos+1); if(s<0 || s>=image->sectorSize) Stop(_("Sector must be in range [0..%lld]\n"),image->sectorSize-1); if(i<0 || i>=2048) Stop(_("Byte position must be in range [0..2047]")); if(byte<0 || byte>=256) Stop(_("Byte value must be in range [0..255]")); PrintLog(_("Setting byte %d in sector %lld to value %d.\n"), i, s, byte); /*** Set the byte */ s = 2048*s + i; if(!LargeSeek(image->file, (gint64)s)) Stop(_("Failed seeking to start of image: %s\n"),strerror(errno)); buf[0] = byte; if(LargeWrite(image->file, buf, 1) != 1) Stop(_("Could not write the new byte value")); CloseImage(image); } /* * Debugging function to simulate medium with unreadable sectors */ void Erase(char *arg) { Image *image; gint64 start,end,s; char *dashpos = NULL; char *colonpos = NULL; char *simulation_hint = NULL; /*** Open the image file */ image = OpenImageFromFile(Closure->imageName, O_RDWR, IMG_PERMS); if(!image) Stop(_("Can't open %s:\n%s"), Closure->imageName, strerror(errno)); ExamineUDF(image); /* get the volume label */ /** See if there is a special debugging option following the sector range. This is intentionally an undocumented feature. */ colonpos = strchr(arg,':'); if(colonpos) { *colonpos = 0; simulation_hint=colonpos+1; } /*** See which sectors to erase */ dashpos = strchr(arg,'-'); if(dashpos) { *dashpos = 0; start = atoi(arg); end = atoi(dashpos+1); } else start = end = atoi(arg); if(start>end || start < 0 || end >= image->sectorSize) Stop(_("Sectors must be in range [0..%lld].\n"),image->sectorSize-1); PrintLog(_("Erasing sectors [%lld,%lld]\n"),start,end); /*** Erase them. */ if(!LargeSeek(image->file, (gint64)(2048*start))) Stop(_("Failed seeking to start of image: %s\n"),strerror(errno)); for(s=start; s<=end; s++) { unsigned char missing[2048]; int m = (end == image->sectorSize-1) ? image->inLast : 2048; int n; CreateDebuggingSector(missing, s, image->imageFP, FINGERPRINT_SECTOR, image->isoInfo ? image->isoInfo->volumeLabel : NULL, simulation_hint); n = LargeWrite(image->file, missing, m); if(n != m) Stop(_("Failed writing to sector %lld in image: %s"),s,strerror(errno)); } /*** Clean up */ CloseImage(image); } /* * Debugging function for truncating images */ void TruncateImageFile(char *arg) { Image *image; gint64 end; /*** Open the image file */ image = OpenImageFromFile(Closure->imageName, O_RDWR, IMG_PERMS); if(!image) Stop(_("Can't open %s:\n%s"), Closure->imageName, strerror(errno)); /*** Determine last sector */ end = atoi(arg); if(end >= image->sectorSize) Stop(_("New length must be in range [0..%lld].\n"),image->sectorSize-1); PrintLog(_("Truncating image to %lld sectors.\n"),end); /*** Truncate it. */ if(!LargeTruncate(image->file, (gint64)(2048*end))) Stop(_("Could not truncate %s: %s\n"),Closure->imageName,strerror(errno)); /*** Clean up */ CloseImage(image); } /* * Debugging function to create an ISO image filled with random numbers */ void RandomImage(char *image_name, char *n_sectors, int mark) { LargeFile *image; IsoHeader *ih; gint64 sectors; gint64 s = 25; /* number of ISO headers */ int percent, last_percent = 0; guint32 size,invert; sectors = atoi(n_sectors); if(sectors < 64) sectors = 64; /*** Open the image file */ LargeUnlink(image_name); if(!(image = LargeOpen(image_name, O_RDWR | O_CREAT, IMG_PERMS))) Stop(_("Can't open %s:\n%s"),image_name,strerror(errno)); /*** Print banner */ PrintLog(_("\nCreating random image with %lld sectors.\n\n" "There is no need for permanently storing this image;\n" "you can always reproduce it by calling\n" "dvdisaster --debug %s %lld --random-seed %d\n\n"), sectors, mark ? "--marked-image" : "--random-image", sectors, Closure->randomSeed); if(Closure->randomSeed >= 0) { SRandom(Closure->randomSeed); invert = 0; } else { SRandom(-Closure->randomSeed); invert = 0xffffffff; } /*** Create and write the ISO file system. Otherwise some writing software will not recognize the image. */ ih = InitIsoHeader(); size = sectors-s; if(size>=2048*1024) size=2048*1024-1; AddFile(ih, "random.data", 2048*size); WriteIsoHeader(ih, image); FreeIsoHeader(ih); /*** Create it */ while(s=0); #else do buf[i--] = SwapBytes32(Random32() ^ invert); while(i>=0); #endif if(mark) /* Mark the sector with its number. */ { int i; for(i=0; i<2048; i+=128) sprintf(((char*)buf)+i, "Sector %8lld", (long long int)s); } n = LargeWrite(image, buf, 2048); s++; if(n != 2048) Stop(_("Failed writing to sector %lld in image: %s"),s,strerror(errno)); percent = (100*s)/sectors; if(last_percent != percent) { PrintProgress(_("Progress: %3d%%"),percent); last_percent = percent; } } /*** Clean up */ if(!LargeClose(image)) Stop(_("Error closing image file:\n%s"), strerror(errno)); } /* * Replaces the "unreadable sector" marker with zeros. */ void ZeroUnreadable(void) { Image *image; unsigned char buf[2048],zeros[2048]; gint64 s,cnt=0; int percent, last_percent = 0; image = OpenImageFromFile(Closure->imageName, O_RDWR, IMG_PERMS); if(!image) Stop(_("Can't open %s:\n%s"), Closure->imageName, strerror(errno)); PrintLog(_("Replacing the \"unreadable sector\" markers with zeros.\n")); memset(zeros, 0, 2048); if(!LargeSeek(image->file, (gint64)0)) Stop(_("Failed seeking to start of image: %s\n"),strerror(errno)); for(s=0; ssectorSize; s++) { int n = LargeRead(image->file, buf, 2048); if(n != 2048) Stop(_("Could not read image sector %lld:\n%s\n"),s,strerror(errno)); /* Replace the dead sector marker */ if(CheckForMissingSector(buf, s, image->imageFP, FINGERPRINT_SECTOR) != SECTOR_PRESENT) { if(!LargeSeek(image->file, (gint64)(2048*s))) Stop(_("Failed seeking to sector %lld in image: %s"),s,strerror(errno)); n = LargeWrite(image->file, zeros, 2048); n=2048; if(n != 2048) Stop(_("Failed writing to sector %lld in image: %s"),s,strerror(errno)); cnt++; } percent = (100*s)/image->sectorSize; if(last_percent != percent) { PrintProgress(_("Progress: %3d%%"),percent); last_percent = percent; } } PrintProgress(_("%lld \"unreadable sector\" markers replaced.\n"), cnt); CloseImage(image); } /** ** Debugging functions to show contents of a given sector **/ /* * produce a hex dump */ void HexDump(unsigned char *buf, int len, int step) { int i,j; for(i=0; i= len) PrintLog((j&0x07) == 0x07 ? " " : " "); else PrintLog("%02x%s", buf[i+j], (j&0x07) == 0x07 ? " " : " "); for(j=0; j= len) break; if((j&0x07) == 0x07) PrintLog("%c ", isprint(buf[i+j]) ? buf[i+j] : '.'); else PrintLog("%c", isprint(buf[i+j]) ? buf[i+j] : '.'); } PrintLog("\n"); } } /* * produce a C #include file */ void CDump(unsigned char *buf, int lba, int len, int step) { int i; g_printf("#define SECTOR_LENGTH %d\n" "#define SECTOR_LBA %d\n" "unsigned char sector_frame[%d] = {\n", len, lba, len); len--; for(i=0; i<=len; i++) { g_printf("%3d%c ", *buf++, i==len ? ' ' : ','); if(i%step == (step-1)) g_printf("\n"); } printf("};\n"); } /* * Show Ecc header from image file */ void ShowHeader(char *arg) { Image *image; gint64 sector; int n; EccHeader *eh = alloca(4096); /*** Open the image file */ image = OpenImageFromFile(Closure->imageName, O_RDONLY, IMG_PERMS); if(!image) Stop(_("Can't open %s:\n%s"), Closure->imageName, strerror(errno)); /*** Determine sector to show */ sector = atoi(arg); if(sector < 0 || sector >= image->sectorSize) Stop(_("Sector must be in range [0..%lld]\n"),image->sectorSize-1); /*** Load it. */ if(!LargeSeek(image->file, (gint64)(2048*sector))) Stop(_("Failed seeking to sector %lld in image: %s"),sector,strerror(errno)); n = LargeRead(image->file, eh, 2048); if(n != 2048) Stop(_("Failed reading sector %lld in image: %s"),sector,strerror(errno)); /*** Clean up */ CloseImage(image); /*** Show it */ PrintEccHeader(eh); } /* * Show sector from image file */ void ShowSector(char *arg) { Image *image; gint64 sector; int n; unsigned char buf[2048]; /*** Open the image file */ image = OpenImageFromFile(Closure->imageName, O_RDONLY, IMG_PERMS); if(!image) Stop(_("Can't open %s:\n%s"), Closure->imageName, strerror(errno)); /*** Determine sector to show */ sector = atoi(arg); if(sector < 0 || sector >= image->sectorSize) Stop(_("Sector must be in range [0..%lld]\n"),image->sectorSize-1); PrintLog(_("Contents of sector %lld:\n\n"),sector); /*** Show it. */ if(!LargeSeek(image->file, (gint64)(2048*sector))) Stop(_("Failed seeking to sector %lld in image: %s"),sector,strerror(errno)); n = LargeRead(image->file, buf, 2048); if(n != 2048) Stop(_("Failed reading sector %lld in image: %s"),sector,strerror(errno)); if(Closure->debugCDump) CDump(buf, sector, 2048, 16); else { HexDump(buf, 2048, 32); g_printf("CRC32 = %04x\n", Crc32(buf, 2048)); } /*** Clean up */ CloseImage(image); } /* * Read sector from drive */ void ReadSector(char *arg) { AlignedBuffer *ab = CreateAlignedBuffer(2048); Image *image; gint64 sector; int status; /*** Open the device */ image = OpenImageFromDevice(Closure->device); if(!image) Stop(_("Can't open %s:\n%s"), Closure->imageName, strerror(errno)); /*** Determine sector to show */ sector = atoi(arg); if(sector < 0 || sector >= image->dh->sectors) { CloseImage(image); FreeAlignedBuffer(ab); Stop(_("Sector must be in range [0..%lld]\n"),image->dh->sectors-1); } PrintLog(_("Contents of sector %lld:\n\n"),sector); /*** Read it. */ status = ReadSectors(image->dh, ab->buf, sector, 1); /*** Print results */ if(status) { CloseImage(image); FreeAlignedBuffer(ab); Stop(_("Failed reading sector %lld: %s"),sector,strerror(errno)); } if(Closure->debugCDump) CDump(ab->buf, sector, 2048, 16); else { HexDump(ab->buf, 2048, 32); g_printf("CRC32 = %04x\n", Crc32(ab->buf, 2048)); } CloseImage(image); FreeAlignedBuffer(ab); } /*** *** Read a raw CD sector ***/ void RawSector(char *arg) { AlignedBuffer *ab = CreateAlignedBuffer(4096); Sense *sense; unsigned char cdb[MAX_CDB_SIZE]; Image *image; gint64 lba; int length=0,status; int offset=16; /*** Open the device */ image = OpenImageFromDevice(Closure->device); if(!image) Stop(_("Can't open %s:\n%s"), Closure->imageName, strerror(errno)); sense = &image->dh->sense; /*** Only CD can be read in raw mode */ if(image->dh->mainType != CD) { CloseImage(image); FreeAlignedBuffer(ab); Stop(_("Raw reading only possible on CD media\n")); } /*** Determine sector to show */ lba = atoi(arg); if(lba < 0 || lba >= image->dh->sectors) { CloseImage(image); FreeAlignedBuffer(ab); Stop(_("Sector must be in range [0..%lld]\n"),image->dh->sectors-1); } PrintLog(_("Contents of sector %lld:\n\n"),lba); /*** Try the raw read */ memset(cdb, 0, MAX_CDB_SIZE); cdb[0] = 0xbe; /* READ CD */ switch(image->dh->subType) /* Expected sector type */ { case DATA1: /* data mode 1 */ cdb[1] = 2<<2; #if 1 cdb[9] = 0xb8; /* we want Sync + Header + User data + EDC/ECC */ length=MAX_RAW_TRANSFER_SIZE; #else cdb[9] = 0xba; /* we want Sync + Header + User data + EDC/ECC + C2 */ length=2646; #endif offset=16; break; case XA21: /* xa mode 2 form 1 */ cdb[1] = 4<<2; cdb[9] = 0xf8; length=MAX_RAW_TRANSFER_SIZE; offset=24; break; } cdb[2] = (lba >> 24) & 0xff; cdb[3] = (lba >> 16) & 0xff; cdb[4] = (lba >> 8) & 0xff; cdb[5] = lba & 0xff; cdb[6] = 0; /* number of sectors to read (3 bytes) */ cdb[7] = 0; cdb[8] = 1; /* read nsectors */ cdb[10] = 0; /* reserved stuff */ cdb[11] = 0; /* no special wishes for the control byte */ CreateMissingSector(ab->buf, lba, NULL, 0, NULL); status = SendPacket(image->dh, cdb, 12, ab->buf, length, sense, DATA_READ); if(status<0) /* Read failed */ { RememberSense(sense->sense_key, sense->asc, sense->ascq); CloseImage(image); FreeAlignedBuffer(ab); Stop("Sector read failed: %s\n", GetLastSenseString(FALSE)); } else { if(Closure->debugCDump) CDump(ab->buf, lba, length, 16); else { HexDump(ab->buf, length, 32); g_printf("CRC32 = %04x\n", Crc32(ab->buf+offset, 2048)); } } FreeAlignedBuffer(ab); } /*** *** Send a CDB to the drive and report what happens *** * Sending ill-formed cdbs may kill your system * and/or damage yout drive permanently. * * Example command line call for sending an inquiry: * * ./dvdisaster --debug --send-cdb 12,00,00,00,24,00:24 * * The first six bytes make up the cdb; cdbs with upto 12 bytes are possible. * The :24 arg is the allocation length. * Note that the allocation length must match those specified in the cdb; * differing values may crash the system. */ enum { SHIFT0, SHIFT4, ALLOC }; void SendCDB(char *cdb_raw) { AlignedBuffer *ab = CreateAlignedBuffer(MAX_CLUSTER_SIZE); int cdb_len = 0; int alloc_len = 0; unsigned char cdb[16]; int mode = SHIFT4; int nibble=0; int status; char *c = cdb_raw; while(*c && cdb_len<16) { if(*c == ',' || *c== ':') { if(*c == ':') mode = ALLOC; c++; continue; } if(*c >= '0' && *c <= '9') nibble = *c - '0'; else if(*c >= 'a' && *c <= 'f') nibble = *c - 'a' + 10; else if(*c >= 'A' && *c <= 'F') nibble = *c - 'A' + 10; else Stop("illegal char '%c' in cdb \"%s\"\n",*c,cdb_raw); switch(mode) { case SHIFT0: cdb[cdb_len] |= nibble; mode = SHIFT4; cdb_len++; break; case SHIFT4: cdb[cdb_len] = nibble << 4; mode = SHIFT0; break; case ALLOC: alloc_len = (alloc_len << 4) | nibble; break; } c++; } PrintLog("\n"); status = SendReadCDB(Closure->device, ab->buf, cdb, cdb_len, alloc_len); if(!status) { g_printf("\nDrive returned:\n\n"); HexDump(ab->buf, alloc_len, 16); } FreeAlignedBuffer(ab); } /*** *** Create a bitmap of simulated defects ***/ Bitmap* SimulateDefects(gint64 size) { Bitmap *bm = CreateBitmap0(size); gint64 defects = (size*(gint64)Closure->simulateDefects)/(gint64)100; SRandom(Closure->randomSeed); /* Create sequences of n sectors until the number of defects is reached. */ while(defects) { double scale, size_scale; int n, bit; scale = (double)defects/((double)MY_RAND_MAX+1.0); if(defects > 32) n = (int)(scale*(double)Random()); else n = defects; size_scale = (double)(size-n)/((double)MY_RAND_MAX+1.0); bit = (int)(size_scale*(double)Random()); while(n--) { if(!GetBit(bm, bit)) { SetBit(bm, bit); defects--; } bit++; } } return bm; } /*** *** Copy a sector between two image files. ***/ void CopySector(char *arg) { LargeFile *from, *to; char *from_path, *to_path; guint64 from_sector, to_sector, sectors; unsigned char buf[2048]; char *cpos = NULL; /*** Evaluate arguments */ cpos = strchr(arg,','); if(!cpos) Stop(_("2nd argument is missing")); *cpos = 0; from_path = arg; arg = cpos+1; cpos = strchr(arg,','); if(!cpos) Stop(_("3rd argument is missing")); *cpos = 0; from_sector = atoll(arg); arg = cpos+1; cpos = strchr(arg,','); if(!cpos) Stop(_("4th argument is missing")); *cpos = 0; to_path = arg; to_sector = atoll(cpos+1); /*** Check the given files */ if(!(from = LargeOpen(from_path, O_RDONLY, IMG_PERMS))) Stop(_("Can't open %s:\n%s"), from_path, strerror(errno)); LargeStat(from_path, §ors); sectors /= 2048; if(from_sector<0 || from_sector>sectors-1) Stop(_("Source sector must be in range [0..%lld]\n"), sectors-1); if(!(to = LargeOpen(to_path, O_WRONLY, IMG_PERMS))) Stop(_("Can't open %s:\n%s"), to_path, strerror(errno)); LargeStat(to_path, §ors); sectors /= 2048; if(to_sector<0 || to_sector>sectors-1) Stop(_("Destination sector must be in range [0..%lld]\n"), sectors-1); /*** Copy the sector */ PrintLog(_("Copying sector %lld from %s to sector %lld in %s.\n"), from_sector, from_path, to_sector, to_path); if(!LargeSeek(from, (gint64)(2048*from_sector))) Stop(_("Failed seeking to sector %lld in image: %s"), from_sector, strerror(errno)); if(LargeRead(from, buf, 2048) != 2048) Stop(_("Failed reading sector %lld in image: %s"), from_sector, strerror(errno)); if(!LargeSeek(to, (gint64)(2048*to_sector))) Stop(_("Failed seeking to sector %lld in image: %s"), to_sector, strerror(errno)); if(LargeWrite(to, buf, 2048) != 2048) Stop(_("Failed writing to sector %lld in image: %s"), to_sector, strerror(errno)); /*** Clean up */ LargeClose(from); LargeClose(to); } /*** *** Compare or merge images ***/ void MergeImages(char *arg, int mode) { LargeFile *left, *right; char *left_path, *right_path; guint64 left_sectors, right_sectors,min_sectors,s; int percent,last_percent = 0; char *cpos = NULL; /*** Evaluate arguments */ cpos = strchr(arg,','); if(!cpos) Stop(_("2nd argument is missing")); *cpos = 0; left_path = arg; right_path = cpos+1; /*** Check the given files */ if(!(left = LargeOpen(left_path, mode ? O_RDWR : O_RDONLY, IMG_PERMS))) Stop(_("Can't open %s:\n%s"), left_path, strerror(errno)); LargeStat(left_path, &left_sectors); left_sectors /= 2048; if(!(right = LargeOpen(right_path, O_RDONLY, IMG_PERMS))) Stop(_("Can't open %s:\n%s"), right_path, strerror(errno)); LargeStat(right_path, &right_sectors); right_sectors /= 2048; /*** Compare/merge the images */ if(!mode) PrintLog("Comparing %s (%lld sectors) with %s (%lld sectors).\n", left_path, left_sectors, right_path, right_sectors); else PrintLog("Merging %s (%lld sectors) with %s (%lld sectors).\n", left_path, left_sectors, right_path, right_sectors); /*** Compare them */ if(left_sectors < right_sectors) min_sectors = left_sectors; else min_sectors = right_sectors; for(s=0; s Sector %lld missing\n", s); } else { PrintLog("! Sector %lld differs in images\n", s); } } percent = (100*s)/left_sectors; if(last_percent != percent) { PrintProgress(_("Progress: %3d%%"),percent); last_percent = percent; } } if(left_sectors > right_sectors) { PrintLog("%lld sectors missing at the end of %s\n", left_sectors-right_sectors, right_path); } if(left_sectors < right_sectors) { if(!mode) PrintLog("%lld sectors missing at the end of %s\n", right_sectors-left_sectors, left_path); else { unsigned char buf[2048]; PrintLog("Transferring %lld sectors from the end of %s to %s.\n", right_sectors-left_sectors, right_path, left_path); for(s=left_sectors; s