Serenity Operating System
at master 165 lines 5.9 kB view raw
1/* 2 * Copyright (c) 2021, the SerenityOS developers. 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Random.h> 8#include <LibCore/ArgsParser.h> 9#include <errno.h> 10#include <fcntl.h> 11#include <inttypes.h> 12#include <string.h> 13#include <sys/stat.h> 14#include <unistd.h> 15 16bool verify_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer); 17bool write_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer); 18 19bool verify_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer) 20{ 21 auto offset = block * buffer.size(); 22 auto rs = lseek(fd, offset, SEEK_SET); 23 if (rs < 0) { 24 fprintf(stderr, "Couldn't seek to block %" PRIi64 " (offset %" PRIi64 ") while verifying: %s\n", block, offset, strerror(errno)); 25 return false; 26 } 27 auto rw = read(fd, buffer.data(), buffer.size()); 28 if (rw != static_cast<int>(buffer.size())) { 29 fprintf(stderr, "Failure to read block %" PRIi64 ": %s\n", block, strerror(errno)); 30 return false; 31 } 32 srand((seed + 1) * (block + 1)); 33 for (size_t i = 0; i < buffer.size(); i++) { 34 if (buffer[i] != rand() % 256) { 35 fprintf(stderr, "Discrepancy detected at block %" PRIi64 " offset %zd\n", block, i); 36 return false; 37 } 38 } 39 return true; 40} 41 42bool write_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer) 43{ 44 auto offset = block * buffer.size(); 45 auto rs = lseek(fd, offset, SEEK_SET); 46 if (rs < 0) { 47 fprintf(stderr, "Couldn't seek to block %" PRIi64 " (offset %" PRIi64 ") while verifying: %s\n", block, offset, strerror(errno)); 48 return false; 49 } 50 srand((seed + 1) * (block + 1)); 51 for (size_t i = 0; i < buffer.size(); i++) 52 buffer[i] = rand(); 53 auto rw = write(fd, buffer.data(), buffer.size()); 54 if (rw != static_cast<int>(buffer.size())) { 55 fprintf(stderr, "Failure to write block %" PRIi64 ": %s\n", block, strerror(errno)); 56 return false; 57 } 58 return true; 59} 60 61int main(int argc, char** argv) 62{ 63 Vector<StringView> arguments; 64 arguments.ensure_capacity(argc); 65 for (auto i = 0; i < argc; ++i) 66 arguments.append({ argv[i], strlen(argv[i]) }); 67 68 DeprecatedString target; 69 int min_block_offset = 0; 70 int block_length = 2048; 71 int block_size = 512; 72 int count = 1024; 73 int rng_seed = 0; 74 bool paranoid_mode = false; 75 bool random_mode = false; 76 bool stop_mode = false; 77 bool uninitialized_mode = false; 78 79 Core::ArgsParser args_parser; 80 args_parser.add_option(min_block_offset, "Minimum block offset to consider", "min-offset", 'o', "size"); 81 args_parser.add_option(block_length, "Number of blocks to consider", "length", 's', "size"); 82 args_parser.add_option(block_size, "Block size", "block-size", 'b', "size"); 83 args_parser.add_option(count, "Number of write/read cycles to run", "number", 'n', "number"); 84 args_parser.add_option(rng_seed, "Random number generator seed", "seed", 'S', "number"); 85 args_parser.add_option(paranoid_mode, "Check entire range for consistency after each write", "paranoid", 'p'); 86 args_parser.add_option(random_mode, "Write one block inside range at random", "random", 'r'); 87 args_parser.add_option(stop_mode, "Stop after first error", "abort-on-error", 'a'); 88 args_parser.add_option(uninitialized_mode, "Don't pre-initialize block range", "uninitialized", 'u'); 89 args_parser.add_positional_argument(target, "Target device/file path", "target"); 90 args_parser.parse(arguments); 91 92 auto buffer_result = AK::ByteBuffer::create_zeroed(block_size); 93 if (buffer_result.is_error()) { 94 warnln("Failed to allocate a buffer of {} bytes", block_size); 95 return EXIT_FAILURE; 96 } 97 auto buffer = buffer_result.release_value(); 98 99 int fd = open(target.characters(), O_CREAT | O_RDWR, 0666); 100 if (fd < 0) { 101 perror("Couldn't create target file"); 102 return EXIT_FAILURE; 103 } 104 105 if (!uninitialized_mode) { 106 int old_percent = -100; 107 for (int i = min_block_offset; i < min_block_offset + block_length; i++) { 108 int percent; 109 if (block_length <= 1) 110 percent = 100; 111 else 112 percent = 100 * (i - min_block_offset) / (block_length - 1); 113 if (old_percent != percent) { 114 printf("Pre-initializing entire block range (%3d%%)...\n", percent); 115 old_percent = percent; 116 } 117 118 if (!write_block(fd, rng_seed, i, buffer)) 119 return EXIT_FAILURE; 120 } 121 } 122 123 int r = EXIT_SUCCESS; 124 for (int i = 0; i < count; i++) { 125 printf("(%d/%d)\tPass %d...\n", i + 1, count, i + 1); 126 127 for (int j = min_block_offset; j < min_block_offset + block_length; j++) { 128 off_t block; 129 if (random_mode) 130 while ((block = AK::get_random<off_t>()) < 0) 131 ; 132 else 133 block = j; 134 block = min_block_offset + block % block_length; 135 136 if (paranoid_mode) { 137 for (int k = min_block_offset; j < min_block_offset + block_length; j++) { 138 if (!verify_block(fd, rng_seed, k, buffer)) { 139 if (stop_mode) 140 return EXIT_FAILURE; 141 else 142 r = EXIT_FAILURE; 143 } 144 } 145 } else { 146 if (!verify_block(fd, rng_seed, block, buffer)) { 147 if (stop_mode) 148 return EXIT_FAILURE; 149 else 150 r = EXIT_FAILURE; 151 } 152 } 153 154 if (!write_block(fd, rng_seed, block, buffer)) { 155 if (stop_mode) 156 return EXIT_FAILURE; 157 else 158 r = EXIT_FAILURE; 159 } 160 } 161 } 162 163 close(fd); 164 return r; 165}