Error out if no volumes are specified instead of core-dumping.
[dragonfly.git] / sbin / hammer / cmd_mirror.c
blob99c945525ddd6b1d754d01ebf7d7c1233f700c6b
1 /*
2 * Copyright (c) 2008 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
34 * $DragonFly: src/sbin/hammer/cmd_mirror.c,v 1.3 2008/07/04 07:20:43 dillon Exp $
37 #include "hammer.h"
39 #define SERIALBUF_SIZE (512 * 1024)
41 struct hammer_pfs_head {
42 struct hammer_ioc_mrecord mrec;
43 u_int32_t version;
44 struct hammer_pseudofs_data pfsd;
47 static int read_mrecords(int fd, char *buf, u_int size,
48 hammer_ioc_mrecord_t pickup);
49 static void generate_mrec_header(int fd, int fdout,
50 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp);
51 static void validate_mrec_header(int fd, int fdin,
52 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp);
53 static void run_cmd(const char *path, ...);
54 static void mirror_usage(int code);
56 void
57 hammer_cmd_mirror_read(char **av, int ac)
59 struct hammer_ioc_mirror_rw mirror;
60 const char *filesystem;
61 char *buf = malloc(SERIALBUF_SIZE);
62 int fd;
64 if (ac > 2)
65 mirror_usage(1);
66 filesystem = av[0];
68 bzero(&mirror, sizeof(mirror));
69 hammer_key_beg_init(&mirror.key_beg);
70 hammer_key_end_init(&mirror.key_end);
72 fd = open(filesystem, O_RDONLY);
73 if (fd < 0)
74 err(1, "Unable to open %s", filesystem);
76 hammer_get_cycle(&mirror.key_beg);
78 generate_mrec_header(fd, 1, &mirror.tid_beg, &mirror.tid_end);
80 mirror.ubuf = buf;
81 mirror.size = SERIALBUF_SIZE;
82 if (ac == 2)
83 mirror.tid_beg = strtoull(av[1], NULL, 0);
85 do {
86 mirror.count = 0;
87 if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) {
88 fprintf(stderr, "Mirror-read %s failed: %s\n",
89 filesystem, strerror(errno));
90 exit(1);
92 if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) {
93 fprintf(stderr,
94 "Mirror-read %s interrupted by timer at"
95 " %016llx %08x\n",
96 filesystem,
97 mirror.key_cur.obj_id,
98 mirror.key_cur.localization);
99 if (CyclePath)
100 hammer_set_cycle(&mirror.key_cur);
101 exit(0);
103 mirror.key_beg = mirror.key_cur;
104 if (mirror.count)
105 write(1, mirror.ubuf, mirror.count);
106 } while (mirror.count != 0);
108 /* generate_mrec_update(fd, 1); */
110 if (CyclePath)
111 hammer_reset_cycle();
112 fprintf(stderr, "Mirror-read %s succeeded\n", filesystem);
115 void
116 hammer_cmd_mirror_write(char **av, int ac)
118 struct hammer_ioc_mirror_rw mirror;
119 const char *filesystem;
120 char *buf = malloc(SERIALBUF_SIZE);
121 int fd;
122 struct hammer_ioc_mrecord pickup;
124 if (ac > 2)
125 mirror_usage(1);
126 filesystem = av[0];
128 bzero(&mirror, sizeof(mirror));
129 hammer_key_beg_init(&mirror.key_beg);
130 hammer_key_end_init(&mirror.key_end);
132 fd = open(filesystem, O_RDONLY);
133 if (fd < 0)
134 err(1, "Unable to open %s", filesystem);
136 validate_mrec_header(fd, 0, &mirror.tid_beg, &mirror.tid_end);
138 mirror.ubuf = buf;
139 mirror.size = SERIALBUF_SIZE;
141 pickup.signature = 0;
143 for (;;) {
144 mirror.count = 0;
145 mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
146 if (mirror.size <= 0)
147 break;
148 if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) {
149 fprintf(stderr, "Mirror-write %s failed: %s\n",
150 filesystem, strerror(errno));
151 exit(1);
153 if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) {
154 fprintf(stderr,
155 "Mirror-write %s interrupted by timer at"
156 " %016llx %08x\n",
157 filesystem,
158 mirror.key_cur.obj_id,
159 mirror.key_cur.localization);
160 exit(0);
162 mirror.key_beg = mirror.key_cur;
164 fprintf(stderr, "Mirror-write %s succeeded\n", filesystem);
167 void
168 hammer_cmd_mirror_copy(char **av, int ac)
170 pid_t pid1;
171 pid_t pid2;
172 int fds[2];
173 char *ptr;
175 if (ac != 2)
176 mirror_usage(1);
178 if (pipe(fds) < 0) {
179 perror("pipe");
180 exit(1);
184 * Source
186 if ((pid1 = fork()) == 0) {
187 dup2(fds[0], 0);
188 dup2(fds[0], 1);
189 close(fds[0]);
190 close(fds[1]);
191 if ((ptr = strchr(av[0], ':')) != NULL) {
192 *ptr++ = 0;
193 run_cmd("/usr/bin/ssh", "ssh",
194 av[0], "hammer mirror-read", ptr, NULL);
195 } else {
196 hammer_cmd_mirror_read(av, 1);
198 _exit(1);
202 * Target
204 if ((pid2 = fork()) == 0) {
205 dup2(fds[1], 0);
206 dup2(fds[1], 1);
207 close(fds[0]);
208 close(fds[1]);
209 if ((ptr = strchr(av[1], ':')) != NULL) {
210 *ptr++ = 0;
211 run_cmd("/usr/bin/ssh", "ssh",
212 av[1], "hammer mirror-write", ptr, NULL);
213 } else {
214 hammer_cmd_mirror_write(av + 1, 1);
216 _exit(1);
218 close(fds[0]);
219 close(fds[1]);
221 while (waitpid(pid1, NULL, 0) <= 0)
223 while (waitpid(pid2, NULL, 0) <= 0)
227 static int
228 read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_t pickup)
230 u_int count;
231 size_t n;
232 size_t i;
234 count = 0;
235 while (size - count >= HAMMER_MREC_HEADSIZE) {
237 * Cached the record header in case we run out of buffer
238 * space.
240 if (pickup->signature == 0) {
241 for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
242 i = read(fd, (char *)pickup + n,
243 HAMMER_MREC_HEADSIZE - n);
244 if (i <= 0)
245 break;
247 if (n == 0)
248 break;
249 if (n != HAMMER_MREC_HEADSIZE) {
250 fprintf(stderr, "read_mrecords: short read on pipe\n");
251 exit(1);
254 if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) {
255 fprintf(stderr, "read_mrecords: malformed record on pipe, bad signature\n");
256 exit(1);
258 if (pickup->rec_crc != crc32((char *)pickup + HAMMER_MREC_CRCOFF, HAMMER_MREC_HEADSIZE - HAMMER_MREC_CRCOFF)) {
259 fprintf(stderr, "read_mrecords: malformed record on pipe, bad crc\n");
260 exit(1);
263 if (pickup->rec_size < HAMMER_MREC_HEADSIZE ||
264 pickup->rec_size > HAMMER_MREC_HEADSIZE + HAMMER_XBUFSIZE) {
265 fprintf(stderr, "read_mrecords: malformed record on pipe, illegal rec_size\n");
266 exit(1);
268 if (HAMMER_MREC_HEADSIZE + pickup->leaf.data_len > pickup->rec_size) {
269 fprintf(stderr, "read_mrecords: malformed record on pipe, illegal element data_len\n");
270 exit(1);
274 * Stop if we have insufficient space for the record and data.
276 if (size - count < pickup->rec_size)
277 break;
280 * Read the remainder and clear the pickup signature.
282 bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE);
283 pickup->signature = 0;
284 for (n = HAMMER_MREC_HEADSIZE; n < pickup->rec_size; n += i) {
285 i = read(fd, buf + count + n, pickup->rec_size - n);
286 if (i <= 0)
287 break;
289 if (n != pickup->rec_size) {
290 fprintf(stderr, "read_mrecords: short read on pipe\n");
291 exit(1);
293 if (pickup->leaf.data_len && pickup->leaf.data_offset) {
294 if (hammer_crc_test_leaf(buf + count + HAMMER_MREC_HEADSIZE, &pickup->leaf) == 0) {
295 fprintf(stderr, "read_mrecords: data_crc did not match data! obj=%016llx key=%016llx\n", pickup->leaf.base.obj_id, pickup->leaf.base.key);
296 fprintf(stderr, "continuing, but there are problems\n");
300 count += pickup->rec_size;
302 return(count);
306 * Generate a mirroring header with the pfs information of the
307 * originating filesytem.
309 static void
310 generate_mrec_header(int fd, int fdout,
311 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
313 struct hammer_ioc_pseudofs_rw pfs;
314 struct hammer_pfs_head pfs_head;
316 bzero(&pfs, sizeof(pfs));
317 bzero(&pfs_head, sizeof(pfs_head));
318 pfs.ondisk = &pfs_head.pfsd;
319 pfs.bytes = sizeof(pfs_head.pfsd);
320 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
321 fprintf(stderr, "mirror-read: not a HAMMER fs/pseudofs!\n");
322 exit(1);
324 if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
325 fprintf(stderr, "mirror-read: HAMMER pfs version mismatch!\n");
326 exit(1);
330 * sync_beg_tid - lowest TID on source after which a full history
331 * is available.
333 * sync_end_tid - highest fully synchronized TID from source.
335 *tid_begp = pfs_head.pfsd.sync_beg_tid;
336 *tid_endp = pfs_head.pfsd.sync_end_tid;
338 pfs_head.version = pfs.version;
339 pfs_head.mrec.signature = HAMMER_IOC_MIRROR_SIGNATURE;
340 pfs_head.mrec.rec_size = sizeof(pfs_head);
341 pfs_head.mrec.type = HAMMER_MREC_TYPE_PFSD;
342 pfs_head.mrec.rec_crc = crc32((char *)&pfs_head + HAMMER_MREC_CRCOFF,
343 sizeof(pfs_head) - HAMMER_MREC_CRCOFF);
344 write(fdout, &pfs_head, sizeof(pfs_head));
348 * Validate the pfs information from the originating filesystem
349 * against the target filesystem. shared_uuid must match.
351 static void
352 validate_mrec_header(int fd, int fdin,
353 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
355 struct hammer_ioc_pseudofs_rw pfs;
356 struct hammer_pfs_head pfs_head;
357 struct hammer_pseudofs_data pfsd;
358 size_t bytes;
359 size_t n;
360 size_t i;
363 * Get the PFSD info from the target filesystem.
365 bzero(&pfs, sizeof(pfs));
366 bzero(&pfsd, sizeof(pfsd));
367 pfs.ondisk = &pfsd;
368 pfs.bytes = sizeof(pfsd);
369 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
370 fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n");
371 exit(1);
373 if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
374 fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n");
375 exit(1);
379 * Read in the PFSD header from the sender.
381 for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
382 i = read(fdin, (char *)&pfs_head + n, HAMMER_MREC_HEADSIZE - n);
383 if (i <= 0)
384 break;
386 if (n != HAMMER_MREC_HEADSIZE) {
387 fprintf(stderr, "mirror-write: short read of PFS header\n");
388 exit(1);
390 if (pfs_head.mrec.signature != HAMMER_IOC_MIRROR_SIGNATURE) {
391 fprintf(stderr, "mirror-write: PFS header has bad signature\n");
392 exit(1);
394 if (pfs_head.mrec.type != HAMMER_MREC_TYPE_PFSD) {
395 fprintf(stderr, "mirror-write: Expected PFS header, got mirroring record header instead!\n");
396 exit(1);
398 bytes = pfs_head.mrec.rec_size;
399 if (bytes < HAMMER_MREC_HEADSIZE)
400 bytes = (int)HAMMER_MREC_HEADSIZE;
401 if (bytes > sizeof(pfs_head))
402 bytes = sizeof(pfs_head);
403 while (n < bytes) {
404 i = read(fdin, (char *)&pfs_head + n, bytes - n);
405 if (i <= 0)
406 break;
407 n += i;
409 if (n != bytes) {
410 fprintf(stderr, "mirror-write: short read of PFS payload\n");
411 exit(1);
413 if (pfs_head.version != pfs.version) {
414 fprintf(stderr, "mirror-write: Version mismatch in PFS header\n");
415 exit(1);
417 if (pfs_head.mrec.rec_size != sizeof(pfs_head)) {
418 fprintf(stderr, "mirror-write: The PFS header has the wrong size!\n");
419 exit(1);
423 * Whew. Ok, is the read PFS info compatible with the target?
425 if (bcmp(&pfs_head.pfsd.shared_uuid, &pfsd.shared_uuid, sizeof(pfsd.shared_uuid)) != 0) {
426 fprintf(stderr, "mirror-write: source and target have different shared_uuid's!\n");
427 exit(1);
429 if ((pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) {
430 fprintf(stderr, "mirror-write: target must be in slave mode\n");
431 exit(1);
433 *tid_begp = pfs_head.pfsd.sync_beg_tid;
434 *tid_endp = pfs_head.pfsd.sync_end_tid;
437 static void
438 run_cmd(const char *path, ...)
440 va_list va;
441 char *av[16];
442 int n;
444 va_start(va, path);
445 for (n = 0; n < 16; ++n) {
446 av[n] = va_arg(va, char *);
447 if (av[n] == NULL)
448 break;
450 va_end(va);
451 assert(n != 16);
452 execv(path, av);
455 static void
456 mirror_usage(int code)
458 fprintf(stderr,
459 "hammer mirror-read <filesystem>\n"
460 "hammer mirror-write <filesystem>\n"
461 "hammer mirror-copy [[user@]host:]fs [[user@]host:]fs\n"
463 exit(code);