Recently they suddenly brought several block devices, should I throw them to the dump or can I still use them.
I thought to run the old-school badblocks -svw, but it turned out that they forgot about it and still haven't switched to 64-bit addressing:
badblocks -svw /dev/sdb
badblocks: Value too large for defined data type invalid end block (5860522584): must be 32-bit value
Given this and lack of free time and desire to write fairly boring code, set the task deepssek v3.1 terminus, here's what I got (typos and critical logic errors in the code were fixed):
1:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <scsi/sg.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#define LOCAL_BLOCK_SIZE (1024*1024) // 1MB блоки
#define SECTOR_SIZE 512
// Структура для SCSI команды WRITE LONG (ремэппинг)
struct write_long_cmd {
unsigned char opcode; // 0xE3 для WRITE LONG
unsigned char obsolete;
unsigned char lba_high;
unsigned char lba_mid;
unsigned char lba_low;
unsigned char sector_count;
unsigned char control;
};
// Получение размера устройства в байтах
uint64_t get_device_size(int fd) {
uint64_t size;
if (ioctl(fd, BLKGETSIZE64, &size) == -1) {
perror("BLKGETSIZE64 failed");
exit(1);
}
return size;
}
// Синхронное чтение с прямым I/O
ssize_t direct_read(int fd, void *buf, size_t count, off_t offset) {
return pread(fd, buf, count, offset);
}
// Синхронная запись с прямым I/O
ssize_t direct_write(int fd, void*buf, size_t count, off_t offset) {
return pwrite(fd, buf, count, offset);
}
// Отправка SCSI команды для ремэппинга сектора
int remap_sector(int sg_fd, uint64_t lba) {
struct write_long_cmd cmd = {
.opcode = 0xE3, // WRITE LONG
.lba_high = (lba >> 16) & 0xFF,
.lba_mid = (lba >> 8) & 0xFF,
.lba_low = lba & 0xFF,
.sector_count = 1,
.control = 0
};
unsigned char sense_buffer[32];
struct sg_io_hdr io_hdr = {
.interface_id = 'S',
.dxfer_direction = SG_DXFER_TO_DEV,
.cmd_len = sizeof(cmd),
.mx_sb_len = sizeof(sense_buffer),
.dxfer_len = SECTOR_SIZE,
.dxferp = NULL, // WRITE LONG без передачи данных
.cmdp = (unsigned char*)&cmd,
.sbp = sense_buffer,
.timeout = 30000 // 30 секунд
};
return ioctl(sg_fd, SG_IO, &io_hdr);
}
int main(int argc, char*argv[]) {
if (argc != 2) {
fprintf(stderr, "Использование: %s /dev/sdX\n", argv[0]);
return 1;
}
// Открываем устройство для прямого I/O
int fd = open(argv[1], O_RDWR | O_DIRECT | O_SYNC);
if (fd == -1) {
perror("Ошибка открытия устройства");
return 1;
}
// Открываем SCSI generic интерфейс для ремэппинга
int sg_fd = open(argv[1], O_RDWR);
if (sg_fd == -1) {
perror("Ошибка открытия SCSI интерфейса");
close(fd);
return 1;
}
uint64_t device_size = get_device_size(fd);
uint64_t total_blocks = device_size / LOCAL_BLOCK_SIZE;
printf("Размер устройства: %lu байт (%lu блоков по 1MB)\n",
device_size, total_blocks);
// Выделяем выровненную память для прямого I/O
void *buffer;
if (posix_memalign(&buffer, 4096, LOCAL_BLOCK_SIZE) != 0) {
perror("Ошибка выделения выровненной памяти");
close(fd);
close(sg_fd);
return 1;
}
memset(buffer, 0, LOCAL_BLOCK_SIZE);
int bad_sectors_found = 0;
int pass = 1;
do {
printf("Проход %d: сканирование %lu блоков...\n", pass, total_blocks);
bad_sectors_found = 0;
for (uint64_t block = 0; block < total_blocks; block++) {
off_t offset = block* LOCAL_BLOCK_SIZE;
// Вывод прогресса каждые 1000 блоков
if (block % 1000 == 0) {
printf("Прогресс: %.2f%%\r",
(double)block*100.0 / total_blocks);
fflush(stdout);
}
// Чтение блока 1MB
ssize_t bytes_read = direct_read(fd, buffer, LOCAL_BLOCK_SIZE, offset);
if (bytes_read != LOCAL_BLOCK_SIZE) {
printf("\nОшибка чтения блока %lu (смещение: 0x%lx)\n",
block, offset);
// Детальное сканирование посекторно
for (int sector = 0; sector < LOCAL_BLOCK_SIZE/SECTOR_SIZE; sector++) {
off_t sector_offset = offset + sector *SECTOR_SIZE;
uint64_t lba = sector_offset / SECTOR_SIZE;
ssize_t sector_read = direct_read(fd, buffer,
SECTOR_SIZE, sector_offset);
if (sector_read != SECTOR_SIZE) {
printf(" Нечитаемый сектор LBA %lu (ошибка: %s)\n",
lba, strerror(errno));
// Перезапись сектора с ремэппингом
int retry = 0;
while (1) {
retry++;
// Сначала пытаемся обычной записью
ssize_t written = direct_write(fd, buffer,
SECTOR_SIZE, sector_offset);
if (written == SECTOR_SIZE) {
printf(" Сектор перезаписан успешно (попытка %d)\n", retry);
break;
}
// Если обычная запись не сработала, пробуем ремэппинг
if (remap_sector(sg_fd, lba) == 0) {
printf(" Ремэппинг сектора выполнен (попытка %d)\n", retry);
break;
}
printf(" Ошибка перезаписи/ремэппинга, повтор...\n");
usleep(100000); // 100ms задержка между попытками
}
bad_sectors_found++;
// Проверяем, что сектор теперь читается
sector_read = direct_read(fd, buffer, SECTOR_SIZE, sector_offset);
if (sector_read == SECTOR_SIZE) {
printf(" Сектор восстановлен успешно\n");
} else {
printf(" ВНИМАНИЕ: сектор все еще не читается!\n");
}
}
}
}
}
printf("\nПроход %d завершен. Найдено нечитаемых секторов: %d\n",
pass, bad_sectors_found);
pass++;
} while (bad_sectors_found > 0);
printf("Восстановление завершено. Все сектора читаются.\n");
free(buffer);
close(fd);
close(sg_fd);
return 0;
}
2:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <errno.h>
#include <stdint.h>
const uint16_t SECTOR_SIZE = 512;
const uint8_t BLOCK_SIZE_MB = 1;
const uint32_t _BLOCK_SIZE = (BLOCK_SIZE_MB*1024*1024);
const uint32_t WRITE_SIZE = (BLOCK_SIZE_MB*1024*1024);
const uint16_t RETRIES = 1024;
typedef struct {
int fd;
unsigned long long device_size;
unsigned long long current_pos;
} disk_ctx_t;
// Получение размера устройства
int get_device_size(int fd, unsigned long long *size) {
if (ioctl(fd, BLKGETSIZE64, size) == -1) {
return -1;
}
return 0;
}
// Чтение блока с проверкой ошибок
int read_block(int fd, void*buffer, unsigned long long offset, size_t size) {
if (lseek(fd, offset, SEEK_SET) == -1) return -1;
ssize_t bytes_read = read(fd, buffer, size);
return (bytes_read == (ssize_t)size) ? 0 : -1;
}
// Запись блока
int write_block(int fd, void*buffer, unsigned long long offset, size_t size) {
if (lseek(fd, offset, SEEK_SET) == -1) return -1;
ssize_t bytes_written = write(fd, buffer, size);
return (bytes_written == (ssize_t)size) ? 0 : -1;
}
// Поиск первого битого сектора в блоке
unsigned long long find_bad_sector(int fd, unsigned long long block_start) {
unsigned char sector[SECTOR_SIZE];
for (unsigned long long sector_offset = 0; sector_offset < _BLOCK_SIZE; sector_offset += SECTOR_SIZE) {
if (read_block(fd, sector, block_start + sector_offset, SECTOR_SIZE) == -1) {
return block_start + sector_offset;
}
}
return 0; // Все сектора читаются
}
// Восстановление проблемной области
int repair_area(int fd, unsigned long long bad_sector) {
unsigned char *repair_buffer = calloc(0xaa, WRITE_SIZE);
if (!repair_buffer) return -1;
for (int attempt = 0; attempt < RETRIES; attempt++) {
if (write_block(fd, repair_buffer, bad_sector, WRITE_SIZE) == 0) {
free(repair_buffer);
return 0; // Успех
}
usleep(100000); // 100ms задержка между попытками
}
free(repair_buffer);
return -1; // Все попытки провалились
}
int main(int argc, char*argv[]) {
if (argc != 2) {
fprintf(stderr, "Использование: %s <устройство>\n", argv[0]);
return 1;
}
int fd = open(argv[1], O_RDWR | O_DIRECT);
if (fd == -1) {
perror("Ошибка открытия устройства");
return 1;
}
disk_ctx_t ctx = {0};
ctx.fd = fd;
if (get_device_size(fd, &ctx.device_size) == -1) {
perror("Ошибка получения размера устройства");
close(fd);
return 1;
}
printf("Размер устройства: %llu байт\n", ctx.device_size);
unsigned char *block_buffer = aligned_alloc(4096, _BLOCK_SIZE);
if (!block_buffer) {
perror("Ошибка выделения памяти");
close(fd);
return 1;
}
int bad_sectors_found = 0;
int pass = 1;
do {
printf("Проход %d...\n", pass++);
bad_sectors_found = 0;
ctx.current_pos = 0;
while (ctx.current_pos < ctx.device_size) {
size_t read_size = (ctx.device_size - ctx.current_pos < _BLOCK_SIZE) ?
ctx.device_size - ctx.current_pos : _BLOCK_SIZE;
if (read_block(fd, block_buffer, ctx.current_pos, read_size) == -1) {
printf("Ошибка чтения блока по смещению %llu\n", ctx.current_pos);
unsigned long long bad_sector = find_bad_sector(fd, ctx.current_pos);
if (bad_sector) {
printf("Найден битый сектор по смещению %llu\n", bad_sector);
if (repair_area(fd, bad_sector) == 0) {
printf("Сектор %llu восстановлен\n", bad_sector);
} else {
printf("Не удалось восстановить сектор %llu\n", bad_sector);
}
bad_sectors_found++;
ctx.current_pos = bad_sector + WRITE_SIZE;
} else {
ctx.current_pos += read_size;
}
} else {
ctx.current_pos += read_size;
}
// Прогресс каждые 1GB
if (ctx.current_pos % (1024*1024*1024) == 0) {
printf("Прогресс: %.1f%%\n",
(double)ctx.current_pos / ctx.device_size* 100);
}
}
} while (bad_sectors_found > 0);
free(block_buffer);
close(fd);
printf("Проверка завершена\n");
return 0;
}
Of course, the code is far from ideal, but it basically solves the set task.








