summaryrefslogtreecommitdiff
path: root/examples/redis-unstable/src/redis-check-rdb.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/redis-unstable/src/redis-check-rdb.c')
-rw-r--r--examples/redis-unstable/src/redis-check-rdb.c451
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);
-}