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