"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 }