Created
June 19, 2021 10:53
-
-
Save alexh-name/58fd819d8558b931c78fd0ab551e13a3 to your computer and use it in GitHub Desktop.
mirror of http://dave.jikos.cz/fix-dev-count.c. thanks!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* One-time fix for mismatch of devices after a failed mount with message like | |
* | |
* BTRFS error (device sde): super_num_devices 7 mismatch with num_devices 6 found here | |
* | |
* Compile: | |
* gcc -o fix-dev-count fix-dev-count.c | |
* | |
* Run: | |
* ./fix-dev-count /dev/sdx <number> | |
* | |
* where sdx is your devices and <number> is the value from the error message, | |
* in this case 6. | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <sys/fcntl.h> | |
#include <sys/stat.h> | |
#include <inttypes.h> | |
#include <string.h> | |
#define BLOCKSIZE (4096) | |
static char buf[BLOCKSIZE]; | |
static int csum_size; | |
typedef unsigned int u32; | |
typedef unsigned int __le32; | |
typedef unsigned short int __le16; | |
typedef unsigned long long __le64; | |
typedef unsigned long long u64; | |
typedef unsigned char u8; | |
#define BTRFS_FSID_SIZE 16 | |
#define BTRFS_UUID_SIZE 16 | |
#define BTRFS_CSUM_SIZE 32 | |
#define BTRFS_SUPER_INFO_OFFSET (64 * 1024) | |
#define BTRFS_SUPER_INFO_SIZE 4096 | |
#define BTRFS_CSUM_TYPE_CRC32 0 | |
static int btrfs_csum_sizes[] = { 4 }; | |
#if __BYTE_ORDER == __BIG_ENDIAN | |
#define cpu_to_le64(x) ((__le64)(u64)(bswap_64(x))) | |
#define le64_to_cpu(x) ((u64)(__le64)(bswap_64(x))) | |
#define cpu_to_le32(x) ((__le32)(u32)(bswap_32(x))) | |
#define le32_to_cpu(x) ((u32)(__le32)(bswap_32(x))) | |
#else | |
#define cpu_to_le64(x) ((__le64)(u64)(x)) | |
#define le64_to_cpu(x) ((u64)(__le64)(x)) | |
#define cpu_to_le32(x) ((__le32)(u32)(x)) | |
#define le32_to_cpu(x) ((u32)(__le32)(x)) | |
#endif | |
#define get_unaligned_le32(p) le32_to_cpu(((const struct __una_u32 *)(p))->x) | |
#define put_unaligned_le32(val,p) (((struct __una_u32 *)(p))->x = cpu_to_le32(val)) | |
struct __una_u32 { u32 x; } __attribute__((__packed__)); | |
#define get_unaligned_le64(p) le64_to_cpu(((const struct __una_u64 *)(p))->x) | |
#define put_unaligned_le64(val,p) (((struct __una_u64 *)(p))->x = cpu_to_le64(val)) | |
struct __una_u64 { u64 x; } __attribute__((__packed__)); | |
u32 crc32c_le(u32 seed, unsigned char const *data, size_t length); | |
void crc32c_optimization_init(void); | |
#define crc32c(seed, data, length) crc32c_le(seed, (unsigned char const *)data, length) | |
#define btrfs_crc32c crc32c | |
/* crc32.c */ | |
u32 __crc32c_le(u32 crc, unsigned char const *data, size_t length); | |
static u32 (*crc_function)(u32 crc, unsigned char const *data, size_t length) = __crc32c_le; | |
static const u32 crc32c_table[256] = { | |
0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, | |
0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, | |
0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, | |
0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, | |
0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, | |
0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, | |
0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, | |
0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, | |
0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, | |
0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, | |
0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, | |
0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, | |
0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, | |
0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, | |
0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, | |
0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, | |
0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, | |
0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, | |
0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, | |
0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, | |
0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, | |
0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, | |
0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, | |
0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, | |
0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, | |
0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, | |
0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, | |
0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, | |
0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, | |
0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, | |
0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, | |
0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, | |
0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, | |
0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, | |
0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, | |
0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, | |
0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, | |
0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, | |
0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, | |
0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, | |
0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, | |
0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, | |
0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, | |
0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, | |
0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, | |
0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, | |
0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, | |
0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, | |
0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, | |
0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, | |
0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, | |
0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, | |
0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, | |
0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, | |
0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, | |
0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, | |
0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, | |
0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, | |
0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, | |
0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, | |
0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, | |
0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, | |
0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, | |
0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L | |
}; | |
u32 __crc32c_le(u32 crc, unsigned char const *data, size_t length) | |
{ | |
while (length--) | |
crc = | |
crc32c_table[(crc ^ *data++) & 0xFFL] ^ (crc >> 8); | |
return crc; | |
} | |
u32 crc32c_le(u32 crc, unsigned char const *data, size_t length) | |
{ | |
return crc_function(crc, data, length); | |
} | |
/* crc32.c EOF*/ | |
struct btrfs_super_block { | |
char csum[BTRFS_CSUM_SIZE]; | |
char fsid[BTRFS_FSID_SIZE]; | |
__le64 bytenr; | |
__le64 flags; | |
__le64 magic; | |
__le64 generation; | |
__le64 root; | |
__le64 chunk_root; | |
__le64 log_root; | |
__le64 log_root_transid; | |
__le64 total_bytes; | |
__le64 bytes_used; | |
__le64 root_dir_objectid; | |
__le64 num_devices; | |
__le32 sectorsize; | |
__le32 nodesize; | |
__le32 leafsize; | |
__le32 stripesize; | |
__le32 sys_chunk_array_size; | |
__le64 chunk_root_generation; | |
__le64 compat_flags; | |
__le64 compat_ro_flags; | |
__le64 incompat_flags; | |
__le16 csum_type; | |
u8 root_level; | |
u8 chunk_root_level; | |
u8 log_root_level; | |
/* | |
struct btrfs_dev_item dev_item; | |
char label[BTRFS_LABEL_SIZE]; | |
__le64 cache_generation; | |
__le64 reserved[31]; | |
u8 sys_chunk_array[BTRFS_SYSTEM_CHUNK_ARRAY_SIZE]; | |
struct btrfs_root_backup super_roots[BTRFS_NUM_BACKUP_ROOTS]; | |
*/ | |
} __attribute__ ((__packed__)); | |
struct btrfs_header { | |
u8 csum[BTRFS_CSUM_SIZE]; | |
u8 fsid[BTRFS_FSID_SIZE]; | |
__le64 bytenr; | |
__le64 flags; | |
u8 chunk_tree_uuid[BTRFS_UUID_SIZE]; | |
__le64 generation; | |
__le64 owner; | |
__le32 nritems; | |
u8 level; | |
} __attribute__ ((__packed__)); | |
u32 btrfs_csum_data(char *data, u32 seed, size_t len) | |
{ | |
return crc32c(seed, data, len); | |
} | |
void btrfs_csum_final(u32 crc, char *result) | |
{ | |
put_unaligned_le32(~crc, result); | |
} | |
static int check_csum_superblock(void *sb) | |
{ | |
char result[csum_size]; | |
u32 crc = ~(u32)0; | |
crc = btrfs_csum_data((char *)sb + BTRFS_CSUM_SIZE, | |
crc, BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE); | |
btrfs_csum_final(crc, result); | |
return !memcmp(sb, &result, csum_size); | |
} | |
static void update_block_csum(void *block, int is_sb) | |
{ | |
char result[csum_size]; | |
struct btrfs_header *hdr; | |
u32 crc = ~(u32)0; | |
if (is_sb) { | |
crc = btrfs_csum_data((char *)block + BTRFS_CSUM_SIZE, crc, | |
BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE); | |
} else { | |
crc = btrfs_csum_data((char *)block + BTRFS_CSUM_SIZE, crc, | |
BLOCKSIZE - BTRFS_CSUM_SIZE); | |
} | |
btrfs_csum_final(crc, result); | |
memset(block, 0, BTRFS_CSUM_SIZE); | |
hdr = (struct btrfs_header *)block; | |
memcpy(&hdr->csum, result, csum_size); | |
} | |
int main(int argc, char **argv) { | |
int fd; | |
loff_t off; | |
loff_t fsize; | |
struct stat st; | |
int ret; | |
struct btrfs_header *hdr; | |
int devcount; | |
struct btrfs_super_block *sb; | |
if (argc <= 2) { | |
printf("Usage: %s image number\n", argv[0]); | |
exit(1); | |
} | |
fd = open(argv[1], O_RDWR | O_EXCL); | |
if (fd == -1) { | |
perror("open()"); | |
exit(1); | |
} | |
devcount = atoi(argv[2]); | |
/* verify superblock */ | |
csum_size = btrfs_csum_sizes[BTRFS_CSUM_TYPE_CRC32]; | |
fstat(fd, &st); | |
fsize = st.st_size; | |
off = BTRFS_SUPER_INFO_OFFSET; | |
ret = pread(fd, buf, BLOCKSIZE, off); | |
if (ret <= 0) { | |
printf("pread error %d at offset %llu\n", | |
ret, (unsigned long long)off); | |
exit(1); | |
} | |
if (ret != BLOCKSIZE) { | |
printf("pread error at offset %llu: read only %d bytes\n", | |
(unsigned long long)off, ret); | |
exit(1); | |
} | |
hdr = (struct btrfs_header *)buf; | |
/* verify checksum */ | |
if (!check_csum_superblock(&hdr->csum)) { | |
printf("super block checksum does not match at offset %llu, will be corrected\n", | |
(unsigned long long)off); | |
} else { | |
printf("super block checksum is ok\n"); | |
} | |
sb = (struct btrfs_super_block *)buf; | |
ret = le64_to_cpu(sb->num_devices); | |
printf("Found devices %d\n", ret); | |
printf("Set devices to %d\n", devcount); | |
sb->num_devices = cpu_to_le64(devcount); | |
printf("Update csum\n"); | |
update_block_csum(buf, 1); | |
ret = pwrite(fd, buf, BLOCKSIZE, off); | |
if (ret <= 0) { | |
printf("pwrite error %d at offset %llu\n", | |
ret, (unsigned long long)off); | |
exit(1); | |
} | |
if (ret != BLOCKSIZE) { | |
printf("pwrite error at offset %llu: written only %d bytes\n", | |
(unsigned long long)off, ret); | |
exit(1); | |
} | |
fsync(fd); | |
close(fd); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment