diff options
Diffstat (limited to 'src/third_party/wiredtiger/test/salvage/salvage.c')
-rw-r--r-- | src/third_party/wiredtiger/test/salvage/salvage.c | 713 |
1 files changed, 713 insertions, 0 deletions
diff --git a/src/third_party/wiredtiger/test/salvage/salvage.c b/src/third_party/wiredtiger/test/salvage/salvage.c new file mode 100644 index 00000000000..1c4d54df9e9 --- /dev/null +++ b/src/third_party/wiredtiger/test/salvage/salvage.c @@ -0,0 +1,713 @@ +/*- + * Public Domain 2014-2015 MongoDB, Inc. + * Public Domain 2008-2014 WiredTiger, Inc. + * + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "wt_internal.h" + +#include <assert.h> + +#define DUMP "__slvg.dump" /* Dump file */ +#define LOAD "__slvg.load" /* Build file */ +#define RSLT "__slvg.result" /* Result file */ +#define SLVG "__slvg.slvg" /* Salvage file */ + +#define PSIZE (2 * 1024) +#define OSIZE (PSIZE / 20) + +void build(int, int, int); +void copy(u_int, u_int); +void empty(int); +void print_res(int, int, int); +void process(void); +void run(int); +void t(int, u_int, int); +int usage(void); + +static const char *progname; /* Program name */ + +static FILE *res_fp; /* Results file */ +static u_int page_type; /* File types */ +static int value_unique; /* Values are unique */ +static int verbose; /* -v flag */ + +extern int __wt_optind; +extern char *__wt_optarg; + +int +main(int argc, char *argv[]) +{ + u_int ptype; + int ch, r; + + if ((progname = strrchr(argv[0], '/')) == NULL) + progname = argv[0]; + else + ++progname; + + r = 0; + ptype = 0; + while ((ch = __wt_getopt(progname, argc, argv, "r:t:v")) != EOF) + switch (ch) { + case 'r': + r = atoi(__wt_optarg); + if (r == 0) + return (usage()); + break; + case 't': + if (strcmp(__wt_optarg, "fix") == 0) + ptype = WT_PAGE_COL_FIX; + else if (strcmp(__wt_optarg, "var") == 0) + ptype = WT_PAGE_COL_VAR; + else if (strcmp(__wt_optarg, "row") == 0) + ptype = WT_PAGE_ROW_LEAF; + else + return (usage()); + break; + case 'v': + verbose = 1; + break; + case '?': + default: + return (usage()); + } + argc -= __wt_optind; + argv += __wt_optind; + if (argc != 0) + return (usage()); + + printf("salvage test run started\n"); + + t(r, ptype, 1); + t(r, ptype, 0); + + printf("salvage test run completed\n"); + return (EXIT_SUCCESS); +} + +void +t(int r, u_int ptype, int unique) +{ + printf("%sunique values\n", unique ? "" : "non-"); + value_unique = unique; + +#define NTESTS 24 + if (r == 0) { + if (ptype == 0) { + page_type = WT_PAGE_COL_FIX; + for (r = 1; r <= NTESTS; ++r) + run(r); + + page_type = WT_PAGE_COL_VAR; + for (r = 1; r <= NTESTS; ++r) + run(r); + + page_type = WT_PAGE_ROW_LEAF; + for (r = 1; r <= NTESTS; ++r) + run(r); + } else { + page_type = ptype; + for (r = 1; r <= NTESTS; ++r) + run(r); + } + } else if (ptype == 0) { + page_type = WT_PAGE_COL_FIX; + run(r); + page_type = WT_PAGE_COL_VAR; + run(r); + page_type = WT_PAGE_ROW_LEAF; + run(r); + } else { + page_type = ptype; + run(r); + } +} + +int +usage(void) +{ + (void)fprintf(stderr, + "usage: %s [-v] [-r run] [-t fix|rle|var|row]\n", progname); + return (EXIT_FAILURE); +} + +void +run(int r) +{ + char buf[128]; + + printf("\t%s: run %d\n", __wt_page_type_string(page_type), r); + + assert(system("rm -f WiredTiger* __slvg.* __schema.*") == 0); + assert((res_fp = fopen(RSLT, "w")) != NULL); + + /* + * Each run builds the LOAD file, and then appends the first page of + * the LOAD file into the SLVG file. The SLVG file is then salvaged, + * verified, and dumped into the DUMP file, which is compared to the + * results file, which are the expected results. + */ + switch (r) { + case 1: + /* + * Smoke test: empty files. + */ + build(0, 0, 0); copy(0, 0); + break; + case 2: + /* + * Smoke test: + * Sequential pages, all pages should be kept. + */ + build(100, 100, 20); copy(6, 1); + build(200, 200, 20); copy(7, 21); + build(300, 300, 20); copy(8, 41); + print_res(100, 100, 20); + print_res(200, 200, 20); + print_res(300, 300, 20); + break; + case 3: + /* + * Smoke test: + * Sequential pages, all pages should be kept. + */ + build(100, 100, 20); copy(8, 1); + build(200, 200, 20); copy(7, 21); + build(300, 300, 20); copy(6, 41); + print_res(100, 100, 20); + print_res(200, 200, 20); + print_res(300, 300, 20); + break; + case 4: + /* + * Case #1: + * 3 pages, each with 20 records starting with the same record + * and sequential LSNs; salvage should leave the page with the + * largest LSN. + */ + build(100, 100, 20); copy(6, 1); + build(100, 200, 20); copy(7, 1); + build(100, 300, 20); copy(8, 1); + print_res(100, 300, 20); + break; + case 5: + /* + * Case #1: + * 3 pages, each with 20 records starting with the same record + * and sequential LSNs; salvage should leave the page with the + * largest LSN. + */ + build(100, 100, 20); copy(6, 1); + build(100, 200, 20); copy(8, 1); + build(100, 300, 20); copy(7, 1); + print_res(100, 200, 20); + break; + case 6: + /* + * Case #1: + * 3 pages, each with 20 records starting with the same record + * and sequential LSNs; salvage should leave the page with the + * largest LSN. + */ + build(100, 100, 20); copy(8, 1); + build(100, 200, 20); copy(7, 1); + build(100, 300, 20); copy(6, 1); + print_res(100, 100, 20); + break; + case 7: + /* + * Case #2: + * The second page overlaps the beginning of the first page, and + * the first page has a higher LSN. + */ + build(110, 100, 20); copy(7, 11); + build(100, 200, 20); copy(6, 1); + print_res(100, 200, 10); + print_res(110, 100, 20); + break; + case 8: + /* + * Case #2: + * The second page overlaps the beginning of the first page, and + * the second page has a higher LSN. + */ + build(110, 100, 20); copy(6, 11); + build(100, 200, 20); copy(7, 1); + print_res(100, 200, 20); + print_res(120, 110, 10); + break; + case 9: + /* + * Case #3: + * The second page overlaps with the end of the first page, and + * the first page has a higher LSN. + */ + build(100, 100, 20); copy(7, 1); + build(110, 200, 20); copy(6, 11); + print_res(100, 100, 20); + print_res(120, 210, 10); + break; + case 10: + /* + * Case #3: + * The second page overlaps with the end of the first page, and + * the second page has a higher LSN. + */ + build(100, 100, 20); copy(6, 1); + build(110, 200, 20); copy(7, 11); + print_res(100, 100, 10); + print_res(110, 200, 20); + break; + case 11: + /* + * Case #4: + * The second page is a prefix of the first page, and the first + * page has a higher LSN. + */ + build(100, 100, 20); copy(7, 1); + build(100, 200, 5); copy(6, 1); + print_res(100, 100, 20); + break; + case 12: + /* + * Case #4: + * The second page is a prefix of the first page, and the second + * page has a higher LSN. + */ + build(100, 100, 20); copy(6, 1); + build(100, 200, 5); copy(7, 1); + print_res(100, 200, 5); + print_res(105, 105, 15); + break; + case 13: + /* + * Case #5: + * The second page is in the middle of the first page, and the + * first page has a higher LSN. + */ + build(100, 100, 40); copy(7, 1); + build(110, 200, 10); copy(6, 11); + print_res(100, 100, 40); + break; + case 14: + /* + * Case #5: + * The second page is in the middle of the first page, and the + * second page has a higher LSN. + */ + build(100, 100, 40); copy(6, 1); + build(110, 200, 10); copy(7, 11); + print_res(100, 100, 10); + print_res(110, 200, 10); + print_res(120, 120, 20); + break; + case 15: + /* + * Case #6: + * The second page is a suffix of the first page, and the first + * page has a higher LSN. + */ + build(100, 100, 40); copy(7, 1); + build(130, 200, 10); copy(6, 31); + print_res(100, 100, 40); + break; + case 16: + /* + * Case #6: + * The second page is a suffix of the first page, and the second + * page has a higher LSN. + */ + build(100, 100, 40); copy(6, 1); + build(130, 200, 10); copy(7, 31); + print_res(100, 100, 30); + print_res(130, 200, 10); + break; + case 17: + /* + * Case #9: + * The first page is a prefix of the second page, and the first + * page has a higher LSN. + */ + build(100, 100, 20); copy(7, 1); + build(100, 200, 40); copy(6, 1); + print_res(100, 100, 20); + print_res(120, 220, 20); + break; + case 18: + /* + * Case #9: + * The first page is a prefix of the second page, and the second + * page has a higher LSN. + */ + build(100, 100, 20); copy(6, 1); + build(100, 200, 40); copy(7, 1); + print_res(100, 200, 40); + break; + case 19: + /* + * Case #10: + * The first page is a suffix of the second page, and the first + * page has a higher LSN. + */ + build(130, 100, 10); copy(7, 31); + build(100, 200, 40); copy(6, 1); + print_res(100, 200, 30); + print_res(130, 100, 10); + break; + case 20: + /* + * Case #10: + * The first page is a suffix of the second page, and the second + * page has a higher LSN. + */ + build(130, 100, 10); copy(6, 31); + build(100, 200, 40); copy(7, 1); + print_res(100, 200, 40); + break; + case 21: + /* + * Case #11: + * The first page is in the middle of the second page, and the + * first page has a higher LSN. + */ + build(110, 100, 10); copy(7, 11); + build(100, 200, 40); copy(6, 1); + print_res(100, 200, 10); + print_res(110, 100, 10); + print_res(120, 220, 20); + break; + case 22: + /* + * Case #11: + * The first page is in the middle of the second page, and the + * second page has a higher LSN. + */ + build(110, 100, 10); copy(6, 11); + build(100, 200, 40); copy(7, 1); + print_res(100, 200, 40); + break; + case 23: + /* + * Column-store only: missing an initial key range of 99 + * records. + */ + build(100, 100, 10); copy(1, 100); + empty(99); + print_res(100, 100, 10); + break; + case 24: + /* + * Column-store only: missing a middle key range of 37 + * records. + */ + build(100, 100, 10); copy(1, 1); + build(138, 138, 10); copy(1, 48); + print_res(100, 100, 10); + empty(37); + print_res(138, 138, 10); + break; + default: + fprintf(stderr, "salvage: %d: no such test\n", r); + exit(EXIT_FAILURE); + } + + assert(fclose(res_fp) == 0); + + process(); + + snprintf(buf, sizeof(buf), "cmp %s %s > /dev/null", DUMP, RSLT); + if (system(buf)) { + fprintf(stderr, + "check failed, salvage results were incorrect\n"); + exit(EXIT_FAILURE); + } +} + +/* + * file_exists -- + * Return if the file exists. + */ +static int +file_exists(const char *path) +{ + struct stat sb; + + return (stat(path, &sb) == 0); +} + +/* + * build -- + * Build a row- or column-store page in a file. + */ +void +build(int ikey, int ivalue, int cnt) +{ + WT_CONNECTION *conn; + WT_CURSOR *cursor; + WT_ITEM key, value; + WT_SESSION *session; + char config[256], kbuf[64], vbuf[64]; + int new_slvg; + + /* + * Disable logging: we're modifying files directly, we don't want to + * run recovery. + */ + assert(wiredtiger_open( + NULL, NULL, "create,log=(enabled=false)", &conn) == 0); + assert(conn->open_session(conn, NULL, NULL, &session) == 0); + assert(session->drop(session, "file:" LOAD, "force") == 0); + + switch (page_type) { + case WT_PAGE_COL_FIX: + (void)snprintf(config, sizeof(config), + "key_format=r,value_format=7t," + "allocation_size=%d," + "internal_page_max=%d,internal_item_max=%d," + "leaf_page_max=%d,leaf_item_max=%d", + PSIZE, PSIZE, OSIZE, PSIZE, OSIZE); + break; + case WT_PAGE_COL_VAR: + (void)snprintf(config, sizeof(config), + "key_format=r," + "allocation_size=%d," + "internal_page_max=%d,internal_item_max=%d," + "leaf_page_max=%d,leaf_item_max=%d", + PSIZE, PSIZE, OSIZE, PSIZE, OSIZE); + break; + case WT_PAGE_ROW_LEAF: + (void)snprintf(config, sizeof(config), + "key_format=u," + "allocation_size=%d," + "internal_page_max=%d,internal_item_max=%d," + "leaf_page_max=%d,leaf_item_max=%d", + PSIZE, PSIZE, OSIZE, PSIZE, OSIZE); + break; + default: + assert(0); + } + assert(session->create(session, "file:" LOAD, config) == 0); + assert(session->open_cursor( + session, "file:" LOAD, NULL, "bulk", &cursor) == 0); + for (; cnt > 0; --cnt, ++ikey, ++ivalue) { + switch (page_type) { /* Build the key. */ + case WT_PAGE_COL_FIX: + case WT_PAGE_COL_VAR: + break; + case WT_PAGE_ROW_LEAF: + snprintf(kbuf, sizeof(kbuf), "%010d KEY------", ikey); + key.data = kbuf; + key.size = 20; + cursor->set_key(cursor, &key); + break; + } + + switch (page_type) { /* Build the value. */ + case WT_PAGE_COL_FIX: + cursor->set_value(cursor, ivalue & 0x7f); + break; + case WT_PAGE_COL_VAR: + case WT_PAGE_ROW_LEAF: + snprintf(vbuf, sizeof(vbuf), + "%010d VALUE----", value_unique ? ivalue : 37); + value.data = vbuf; + value.size = 20; + cursor->set_value(cursor, &value); + } + assert(cursor->insert(cursor) == 0); + } + + /* + * The first time through this routine we create the salvage file and + * then remove it (all we want is the appropriate schema entry, we're + * creating the salvage file itself by hand). + */ + new_slvg = !file_exists(SLVG); + if (new_slvg) { + assert(session->drop(session, "file:" SLVG, "force") == 0); + assert(session->create(session, "file:" SLVG, config) == 0); + } + assert(conn->close(conn, 0) == 0); + if (new_slvg) + (void)remove(SLVG); +} + +/* + * copy -- + * Copy the created page to the end of the salvage file. + */ +void +copy(u_int gen, u_int recno) +{ + FILE *ifp, *ofp; + WT_PAGE_HEADER *dsk; + WT_BLOCK_HEADER *blk; + char buf[PSIZE]; + + assert((ifp = fopen(LOAD, "r")) != NULL); + + /* + * If the salvage file doesn't exist, then we're creating it: + * copy the first sector (the file description). + * Otherwise, we are appending to an existing file. + */ + if (file_exists(SLVG)) + assert((ofp = fopen(SLVG, "a")) != NULL); + else { + assert((ofp = fopen(SLVG, "w")) != NULL); + assert(fread(buf, 1, PSIZE, ifp) == PSIZE); + assert(fwrite(buf, 1, PSIZE, ofp) == PSIZE); + } + + /* + * If there's data, copy/update the first formatted page. + */ + if (gen != 0) { + assert(fseek(ifp, (long)PSIZE, SEEK_SET) == 0); + assert(fread(buf, 1, PSIZE, ifp) == PSIZE); + dsk = (void *)buf; + if (page_type != WT_PAGE_ROW_LEAF) + dsk->recno = recno; + dsk->write_gen = gen; + blk = WT_BLOCK_HEADER_REF(buf); + blk->cksum = 0; + blk->cksum = __wt_cksum(dsk, PSIZE); + assert(fwrite(buf, 1, PSIZE, ofp) == PSIZE); + } + + assert(fclose(ifp) == 0); + assert(fclose(ofp) == 0); +} + +/* + * process -- + * Salvage, verify and dump the created file. + */ +void +process(void) +{ + FILE *fp; + WT_CONNECTION *conn; + WT_CURSOR *cursor; + const char *key, *value; + WT_SESSION *session; + char config[100]; + + /* Salvage. */ + config[0] = '\0'; + if (verbose) + snprintf(config, sizeof(config), + "error_prefix=\"%s\",verbose=[salvage,verify],", + progname); + strcat(config, "log=(enabled=false),"); + + assert(wiredtiger_open(NULL, NULL, config, &conn) == 0); + assert(conn->open_session(conn, NULL, NULL, &session) == 0); + assert(session->salvage(session, "file:" SLVG, 0) == 0); + assert(conn->close(conn, 0) == 0); + + /* Verify. */ + assert(wiredtiger_open(NULL, NULL, config, &conn) == 0); + assert(conn->open_session(conn, NULL, NULL, &session) == 0); + assert(session->verify(session, "file:" SLVG, 0) == 0); + assert(conn->close(conn, 0) == 0); + + /* Dump. */ + assert((fp = fopen(DUMP, "w")) != NULL); + assert(wiredtiger_open(NULL, NULL, config, &conn) == 0); + assert(conn->open_session(conn, NULL, NULL, &session) == 0); + assert(session->open_cursor( + session, "file:" SLVG, NULL, "dump=print", &cursor) == 0); + while (cursor->next(cursor) == 0) { + if (page_type == WT_PAGE_ROW_LEAF) { + assert(cursor->get_key(cursor, &key) == 0); + assert(fputs(key, fp) >= 0); + assert(fputc('\n', fp) >= 0); + } + assert(cursor->get_value(cursor, &value) == 0); + assert(fputs(value, fp) >= 0); + assert(fputc('\n', fp) >= 0); + } + assert(conn->close(conn, 0) == 0); + assert(fclose(fp) == 0); +} + +/* + * empty -- + * Print empty print_res, for fixed-length column-store files. + */ +void +empty(int cnt) +{ + int i; + + if (page_type == WT_PAGE_COL_FIX) + for (i = 0; i < cnt; ++i) + fputs("\\00\n", res_fp); +} + +/* + * print_res -- + * Write results file. + */ +void +print_res(int key, int value, int cnt) +{ + static const char hex[] = "0123456789abcdef"; + int ch; + + for (; cnt > 0; ++key, ++value, --cnt) { + switch (page_type) { /* Print key */ + case WT_PAGE_COL_FIX: + case WT_PAGE_COL_VAR: + break; + case WT_PAGE_ROW_LEAF: + fprintf(res_fp, "%010d KEY------\n", key); + break; + } + + switch (page_type) { /* Print value */ + case WT_PAGE_COL_FIX: + ch = value & 0x7f; + if (isprint(ch)) { + if (ch == '\\') + fputc('\\', res_fp); + fputc(ch, res_fp); + } else { + fputc('\\', res_fp); + fputc(hex[(ch & 0xf0) >> 4], res_fp); + fputc(hex[ch & 0x0f], res_fp); + } + fputc('\n', res_fp); + break; + case WT_PAGE_COL_VAR: + case WT_PAGE_ROW_LEAF: + fprintf(res_fp, + "%010d VALUE----\n", value_unique ? value : 37); + break; + } + } +} |