"Fossies" - the Fresh Open Source Software Archive 
Member "rufus-3.13/src/badblocks.c" (20 Nov 2020, 15320 Bytes) of package /linux/misc/rufus-3.13.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "badblocks.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
3.12_vs_3.13.
1 /*
2 * badblocks.c - Bad blocks checker
3 *
4 * Copyright 1992-1994 Remy Card <card@masi.ibp.fr>
5 * Copyright 1995-1999 Theodore Ts'o
6 * Copyright 1999 David Beattie
7 * Copyright 2011-2019 Pete Batard <pete@akeo.ie>
8 *
9 * This file is based on the minix file system programs fsck and mkfs
10 * written and copyrighted by Linus Torvalds <Linus.Torvalds@cs.helsinki.fi>
11 *
12 * %Begin-Header%
13 * This file may be redistributed under the terms of the GNU Public License.
14 * %End-Header%
15 */
16
17 #include <errno.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <malloc.h>
21 #include <string.h>
22 #include <time.h>
23 #include <setjmp.h>
24 #include <windows.h>
25 #include <stdint.h>
26
27 #include "rufus.h"
28 #include "resource.h"
29 #include "msapi_utf8.h"
30 #include "localization.h"
31
32 #include "badblocks.h"
33 #include "file.h"
34
35 FILE* log_fd = NULL;
36 static const char abort_msg[] = "Too many bad blocks, aborting test\n";
37 static const char bb_prefix[] = "Bad Blocks: ";
38
39 /*
40 *From e2fsprogs/lib/ext2fs/badblocks.c
41 */
42
43 /*
44 * Badblocks list
45 */
46 struct bb_struct_u64_list {
47 int magic;
48 int num;
49 int size;
50 uint64_t *list;
51 int badblocks_flags;
52 };
53
54 struct bb_struct_u64_iterate {
55 int magic;
56 bb_u64_list bb;
57 int ptr;
58 };
59
60 static errcode_t make_u64_list(int size, int num, uint64_t *list, bb_u64_list *ret)
61 {
62 bb_u64_list bb;
63
64 bb = calloc(1, sizeof(struct bb_struct_u64_list));
65 if (bb == NULL)
66 return BB_ET_NO_MEMORY;
67 bb->magic = BB_ET_MAGIC_BADBLOCKS_LIST;
68 bb->size = size ? size : 10;
69 bb->num = num;
70 bb->list = malloc(sizeof(blk64_t) * bb->size);
71 if (bb->list == NULL) {
72 free(bb);
73 bb = NULL;
74 return BB_ET_NO_MEMORY;
75 }
76 if (list)
77 memcpy(bb->list, list, bb->size * sizeof(blk64_t));
78 else
79 memset(bb->list, 0, bb->size * sizeof(blk64_t));
80 *ret = bb;
81 return 0;
82 }
83
84 /*
85 * This procedure creates an empty badblocks list.
86 */
87 static errcode_t bb_badblocks_list_create(bb_badblocks_list *ret, int size)
88 {
89 return make_u64_list(size, 0, 0, (bb_badblocks_list *) ret);
90 }
91
92 /*
93 * This procedure adds a block to a badblocks list.
94 */
95 static errcode_t bb_u64_list_add(bb_u64_list bb, uint64_t blk)
96 {
97 int i, j;
98 uint64_t* old_bb_list = bb->list;
99
100 BB_CHECK_MAGIC(bb, BB_ET_MAGIC_BADBLOCKS_LIST);
101
102 if (bb->num >= bb->size) {
103 bb->size += 100;
104 bb->list = realloc(bb->list, bb->size * sizeof(uint64_t));
105 if (bb->list == NULL) {
106 bb->list = old_bb_list;
107 bb->size -= 100;
108 return BB_ET_NO_MEMORY;
109 }
110 // coverity[suspicious_sizeof]
111 memset(&bb->list[bb->size-100], 0, 100 * sizeof(uint64_t));
112 }
113
114 /*
115 * Add special case code for appending to the end of the list
116 */
117 i = bb->num-1;
118 if ((bb->num != 0) && (bb->list[i] == blk))
119 return 0;
120 if ((bb->num == 0) || (bb->list[i] < blk)) {
121 bb->list[bb->num++] = blk;
122 return 0;
123 }
124
125 j = bb->num;
126 for (i=0; i < bb->num; i++) {
127 if (bb->list[i] == blk)
128 return 0;
129 if (bb->list[i] > blk) {
130 j = i;
131 break;
132 }
133 }
134 for (i=bb->num; i > j; i--)
135 bb->list[i] = bb->list[i-1];
136 bb->list[j] = blk;
137 bb->num++;
138 return 0;
139 }
140
141 static errcode_t bb_badblocks_list_add(bb_badblocks_list bb, blk64_t blk)
142 {
143 return bb_u64_list_add((bb_u64_list) bb, blk);
144 }
145
146 /*
147 * This procedure finds a particular block is on a badblocks
148 * list.
149 */
150 static int bb_u64_list_find(bb_u64_list bb, blk64_t blk)
151 {
152 int low, high, mid;
153
154 if (bb->magic != BB_ET_MAGIC_BADBLOCKS_LIST)
155 return -1;
156
157 if (bb->num == 0)
158 return -1;
159
160 low = 0;
161 high = bb->num-1;
162 if (blk == bb->list[low])
163 return low;
164 if (blk == bb->list[high])
165 return high;
166
167 while (low < high) {
168 mid = ((unsigned)low + (unsigned)high)/2;
169 if (mid == low || mid == high)
170 break;
171 if (blk == bb->list[mid])
172 return mid;
173 if (blk < bb->list[mid])
174 high = mid;
175 else
176 low = mid;
177 }
178 return -1;
179 }
180
181 /*
182 * This procedure tests to see if a particular block is on a badblocks
183 * list.
184 */
185 static int bb_u64_list_test(bb_u64_list bb, blk64_t blk)
186 {
187 if (bb_u64_list_find(bb, blk) < 0)
188 return 0;
189 else
190 return 1;
191 }
192
193 static int bb_badblocks_list_test(bb_badblocks_list bb, blk64_t blk)
194 {
195 return bb_u64_list_test((bb_u64_list) bb, blk);
196 }
197
198 static int bb_u64_list_iterate(bb_u64_iterate iter, blk64_t *blk)
199 {
200 bb_u64_list bb;
201
202 if (iter->magic != BB_ET_MAGIC_BADBLOCKS_ITERATE)
203 return 0;
204
205 bb = iter->bb;
206
207 if (bb->magic != BB_ET_MAGIC_BADBLOCKS_LIST)
208 return 0;
209
210 if (iter->ptr < bb->num) {
211 *blk = bb->list[iter->ptr++];
212 return 1;
213 }
214 *blk = 0;
215 return 0;
216 }
217
218 static int bb_badblocks_list_iterate(bb_badblocks_iterate iter, blk64_t *blk)
219 {
220 return bb_u64_list_iterate((bb_u64_iterate) iter, blk);
221 }
222
223 /*
224 * from e2fsprogs/misc/badblocks.c
225 */
226 static int v_flag = 1; /* verbose */
227 static int s_flag = 1; /* show progress of test */
228 static int cancel_ops = 0; /* abort current operation */
229 static int cur_pattern, nr_pattern;
230 static int cur_op;
231 /* Abort test if more than this number of bad blocks has been encountered */
232 static unsigned int max_bb = BB_BAD_BLOCKS_THRESHOLD;
233 static blk64_t currently_testing = 0;
234 static blk64_t num_blocks = 0;
235 static uint32_t num_read_errors = 0;
236 static uint32_t num_write_errors = 0;
237 static uint32_t num_corruption_errors = 0;
238 static bb_badblocks_list bb_list = NULL;
239 static blk64_t next_bad = 0;
240 static bb_badblocks_iterate bb_iter = NULL;
241
242 static __inline void *allocate_buffer(size_t size) {
243 return _mm_malloc(size, BB_SYS_PAGE_SIZE);
244 }
245
246 static __inline void free_buffer(void* p) {
247 _mm_free(p);
248 }
249
250 /*
251 * This routine reports a new bad block. If the bad block has already
252 * been seen before, then it returns 0; otherwise it returns 1.
253 */
254 static int bb_output (blk64_t bad, enum error_types error_type)
255 {
256 errcode_t error_code;
257
258 if (bb_badblocks_list_test(bb_list, bad))
259 return 0;
260
261 uprintf("%s%lu\n", bb_prefix, (unsigned long)bad);
262 fprintf(log_fd, "Block %lu: %s error\n", (unsigned long)bad, (error_type==READ_ERROR)?"read":
263 ((error_type == WRITE_ERROR)?"write":"corruption"));
264 fflush(log_fd);
265
266 error_code = bb_badblocks_list_add(bb_list, bad);
267 if (error_code) {
268 uprintf("%sError %d adding to in-memory bad block list", bb_prefix, error_code);
269 return 0;
270 }
271
272 /* kludge:
273 increment the iteration through the bb_list if
274 an element was just added before the current iteration
275 position. This should not cause next_bad to change. */
276 if (bb_iter && bad < next_bad)
277 bb_badblocks_list_iterate (bb_iter, &next_bad);
278
279 if (error_type == READ_ERROR) {
280 num_read_errors++;
281 } else if (error_type == WRITE_ERROR) {
282 num_write_errors++;
283 } else if (error_type == CORRUPTION_ERROR) {
284 num_corruption_errors++;
285 }
286 return 1;
287 }
288
289 static float calc_percent(unsigned long current, unsigned long total) {
290 float percent = 0.0;
291 if (total <= 0)
292 return percent;
293 if (current >= total) {
294 percent = 100.0f;
295 } else {
296 percent=(100.0f*(float)current/(float)total);
297 }
298 return percent;
299 }
300
301 static void print_status(void)
302 {
303 float percent;
304
305 percent = calc_percent((unsigned long) currently_testing,
306 (unsigned long) num_blocks);
307 PrintInfo(0, MSG_235, lmprintf(MSG_191 + ((cur_op==OP_WRITE)?0:1)),
308 cur_pattern, nr_pattern,
309 percent,
310 num_read_errors,
311 num_write_errors,
312 num_corruption_errors);
313 percent = (percent/2.0f) + ((cur_op==OP_READ)? 50.0f : 0.0f);
314 UpdateProgress(OP_BADBLOCKS, (((cur_pattern-1)*100.0f) + percent) / nr_pattern);
315 }
316
317 static void CALLBACK alarm_intr(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
318 {
319 if (!num_blocks)
320 return;
321 if (FormatStatus) {
322 uprintf("%sInterrupting at block %" PRIu64 "\n", bb_prefix,
323 (unsigned long long) currently_testing);
324 cancel_ops = -1;
325 }
326 print_status();
327 }
328
329 static void pattern_fill(unsigned char *buffer, unsigned int pattern,
330 size_t n)
331 {
332 unsigned int i, nb;
333 unsigned char bpattern[sizeof(pattern)], *ptr;
334
335 if (pattern == (unsigned int) ~0) {
336 PrintInfo(3500, MSG_236);
337 srand((unsigned int)GetTickCount64());
338 for (ptr = buffer; ptr < buffer + n; ptr++) {
339 // coverity[dont_call]
340 (*ptr) = rand() % (1 << (8 * sizeof(char)));
341 }
342 } else {
343 PrintInfo(3500, MSG_237, pattern);
344 bpattern[0] = 0;
345 for (i = 0; i < sizeof(bpattern); i++) {
346 if (pattern == 0)
347 break;
348 bpattern[i] = pattern & 0xFF;
349 pattern = pattern >> 8;
350 }
351 nb = i ? (i-1) : 0;
352 for (ptr = buffer, i = nb; ptr < buffer + n; ptr++) {
353 *ptr = bpattern[i];
354 if (i == 0)
355 i = nb;
356 else
357 i--;
358 }
359 cur_pattern++;
360 }
361 }
362
363 /*
364 * Perform a read of a sequence of blocks; return the number of blocks
365 * successfully sequentially read.
366 */
367 static int64_t do_read (HANDLE hDrive, unsigned char * buffer, uint64_t tryout,
368 uint64_t block_size, blk64_t current_block)
369 {
370 int64_t got;
371
372 if (v_flag > 1)
373 print_status();
374
375 /* Try the read */
376 got = read_sectors(hDrive, block_size, current_block, tryout, buffer);
377 if (got < 0)
378 got = 0;
379 if (got & 511)
380 uprintf("%sWeird value (%ld) in do_read\n", bb_prefix, got);
381 got /= block_size;
382 return got;
383 }
384
385 /*
386 * Perform a write of a sequence of blocks; return the number of blocks
387 * successfully sequentially written.
388 */
389 static int64_t do_write(HANDLE hDrive, unsigned char * buffer, uint64_t tryout,
390 uint64_t block_size, blk64_t current_block)
391 {
392 int64_t got;
393
394 if (v_flag > 1)
395 print_status();
396
397 /* Try the write */
398 got = write_sectors(hDrive, block_size, current_block, tryout, buffer);
399 if (got < 0)
400 got = 0;
401 if (got & 511)
402 uprintf("%sWeird value (%ld) in do_write\n", bb_prefix, got);
403 got /= block_size;
404 return got;
405 }
406
407 static unsigned int test_rw(HANDLE hDrive, blk64_t last_block, size_t block_size, blk64_t first_block,
408 size_t blocks_at_once, int pattern_type, int nb_passes)
409 {
410 const unsigned int pattern[BADLOCKS_PATTERN_TYPES][BADBLOCK_PATTERN_COUNT] =
411 { BADBLOCK_PATTERN_ONE_PASS, BADBLOCK_PATTERN_TWO_PASSES, BADBLOCK_PATTERN_SLC,
412 BADCLOCK_PATTERN_MLC, BADBLOCK_PATTERN_TLC };
413 unsigned char *buffer = NULL, *read_buffer;
414 int i, pat_idx;
415 unsigned int bb_count = 0;
416 blk64_t got, tryout, recover_block = ~0, *blk_id;
417 size_t id_offset = 0;
418
419 if ((pattern_type < 0) || (pattern_type >= BADLOCKS_PATTERN_TYPES)) {
420 uprintf("%sInvalid pattern type\n", bb_prefix);
421 cancel_ops = -1;
422 return 0;
423 }
424 if ((nb_passes < 1) || (nb_passes > BADBLOCK_PATTERN_COUNT)) {
425 uprintf("%sInvalid number of passes\n", bb_prefix);
426 cancel_ops = -1;
427 return 0;
428 }
429
430 buffer = allocate_buffer(2 * blocks_at_once * block_size);
431 if (!buffer) {
432 uprintf("%sError while allocating buffers\n", bb_prefix);
433 cancel_ops = -1;
434 return 0;
435 }
436 read_buffer = buffer + blocks_at_once * block_size;
437
438 uprintf("%sChecking from block %lu to %lu (1 block = %s)\n", bb_prefix,
439 (unsigned long) first_block, (unsigned long) last_block - 1,
440 SizeToHumanReadable(BADBLOCK_BLOCK_SIZE, FALSE, FALSE));
441 nr_pattern = nb_passes;
442 cur_pattern = 0;
443
444 for (pat_idx = 0; pat_idx < nb_passes; pat_idx++) {
445 if (cancel_ops)
446 goto out;
447 if (detect_fakes && (pat_idx == 0)) {
448 srand((unsigned int)GetTickCount64());
449 id_offset = rand() * (block_size - sizeof(blk64_t)) / RAND_MAX;
450 uprintf("%sUsing offset %d for fake device check\n", bb_prefix, id_offset);
451 }
452 // coverity[dont_call]
453 pattern_fill(buffer, pattern[pattern_type][pat_idx], blocks_at_once * block_size);
454 num_blocks = last_block - 1;
455 currently_testing = first_block;
456 if (s_flag | v_flag)
457 uprintf("%sWriting test pattern 0x%02X\n", bb_prefix, pattern[pattern_type][pat_idx]);
458 cur_op = OP_WRITE;
459 tryout = blocks_at_once;
460 while (currently_testing < last_block) {
461 if (cancel_ops)
462 goto out;
463 if (max_bb && bb_count >= max_bb) {
464 if (s_flag || v_flag) {
465 uprintf(abort_msg);
466 fprintf(log_fd, "%s", abort_msg);
467 fflush(log_fd);
468 }
469 cancel_ops = -1;
470 goto out;
471 }
472 if (currently_testing + tryout > last_block)
473 tryout = last_block - currently_testing;
474 if (detect_fakes && (pat_idx == 0)) {
475 /* Add the block number at a fixed (random) offset during each pass to
476 allow for the detection of 'fake' media (eg. 2GB USB masquerading as 16GB) */
477 for (i=0; i<(int)blocks_at_once; i++) {
478 blk_id = (blk64_t*)(intptr_t)(buffer + id_offset+ i*block_size);
479 *blk_id = (blk64_t)(currently_testing + i);
480 }
481 }
482 got = do_write(hDrive, buffer, tryout, block_size, currently_testing);
483 if (v_flag > 1)
484 print_status();
485
486 if (got == 0 && tryout == 1)
487 bb_count += bb_output(currently_testing++, WRITE_ERROR);
488 currently_testing += got;
489 if (got != tryout) {
490 tryout = 1;
491 if (recover_block == ~0)
492 recover_block = currently_testing -
493 got + blocks_at_once;
494 continue;
495 } else if (currently_testing == recover_block) {
496 tryout = blocks_at_once;
497 recover_block = ~0;
498 }
499 }
500
501 num_blocks = 0;
502 if (s_flag | v_flag)
503 uprintf("%sReading and comparing\n", bb_prefix);
504 cur_op = OP_READ;
505 num_blocks = last_block;
506 currently_testing = first_block;
507
508 tryout = blocks_at_once;
509 while (currently_testing < last_block) {
510 if (cancel_ops) goto out;
511 if (max_bb && bb_count >= max_bb) {
512 if (s_flag || v_flag) {
513 uprintf(abort_msg);
514 fprintf(log_fd, "%s", abort_msg);
515 fflush(log_fd);
516 }
517 cancel_ops = -1;
518 goto out;
519 }
520 if (currently_testing + tryout > last_block)
521 tryout = last_block - currently_testing;
522 if (detect_fakes && (pat_idx == 0)) {
523 for (i=0; i<(int)blocks_at_once; i++) {
524 blk_id = (blk64_t*)(intptr_t)(buffer + id_offset+ i*block_size);
525 *blk_id = (blk64_t)(currently_testing + i);
526 }
527 }
528 got = do_read(hDrive, read_buffer, tryout, block_size,
529 currently_testing);
530 if (got == 0 && tryout == 1)
531 bb_count += bb_output(currently_testing++, READ_ERROR);
532 currently_testing += got;
533 if (got != tryout) {
534 tryout = 1;
535 if (recover_block == ~0)
536 recover_block = currently_testing -
537 got + blocks_at_once;
538 continue;
539 } else if (currently_testing == recover_block) {
540 tryout = blocks_at_once;
541 recover_block = ~0;
542 }
543 for (i=0; i < got; i++) {
544 if (memcmp(read_buffer + i * block_size,
545 buffer + i * block_size,
546 block_size))
547 bb_count += bb_output(currently_testing+i-got, CORRUPTION_ERROR);
548 }
549 if (v_flag > 1)
550 print_status();
551 }
552
553 num_blocks = 0;
554 }
555 out:
556 free_buffer(buffer);
557 return bb_count;
558 }
559
560 BOOL BadBlocks(HANDLE hPhysicalDrive, ULONGLONG disk_size, int nb_passes,
561 int flash_type, badblocks_report *report, FILE* fd)
562 {
563 errcode_t error_code;
564 blk64_t last_block = disk_size / BADBLOCK_BLOCK_SIZE;
565
566 if (report == NULL) return FALSE;
567 num_read_errors = 0;
568 num_write_errors = 0;
569 num_corruption_errors = 0;
570 report->bb_count = 0;
571 if (fd != NULL) {
572 log_fd = fd;
573 } else {
574 log_fd = freopen(NULL, "w", stderr);
575 }
576
577 error_code = bb_badblocks_list_create(&bb_list, 0);
578 if (error_code) {
579 uprintf("%sError %d while creating in-memory bad blocks list", bb_prefix, error_code);
580 return FALSE;
581 }
582
583 cancel_ops = 0;
584 /* use a timer to update status every second */
585 SetTimer(hMainDialog, TID_BADBLOCKS_UPDATE, 1000, alarm_intr);
586 report->bb_count = test_rw(hPhysicalDrive, last_block, BADBLOCK_BLOCK_SIZE, 0, BB_BLOCKS_AT_ONCE, flash_type, nb_passes);
587 KillTimer(hMainDialog, TID_BADBLOCKS_UPDATE);
588 free(bb_list->list);
589 free(bb_list);
590 report->num_read_errors = num_read_errors;
591 report->num_write_errors = num_write_errors;
592 report->num_corruption_errors = num_corruption_errors;
593
594 if ((cancel_ops) && (!report->bb_count))
595 return FALSE;
596 return TRUE;
597 }