| /* 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, uint32_t offset, uint32_t badClus) { |
| uint32_t old = Fs->fat_decode((Fs_t*)Fs, offset); |
| if(old == 0) { |
| fatEncode((Fs_t*)Fs, offset, badClus); |
| return; |
| } |
| if(old == badClus) { |
| fprintf(stderr, "Cluster %d already marked\n", offset); |
| } else { |
| fprintf(stderr, "Cluster %d is busy\n", offset); |
| } |
| } |
| |
| static char *in_buf; |
| static char *pat_buf; |
| static size_t 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, |
| uint32_t cluster, uint32_t badClus, |
| char *buffer, int doWrite) { |
| uint32_t start; |
| off_t ret; |
| mt_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(Fs, start); |
| if(doWrite) { |
| ret = force_pwrite(dev, buffer, pos, in_len); |
| if(ret < 0 || (size_t) ret < in_len ) |
| bad = 1; |
| } else { |
| ret = force_pread(dev, in_buf, pos, in_len); |
| if(ret < (off_t) in_len ) |
| bad = 1; |
| else if(buffer) { |
| size_t i; |
| for(i=0; i<in_len; i++) |
| if(in_buf[i] != buffer[i]) { |
| bad = 1; |
| break; |
| } |
| } |
| } |
| |
| if(bad) { |
| printf("Bad cluster %d found\n", cluster); |
| fatEncode((Fs_t*)Fs, cluster, badClus); |
| return 1; |
| } |
| return 0; |
| } |
| |
| void mbadblocks(int argc, char **argv, int type UNUSEDP) NORETURN; |
| 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; |
| int 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 = atoui(optarg); |
| break; |
| case 'E': |
| endSector = atoui(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; |
| } |
| init_random(); |
| for(i=0; i < in_len * N_PATTERN; i++) { |
| pat_buf[i] = (char) random(); |
| } |
| } |
| for(i=0; i < Fs->clus_start; i++ ){ |
| ssize_t r; |
| r = PREADS(Fs->head.Next, in_buf, |
| sectorsToBytes(Fs, i), Fs->sector_size); |
| if( r < 0 ){ |
| perror("early error"); |
| ret = -1; |
| goto exit_0; |
| } |
| if((size_t) r < 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"); |
| uint32_t offset = strtou32(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->head.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); |
| } |