diff options
Diffstat (limited to 'examples/redis-unstable/src/redis-check-rdb.c')
| -rw-r--r-- | examples/redis-unstable/src/redis-check-rdb.c | 451 |
1 files changed, 0 insertions, 451 deletions
diff --git a/examples/redis-unstable/src/redis-check-rdb.c b/examples/redis-unstable/src/redis-check-rdb.c deleted file mode 100644 index 11781dd..0000000 --- a/examples/redis-unstable/src/redis-check-rdb.c +++ /dev/null @@ -1,451 +0,0 @@ -/* - * Copyright (c) 2016-Present, Redis Ltd. - * All rights reserved. - * - * Licensed under your choice of (a) the Redis Source Available License 2.0 - * (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the - * GNU Affero General Public License v3 (AGPLv3). - */ - -#include "mt19937-64.h" -#include "server.h" -#include "rdb.h" - -#include <stdarg.h> -#include <sys/time.h> -#include <unistd.h> -#include <sys/stat.h> - -void createSharedObjects(void); -void rdbLoadProgressCallback(rio *r, const void *buf, size_t len); -int rdbCheckMode = 0; - -struct { - rio *rio; - robj *key; /* Current key we are reading. */ - int key_type; /* Current key type if != -1. */ - unsigned long keys; /* Number of keys processed. */ - unsigned long expires; /* Number of keys with an expire. */ - unsigned long already_expired; /* Number of keys already expired. */ - unsigned long subexpires; /* Number of keys with subexpires */ - int doing; /* The state while reading the RDB. */ - int error_set; /* True if error is populated. */ - char error[1024]; -} rdbstate; - -/* At every loading step try to remember what we were about to do, so that - * we can log this information when an error is encountered. */ -#define RDB_CHECK_DOING_START 0 -#define RDB_CHECK_DOING_READ_TYPE 1 -#define RDB_CHECK_DOING_READ_EXPIRE 2 -#define RDB_CHECK_DOING_READ_KEY 3 -#define RDB_CHECK_DOING_READ_OBJECT_VALUE 4 -#define RDB_CHECK_DOING_CHECK_SUM 5 -#define RDB_CHECK_DOING_READ_LEN 6 -#define RDB_CHECK_DOING_READ_AUX 7 -#define RDB_CHECK_DOING_READ_MODULE_AUX 8 -#define RDB_CHECK_DOING_READ_FUNCTIONS 9 - -char *rdb_check_doing_string[] = { - "start", - "read-type", - "read-expire", - "read-key", - "read-object-value", - "check-sum", - "read-len", - "read-aux", - "read-module-aux", - "read-functions" -}; - -char *rdb_type_string[] = { - "string", - "list-linked", - "set-hashtable", - "zset-v1", - "hash-hashtable", - "zset-v2", - "module-pre-release", - "module-value", - "", - "hash-zipmap", - "list-ziplist", - "set-intset", - "zset-ziplist", - "hash-ziplist", - "quicklist", - "stream", - "hash-listpack", - "zset-listpack", - "quicklist-v2", - "stream-v2", - "set-listpack", - "stream-v3", - "hash-hashtable-md-pre-release", - "hash-listpack-md-pre-release", - "hash-hashtable-md", - "hash-listpack-md", - "stream-v4", -}; - -/* Show a few stats collected into 'rdbstate' */ -void rdbShowGenericInfo(void) { - printf("[info] %lu keys read\n", rdbstate.keys); - printf("[info] %lu expires\n", rdbstate.expires); - printf("[info] %lu already expired\n", rdbstate.already_expired); - printf("[info] %lu subexpires\n", rdbstate.subexpires); -} - -/* Called on RDB errors. Provides details about the RDB and the offset - * we were when the error was detected. */ -void rdbCheckError(const char *fmt, ...) { - char msg[1024]; - va_list ap; - - va_start(ap, fmt); - vsnprintf(msg, sizeof(msg), fmt, ap); - va_end(ap); - - printf("--- RDB ERROR DETECTED ---\n"); - printf("[offset %llu] %s\n", - (unsigned long long) (rdbstate.rio ? - rdbstate.rio->processed_bytes : 0), msg); - printf("[additional info] While doing: %s\n", - rdb_check_doing_string[rdbstate.doing]); - if (rdbstate.key) - printf("[additional info] Reading key '%s'\n", - (char*)rdbstate.key->ptr); - if (rdbstate.key_type != -1) - printf("[additional info] Reading type %d (%s)\n", - rdbstate.key_type, - ((unsigned)rdbstate.key_type < - sizeof(rdb_type_string)/sizeof(char*)) ? - rdb_type_string[rdbstate.key_type] : "unknown"); - rdbShowGenericInfo(); -} - -/* Print information during RDB checking. */ -void rdbCheckInfo(const char *fmt, ...) { - char msg[1024]; - va_list ap; - - va_start(ap, fmt); - vsnprintf(msg, sizeof(msg), fmt, ap); - va_end(ap); - - printf("[offset %llu] %s\n", - (unsigned long long) (rdbstate.rio ? - rdbstate.rio->processed_bytes : 0), msg); -} - -/* Used inside rdb.c in order to log specific errors happening inside - * the RDB loading internals. */ -void rdbCheckSetError(const char *fmt, ...) { - va_list ap; - - va_start(ap, fmt); - vsnprintf(rdbstate.error, sizeof(rdbstate.error), fmt, ap); - va_end(ap); - rdbstate.error_set = 1; -} - -/* During RDB check we setup a special signal handler for memory violations - * and similar conditions, so that we can log the offending part of the RDB - * if the crash is due to broken content. */ -void rdbCheckHandleCrash(int sig, siginfo_t *info, void *secret) { - UNUSED(sig); - UNUSED(info); - UNUSED(secret); - - rdbCheckError("Server crash checking the specified RDB file!"); - exit(1); -} - -void rdbCheckSetupSignals(void) { - struct sigaction act; - - sigemptyset(&act.sa_mask); - act.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO; - act.sa_sigaction = rdbCheckHandleCrash; - sigaction(SIGSEGV, &act, NULL); - sigaction(SIGBUS, &act, NULL); - sigaction(SIGFPE, &act, NULL); - sigaction(SIGILL, &act, NULL); - sigaction(SIGABRT, &act, NULL); -} - -/* Check the specified RDB file. Return 0 if the RDB looks sane, otherwise - * 1 is returned. - * The file is specified as a filename in 'rdbfilename' if 'fp' is NULL, - * otherwise the already open file 'fp' is checked. */ -int redis_check_rdb(char *rdbfilename, FILE *fp) { - uint64_t dbid; - int selected_dbid = -1; - int type, rdbver; - char buf[1024]; - long long expiretime, now = mstime(); - static rio rdb; /* Pointed by global struct riostate. */ - struct stat sb; - - int closefile = (fp == NULL); - if (fp == NULL && (fp = fopen(rdbfilename,"r")) == NULL) return 1; - - if (fstat(fileno(fp), &sb) == -1) - sb.st_size = 0; - - startLoadingFile(sb.st_size, rdbfilename, RDBFLAGS_NONE); - rioInitWithFile(&rdb,fp); - rdbstate.rio = &rdb; - rdb.update_cksum = rdbLoadProgressCallback; - if (rioRead(&rdb,buf,9) == 0) goto eoferr; - buf[9] = '\0'; - if (memcmp(buf,"REDIS",5) != 0) { - rdbCheckError("Wrong signature trying to load DB from file"); - goto err; - } - rdbver = atoi(buf+5); - if (rdbver < 1 || rdbver > RDB_VERSION) { - rdbCheckError("Can't handle RDB format version %d",rdbver); - goto err; - } - - expiretime = -1; - while(1) { - robj *key, *val; - - /* Read type. */ - rdbstate.doing = RDB_CHECK_DOING_READ_TYPE; - if ((type = rdbLoadType(&rdb)) == -1) goto eoferr; - - /* Handle special types. */ - if (type == RDB_OPCODE_EXPIRETIME) { - rdbstate.doing = RDB_CHECK_DOING_READ_EXPIRE; - /* EXPIRETIME: load an expire associated with the next key - * to load. Note that after loading an expire we need to - * load the actual type, and continue. */ - expiretime = rdbLoadTime(&rdb); - expiretime *= 1000; - if (rioGetReadError(&rdb)) goto eoferr; - continue; /* Read next opcode. */ - } else if (type == RDB_OPCODE_EXPIRETIME_MS) { - /* EXPIRETIME_MS: milliseconds precision expire times introduced - * with RDB v3. Like EXPIRETIME but no with more precision. */ - rdbstate.doing = RDB_CHECK_DOING_READ_EXPIRE; - expiretime = rdbLoadMillisecondTime(&rdb, rdbver); - if (rioGetReadError(&rdb)) goto eoferr; - continue; /* Read next opcode. */ - } else if (type == RDB_OPCODE_FREQ) { - /* FREQ: LFU frequency. */ - uint8_t byte; - if (rioRead(&rdb,&byte,1) == 0) goto eoferr; - continue; /* Read next opcode. */ - } else if (type == RDB_OPCODE_IDLE) { - /* IDLE: LRU idle time. */ - if (rdbLoadLen(&rdb,NULL) == RDB_LENERR) goto eoferr; - continue; /* Read next opcode. */ - } else if (type == RDB_OPCODE_KEY_META) { - /* KEY_META: Module metadata for the next key. */ - uint64_t numClasses; - if ((numClasses = rdbLoadLen(&rdb,NULL)) == RDB_LENERR) goto eoferr; - /* Skip metadata by reading and discarding each class's data */ - for (uint64_t i = 0; i < numClasses; i++) { - /* Read 4-byte CLASS_SPEC */ - uint32_t classSpec; - if (rioRead(&rdb, &classSpec, 4) == 0) goto eoferr; - /* Skip module value using rdbLoadCheckModuleValue */ - robj *o = rdbLoadCheckModuleValue(&rdb, "metadata"); - if (o == NULL) goto eoferr; - decrRefCount(o); - } - continue; /* Read next opcode. */ - } else if (type == RDB_OPCODE_EOF) { - /* EOF: End of file, exit the main loop. */ - break; - } else if (type == RDB_OPCODE_SELECTDB) { - /* SELECTDB: Select the specified database. */ - rdbstate.doing = RDB_CHECK_DOING_READ_LEN; - if ((dbid = rdbLoadLen(&rdb,NULL)) == RDB_LENERR) - goto eoferr; - rdbCheckInfo("Selecting DB ID %llu", (unsigned long long)dbid); - selected_dbid = dbid; - continue; /* Read type again. */ - } else if (type == RDB_OPCODE_RESIZEDB) { - /* RESIZEDB: Hint about the size of the keys in the currently - * selected data base, in order to avoid useless rehashing. */ - uint64_t db_size, expires_size; - rdbstate.doing = RDB_CHECK_DOING_READ_LEN; - if ((db_size = rdbLoadLen(&rdb,NULL)) == RDB_LENERR) - goto eoferr; - if ((expires_size = rdbLoadLen(&rdb,NULL)) == RDB_LENERR) - goto eoferr; - continue; /* Read type again. */ - } else if (type == RDB_OPCODE_SLOT_INFO) { - uint64_t slot_id, slot_size, expires_slot_size; - if ((slot_id = rdbLoadLen(&rdb,NULL)) == RDB_LENERR) - goto eoferr; - if ((slot_size = rdbLoadLen(&rdb,NULL)) == RDB_LENERR) - goto eoferr; - if ((expires_slot_size = rdbLoadLen(&rdb,NULL)) == RDB_LENERR) - goto eoferr; - continue; /* Read type again. */ - } else if (type == RDB_OPCODE_AUX) { - /* AUX: generic string-string fields. Use to add state to RDB - * which is backward compatible. Implementations of RDB loading - * are required to skip AUX fields they don't understand. - * - * An AUX field is composed of two strings: key and value. */ - robj *auxkey, *auxval; - rdbstate.doing = RDB_CHECK_DOING_READ_AUX; - if ((auxkey = rdbLoadStringObject(&rdb)) == NULL) goto eoferr; - if ((auxval = rdbLoadStringObject(&rdb)) == NULL) { - decrRefCount(auxkey); - goto eoferr; - } - - rdbCheckInfo("AUX FIELD %s = '%s'", - (char*)auxkey->ptr, (char*)auxval->ptr); - decrRefCount(auxkey); - decrRefCount(auxval); - continue; /* Read type again. */ - } else if (type == RDB_OPCODE_MODULE_AUX) { - /* AUX: Auxiliary data for modules. */ - uint64_t moduleid, when_opcode, when; - rdbstate.doing = RDB_CHECK_DOING_READ_MODULE_AUX; - if ((moduleid = rdbLoadLen(&rdb,NULL)) == RDB_LENERR) goto eoferr; - if ((when_opcode = rdbLoadLen(&rdb,NULL)) == RDB_LENERR) goto eoferr; - if ((when = rdbLoadLen(&rdb,NULL)) == RDB_LENERR) goto eoferr; - if (when_opcode != RDB_MODULE_OPCODE_UINT) { - rdbCheckError("bad when_opcode"); - goto err; - } - - char name[10]; - moduleTypeNameByID(name,moduleid); - rdbCheckInfo("MODULE AUX for: %s", name); - - robj *o = rdbLoadCheckModuleValue(&rdb,name); - decrRefCount(o); - continue; /* Read type again. */ - } else if (type == RDB_OPCODE_FUNCTION_PRE_GA) { - rdbCheckError("Pre-release function format not supported %d",rdbver); - goto err; - } else if (type == RDB_OPCODE_FUNCTION2) { - sds err = NULL; - rdbstate.doing = RDB_CHECK_DOING_READ_FUNCTIONS; - if (rdbFunctionLoad(&rdb, rdbver, NULL, 0, &err) != C_OK) { - rdbCheckError("Failed loading library, %s", err); - sdsfree(err); - goto err; - } - continue; - } else { - if (!rdbIsObjectType(type)) { - rdbCheckError("Invalid object type: %d", type); - goto err; - } - rdbstate.key_type = type; - } - - /* Read key */ - rdbstate.doing = RDB_CHECK_DOING_READ_KEY; - if ((key = rdbLoadStringObject(&rdb)) == NULL) goto eoferr; - rdbstate.key = key; - rdbstate.keys++; - /* Read value */ - rdbstate.doing = RDB_CHECK_DOING_READ_OBJECT_VALUE; - if ((val = rdbLoadObject(type,&rdb,key->ptr,selected_dbid,NULL)) == NULL) - goto eoferr; - /* Check if the key already expired. */ - if (expiretime != -1 && expiretime < now) - rdbstate.already_expired++; - if (expiretime != -1) rdbstate.expires++; - /* If hash with HFEs then with expiration on fields then need to count it */ - if ((val->type == OBJ_HASH) && (hashTypeGetMinExpire(val, 1) != EB_EXPIRE_TIME_INVALID)) - rdbstate.subexpires++; - - rdbstate.key = NULL; - decrRefCount(key); - decrRefCount(val); - rdbstate.key_type = -1; - expiretime = -1; - } - /* Verify the checksum if RDB version is >= 5 */ - if (rdbver >= 5 && server.rdb_checksum) { - uint64_t cksum, expected = rdb.cksum; - - rdbstate.doing = RDB_CHECK_DOING_CHECK_SUM; - if (rioRead(&rdb,&cksum,8) == 0) goto eoferr; - memrev64ifbe(&cksum); - if (cksum == 0) { - rdbCheckInfo("RDB file was saved with checksum disabled: no check performed."); - } else if (cksum != expected) { - rdbCheckError("RDB CRC error"); - goto err; - } else { - rdbCheckInfo("Checksum OK"); - } - } - - if (closefile) fclose(fp); - stopLoading(1); - return 0; - -eoferr: /* unexpected end of file is handled here with a fatal exit */ - if (rdbstate.error_set) { - rdbCheckError(rdbstate.error); - } else { - rdbCheckError("Unexpected EOF reading RDB file"); - } -err: - if (closefile) fclose(fp); - stopLoading(0); - return 1; -} - -/* RDB check main: called form server.c when Redis is executed with the - * redis-check-rdb alias, on during RDB loading errors. - * - * The function works in two ways: can be called with argc/argv as a - * standalone executable, or called with a non NULL 'fp' argument if we - * already have an open file to check. This happens when the function - * is used to check an RDB preamble inside an AOF file. - * - * When called with fp = NULL, the function never returns, but exits with the - * status code according to success (RDB is sane) or error (RDB is corrupted). - * Otherwise if called with a non NULL fp, the function returns C_OK or - * C_ERR depending on the success or failure. */ -int redis_check_rdb_main(int argc, char **argv, FILE *fp) { - struct timeval tv; - - if (argc != 2 && fp == NULL) { - fprintf(stderr, "Usage: %s <rdb-file-name>\n", argv[0]); - exit(1); - } else if (!strcmp(argv[1],"-v") || !strcmp(argv[1], "--version")) { - sds version = getVersion(); - printf("redis-check-rdb %s\n", version); - sdsfree(version); - exit(0); - } - - gettimeofday(&tv, NULL); - init_genrand64(((long long) tv.tv_sec * 1000000 + tv.tv_usec) ^ getpid()); - - /* In order to call the loading functions we need to create the shared - * integer objects, however since this function may be called from - * an already initialized Redis instance, check if we really need to. */ - if (shared.integers[0] == NULL) - createSharedObjects(); - server.loading_process_events_interval_bytes = 0; - server.sanitize_dump_payload = SANITIZE_DUMP_YES; - rdbCheckMode = 1; - rdbCheckInfo("Checking RDB file %s", argv[1]); - rdbCheckSetupSignals(); - int retval = redis_check_rdb(argv[1],fp); - if (retval == 0) { - rdbCheckInfo("\\o/ RDB looks OK! \\o/"); - rdbShowGenericInfo(); - } - if (fp) return (retval == 0) ? C_OK : C_ERR; - exit(retval); -} |
