Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
e9f31ba9
Commit
e9f31ba9
authored
Jul 01, 2016
by
antirez
Browse files
In Redis RDB check: better error reporting.
parent
e97fadb0
Changes
4
Hide whitespace changes
Inline
Side-by-side
src/rdb.c
View file @
e9f31ba9
...
@@ -43,11 +43,20 @@
...
@@ -43,11 +43,20 @@
#define rdbExitReportCorruptRDB(reason) rdbCheckThenExit(reason, __LINE__);
#define rdbExitReportCorruptRDB(reason) rdbCheckThenExit(reason, __LINE__);
extern
int
rdbCheckMode
;
void
rdbCheckError
(
const
char
*
fmt
,
...);
void
rdbCheckThenExit
(
char
*
reason
,
int
where
)
{
void
rdbCheckThenExit
(
char
*
reason
,
int
where
)
{
serverLog
(
LL_WARNING
,
"Corrupt RDB detected at rdb.c:%d (%s). "
if
(
!
rdbCheckMode
)
{
"Running 'redis-check-rdb %s'"
,
serverLog
(
LL_WARNING
,
"Corrupt RDB detected at rdb.c:%d (%s). "
where
,
reason
,
server
.
rdb_filename
);
"Running 'redis-check-rdb %s'"
,
redis_check_rdb
(
server
.
rdb_filename
);
where
,
reason
,
server
.
rdb_filename
);
char
*
argv
[
2
]
=
{
""
,
server
.
rdb_filename
};
redis_check_rdb_main
(
2
,
argv
);
}
else
{
rdbCheckError
(
"Internal error in RDB reading function at rdb.c:%d (%s)"
,
where
,
reason
);
}
exit
(
1
);
exit
(
1
);
}
}
...
...
src/redis-check-rdb.c
View file @
e9f31ba9
...
@@ -32,8 +32,10 @@
...
@@ -32,8 +32,10 @@
#include <stdarg.h>
#include <stdarg.h>
void
createSharedObjects
(
void
);
void
rdbLoadProgressCallback
(
rio
*
r
,
const
void
*
buf
,
size_t
len
);
void
rdbLoadProgressCallback
(
rio
*
r
,
const
void
*
buf
,
size_t
len
);
long
long
rdbLoadMillisecondTime
(
rio
*
rdb
);
long
long
rdbLoadMillisecondTime
(
rio
*
rdb
);
int
rdbCheckMode
=
0
;
struct
{
struct
{
rio
*
rio
;
rio
*
rio
;
...
@@ -44,10 +46,34 @@ struct {
...
@@ -44,10 +46,34 @@ struct {
int
doing
;
/* The state while reading the RDB. */
int
doing
;
/* The state while reading the RDB. */
}
rdbstate
;
}
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_START 0
#define RDB_CHECK_DOING_READ_EXPIRE 1
#define RDB_CHECK_DOING_READ_TYPE 1
#define RDB_CHECK_DOING_READ_KEY 2
#define RDB_CHECK_DOING_READ_EXPIRE 2
#define RDB_CHECK_DOING_READ_VALUE 3
#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
char
*
rdb_check_doing_string
[]
=
{
"start"
,
"read-type"
,
"read-expire"
,
"read-key"
,
"read-object-value"
,
"check-sum"
,
"read-len"
,
"read-aux"
};
/* 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
);
}
/* Called on RDB errors. Provides details about the RDB and the offset
/* Called on RDB errors. Provides details about the RDB and the offset
* we were when the error was detected. */
* we were when the error was detected. */
...
@@ -59,12 +85,16 @@ void rdbCheckError(const char *fmt, ...) {
...
@@ -59,12 +85,16 @@ void rdbCheckError(const char *fmt, ...) {
vsnprintf
(
msg
,
sizeof
(
msg
),
fmt
,
ap
);
vsnprintf
(
msg
,
sizeof
(
msg
),
fmt
,
ap
);
va_end
(
ap
);
va_end
(
ap
);
printf
(
"
***
RDB
CHECK FAILED: %s ***
\n
"
,
msg
);
printf
(
"
---
RDB
ERROR DETECTED ---
\n
"
);
printf
(
"
AT RDB OFFSET:
%llu
\n
"
,
printf
(
"
[offset
%llu
] %s
\n
"
,
(
unsigned
long
long
)
(
rdbstate
.
rio
?
(
unsigned
long
long
)
(
rdbstate
.
rio
?
rdbstate
.
rio
->
processed_bytes
:
0
));
rdbstate
.
rio
->
processed_bytes
:
0
),
msg
);
printf
(
"[additional info] While doing: %s
\n
"
,
rdb_check_doing_string
[
rdbstate
.
doing
]);
if
(
rdbstate
.
key
)
if
(
rdbstate
.
key
)
printf
(
"READING KEY: %s
\n
"
,
(
char
*
)
rdbstate
.
key
->
ptr
);
printf
(
"[additional info] Reading key '%s'
\n
"
,
(
char
*
)
rdbstate
.
key
->
ptr
);
rdbShowGenericInfo
();
}
}
/* Print informations during RDB checking. */
/* Print informations during RDB checking. */
...
@@ -138,15 +168,18 @@ int redis_check_rdb(char *rdbfilename) {
...
@@ -138,15 +168,18 @@ int redis_check_rdb(char *rdbfilename) {
expiretime
=
-
1
;
expiretime
=
-
1
;
/* Read type. */
/* Read type. */
rdbstate
.
doing
=
RDB_CHECK_DOING_READ_TYPE
;
if
((
type
=
rdbLoadType
(
&
rdb
))
==
-
1
)
goto
eoferr
;
if
((
type
=
rdbLoadType
(
&
rdb
))
==
-
1
)
goto
eoferr
;
/* Handle special types. */
/* Handle special types. */
if
(
type
==
RDB_OPCODE_EXPIRETIME
)
{
if
(
type
==
RDB_OPCODE_EXPIRETIME
)
{
rdbstate
.
doing
=
RDB_CHECK_DOING_READ_EXPIRE
;
/* EXPIRETIME: load an expire associated with the next key
/* EXPIRETIME: load an expire associated with the next key
* to load. Note that after loading an expire we need to
* to load. Note that after loading an expire we need to
* load the actual type, and continue. */
* load the actual type, and continue. */
if
((
expiretime
=
rdbLoadTime
(
&
rdb
))
==
-
1
)
goto
eoferr
;
if
((
expiretime
=
rdbLoadTime
(
&
rdb
))
==
-
1
)
goto
eoferr
;
/* We read the time so we need to read the object type again. */
/* We read the time so we need to read the object type again. */
rdbstate
.
doing
=
RDB_CHECK_DOING_READ_TYPE
;
if
((
type
=
rdbLoadType
(
&
rdb
))
==
-
1
)
goto
eoferr
;
if
((
type
=
rdbLoadType
(
&
rdb
))
==
-
1
)
goto
eoferr
;
/* the EXPIRETIME opcode specifies time in seconds, so convert
/* the EXPIRETIME opcode specifies time in seconds, so convert
* into milliseconds. */
* into milliseconds. */
...
@@ -154,14 +187,17 @@ int redis_check_rdb(char *rdbfilename) {
...
@@ -154,14 +187,17 @@ int redis_check_rdb(char *rdbfilename) {
}
else
if
(
type
==
RDB_OPCODE_EXPIRETIME_MS
)
{
}
else
if
(
type
==
RDB_OPCODE_EXPIRETIME_MS
)
{
/* EXPIRETIME_MS: milliseconds precision expire times introduced
/* EXPIRETIME_MS: milliseconds precision expire times introduced
* with RDB v3. Like EXPIRETIME but no with more precision. */
* with RDB v3. Like EXPIRETIME but no with more precision. */
rdbstate
.
doing
=
RDB_CHECK_DOING_READ_EXPIRE
;
if
((
expiretime
=
rdbLoadMillisecondTime
(
&
rdb
))
==
-
1
)
goto
eoferr
;
if
((
expiretime
=
rdbLoadMillisecondTime
(
&
rdb
))
==
-
1
)
goto
eoferr
;
/* We read the time so we need to read the object type again. */
/* We read the time so we need to read the object type again. */
rdbstate
.
doing
=
RDB_CHECK_DOING_READ_TYPE
;
if
((
type
=
rdbLoadType
(
&
rdb
))
==
-
1
)
goto
eoferr
;
if
((
type
=
rdbLoadType
(
&
rdb
))
==
-
1
)
goto
eoferr
;
}
else
if
(
type
==
RDB_OPCODE_EOF
)
{
}
else
if
(
type
==
RDB_OPCODE_EOF
)
{
/* EOF: End of file, exit the main loop. */
/* EOF: End of file, exit the main loop. */
break
;
break
;
}
else
if
(
type
==
RDB_OPCODE_SELECTDB
)
{
}
else
if
(
type
==
RDB_OPCODE_SELECTDB
)
{
/* SELECTDB: Select the specified database. */
/* SELECTDB: Select the specified database. */
rdbstate
.
doing
=
RDB_CHECK_DOING_READ_LEN
;
if
((
dbid
=
rdbLoadLen
(
&
rdb
,
NULL
))
==
RDB_LENERR
)
if
((
dbid
=
rdbLoadLen
(
&
rdb
,
NULL
))
==
RDB_LENERR
)
goto
eoferr
;
goto
eoferr
;
rdbCheckInfo
(
"Selecting DB ID %d"
,
dbid
);
rdbCheckInfo
(
"Selecting DB ID %d"
,
dbid
);
...
@@ -170,6 +206,7 @@ int redis_check_rdb(char *rdbfilename) {
...
@@ -170,6 +206,7 @@ int redis_check_rdb(char *rdbfilename) {
/* RESIZEDB: Hint about the size of the keys in the currently
/* RESIZEDB: Hint about the size of the keys in the currently
* selected data base, in order to avoid useless rehashing. */
* selected data base, in order to avoid useless rehashing. */
uint64_t
db_size
,
expires_size
;
uint64_t
db_size
,
expires_size
;
rdbstate
.
doing
=
RDB_CHECK_DOING_READ_LEN
;
if
((
db_size
=
rdbLoadLen
(
&
rdb
,
NULL
))
==
RDB_LENERR
)
if
((
db_size
=
rdbLoadLen
(
&
rdb
,
NULL
))
==
RDB_LENERR
)
goto
eoferr
;
goto
eoferr
;
if
((
expires_size
=
rdbLoadLen
(
&
rdb
,
NULL
))
==
RDB_LENERR
)
if
((
expires_size
=
rdbLoadLen
(
&
rdb
,
NULL
))
==
RDB_LENERR
)
...
@@ -182,6 +219,7 @@ int redis_check_rdb(char *rdbfilename) {
...
@@ -182,6 +219,7 @@ int redis_check_rdb(char *rdbfilename) {
*
*
* An AUX field is composed of two strings: key and value. */
* An AUX field is composed of two strings: key and value. */
robj
*
auxkey
,
*
auxval
;
robj
*
auxkey
,
*
auxval
;
rdbstate
.
doing
=
RDB_CHECK_DOING_READ_AUX
;
if
((
auxkey
=
rdbLoadStringObject
(
&
rdb
))
==
NULL
)
goto
eoferr
;
if
((
auxkey
=
rdbLoadStringObject
(
&
rdb
))
==
NULL
)
goto
eoferr
;
if
((
auxval
=
rdbLoadStringObject
(
&
rdb
))
==
NULL
)
goto
eoferr
;
if
((
auxval
=
rdbLoadStringObject
(
&
rdb
))
==
NULL
)
goto
eoferr
;
...
@@ -189,13 +227,20 @@ int redis_check_rdb(char *rdbfilename) {
...
@@ -189,13 +227,20 @@ int redis_check_rdb(char *rdbfilename) {
decrRefCount
(
auxkey
);
decrRefCount
(
auxkey
);
decrRefCount
(
auxval
);
decrRefCount
(
auxval
);
continue
;
/* Read type again. */
continue
;
/* Read type again. */
}
else
{
if
(
!
rdbIsObjectType
(
type
))
{
rdbCheckError
(
"Invalid object type: %d"
,
type
);
return
1
;
}
}
}
/* Read key */
/* Read key */
rdbstate
.
doing
=
RDB_CHECK_DOING_READ_KEY
;
if
((
key
=
rdbLoadStringObject
(
&
rdb
))
==
NULL
)
goto
eoferr
;
if
((
key
=
rdbLoadStringObject
(
&
rdb
))
==
NULL
)
goto
eoferr
;
rdbstate
.
key
=
key
;
rdbstate
.
key
=
key
;
rdbstate
.
keys
++
;
rdbstate
.
keys
++
;
/* Read value */
/* Read value */
rdbstate
.
doing
=
RDB_CHECK_DOING_READ_OBJECT_VALUE
;
if
((
val
=
rdbLoadObject
(
type
,
&
rdb
))
==
NULL
)
goto
eoferr
;
if
((
val
=
rdbLoadObject
(
type
,
&
rdb
))
==
NULL
)
goto
eoferr
;
/* Check if the key already expired. This function is used when loading
/* Check if the key already expired. This function is used when loading
* an RDB file from disk, either at startup, or when an RDB was
* an RDB file from disk, either at startup, or when an RDB was
...
@@ -213,6 +258,7 @@ int redis_check_rdb(char *rdbfilename) {
...
@@ -213,6 +258,7 @@ int redis_check_rdb(char *rdbfilename) {
if
(
rdbver
>=
5
&&
server
.
rdb_checksum
)
{
if
(
rdbver
>=
5
&&
server
.
rdb_checksum
)
{
uint64_t
cksum
,
expected
=
rdb
.
cksum
;
uint64_t
cksum
,
expected
=
rdb
.
cksum
;
rdbstate
.
doing
=
RDB_CHECK_DOING_CHECK_SUM
;
if
(
rioRead
(
&
rdb
,
&
cksum
,
8
)
==
0
)
goto
eoferr
;
if
(
rioRead
(
&
rdb
,
&
cksum
,
8
)
==
0
)
goto
eoferr
;
memrev64ifbe
(
&
cksum
);
memrev64ifbe
(
&
cksum
);
if
(
cksum
==
0
)
{
if
(
cksum
==
0
)
{
...
@@ -235,18 +281,20 @@ eoferr: /* unexpected end of file is handled here with a fatal exit */
...
@@ -235,18 +281,20 @@ eoferr: /* unexpected end of file is handled here with a fatal exit */
*
*
* The function never returns, but exits with the status code according
* The function never returns, but exits with the status code according
* to success (RDB is sane) or error (RDB is corrupted). */
* to success (RDB is sane) or error (RDB is corrupted). */
int
redis_check_rdb_main
(
char
**
argv
,
int
argc
)
{
int
redis_check_rdb_main
(
int
argc
,
char
**
argv
)
{
if
(
argc
!=
2
)
{
if
(
argc
!=
2
)
{
fprintf
(
stderr
,
"Usage: %s <rdb-file-name>
\n
"
,
argv
[
0
]);
fprintf
(
stderr
,
"Usage: %s <rdb-file-name>
\n
"
,
argv
[
0
]);
exit
(
1
);
exit
(
1
);
}
}
createSharedObjects
();
/* Needed for loading. */
createSharedObjects
();
/* Needed for loading. */
server
.
loading_process_events_interval_bytes
=
0
;
server
.
loading_process_events_interval_bytes
=
0
;
rdbCheckMode
=
1
;
rdbCheckInfo
(
"Checking RDB file %s"
,
argv
[
1
]);
rdbCheckInfo
(
"Checking RDB file %s"
,
argv
[
1
]);
rdbCheckSetupSignals
();
rdbCheckSetupSignals
();
int
retval
=
redis_check_rdb
(
argv
[
1
]);
int
retval
=
redis_check_rdb
(
argv
[
1
]);
if
(
retval
==
0
)
{
if
(
retval
==
0
)
{
rdbCheckInfo
(
"
\\
o/ RDB looks OK!
\\
o/"
);
rdbCheckInfo
(
"
\\
o/ RDB looks OK!
\\
o/"
);
rdbShowGenericInfo
();
}
}
exit
(
retval
);
exit
(
retval
);
}
}
src/server.c
View file @
e9f31ba9
...
@@ -4033,7 +4033,7 @@ int main(int argc, char **argv) {
...
@@ -4033,7 +4033,7 @@ int main(int argc, char **argv) {
* the program main. However the program is part of the Redis executable
* the program main. However the program is part of the Redis executable
* so that we can easily execute an RDB check on loading errors. */
* so that we can easily execute an RDB check on loading errors. */
if
(
strstr
(
argv
[
0
],
"redis-check-rdb"
)
!=
NULL
)
if
(
strstr
(
argv
[
0
],
"redis-check-rdb"
)
!=
NULL
)
redis_check_rdb_main
(
arg
v
,
arg
c
);
redis_check_rdb_main
(
arg
c
,
arg
v
);
if
(
argc
>=
2
)
{
if
(
argc
>=
2
)
{
j
=
1
;
/* First option to parse in argv[] */
j
=
1
;
/* First option to parse in argv[] */
...
...
src/server.h
View file @
e9f31ba9
...
@@ -1597,7 +1597,7 @@ void sentinelIsRunning(void);
...
@@ -1597,7 +1597,7 @@ void sentinelIsRunning(void);
/* redis-check-rdb */
/* redis-check-rdb */
int
redis_check_rdb
(
char
*
rdbfilename
);
int
redis_check_rdb
(
char
*
rdbfilename
);
int
redis_check_rdb_main
(
char
**
argv
,
int
argc
);
int
redis_check_rdb_main
(
int
argc
,
char
**
argv
);
/* Scripting */
/* Scripting */
void
scriptingInit
(
int
setup
);
void
scriptingInit
(
int
setup
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment