/* Copyright 1995-1999,2001-2003,2007,2009,2011 Alain Knaff. * This file is part of mtools. * * Mtools 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. * * Mtools 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 Mtools. If not, see . * * mbadblocks.c * Mark bad blocks on disk * */ #include "sysincludes.h" #include "msdos.h" #include "mtools.h" #include "mainloop.h" #include "fsP.h" #define N_PATTERN 311 static void usage(int ret) NORETURN; static void usage(int ret) { fprintf(stderr, "Mtools version %s, dated %s\n", mversion, mdate); fprintf(stderr, "Usage: %s: [-c clusterList] [-s sectorList] [-c] [-V] device\n", progname); exit(ret); } static void checkListTwice(char *filename) { if(filename != NULL) { fprintf(stderr, "Only one of the -c or -s options may be given\n"); exit(1); } } /** * Marks given cluster as bad, but prints error instead if cluster already used */ static void mark(Fs_t *Fs, long offset, unsigned int badClus) { unsigned int old = Fs->fat_decode((Fs_t*)Fs, offset); if(old == 0) { fatEncode((Fs_t*)Fs, offset, badClus); return; } if(old == badClus) { fprintf(stderr, "Cluster %ld already marked\n", offset); } else { fprintf(stderr, "Cluster %ld is busy\n", offset); } } static char *in_buf; static char *pat_buf; static int in_len; static void progress(unsigned int i, unsigned int total) { if(i % 10 == 0) fprintf(stderr, " \r%d/%d\r", i, total); } static int scan(Fs_t *Fs, Stream_t *dev, long cluster, unsigned int badClus, char *buffer, int doWrite) { off_t start; off_t ret; off_t pos; int bad=0; if(Fs->fat_decode((Fs_t*)Fs, cluster)) /* cluster busy, or already marked */ return 0; start = (cluster - 2) * Fs->cluster_size + Fs->clus_start; pos = sectorsToBytes((Stream_t*)Fs, start); if(doWrite) { ret = force_write(dev, buffer, pos, in_len); if(ret < in_len ) bad = 1; } else { ret = force_read(dev, in_buf, pos, in_len); if(ret < in_len ) bad = 1; else if(buffer) { int i; for(i=0; icluster_size * Fs->sector_size; in_buf = malloc(in_len); if(!in_buf) { printOom(); ret = 1; goto exit_0; } if(writeMode) { pat_buf=malloc(in_len * N_PATTERN); if(!pat_buf) { printOom(); ret = 1; goto exit_0; } srandom(time(NULL)); for(i=0; i < in_len * N_PATTERN; i++) { pat_buf[i] = random(); } } for(i=0; i < Fs->clus_start; i++ ){ ret = READS(Fs->Next, in_buf, sectorsToBytes((Stream_t*)Fs, i), Fs->sector_size); if( ret < 0 ){ perror("early error"); goto exit_0; } if(ret < (signed int) Fs->sector_size){ fprintf(stderr,"end of file in file_read\n"); ret = 1; goto exit_0; } } ret = 0; badClus = Fs->last_fat + 1; if(startSector < 2) startSector = 2; if(endSector > Fs->num_clus + 2 || endSector <= 0) endSector = Fs->num_clus + 2; if(filename) { char line[80]; FILE *f = fopen(filename, "r"); if(f == NULL) { fprintf(stderr, "Could not open %s (%s)\n", filename, strerror(errno)); ret = 1; goto exit_0; } while(fgets(line, sizeof(line), f)) { char *ptr = line + strspn(line, " \t"); long offset = strtoul(ptr, 0, 0); if(sectorMode) offset = (offset-Fs->clus_start)/Fs->cluster_size + 2; if(offset < 2) { fprintf(stderr, "Sector before start\n"); } else if(offset >= Fs->num_clus) { fprintf(stderr, "Sector beyond end\n"); } else { mark(Fs, offset, badClus); ret = 1; } } } else { Stream_t *dev; dev = Fs->Next; if(dev->Next) dev = dev->Next; in_len = Fs->cluster_size * Fs->sector_size; if(writeMode) { /* Write pattern */ for(i=startSector; i< endSector; i++){ if(got_signal) break; progress(i, Fs->num_clus); ret |= scan(Fs, dev, i, badClus, pat_buf + in_len * (i % N_PATTERN), 1); } /* Flush cache, so that we are sure we read the data back from disk, rather than from the cache */ if(!got_signal) DISCARD(dev); /* Read data back, and compare to pattern */ for(i=startSector; i< endSector; i++){ if(got_signal) break; progress(i, Fs->num_clus); ret |= scan(Fs, dev, i, badClus, pat_buf + in_len * (i % N_PATTERN), 0); } } else { for(i=startSector; i< endSector; i++){ if(got_signal) break; progress(i, Fs->num_clus); ret |= scan(Fs, dev, i, badClus, NULL, 0); } } } exit_0: FREE(&Dir); exit(ret); }