2d002c36485cc9587993f31a0d4d1ea8f5fbf771
[platform/upstream/mtools.git] / mbadblocks.c
1 /*  Copyright 1995-1999,2001-2003,2007,2009,2011 Alain Knaff.
2  *  This file is part of mtools.
3  *
4  *  Mtools is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  Mtools is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with Mtools.  If not, see <http://www.gnu.org/licenses/>.
16  *
17  * mbadblocks.c
18  * Mark bad blocks on disk
19  *
20  */
21
22 #include "sysincludes.h"
23 #include "msdos.h"
24 #include "mtools.h"
25 #include "mainloop.h"
26 #include "fsP.h"
27
28 #define N_PATTERN 311
29
30 static void usage(int ret) NORETURN;
31 static void usage(int ret)
32 {
33         fprintf(stderr, "Mtools version %s, dated %s\n",
34                 mversion, mdate);
35         fprintf(stderr, "Usage: %s: [-c clusterList] [-s sectorList] [-c] [-V] device\n",
36                 progname);
37         exit(ret);
38 }
39
40 static void checkListTwice(char *filename) {
41         if(filename != NULL) {
42                 fprintf(stderr, "Only one of the -c or -s options may be given\n");
43                 exit(1);
44         }
45 }
46
47 /**
48  * Marks given cluster as bad, but prints error instead if cluster already used
49  */
50 static void mark(Fs_t *Fs, long offset, unsigned int badClus) {
51         unsigned int old = Fs->fat_decode((Fs_t*)Fs, offset);
52         if(old == 0) {
53                 fatEncode((Fs_t*)Fs, offset, badClus);
54                 return;
55         }
56         if(old == badClus) {
57                 fprintf(stderr, "Cluster %ld already marked\n", offset);
58         } else {
59                 fprintf(stderr, "Cluster %ld is busy\n", offset);
60         }
61 }
62
63 static char *in_buf;
64 static char *pat_buf;
65 static int in_len;
66
67
68 static void progress(unsigned int i, unsigned int total) {
69         if(i % 10 == 0)
70                 fprintf(stderr, "                     \r%d/%d\r", i, total);
71 }
72
73 static int scan(Fs_t *Fs, Stream_t *dev,
74                 long cluster, unsigned int badClus,
75                 char *buffer, int write) {
76         off_t start;
77         off_t ret;
78         off_t pos;
79         int bad=0;
80
81         if(Fs->fat_decode((Fs_t*)Fs, cluster))
82                 /* cluster busy, or already marked */
83                 return 0;
84         start = (cluster - 2) * Fs->cluster_size + Fs->clus_start;
85         pos = sectorsToBytes((Stream_t*)Fs, start);
86         if(write) {
87                 ret = force_write(dev, buffer, pos, in_len);
88                 if(ret < in_len )
89                         bad = 1;
90         } else {
91                 ret = force_read(dev, in_buf, pos, in_len);
92                 if(ret < in_len )
93                         bad = 1;
94                 else if(buffer) {
95                         int i;
96                         for(i=0; i<in_len; i++)
97                                 if(in_buf[i] != buffer[i]) {
98                                         bad = 1;
99                                         break;
100                                 }
101                 }
102         }
103
104         if(bad) {
105                 printf("Bad cluster %ld found\n", cluster);
106                 fatEncode((Fs_t*)Fs, cluster, badClus);
107                 return 1;
108         }
109         return 0;
110 }
111
112 void mbadblocks(int argc, char **argv, int type)
113 {
114         unsigned int i;
115         unsigned int startSector=2;
116         unsigned int endSector=0;
117         struct MainParam_t mp;
118         Fs_t *Fs;
119         Stream_t *Dir;
120         int ret;
121         char *filename = NULL;
122         char c;
123         unsigned int badClus;
124         int sectorMode=0;
125         int writeMode=0;
126
127         while ((c = getopt(argc, argv, "i:s:cwS:E:")) != EOF) {
128                 switch(c) {
129                 case 'i':
130                         set_cmd_line_image(optarg, 0);
131                         break;
132                 case 'c':
133                         checkListTwice(filename);
134                         filename = strdup(optarg);
135                         break;
136                 case 's':
137                         checkListTwice(filename);
138                         filename = strdup(optarg);
139                         sectorMode = 1;
140                         break;
141                 case 'S':
142                         startSector = atol(optarg); 
143                         break;
144                 case 'E':
145                         endSector = atol(optarg); 
146                         break;
147                 case 'w':
148                         writeMode = 1;
149                         break;
150                 case 'h':
151                         usage(0);
152                 default:
153                         usage(1);
154                 }
155         }
156
157         if (argc != optind+1 ||
158             !argv[optind][0] || argv[optind][1] != ':' || argv[optind][2]) {
159                 usage(1);
160         }
161
162         init_mp(&mp);
163
164         Dir = open_root_dir(argv[optind][0], O_RDWR, NULL);
165         if (!Dir) {
166                 fprintf(stderr,"%s: Cannot initialize drive\n", argv[0]);
167                 exit(1);
168         }
169
170         Fs = (Fs_t *)GetFs(Dir);
171         in_len = Fs->cluster_size * Fs->sector_size;
172         in_buf = malloc(in_len);
173         if(!in_buf) {
174                 printOom();
175                 ret = 1;
176                 goto exit_0;
177         }
178         if(writeMode) {
179                 int i;
180                 pat_buf=malloc(in_len * N_PATTERN);
181                 if(!pat_buf) {
182                         printOom();
183                         ret = 1;
184                         goto exit_0;
185                 }
186                 srandom(time(NULL));
187                 for(i=0; i < in_len * N_PATTERN; i++) {
188                         pat_buf[i] = random();
189                 }
190         }
191         for(i=0; i < Fs->clus_start; i++ ){
192                 ret = READS(Fs->Next, in_buf, 
193                             sectorsToBytes((Stream_t*)Fs, i), Fs->sector_size);
194                 if( ret < 0 ){
195                         perror("early error");
196                         goto exit_0;
197                 }
198                 if(ret < (signed int) Fs->sector_size){
199                         fprintf(stderr,"end of file in file_read\n");
200                         ret = 1;
201                         goto exit_0;
202                 }
203         }
204         ret = 0;
205
206         badClus = Fs->last_fat + 1;
207
208         if(startSector < 2)
209                 startSector = 2;
210         if(endSector > Fs->num_clus + 2 || endSector <= 0) 
211                 endSector = Fs->num_clus + 2;
212
213         if(filename) {
214                 char line[80];
215
216                 FILE *f = fopen(filename, "r");
217                 if(f == NULL) {
218                         fprintf(stderr, "Could not open %s (%s)\n",
219                                 filename, strerror(errno));
220                         ret = 1;
221                         goto exit_0;
222                 }
223                 while(fgets(line, sizeof(line), f)) {
224                         char *ptr = line + strspn(line, " \t");
225                         long offset = strtoul(ptr, 0, 0);
226                         if(sectorMode)
227                                 offset = (offset-Fs->clus_start)/Fs->cluster_size + 2;
228                         if(offset < 2) {
229                                 fprintf(stderr, "Sector before start\n");
230                         } else if(offset >= Fs->num_clus) {
231                                 fprintf(stderr, "Sector beyond end\n");
232                         } else {
233                                 mark(Fs, offset, badClus);
234                                 ret = 1;
235                         }
236                 }
237         } else {
238                 Stream_t *dev;
239                 dev = Fs->Next;
240                 if(dev->Next)
241                         dev = dev->Next;
242
243                 in_len = Fs->cluster_size * Fs->sector_size;
244                 if(writeMode) {
245                         /* Write pattern */
246                         for(i=startSector; i< endSector; i++){
247                                 if(got_signal)
248                                         break;
249                                 progress(i, Fs->num_clus);
250                                 ret |= scan(Fs, dev, i, badClus, 
251                                             pat_buf + in_len * (i % N_PATTERN),
252                                             1);
253                         }
254
255                         /* Flush cache, so that we are sure we read the data
256                            back from disk, rather than from the cache */
257                         if(!got_signal)
258                                 DISCARD(dev);
259
260                         /* Read data back, and compare to pattern */
261                         for(i=startSector; i< endSector; i++){
262                                 if(got_signal)
263                                         break;
264                                 progress(i, Fs->num_clus);
265                                 ret |= scan(Fs, dev, i, badClus, 
266                                             pat_buf + in_len * (i % N_PATTERN),
267                                             0);
268                         }
269
270                 } else {
271
272                         for(i=startSector; i< endSector; i++){
273                                 if(got_signal)
274                                         break;
275                                 progress(i, Fs->num_clus);
276                                 ret |= scan(Fs, dev, i, badClus, NULL, 0);
277                         }
278                 }
279         }
280  exit_0:
281         FREE(&Dir);
282         exit(ret);
283 }