xref: /aosp_15_r20/external/mtools/mbadblocks.c (revision d5c9a868b113e0ec0db2f27bc2ce8a253e77c4b0)
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,uint32_t offset,uint32_t badClus)50 static void mark(Fs_t *Fs, uint32_t offset, uint32_t badClus) {
51 	uint32_t 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 %d already marked\n", offset);
58 	} else {
59 		fprintf(stderr, "Cluster %d 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,uint32_t cluster,uint32_t badClus,char * buffer,int doWrite)73 static int scan(Fs_t *Fs, Stream_t *dev,
74 		uint32_t cluster, uint32_t badClus,
75 		char *buffer, int doWrite) {
76 	uint32_t start;
77 	off_t ret;
78 	mt_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(Fs, start);
86 	if(doWrite) {
87 		ret = force_pwrite(dev, buffer, pos, in_len);
88 		if(ret < 0 || (size_t) ret < in_len )
89 			bad = 1;
90 	} else {
91 		ret = force_pread(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 %d 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] = (char) random();
189 		}
190 	}
191 	for(i=0; i < Fs->clus_start; i++ ){
192 		ssize_t r;
193 		r = PREADS(Fs->head.Next, in_buf,
194 			   sectorsToBytes(Fs, i), Fs->sector_size);
195 		if( r < 0 ){
196 			perror("early error");
197 			ret = -1;
198 			goto exit_0;
199 		}
200 		if((size_t) r < Fs->sector_size){
201 			fprintf(stderr,"end of file in file_read\n");
202 			ret = 1;
203 			goto exit_0;
204 		}
205 	}
206 	ret = 0;
207 
208 	badClus = Fs->last_fat + 1;
209 
210 	if(startSector < 2)
211 		startSector = 2;
212 	if(endSector > Fs->num_clus + 2 || endSector <= 0)
213 		endSector = Fs->num_clus + 2;
214 
215 	if(filename) {
216 		char line[80];
217 
218 		FILE *f = fopen(filename, "r");
219 		if(f == NULL) {
220 			fprintf(stderr, "Could not open %s (%s)\n",
221 				filename, strerror(errno));
222 			ret = 1;
223 			goto exit_0;
224 		}
225 		while(fgets(line, sizeof(line), f)) {
226 			char *ptr = line + strspn(line, " \t");
227 			uint32_t offset = strtou32(ptr, 0, 0);
228 			if(sectorMode)
229 				offset = (offset-Fs->clus_start)/Fs->cluster_size + 2;
230 			if(offset < 2) {
231 				fprintf(stderr, "Sector before start\n");
232 			} else if(offset >= Fs->num_clus) {
233 				fprintf(stderr, "Sector beyond end\n");
234 			} else {
235 				mark(Fs, offset, badClus);
236 				ret = 1;
237 			}
238 		}
239 	} else {
240 		Stream_t *dev;
241 		dev = Fs->head.Next;
242 		if(dev->Next)
243 			dev = dev->Next;
244 
245 		in_len = Fs->cluster_size * Fs->sector_size;
246 		if(writeMode) {
247 			/* Write pattern */
248 			for(i=startSector; i< endSector; i++){
249 				if(got_signal)
250 					break;
251 				progress(i, Fs->num_clus);
252 				ret |= scan(Fs, dev, i, badClus,
253 					    pat_buf + in_len * (i % N_PATTERN),
254 					    1);
255 			}
256 
257 			/* Flush cache, so that we are sure we read the data
258 			   back from disk, rather than from the cache */
259 			if(!got_signal)
260 				DISCARD(dev);
261 
262 			/* Read data back, and compare to pattern */
263 			for(i=startSector; i< endSector; i++){
264 				if(got_signal)
265 					break;
266 				progress(i, Fs->num_clus);
267 				ret |= scan(Fs, dev, i, badClus,
268 					    pat_buf + in_len * (i % N_PATTERN),
269 					    0);
270 			}
271 
272 		} else {
273 
274 			for(i=startSector; i< endSector; i++){
275 				if(got_signal)
276 					break;
277 				progress(i, Fs->num_clus);
278 				ret |= scan(Fs, dev, i, badClus, NULL, 0);
279 			}
280 		}
281 	}
282  exit_0:
283 	FREE(&Dir);
284 	exit(ret);
285 }
286