/* * lsbd-setup.c * Copyright (C) 2010 Krzysztof Mazur * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Fundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include // #define CHECK_DATA_CHECKSUMS 1 // #define CHECK_FOR_MULTIPLE_COPIES 1 #if 0 typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; #endif #ifdef HAVE_BYTESWAP_H #include #else #define bswap_16(x) \ ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8)) #define bswap_32(x) \ ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) #define bswap_64(x) \ ((((x) & 0xff00000000000000ull) >> 56) \ | (((x) & 0x00ff000000000000ull) >> 40) \ | (((x) & 0x0000ff0000000000ull) >> 24) \ | (((x) & 0x000000ff00000000ull) >> 8) \ | (((x) & 0x00000000ff000000ull) << 8) \ | (((x) & 0x0000000000ff0000ull) << 24) \ | (((x) & 0x000000000000ff00ull) << 40) \ | (((x) & 0x00000000000000ffull) << 56)) #endif #include /* we want internal structures */ #define __LSBD_FORMAT__ 1 #include #define u8_to_cpu(x) (x) #define cpu_to_u8(x) (x) #if BYTE_ORDER == BIG_ENDIAN #define u16_to_cpu(x) (x) #define u32_to_cpu(x) (x) #define u64_to_cpu(x) (x) #define cpu_to_u16(x) (x) #define cpu_to_u32(x) (x) #define cpu_to_u64(x) (x) #elif BYTE_ORDER == LITTLE_ENDIAN #define u16_to_cpu(x) (bswap_16(x)) #define u32_to_cpu(x) (bswap_32(x)) #define u64_to_cpu(x) (bswap_64(x)) #define cpu_to_u16(x) (bswap_16(x)) #define cpu_to_u32(x) (bswap_32(x)) #define cpu_to_u64(x) (bswap_64(x)) #else #error "Please define type conversion macros" #endif static char *device = NULL; struct lsector { u32 mapping; /* */ u64 epoch; }; struct lsbd { int fd; unsigned int sector_size; unsigned int sectors_per_block; unsigned int blocks; unsigned int lsectors; int mirrored; struct lsector *lcache; }; struct lsbd lsbd; static void show_info(void) { fprintf(stderr, "lsbd-fsck "PACKAGE_VERSION"\n" "Copyright (C) 2011 " "Krzysztof Mazur \n\n"); } static void show_usage(char *name) { show_info(); fprintf(stderr, "usage: %s [options] (file)\n" "Options:\n" " -b, --start set partition start\n" " -h, --help show this help\n" " -p, --partition partition number\n" " -s, --size set partition size\n" " -m, --mirror use mirroring\n" " -V, --version show version\n", (name == NULL) ? "lsbd-fsck" : name); exit(0); } static void parse_command_line(int argc, char **args) { int c; int option_index = 0; static struct option long_options[] = { {"help", 0, 0, 'h'}, {"version", 0, 0, 'V'}, {0, 0, 0, 0}, }; while (1) { c = getopt_long(argc, args, "hV", long_options, &option_index); if (c == -1) break; switch (c) { case 'h': show_usage(args[0]); break; case 'V': exit(0); break; default: fprintf(stderr, "Unknown option.\n"); exit(1); } } if (optind < argc) { device = args[optind++]; } } static ssize_t read_all(int fd, void *buf, size_t count) { char *b = buf; size_t readed = 0; ssize_t ret; while (readed < count) { ret = read(fd, b, count - readed); if (ret < 0) return -errno; if (ret == 0) return readed; b += ret; readed += ret; } return readed; } static void *lsbd_read_sector(struct lsbd *p, unsigned int block, unsigned int sector) { ssize_t ret; size_t size; void *b; size = p->sector_size; b = memalign(4096, size); lseek(p->fd, (unsigned long long) size * (block * p->sectors_per_block + sector), SEEK_SET); ret = read_all(p->fd, b, size); if (ret != size) { free(b); return NULL; } return b; } static u32 checksum(const void *buf, size_t count) { const u32 *b = buf; size_t i; u32 csum = 0x12345678; if (count & 3) { fprintf(stderr, "unaligned checksum\n"); return 0; } for (i = 0; i < count / 4; i++) csum += b[i]; return csum; } static int lsbd_block_verify_ok(struct lsbd *p, const struct lsbd_block *b) { u32 csum = checksum(b, sizeof(*b) - 4); if (b->checksum == cpu_to_u32(csum)) return 1; fprintf(stderr, "block: checksum error, checksum: %08x, " "readed: %08x\n", csum, u32_to_cpu(b->checksum)); fprintf(stderr, "%zd\n", sizeof(*b) - 4); return 0; } static int find_first_block(struct lsbd *p) { struct lsbd_block *b; unsigned int block; int ret; block = 0; for (;;) { b = lsbd_read_sector(p, block, 0); if (b == NULL) { fprintf(stderr, "cannot read block %d\n", block); abort(); } ret = lsbd_block_verify_ok(p, b); if (ret) break; fprintf(stderr, "verify_failure %d\n", block); abort(); } fprintf(stderr, "LSBD version %ld.%ld\n", (unsigned long) u32_to_cpu(b->version), (unsigned long) u32_to_cpu(b->revision)); p->sector_size = u32_to_cpu(b->sector_size); p->sectors_per_block = u32_to_cpu(b->sectors_per_block); p->blocks = u32_to_cpu(b->blocks); p->lsectors = u32_to_cpu(b->lsectors); p->mirrored = u32_to_cpu(b->flags) & LSBD_MIRRORED; fprintf(stderr, "sector size: %d kB, block size: %d kB, " "physical %Ld kB\n", p->sector_size >> 10, (p->sector_size >> 10) * p->sectors_per_block, (unsigned long long) p->blocks * (p->sector_size >> 10) * p->sectors_per_block); fprintf(stderr, "logical sectors: %d kB, logical size %Ld kB%s\n", p->lsectors, (unsigned long long) p->lsectors * (p->sector_size >> 10), p->mirrored ? " (mirrored)" : ""); p->lcache = calloc(p->lsectors, sizeof(*p->lcache)); if (p->lcache == NULL) abort(); free(b); return 0; } static int show_sector(struct lsbd *p, unsigned int block, unsigned int sector) { unsigned char *d = lsbd_read_sector(p, block, sector); unsigned int i, j; if (d == NULL) fprintf(stderr, "block %d.%d: I/O error\n", block, sector); fprintf(stderr, "block %d.%d: dump:\n", block, sector); for (i = 0; i < p->sector_size; i += 16) { fprintf(stderr, "%04x: ", i); for (j = 0; j < 16; j++) fprintf(stderr, "%02x ", d[i + j]); fprintf(stderr, "\n"); } fprintf(stderr, "--- end of dump ---\n"); return 0; } static int lsbd_sector_eq(struct lsbd *p, unsigned int a, unsigned int b) { if (p->mirrored) { a &= ~p->sectors_per_block; b &= ~p->sectors_per_block; } return a == b; } static int check_lcache(struct lsbd *p, unsigned int block, struct lsbd_block *b) { unsigned int lcache_offset; unsigned int lcache_chunk; unsigned int lcache_base; unsigned int i; u64 epoch; u32 lcache_checksum; lsbd_lcache_t *cache; epoch = u64_to_cpu(b->epoch); lcache_offset = u32_to_cpu(b->lcache_offset); if (lcache_offset >= p->sector_size) { fprintf(stderr, "block %d: incorrect lcache_offset\n", block); return -EINVAL; } lcache_chunk = u32_to_cpu(b->lcache_chunk); if (lcache_chunk > (p->sector_size - lcache_offset) / sizeof(*cache)) { lcache_chunk = (p->sector_size - lcache_offset) / sizeof(*cache); fprintf(stderr, "block %d: lcache too big\n", block); } lcache_base = u32_to_cpu(b->lcache_base); cache = (void *)((char *) b + lcache_offset); lcache_checksum = checksum(cache, lcache_chunk * sizeof(*cache)); if (u32_to_cpu(b->lcache_checksum) != lcache_checksum) { fprintf(stderr, "block %d: incorrect lcache_checksum " "checksum %08x, readed %08x %d\n", block, lcache_checksum, u32_to_cpu(b->lcache_checksum), lcache_chunk); return -EINVAL; } for (i = 0; i < lcache_chunk; i++) { unsigned int sector_id = lcache_base + i; unsigned int psector; if (sector_id >= p->lsectors) break; psector = u32_to_cpu(cache[i]); if ((p->lcache[sector_id].epoch <= epoch) && p->lcache[sector_id].epoch && !lsbd_sector_eq(p, psector, p->lcache[sector_id].mapping)) { fprintf(stderr, "block %d: invalid lcache mapping %d\n", block, sector_id); fprintf(stderr, "lcache: epoch %Ld, mapping %d\n", epoch, psector); fprintf(stderr, "valid: epoch %Ld, mapping %d\n", p->lcache[sector_id].epoch, p->lcache[sector_id].mapping); } p->lcache[sector_id].mapping = psector; p->lcache[sector_id].epoch = epoch; #if 0 if (sector_id == 2) { fprintf(stderr, "block %d/%d: sector %d " "mapped to " "physical sector %d\n", block, p->blocks, sector_id, psector); } #endif } return 0; } static int check_blocks(struct lsbd *p) { struct lsbd_block *b; struct lsbd_sect *sects; unsigned int block; unsigned int used; int ret; unsigned int ptab_offset; unsigned int i, j; u32 ptab_checksum; u64 epoch; u64 epoch_max = 0; u64 epoch_min = ~0; time_t t = time(NULL); time_t old_t = t; for (block = 0; block < p->blocks; block++) { b = lsbd_read_sector(p, block, 0); if (b == NULL) { fprintf(stderr, "block %d: read error\n", block); continue; } t = time(NULL); if (t != old_t) { fprintf(stderr, "%d/%d blocks\n", block, p->blocks); old_t = t; } ret = lsbd_block_verify_ok(p, b); if (!ret) { fprintf(stderr, "block %d: checksum error\n", block); free(b); continue; } epoch = u64_to_cpu(b->epoch); if (epoch > epoch_max) epoch_max = epoch; if (epoch < epoch_min) epoch_min = epoch; ptab_offset = u32_to_cpu(b->ptab_offset); if (ptab_offset >= p->sector_size) { fprintf(stderr, "block %d: incorrect ptab_offset\n", block); free(b); continue; } sects = (void *)((char *) b + ptab_offset); ptab_checksum = checksum(sects, (p->sectors_per_block - 1) * sizeof(*sects)); if (u32_to_cpu(b->ptab_checksum) != ptab_checksum) { fprintf(stderr, "block %d: incorrect ptab_checksum " "checksum %08x, readed %08x %d\n", block, ptab_checksum, u32_to_cpu(b->ptab_checksum), p->sectors_per_block); free(b); continue; } for (i = 0; i < p->sectors_per_block - 1; i++) { unsigned int sector_id; sector_id = u32_to_cpu(sects[i].id); if (sector_id == LSBD_SECT_INVALID) continue; if (sector_id >= p->lsectors) { fprintf(stderr, "error: sector %ld\n", sector_id); exit(1); continue; } p->lcache[sector_id].mapping = block * p->sectors_per_block + 1 + i; p->lcache[sector_id].epoch = epoch; #if 0 if (sector_id == 924928 >> 3) { fprintf(stderr, "block %d.%d: sector %d, " "physical %d\n", block, i + 1, sector_id, block * p->sectors_per_block + i + 1); show_sector(p, block, i + 1); } #endif } #if CHECK_DATA_CHECKSUMS used = 0; for (i = 0; i < p->sectors_per_block - 1; i++) { unsigned int sector_id; void *s; sector_id = u32_to_cpu(sects[i].id); if (sector_id == LSBD_SECT_INVALID) continue; used++; s = lsbd_read_sector(p, block, 1 + i); if (s == NULL) { fprintf(stderr, "block %d.%d: read error\n", block, 1 + i); continue; } if (checksum(s, p->sector_size) != u32_to_cpu(sects[i].data_checksum)) { fprintf(stderr, "block %d.%d: data checksum " "error\n", block, 1 + i); } free(s); } // printf("block %d used %d\n", block, used); #endif /* * Current versions allow for multiple copies of the same * logical sector in one block and recent versions should * support it. */ #if CHECK_FOR_MULTIPLE_COPIES for (i = 0; i < p->sectors_per_block - 1; i++) { unsigned int sector_id; sector_id = u32_to_cpu(sects[i].id); if (sector_id == LSBD_SECT_INVALID) continue; for (j = i + 1; j < p->sectors_per_block - 1; j++) { if (u32_to_cpu(sects[i].id) == u32_to_cpu(sects[j].id)) { fprintf(stderr, "block %d: " "multiple copies of " "the same sector " "(%d and %d)\n", block, i, j); } } } #endif check_lcache(p, block, b); free(b); } fprintf(stderr, "epoch: %Ld-%Ld\n", epoch_min, epoch_max); return 0; } static void print_used_blocks(struct lsbd *p) { unsigned int i; unsigned char *used; unsigned int u[16]; for (i = 0; i < 16; i++) u[i] = 0; used = calloc(p->blocks, sizeof(*used)); for (i = 0; i < p->lsectors; i++) { unsigned int block; if (p->lcache[i].mapping == LSBD_SECT_INVALID) continue; if (p->lcache[i].mapping == LSBD_SECT_ZERO) continue; block = p->lcache[i].mapping / p->sectors_per_block; if (block >= p->blocks) abort(); used[block]++; } for (i = 0; i < p->blocks - 1; i += 2) u[used[i] + used[i + 1]]++; for (i = 0; i < 16; i++) printf("%d %d\n", i, u[i]); // for (i = 0; i < p->blocks - 1; i+=2) // printf("%d %d\n", i, used[i] + used[i + 1]); } int main(int argc, char **argv) { int fd; parse_command_line(argc, argv); if (device == NULL) { fprintf(stderr, "error: device option is required\n"); exit(EXIT_FAILURE); } fd = open(device, O_RDONLY); if (fd == -1) { perror("open"); exit(EXIT_FAILURE); } lsbd.fd = fd; lsbd.sector_size = 1024; find_first_block(&lsbd); check_blocks(&lsbd); print_used_blocks(&lsbd); exit(EXIT_SUCCESS); }