1 // SPDX-License-Identifier: GPL-2.0
5 * Takes an nbd transaction log file and replays some/all of the write commands.
8 * (C) Robert Bosch GmbH, 2021
16 #include <sys/types.h>
22 /* We don't want to do syslog output in this program */
26 #include "nbd-helper.h"
28 #define BUFSIZE 131072
29 static char g_tmpbuf
[BUFSIZE
];
31 static bool g_with_datalog
= false;
33 #define VERBOSE_DEBUG 3
34 #define VERBOSE_DETAILS 2
35 #define VERBOSE_NORMAL 1
40 unsigned long g_blocksize
= 512;
41 unsigned long long g_cur_blocks
= 0;
42 unsigned long long g_max_blocks
= ULLONG_MAX
;
44 static inline void doread(int f
, char *buf
, size_t len
) {
48 if((res
=read(f
, buf
, len
)) <=0) {
50 /* normal exit, end of transaction log. */
51 printf("End of transaction log, total %llu blocks written.\n",
52 (unsigned long long) g_cur_blocks
);
55 perror ("Error reading transactions");
63 static inline void dowriteimage(int imagefd
, const char *buf
, size_t len
, off_t offset
) {
66 if (g_verbose
>= VERBOSE_DETAILS
) {
67 printf("block %llu (0x%llx): writing to offset %lld (0x%llx), len %lld (0x%llx).\n",
68 g_cur_blocks
, g_cur_blocks
,
69 (long long)offset
, (long long) offset
,
70 (long long) len
, (long long) len
);
74 if((res
=pwrite(imagefd
, buf
, len
, offset
)) <=0) {
77 perror ("Error writing to image file");
87 void process_command(uint32_t command
, uint64_t offset
, uint32_t len
, int logfd
, int imagefd
)
89 if (offset
% g_blocksize
!= 0) {
90 printf(" Got offset %llu (0x%llx), not a multiple of the block size %ld (0x%lx).\n",
91 (unsigned long long)offset
, (unsigned long long)offset
, g_blocksize
, g_blocksize
);
94 if (len
% g_blocksize
!= 0) {
95 printf(" Got len %lu (0x%lx), not a multiple of the block size %ld (0x%lx).\n",
96 (unsigned long) len
, (unsigned long) len
, g_blocksize
, g_blocksize
);
100 switch (command
& NBD_CMD_MASK_COMMAND
) {
104 /* READ, DISCONNECT, FLUSH: nothing to do */
107 if (!g_with_datalog
) {
108 printf(" NBD_CMD_WRITE without data log, replay impossible.\n");
112 doread(logfd
, g_tmpbuf
, g_blocksize
);
113 dowriteimage(imagefd
, g_tmpbuf
, g_blocksize
, offset
);
119 if (g_cur_blocks
== g_max_blocks
) {
120 printf("g_max_blocks (%llu, 0x%llx) reached!.\n", g_max_blocks
, g_max_blocks
);
126 case NBD_CMD_WRITE_ZEROES
:
128 memset(g_tmpbuf
, 0, g_blocksize
);
129 dowriteimage(imagefd
, g_tmpbuf
, g_blocksize
, offset
);
135 if (g_cur_blocks
== g_max_blocks
) {
136 printf("g_max_blocks (%llu, 0x%llx) reached!.\n", g_max_blocks
, g_max_blocks
);
142 printf(" Unexpected command %d (0x%x), replay impossible.\n",
143 (unsigned int) command
, (unsigned int) command
);
148 int main_loop(int logfd
, int imagefd
) {
149 struct nbd_request req
;
150 struct nbd_reply rep
;
160 /* Read a request or reply from the transaction file */
161 doread(logfd
, (char*) &magic
, sizeof(magic
));
162 magic
= ntohl(magic
);
164 case NBD_REQUEST_MAGIC
:
165 doread(logfd
, sizeof(magic
)+(char *)(&req
), sizeof(struct nbd_request
)-sizeof(magic
));
166 cookie
= ntohll(req
.cookie
);
167 offset
= ntohll(req
.from
);
168 len
= ntohl(req
.len
);
169 command
= ntohl(req
.type
);
171 ctext
= getcommandname(command
& NBD_CMD_MASK_COMMAND
);
173 if (g_verbose
>= VERBOSE_NORMAL
) {
174 printf("> H=%016llx C=0x%08x (%13s+%4s) O=%016llx L=%08x\n",
175 (long long unsigned int) cookie
,
178 (command
& NBD_CMD_FLAG_FUA
)?"FUA":"NONE",
179 (long long unsigned int) offset
,
182 process_command(command
, offset
, len
, logfd
, imagefd
);
186 case NBD_REPLY_MAGIC
:
187 doread(logfd
, sizeof(magic
)+(char *)(&rep
), sizeof(struct nbd_reply
)-sizeof(magic
));
188 cookie
= ntohll(rep
.cookie
);
189 error
= ntohl(rep
.error
);
191 if (g_verbose
>= VERBOSE_NORMAL
) {
192 printf("< H=%016llx E=0x%08x\n",
193 (long long unsigned int) cookie
,
198 case NBD_TRACELOG_MAGIC
:
199 doread(logfd
, sizeof(magic
)+(char *)(&req
), sizeof(struct nbd_request
)-sizeof(magic
));
200 cookie
= ntohll(req
.cookie
);
201 offset
= ntohll(req
.from
);
202 len
= ntohl(req
.len
);
203 command
= ntohl(req
.type
);
205 ctext
= gettracelogname(command
);
207 if (g_verbose
>= VERBOSE_NORMAL
) {
208 printf("TRACE_OPTION C=0x%08x (%23s) O=%016llx L=%08x\n",
211 (long long unsigned int) offset
,
214 if (offset
== NBD_TRACELOG_FROM_MAGIC
) {
217 case NBD_TRACELOG_SET_DATALOG
:
218 g_with_datalog
= !!len
;
219 if (g_verbose
>= VERBOSE_NORMAL
)
220 printf("TRACE_OPTION DATALOG set to %d.\n", (int)g_with_datalog
);
223 printf("TRACE_OPTION ? Unknown type\n");
226 printf("TRACE_OPTION ? Unknown FROM_MAGIC\n");
232 printf("? Unknown transaction type %08x, replay impossible.\n", magic
);
241 static void show_help(const char *progname
) {
243 printf("This is nbd-trplay, part of nbd %s.\n", PACKAGE_VERSION
);
244 printf("Use: %s -i <image> -l <log> [-m <max blocks>] [-b <block size]\n", progname
);
245 printf(" Applies up to <max blocks> elements from file <log> to disk image <image>.\n");
246 printf(" Command line parameters:\n");
247 printf(" <image>: name of the initial image file.\n");
248 printf(" <log>: nbd trace log. Must contain actual data (datalog=true).\n");
249 printf(" <block size>: device block size. Default 512.\n");
250 printf(" <max blocks>: where to stop the replay. Default all.\n");
251 printf(" -v: Increase verbose level. Specify multiple times to increase further.\n");
256 int main(int argc
, char **argv
) {
261 printf("%s -i <image> -l <log> [-m <max blocks>] [-b <block size]\n", argv
[0]);
263 while ((opt
= getopt(argc
, argv
, "i:l:m:b:hv")) != -1) {
264 if (g_verbose
>= VERBOSE_DEBUG
) {
265 printf("getopt: opt %c, optarg %s.\n", (char)opt
, optarg
);
277 g_max_blocks
= strtoull(optarg
, NULL
, 0);
278 if (g_max_blocks
== 0) {
279 printf(" Invalid block count.\n");
284 g_blocksize
= strtoul(optarg
, NULL
, 0);
285 if (g_blocksize
== 0) {
286 printf(" Invalid block size.\n");
289 if (g_blocksize
> BUFSIZE
) {
290 printf(" block size is larger than %d, not supported.\n", (int)BUFSIZE
);
295 imagefd
= open(optarg
, O_RDWR
, 0);
297 printf(" Opening disk image failed, errno %d.", errno
);
302 logfd
= open(optarg
, O_RDONLY
, 0);
304 printf(" Opening disk image failed, errno %d.", errno
);
312 printf(" Log file not specified, this is mandatory.\n");
316 printf(" Disk image not specified, this is mandatory.\n");
320 if (g_verbose
>= VERBOSE_NORMAL
) {
321 printf(" block size: %ld bytes (0x%lx bytes).\n", g_blocksize
, g_blocksize
);
322 printf(" max blocks to apply: %llu (0x%llx).\n", g_max_blocks
, g_max_blocks
);
324 main_loop(logfd
, imagefd
);