-
Notifications
You must be signed in to change notification settings - Fork 120
/
nbd-trplay.c
327 lines (286 loc) · 8.15 KB
/
nbd-trplay.c
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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
// SPDX-License-Identifier: GPL-2.0
/*
* nbd-trplay.c
*
* Takes an nbd transaction log file and replays some/all of the write commands.
*
* Based on nbd-trdump
* (C) Robert Bosch GmbH, 2021
*/
#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include "config.h"
/* We don't want to do syslog output in this program */
#undef ISSERVER
#include "cliserv.h"
#include "nbd.h"
#include "nbd-helper.h"
#define BUFSIZE 131072
static char g_tmpbuf[BUFSIZE];
static bool g_with_datalog = false;
#define VERBOSE_DEBUG 3
#define VERBOSE_DETAILS 2
#define VERBOSE_NORMAL 1
#define VERBOSE_OFF 0
int g_verbose = 0;
unsigned long g_blocksize = 512;
unsigned long long g_cur_blocks = 0;
unsigned long long g_max_blocks = ULLONG_MAX;
static inline void doread(int f, char *buf, size_t len) {
ssize_t res;
while(len>0) {
if((res=read(f, buf, len)) <=0) {
if (!res) {
/* normal exit, end of transaction log. */
printf("End of transaction log, total %llu blocks written.\n",
(unsigned long long) g_cur_blocks);
exit(0);
}
perror ("Error reading transactions");
exit(1);
}
len-=res;
buf+=res;
}
}
static inline void dowriteimage(int imagefd, const char *buf, size_t len, off_t offset) {
ssize_t res;
if (g_verbose >= VERBOSE_DETAILS) {
printf("block %llu (0x%llx): writing to offset %lld (0x%llx), len %lld (0x%llx).\n",
g_cur_blocks, g_cur_blocks,
(long long)offset, (long long) offset,
(long long) len, (long long) len);
}
while(len>0) {
if((res=pwrite(imagefd, buf, len, offset)) <=0) {
if (!res)
exit(0);
perror ("Error writing to image file");
exit(1);
}
len-=res;
buf+=res;
offset+=res;
}
}
void process_command(uint32_t command, uint64_t offset, uint32_t len, int logfd, int imagefd)
{
if (offset % g_blocksize != 0) {
printf(" Got offset %llu (0x%llx), not a multiple of the block size %ld (0x%lx).\n",
(unsigned long long)offset, (unsigned long long)offset, g_blocksize, g_blocksize);
exit(1);
}
if (len % g_blocksize != 0) {
printf(" Got len %lu (0x%lx), not a multiple of the block size %ld (0x%lx).\n",
(unsigned long) len, (unsigned long) len, g_blocksize, g_blocksize);
exit(1);
}
switch (command & NBD_CMD_MASK_COMMAND) {
case NBD_CMD_READ:
case NBD_CMD_DISC:
case NBD_CMD_FLUSH:
/* READ, DISCONNECT, FLUSH: nothing to do */
break;
case NBD_CMD_WRITE:
if (!g_with_datalog) {
printf(" NBD_CMD_WRITE without data log, replay impossible.\n");
exit(1);
}
while (len > 0) {
doread(logfd, g_tmpbuf, g_blocksize);
dowriteimage(imagefd, g_tmpbuf, g_blocksize, offset);
offset+=g_blocksize;
len-=g_blocksize;
g_cur_blocks++;
if (g_cur_blocks == g_max_blocks) {
printf("g_max_blocks (%llu, 0x%llx) reached!.\n", g_max_blocks, g_max_blocks);
exit(0);
}
}
break;
case NBD_CMD_TRIM:
case NBD_CMD_WRITE_ZEROES:
while (len > 0) {
memset(g_tmpbuf, 0, g_blocksize);
dowriteimage(imagefd, g_tmpbuf, g_blocksize, offset);
offset+=g_blocksize;
len-=g_blocksize;
g_cur_blocks++;
if (g_cur_blocks == g_max_blocks) {
printf("g_max_blocks (%llu, 0x%llx) reached!.\n", g_max_blocks, g_max_blocks);
exit(0);
}
}
break;
default:
printf(" Unexpected command %d (0x%x), replay impossible.\n",
(unsigned int) command, (unsigned int) command);
exit(1);
}
}
int main_loop(int logfd, int imagefd) {
struct nbd_request req;
struct nbd_reply rep;
uint32_t magic;
uint64_t cookie;
uint32_t error;
uint32_t command;
uint32_t len;
uint64_t offset;
const char * ctext;
while (1) {
/* Read a request or reply from the transaction file */
doread(logfd, (char*) &magic, sizeof(magic));
magic = ntohl(magic);
switch (magic) {
case NBD_REQUEST_MAGIC:
doread(logfd, sizeof(magic)+(char *)(&req), sizeof(struct nbd_request)-sizeof(magic));
cookie = ntohll(req.cookie);
offset = ntohll(req.from);
len = ntohl(req.len);
command = ntohl(req.type);
ctext = getcommandname(command & NBD_CMD_MASK_COMMAND);
if (g_verbose >= VERBOSE_NORMAL) {
printf("> H=%016llx C=0x%08x (%13s+%4s) O=%016llx L=%08x\n",
(long long unsigned int) cookie,
command,
ctext,
(command & NBD_CMD_FLAG_FUA)?"FUA":"NONE",
(long long unsigned int) offset,
len);
}
process_command(command, offset, len, logfd, imagefd);
break;
case NBD_REPLY_MAGIC:
doread(logfd, sizeof(magic)+(char *)(&rep), sizeof(struct nbd_reply)-sizeof(magic));
cookie = ntohll(rep.cookie);
error = ntohl(rep.error);
if (g_verbose >= VERBOSE_NORMAL) {
printf("< H=%016llx E=0x%08x\n",
(long long unsigned int) cookie,
error);
}
break;
case NBD_TRACELOG_MAGIC:
doread(logfd, sizeof(magic)+(char *)(&req), sizeof(struct nbd_request)-sizeof(magic));
cookie = ntohll(req.cookie);
offset = ntohll(req.from);
len = ntohl(req.len);
command = ntohl(req.type);
ctext = gettracelogname(command);
if (g_verbose >= VERBOSE_NORMAL) {
printf("TRACE_OPTION C=0x%08x (%23s) O=%016llx L=%08x\n",
command,
ctext,
(long long unsigned int) offset,
len);
}
if (offset == NBD_TRACELOG_FROM_MAGIC) {
switch (command) {
case NBD_TRACELOG_SET_DATALOG:
g_with_datalog = !!len;
if (g_verbose >= VERBOSE_NORMAL)
printf("TRACE_OPTION DATALOG set to %d.\n", (int)g_with_datalog);
break;
default:
printf("TRACE_OPTION ? Unknown type\n");
}
} else {
printf("TRACE_OPTION ? Unknown FROM_MAGIC\n");
}
break;
default:
printf("? Unknown transaction type %08x, replay impossible.\n", magic);
exit(1);
}
}
/* never reached */
return 0;
}
static void show_help(const char *progname) {
printf("\n");
printf("This is nbd-trplay, part of nbd %s.\n", PACKAGE_VERSION);
printf("Use: %s -i <image> -l <log> [-m <max blocks>] [-b <block size]\n", progname);
printf(" Applies up to <max blocks> elements from file <log> to disk image <image>.\n");
printf(" Command line parameters:\n");
printf(" <image>: name of the initial image file.\n");
printf(" <log>: nbd trace log. Must contain actual data (datalog=true).\n");
printf(" <block size>: device block size. Default 512.\n");
printf(" <max blocks>: where to stop the replay. Default all.\n");
printf(" -v: Increase verbose level. Specify multiple times to increase further.\n");
}
int main(int argc, char **argv) {
int opt;
int imagefd = -1;
int logfd = -1;
printf("%s -i <image> -l <log> [-m <max blocks>] [-b <block size]\n", argv[0]);
while ((opt = getopt(argc, argv, "i:l:m:b:hv")) != -1) {
if (g_verbose >= VERBOSE_DEBUG) {
printf("getopt: opt %c, optarg %s.\n", (char)opt, optarg);
}
switch(opt) {
case 'v':
g_verbose++;
break;
default:
case '?':
case 'h':
show_help(argv[0]);
return 0;
case 'm':
g_max_blocks = strtoull(optarg, NULL, 0);
if (g_max_blocks == 0) {
printf(" Invalid block count.\n");
return 1;
}
break;
case 'b':
g_blocksize = strtoul(optarg, NULL, 0);
if (g_blocksize == 0) {
printf(" Invalid block size.\n");
return 1;
}
if (g_blocksize > BUFSIZE) {
printf(" block size is larger than %d, not supported.\n", (int)BUFSIZE);
return 1;
}
break;
case 'i':
imagefd = open(optarg, O_RDWR, 0);
if (imagefd == -1) {
printf(" Opening disk image failed, errno %d.", errno);
return 1;
}
break;
case 'l':
logfd = open(optarg, O_RDONLY, 0);
if (logfd == -1) {
printf(" Opening disk image failed, errno %d.", errno);
return 1;
}
break;
}
}
if (logfd == -1) {
printf(" Log file not specified, this is mandatory.\n");
return 1;
}
if (imagefd == -1) {
printf(" Disk image not specified, this is mandatory.\n");
return 1;
}
if (g_verbose >= VERBOSE_NORMAL) {
printf(" block size: %ld bytes (0x%lx bytes).\n", g_blocksize, g_blocksize);
printf(" max blocks to apply: %llu (0x%llx).\n", g_max_blocks, g_max_blocks);
}
main_loop(logfd, imagefd);
return 0;
}