summaryrefslogtreecommitdiffstats
path: root/daemons/cmirrord/compat.c
blob: 3f7a9b51d788c88de7c9b54c862c3ec2c5a634b6 (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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/*
 * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
 *
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU Lesser General Public License v.2.1.
 */
#include "logging.h"
#include "cluster.h"
#include "compat.h"
#include "xlate.h"

#include <errno.h>

/*
 * Older versions of the log daemon communicate with different
 * versions of the inter-machine communication structure, which
 * varies in size and fields.  The older versions append the
 * standard upstream version of the structure to every request.
 * COMPAT_OFFSET is where the upstream structure starts.
 */
#define COMPAT_OFFSET 256

static void v5_data_endian_switch(struct clog_request *rq, int to_network __attribute__((unused)))
{
	int i, end;
	int64_t *pi64;
	uint64_t *pu64;
	uint32_t rq_type = rq->u_rq.request_type & ~DM_ULOG_RESPONSE;

	if (rq->u_rq.request_type & DM_ULOG_RESPONSE) {
		switch (rq_type) {
		case DM_ULOG_CTR:
		case DM_ULOG_DTR:
			LOG_ERROR("Invalid response type in endian switch");
			exit(EXIT_FAILURE);

		case DM_ULOG_PRESUSPEND:
		case DM_ULOG_POSTSUSPEND:
		case DM_ULOG_RESUME:
		case DM_ULOG_FLUSH:
		case DM_ULOG_MARK_REGION:
		case DM_ULOG_CLEAR_REGION:
		case DM_ULOG_SET_REGION_SYNC:
		case DM_ULOG_CHECKPOINT_READY:
		case DM_ULOG_MEMBER_JOIN:
		case DM_ULOG_STATUS_INFO:
		case DM_ULOG_STATUS_TABLE:
			/* No outbound data */
			break;

		case DM_ULOG_GET_REGION_SIZE:
		case DM_ULOG_GET_SYNC_COUNT:
			pu64 = (uint64_t *)rq->u_rq.data;
			*pu64 = xlate64(*pu64);
			break;
		case DM_ULOG_IS_CLEAN:
		case DM_ULOG_IN_SYNC:
			pi64 = (int64_t *)rq->u_rq.data;
			*pi64 = xlate64(*pi64);
			break;
		case DM_ULOG_GET_RESYNC_WORK:
		case DM_ULOG_IS_REMOTE_RECOVERING:
			pi64 = (int64_t *)rq->u_rq.data;
			pu64 = ((uint64_t *)rq->u_rq.data) + 1;
			*pi64 = xlate64(*pi64);
			*pu64 = xlate64(*pu64);
			break;
		default:
			LOG_ERROR("Unknown request type, %u", rq_type);
			return;
		}
	} else {
		switch (rq_type) {
		case DM_ULOG_CTR:
		case DM_ULOG_DTR:
			LOG_ERROR("Invalid request type in endian switch");
			exit(EXIT_FAILURE);

		case DM_ULOG_PRESUSPEND:
		case DM_ULOG_POSTSUSPEND:
		case DM_ULOG_RESUME:
		case DM_ULOG_GET_REGION_SIZE:
		case DM_ULOG_FLUSH:
		case DM_ULOG_GET_RESYNC_WORK:
		case DM_ULOG_GET_SYNC_COUNT:
		case DM_ULOG_STATUS_INFO:
		case DM_ULOG_STATUS_TABLE:
		case DM_ULOG_CHECKPOINT_READY:
		case DM_ULOG_MEMBER_JOIN:
			/* No incoming data */
			break;
		case DM_ULOG_IS_CLEAN:
		case DM_ULOG_IN_SYNC:
		case DM_ULOG_IS_REMOTE_RECOVERING:
			pu64 = (uint64_t *)rq->u_rq.data;
			*pu64 = xlate64(*pu64);
			break;
		case DM_ULOG_MARK_REGION:
		case DM_ULOG_CLEAR_REGION:
			end = rq->u_rq.data_size/sizeof(uint64_t);

			pu64 = (uint64_t *)rq->u_rq.data;
			for (i = 0; i < end; i++)
				pu64[i] = xlate64(pu64[i]);
			break;
		case DM_ULOG_SET_REGION_SYNC:
			pu64 = (uint64_t *)rq->u_rq.data;
			pi64 = ((int64_t *)rq->u_rq.data) + 1;
			*pu64 = xlate64(*pu64);
			*pi64 = xlate64(*pi64);
			break;
		default:
			LOG_ERROR("Unknown request type, %u", rq_type);
			exit(EXIT_FAILURE);
		}
	}
}

static int v5_endian_to_network(struct clog_request *rq)
{
	int size;
	struct dm_ulog_request *u_rq = &rq->u_rq;

	size = sizeof(*rq) + u_rq->data_size;

	u_rq->error = xlate32(u_rq->error);
	u_rq->seq = xlate32(u_rq->seq);
	u_rq->request_type = xlate32(u_rq->request_type);
	u_rq->data_size = xlate64(u_rq->data_size);

	rq->originator = xlate32(rq->originator);

	v5_data_endian_switch(rq, 1);

	return size;
}

int clog_request_to_network(struct clog_request *rq)
{
	int r;

	/* FIXME: Remove this safety check */
	if (rq->u.version[0] != xlate64(rq->u.version[1])) {
		LOG_ERROR("Programmer error:  version[0] must be LE");
		exit(EXIT_FAILURE);
	}

	/*
	 * Are we already running in the endian mode we send
	 * over the wire?
	 */
	if (rq->u.version[0] == rq->u.version[1])
		return 0;

	r = v5_endian_to_network(rq);
	if (r < 0)
		return r;
	return 0;
}

static int v5_endian_from_network(struct clog_request *rq)
{
	int size;
	struct dm_ulog_request *u_rq = &rq->u_rq;

	u_rq->error = xlate32(u_rq->error);
	u_rq->seq = xlate32(u_rq->seq);
	u_rq->request_type = xlate32(u_rq->request_type);
	u_rq->data_size = xlate64(u_rq->data_size);

	rq->originator = xlate32(rq->originator);

	size = sizeof(*rq) + u_rq->data_size;

	v5_data_endian_switch(rq, 0);

	return size;
}

int clog_request_from_network(void *data, size_t data_len)
{
	uint64_t *vp = data;
	uint64_t version = xlate64(vp[0]);
	uint64_t unconverted_version = vp[1];
	struct clog_request *rq = data;

	switch (version) {
	case 5: /* Upstream */
		if (version == unconverted_version)
			return 0;
		break;
	case 4: /* RHEL 5.[45] */
	case 3: /* RHEL 5.3 */
	case 2: /* RHEL 5.2 */
		/* FIXME: still need to account for payload */
		if (data_len < (COMPAT_OFFSET + sizeof(*rq)))
			return -ENOSPC;

		rq = (struct clog_request *)((char *)data + COMPAT_OFFSET);
		break;
	default:
		LOG_ERROR("Unable to process cluster message: "
			  "Incompatible version");
		return -EINVAL;
	}

	v5_endian_from_network(rq);
	return 0;
}