diff options
Diffstat (limited to 'mbadblocks.c')
-rw-r--r-- | mbadblocks.c | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/mbadblocks.c b/mbadblocks.c new file mode 100644 index 0000000..df5605f --- /dev/null +++ b/mbadblocks.c @@ -0,0 +1,282 @@ +/* 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 <http://www.gnu.org/licenses/>. + * + * 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; i<in_len; i++) + if(in_buf[i] != buffer[i]) { + bad = 1; + break; + } + } + } + + if(bad) { + printf("Bad cluster %ld found\n", cluster); + fatEncode((Fs_t*)Fs, cluster, badClus); + return 1; + } + return 0; +} + +void mbadblocks(int argc, char **argv, int type UNUSEDP) +{ + unsigned int i; + unsigned int startSector=2; + unsigned int endSector=0; + struct MainParam_t mp; + Fs_t *Fs; + Stream_t *Dir; + int ret; + char *filename = NULL; + char c; + unsigned int badClus; + int sectorMode=0; + int writeMode=0; + + while ((c = getopt(argc, argv, "i:s:cwS:E:")) != EOF) { + switch(c) { + case 'i': + set_cmd_line_image(optarg); + break; + case 'c': + checkListTwice(filename); + filename = strdup(optarg); + break; + case 's': + checkListTwice(filename); + filename = strdup(optarg); + sectorMode = 1; + break; + case 'S': + startSector = atol(optarg); + break; + case 'E': + endSector = atol(optarg); + break; + case 'w': + writeMode = 1; + break; + case 'h': + usage(0); + default: + usage(1); + } + } + + if (argc != optind+1 || + !argv[optind][0] || argv[optind][1] != ':' || argv[optind][2]) { + usage(1); + } + + init_mp(&mp); + + Dir = open_root_dir(argv[optind][0], O_RDWR, NULL); + if (!Dir) { + fprintf(stderr,"%s: Cannot initialize drive\n", argv[0]); + exit(1); + } + + Fs = (Fs_t *)GetFs(Dir); + in_len = Fs->cluster_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); +} |