Update copyright statement
[nbd.git] / nbd-trplay.c
blobdeeea517798cc2a7d3aeab85dbf6c113ad573f97
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * nbd-trplay.c
5 * Takes an nbd transaction log file and replays some/all of the write commands.
7 * Based on nbd-trdump
8 * (C) Robert Bosch GmbH, 2021
9 */
11 #include <stdlib.h>
12 #include <limits.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <sys/time.h>
16 #include <sys/types.h>
17 #include <fcntl.h>
18 #include <stdint.h>
19 #include <stdbool.h>
20 #include <unistd.h>
21 #include "config.h"
22 /* We don't want to do syslog output in this program */
23 #undef ISSERVER
24 #include "cliserv.h"
25 #include "nbd.h"
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
36 #define VERBOSE_OFF 0
38 int g_verbose = 0;
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) {
45 ssize_t res;
47 while(len>0) {
48 if((res=read(f, buf, len)) <=0) {
49 if (!res) {
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);
53 exit(0);
55 perror ("Error reading transactions");
56 exit(1);
58 len-=res;
59 buf+=res;
63 static inline void dowriteimage(int imagefd, const char *buf, size_t len, off_t offset) {
64 ssize_t res;
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);
73 while(len>0) {
74 if((res=pwrite(imagefd, buf, len, offset)) <=0) {
75 if (!res)
76 exit(0);
77 perror ("Error writing to image file");
78 exit(1);
80 len-=res;
81 buf+=res;
82 offset+=res;
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);
92 exit(1);
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);
97 exit(1);
100 switch (command & NBD_CMD_MASK_COMMAND) {
101 case NBD_CMD_READ:
102 case NBD_CMD_DISC:
103 case NBD_CMD_FLUSH:
104 /* READ, DISCONNECT, FLUSH: nothing to do */
105 break;
106 case NBD_CMD_WRITE:
107 if (!g_with_datalog) {
108 printf(" NBD_CMD_WRITE without data log, replay impossible.\n");
109 exit(1);
111 while (len > 0) {
112 doread(logfd, g_tmpbuf, g_blocksize);
113 dowriteimage(imagefd, g_tmpbuf, g_blocksize, offset);
115 offset+=g_blocksize;
116 len-=g_blocksize;
117 g_cur_blocks++;
119 if (g_cur_blocks == g_max_blocks) {
120 printf("g_max_blocks (%llu, 0x%llx) reached!.\n", g_max_blocks, g_max_blocks);
121 exit(0);
124 break;
125 case NBD_CMD_TRIM:
126 case NBD_CMD_WRITE_ZEROES:
127 while (len > 0) {
128 memset(g_tmpbuf, 0, g_blocksize);
129 dowriteimage(imagefd, g_tmpbuf, g_blocksize, offset);
131 offset+=g_blocksize;
132 len-=g_blocksize;
133 g_cur_blocks++;
135 if (g_cur_blocks == g_max_blocks) {
136 printf("g_max_blocks (%llu, 0x%llx) reached!.\n", g_max_blocks, g_max_blocks);
137 exit(0);
140 break;
141 default:
142 printf(" Unexpected command %d (0x%x), replay impossible.\n",
143 (unsigned int) command, (unsigned int) command);
144 exit(1);
148 int main_loop(int logfd, int imagefd) {
149 struct nbd_request req;
150 struct nbd_reply rep;
151 uint32_t magic;
152 uint64_t cookie;
153 uint32_t error;
154 uint32_t command;
155 uint32_t len;
156 uint64_t offset;
157 const char * ctext;
159 while (1) {
160 /* Read a request or reply from the transaction file */
161 doread(logfd, (char*) &magic, sizeof(magic));
162 magic = ntohl(magic);
163 switch (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,
176 command,
177 ctext,
178 (command & NBD_CMD_FLAG_FUA)?"FUA":"NONE",
179 (long long unsigned int) offset,
180 len);
182 process_command(command, offset, len, logfd, imagefd);
184 break;
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,
194 error);
196 break;
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",
209 command,
210 ctext,
211 (long long unsigned int) offset,
212 len);
214 if (offset == NBD_TRACELOG_FROM_MAGIC) {
216 switch (command) {
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);
221 break;
222 default:
223 printf("TRACE_OPTION ? Unknown type\n");
225 } else {
226 printf("TRACE_OPTION ? Unknown FROM_MAGIC\n");
228 break;
231 default:
232 printf("? Unknown transaction type %08x, replay impossible.\n", magic);
233 exit(1);
237 /* never reached */
238 return 0;
241 static void show_help(const char *progname) {
242 printf("\n");
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) {
257 int opt;
258 int imagefd = -1;
259 int logfd = -1;
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);
267 switch(opt) {
268 case 'v':
269 g_verbose++;
270 break;
271 default:
272 case '?':
273 case 'h':
274 show_help(argv[0]);
275 return 0;
276 case 'm':
277 g_max_blocks = strtoull(optarg, NULL, 0);
278 if (g_max_blocks == 0) {
279 printf(" Invalid block count.\n");
280 return 1;
282 break;
283 case 'b':
284 g_blocksize = strtoul(optarg, NULL, 0);
285 if (g_blocksize == 0) {
286 printf(" Invalid block size.\n");
287 return 1;
289 if (g_blocksize > BUFSIZE) {
290 printf(" block size is larger than %d, not supported.\n", (int)BUFSIZE);
291 return 1;
293 break;
294 case 'i':
295 imagefd = open(optarg, O_RDWR, 0);
296 if (imagefd == -1) {
297 printf(" Opening disk image failed, errno %d.", errno);
298 return 1;
300 break;
301 case 'l':
302 logfd = open(optarg, O_RDONLY, 0);
303 if (logfd == -1) {
304 printf(" Opening disk image failed, errno %d.", errno);
305 return 1;
307 break;
311 if (logfd == -1) {
312 printf(" Log file not specified, this is mandatory.\n");
313 return 1;
315 if (imagefd == -1) {
316 printf(" Disk image not specified, this is mandatory.\n");
317 return 1;
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);
326 return 0;