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;
usage(int ret)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
checkListTwice(char * filename)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 */
mark(Fs_t * Fs,long offset,unsigned int badClus)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 size_t in_len;
66
67
progress(unsigned int i,unsigned int total)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
scan(Fs_t * Fs,Stream_t * dev,long cluster,unsigned int badClus,char * buffer,int doWrite)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 < (off_t) in_len )
89 bad = 1;
90 } else {
91 ret = force_read(dev, in_buf, pos, in_len);
92 if(ret < (off_t) in_len )
93 bad = 1;
94 else if(buffer) {
95 size_t 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) NORETURN;
mbadblocks(int argc,char ** argv,int type UNUSEDP)113 void mbadblocks(int argc, char **argv, int type UNUSEDP)
114 {
115 unsigned int i;
116 unsigned int startSector=2;
117 unsigned int endSector=0;
118 struct MainParam_t mp;
119 Fs_t *Fs;
120 Stream_t *Dir;
121 int ret;
122 char *filename = NULL;
123 int c;
124 unsigned int badClus;
125 int sectorMode=0;
126 int writeMode=0;
127
128 while ((c = getopt(argc, argv, "i:s:cwS:E:")) != EOF) {
129 switch(c) {
130 case 'i':
131 set_cmd_line_image(optarg);
132 break;
133 case 'c':
134 checkListTwice(filename);
135 filename = strdup(optarg);
136 break;
137 case 's':
138 checkListTwice(filename);
139 filename = strdup(optarg);
140 sectorMode = 1;
141 break;
142 case 'S':
143 startSector = atoui(optarg);
144 break;
145 case 'E':
146 endSector = atoui(optarg);
147 break;
148 case 'w':
149 writeMode = 1;
150 break;
151 case 'h':
152 usage(0);
153 default:
154 usage(1);
155 }
156 }
157
158 if (argc != optind+1 ||
159 !argv[optind][0] || argv[optind][1] != ':' || argv[optind][2]) {
160 usage(1);
161 }
162
163 init_mp(&mp);
164
165 Dir = open_root_dir(argv[optind][0], O_RDWR, NULL);
166 if (!Dir) {
167 fprintf(stderr,"%s: Cannot initialize drive\n", argv[0]);
168 exit(1);
169 }
170
171 Fs = (Fs_t *)GetFs(Dir);
172 in_len = Fs->cluster_size * Fs->sector_size;
173 in_buf = malloc(in_len);
174 if(!in_buf) {
175 printOom();
176 ret = 1;
177 goto exit_0;
178 }
179 if(writeMode) {
180 pat_buf=malloc(in_len * N_PATTERN);
181 if(!pat_buf) {
182 printOom();
183 ret = 1;
184 goto exit_0;
185 }
186 init_random();
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 = strtol(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 }
284