root/src/bin/badblocks.cpp
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

// for R5:
//#define pread(fd, buf, count, pos) read_pos(fd, pos, buf, count)

int blockSize = 1024;
int group = 64;
int progress = 1;
int verbose = 0;
FILE *outputFile = NULL;

int scan_device(const char *dev, off_t startBlock, off_t endBlock)
{
        char *buffer = NULL;
        size_t len = group * blockSize;
        off_t block = startBlock;
        off_t at = block * blockSize;
        int i;
        int fd;

        buffer = (char *)malloc(len);
        if (buffer == NULL)
                return 1;


        fd = open(dev, O_RDONLY);
        if (fd < 0) {
                perror("open");
                goto err1;
        }
        // Check size

        if (verbose)
                fprintf(stderr, "Scanning '%s', %d * %d bytes at once\n",
                        dev, group, blockSize);

        if (progress)
                fprintf(stderr, "\n");

        for (; block <= endBlock; block += group, at += len) {
                int got;
                if (progress)
                        fprintf(stderr, "checking block %lld\x1b[1A\n", block);
                got = pread(fd, buffer, len, at);
                if (got == len)
                        continue;
                if (got >= 0) {
                        if (verbose)
                                fprintf(stderr, "block %lld (offset %lld): got %d < %zd\n",
                                        block, at, got, len);
                        break;
                }
                if (got < 0) {
                        fprintf(stderr, "block %lld: error: %s\n", block, strerror(errno));
                        /*
                        if (errno != EIO && errno != ENXIO) {
                                perror("pread");
                                goto err2;
                        }
                        */
                }
                // try each block separately
                for (i = 0; i < group; i++) {
                        got = pread(fd, buffer, blockSize, at + blockSize * i);
                        if (got == blockSize)
                                continue;
                        if (got < blockSize) {
                                if (got < 0)
                                        fprintf(stderr, "block %lld: error: %s\n", block + i, strerror(errno));
                                else
                                        fprintf(stderr, "block %lld: read %d bytes\n", block + i, got);
                                fprintf(outputFile, "%lld\n", block + i);
                                fflush(stdout);
                        }
                }
        }
        close(fd);
        free(buffer);
        return 0;

        close(fd);
err1:
        free(buffer);
        return 1;
}

int usage(int ret)
{
        fprintf(stderr, "badblocks [-sv] [-b block-size] [-c block-at-once] "
                "/dev/disk/... [end-block [start-block]]\n");
        return ret;
}

int main(int argc, char **argv)
{
        int ch;
        off_t startBlock = 0LL;
        off_t endBlock = INT64_MAX;
        outputFile = stdout;
        if (argc < 2)
                return usage(1);

        while ((ch = getopt(argc, argv, "b:c:hsv?")) != -1) {
                switch (ch) {
                case 'b':
                        blockSize = atoi(optarg);
                        //fprintf(stderr, "bs %d\n", blockSize);
                        break;
                case 'c':
                        group = atoi(optarg);
                        //fprintf(stderr, "g %d\n", group);
                        break;
                case 's':
                        progress = 1;
                        break;
                case 'v':
                        verbose = 1;
                        break;
                case 'h':
                case '?':
                        return usage(0);
                default:
                        return usage(1);
                }
        }
        argc -= optind;
        argv += optind;

        if (argc > 2)
                startBlock = atoll(argv[2]);
        if (argc > 1)
                endBlock = atoll(argv[1]);

        return scan_device(argv[0], startBlock, endBlock);
}