Imported Upstream version 4.0.18
[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 doWrite) {
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(doWrite) {
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 UNUSEDP)
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);
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                 pat_buf=malloc(in_len * N_PATTERN);
180                 if(!pat_buf) {
181                         printOom();
182                         ret = 1;
183                         goto exit_0;
184                 }
185                 srandom(time(NULL));
186                 for(i=0; i < in_len * N_PATTERN; i++) {
187                         pat_buf[i] = random();
188                 }
189         }
190         for(i=0; i < Fs->clus_start; i++ ){
191                 ret = READS(Fs->Next, in_buf, 
192                             sectorsToBytes((Stream_t*)Fs, i), Fs->sector_size);
193                 if( ret < 0 ){
194                         perror("early error");
195                         goto exit_0;
196                 }
197                 if(ret < (signed int) Fs->sector_size){
198                         fprintf(stderr,"end of file in file_read\n");
199                         ret = 1;
200                         goto exit_0;
201                 }
202         }
203         ret = 0;
204
205         badClus = Fs->last_fat + 1;
206
207         if(startSector < 2)
208                 startSector = 2;
209         if(endSector > Fs->num_clus + 2 || endSector <= 0) 
210                 endSector = Fs->num_clus + 2;
211
212         if(filename) {
213                 char line[80];
214
215                 FILE *f = fopen(filename, "r");
216                 if(f == NULL) {
217                         fprintf(stderr, "Could not open %s (%s)\n",
218                                 filename, strerror(errno));
219                         ret = 1;
220                         goto exit_0;
221                 }
222                 while(fgets(line, sizeof(line), f)) {
223                         char *ptr = line + strspn(line, " \t");
224                         long offset = strtoul(ptr, 0, 0);
225                         if(sectorMode)
226                                 offset = (offset-Fs->clus_start)/Fs->cluster_size + 2;
227                         if(offset < 2) {
228                                 fprintf(stderr, "Sector before start\n");
229                         } else if(offset >= Fs->num_clus) {
230                                 fprintf(stderr, "Sector beyond end\n");
231                         } else {
232                                 mark(Fs, offset, badClus);
233                                 ret = 1;
234                         }
235                 }
236         } else {
237                 Stream_t *dev;
238                 dev = Fs->Next;
239                 if(dev->Next)
240                         dev = dev->Next;
241
242                 in_len = Fs->cluster_size * Fs->sector_size;
243                 if(writeMode) {
244                         /* Write pattern */
245                         for(i=startSector; i< endSector; i++){
246                                 if(got_signal)
247                                         break;
248                                 progress(i, Fs->num_clus);
249                                 ret |= scan(Fs, dev, i, badClus, 
250                                             pat_buf + in_len * (i % N_PATTERN),
251                                             1);
252                         }
253
254                         /* Flush cache, so that we are sure we read the data
255                            back from disk, rather than from the cache */
256                         if(!got_signal)
257                                 DISCARD(dev);
258
259                         /* Read data back, and compare to pattern */
260                         for(i=startSector; i< endSector; i++){
261                                 if(got_signal)
262                                         break;
263                                 progress(i, Fs->num_clus);
264                                 ret |= scan(Fs, dev, i, badClus, 
265                                             pat_buf + in_len * (i % N_PATTERN),
266                                             0);
267                         }
268
269                 } else {
270
271                         for(i=startSector; i< endSector; i++){
272                                 if(got_signal)
273                                         break;
274                                 progress(i, Fs->num_clus);
275                                 ret |= scan(Fs, dev, i, badClus, NULL, 0);
276                         }
277                 }
278         }
279  exit_0:
280         FREE(&Dir);
281         exit(ret);
282 }