summaryrefslogtreecommitdiff
path: root/lib/tdb/test/run-corrupt.c
blob: 1a3c769183243aad665e77784c1fb129d5b4ffe3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include "../common/tdb_private.h"
#include "../common/io.c"
#include "../common/tdb.c"
#include "../common/lock.c"
#include "../common/freelist.c"
#include "../common/traverse.c"
#include "../common/transaction.c"
#include "../common/error.c"
#include "../common/open.c"
#include "../common/check.c"
#include "../common/hash.c"
#include "tap-interface.h"
#include <stdlib.h>
#include "logging.h"

static int check(TDB_DATA key, TDB_DATA data, void *private)
{
	unsigned int *sizes = private;

	if (key.dsize > strlen("hello"))
		return -1;
	if (memcmp(key.dptr, "hello", key.dsize) != 0)
		return -1;

	if (data.dsize != strlen("world"))
		return -1;
	if (memcmp(data.dptr, "world", data.dsize) != 0)
		return -1;

	sizes[0] += key.dsize;
	sizes[1] += data.dsize;
	return 0;
}

static void tdb_flip_bit(struct tdb_context *tdb, unsigned int bit)
{
	unsigned int off = bit / CHAR_BIT;
	unsigned char mask = (1 << (bit % CHAR_BIT));

	if (tdb->map_ptr)
		((unsigned char *)tdb->map_ptr)[off] ^= mask;
	else {
		unsigned char c;
		if (pread(tdb->fd, &c, 1, off) != 1) {
			fprintf(stderr, "pread: %s\n", strerror(errno));
			exit(1);
		}
		c ^= mask;
		if (pwrite(tdb->fd, &c, 1, off) != 1) {
			fprintf(stderr, "pwrite: %s\n", strerror(errno));
			exit(1);
		}
	}
}

static void check_test(struct tdb_context *tdb)
{
	TDB_DATA key, data;
	unsigned int i, verifiable, corrupt, sizes[2], dsize, ksize;

	ok1(tdb_check(tdb, NULL, NULL) == 0);

	key.dptr = (void *)"hello";
	data.dsize = strlen("world");
	data.dptr = (void *)"world";

	/* Key and data size respectively. */
	dsize = ksize = 0;

	/* 5 keys in hash size 2 means we'll have multichains. */
	for (key.dsize = 1; key.dsize <= 5; key.dsize++) {
		ksize += key.dsize;
		dsize += data.dsize;
		if (tdb_store(tdb, key, data, TDB_INSERT) != 0)
			abort();
	}

	/* This is how many bytes we expect to be verifiable. */
	/* From the file header. */
	verifiable = strlen(TDB_MAGIC_FOOD) + 1
		+ 2 * sizeof(uint32_t) + 2 * sizeof(tdb_off_t)
		+ 2 * sizeof(uint32_t);
	/* From the free list chain and hash chains. */
	verifiable += 3 * sizeof(tdb_off_t);
	/* From the record headers & tailer */
	verifiable += 5 * (sizeof(struct tdb_record) + sizeof(uint32_t));
	/* The free block: we ignore datalen, keylen, full_hash. */
	verifiable += sizeof(struct tdb_record) - 3*sizeof(uint32_t) +
		sizeof(uint32_t);
	/* Our check function verifies the key and data. */
	verifiable += ksize + dsize;

	/* Flip one bit at a time, make sure it detects verifiable bytes. */
	for (i = 0, corrupt = 0; i < tdb->map_size * CHAR_BIT; i++) {
		tdb_flip_bit(tdb, i);
		memset(sizes, 0, sizeof(sizes));
		if (tdb_check(tdb, check, sizes) != 0)
			corrupt++;
		else if (sizes[0] != ksize || sizes[1] != dsize)
			corrupt++;
		tdb_flip_bit(tdb, i);
	}
	ok(corrupt == verifiable * CHAR_BIT, "corrupt %u should be %u",
	   corrupt, verifiable * CHAR_BIT);
}

int main(int argc, char *argv[])
{
	struct tdb_context *tdb;

	plan_tests(4);
	/* This should use mmap. */
	tdb = tdb_open_ex("run-corrupt.tdb", 2, TDB_CLEAR_IF_FIRST,
			  O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);

	if (!tdb)
		abort();
	check_test(tdb);
	tdb_close(tdb);

	/* This should not. */
	tdb = tdb_open_ex("run-corrupt.tdb", 2, TDB_CLEAR_IF_FIRST|TDB_NOMMAP,
			  O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);

	if (!tdb)
		abort();
	check_test(tdb);
	tdb_close(tdb);

	return exit_status();
}