2 * Copyright (c) 2008 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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
34 * $DragonFly: src/sbin/hammer/cmd_mirror.c,v 1.3 2008/07/04 07:20:43 dillon Exp $
39 #define SERIALBUF_SIZE (512 * 1024)
41 struct hammer_pfs_head
{
42 struct hammer_ioc_mrecord mrec
;
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
);
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
);
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
);
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
);
81 mirror
.size
= SERIALBUF_SIZE
;
83 mirror
.tid_beg
= strtoull(av
[1], NULL
, 0);
87 if (ioctl(fd
, HAMMERIOC_MIRROR_READ
, &mirror
) < 0) {
88 fprintf(stderr
, "Mirror-read %s failed: %s\n",
89 filesystem
, strerror(errno
));
92 if (mirror
.head
.flags
& HAMMER_IOC_HEAD_INTR
) {
94 "Mirror-read %s interrupted by timer at"
97 mirror
.key_cur
.obj_id
,
98 mirror
.key_cur
.localization
);
100 hammer_set_cycle(&mirror
.key_cur
);
103 mirror
.key_beg
= mirror
.key_cur
;
105 write(1, mirror
.ubuf
, mirror
.count
);
106 } while (mirror
.count
!= 0);
108 /* generate_mrec_update(fd, 1); */
111 hammer_reset_cycle();
112 fprintf(stderr
, "Mirror-read %s succeeded\n", filesystem
);
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
);
122 struct hammer_ioc_mrecord pickup
;
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
);
134 err(1, "Unable to open %s", filesystem
);
136 validate_mrec_header(fd
, 0, &mirror
.tid_beg
, &mirror
.tid_end
);
139 mirror
.size
= SERIALBUF_SIZE
;
141 pickup
.signature
= 0;
145 mirror
.size
= read_mrecords(0, buf
, SERIALBUF_SIZE
, &pickup
);
146 if (mirror
.size
<= 0)
148 if (ioctl(fd
, HAMMERIOC_MIRROR_WRITE
, &mirror
) < 0) {
149 fprintf(stderr
, "Mirror-write %s failed: %s\n",
150 filesystem
, strerror(errno
));
153 if (mirror
.head
.flags
& HAMMER_IOC_HEAD_INTR
) {
155 "Mirror-write %s interrupted by timer at"
158 mirror
.key_cur
.obj_id
,
159 mirror
.key_cur
.localization
);
162 mirror
.key_beg
= mirror
.key_cur
;
164 fprintf(stderr
, "Mirror-write %s succeeded\n", filesystem
);
168 hammer_cmd_mirror_copy(char **av
, int ac
)
186 if ((pid1
= fork()) == 0) {
191 if ((ptr
= strchr(av
[0], ':')) != NULL
) {
193 run_cmd("/usr/bin/ssh", "ssh",
194 av
[0], "hammer mirror-read", ptr
, NULL
);
196 hammer_cmd_mirror_read(av
, 1);
204 if ((pid2
= fork()) == 0) {
209 if ((ptr
= strchr(av
[1], ':')) != NULL
) {
211 run_cmd("/usr/bin/ssh", "ssh",
212 av
[1], "hammer mirror-write", ptr
, NULL
);
214 hammer_cmd_mirror_write(av
+ 1, 1);
221 while (waitpid(pid1
, NULL
, 0) <= 0)
223 while (waitpid(pid2
, NULL
, 0) <= 0)
228 read_mrecords(int fd
, char *buf
, u_int size
, hammer_ioc_mrecord_t pickup
)
235 while (size
- count
>= HAMMER_MREC_HEADSIZE
) {
237 * Cached the record header in case we run out of buffer
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
);
249 if (n
!= HAMMER_MREC_HEADSIZE
) {
250 fprintf(stderr
, "read_mrecords: short read on pipe\n");
254 if (pickup
->signature
!= HAMMER_IOC_MIRROR_SIGNATURE
) {
255 fprintf(stderr
, "read_mrecords: malformed record on pipe, bad signature\n");
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");
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");
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");
274 * Stop if we have insufficient space for the record and data.
276 if (size
- count
< pickup
->rec_size
)
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
);
289 if (n
!= pickup
->rec_size
) {
290 fprintf(stderr
, "read_mrecords: short read on pipe\n");
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
;
306 * Generate a mirroring header with the pfs information of the
307 * originating filesytem.
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");
324 if (pfs
.version
!= HAMMER_IOC_PSEUDOFS_VERSION
) {
325 fprintf(stderr
, "mirror-read: HAMMER pfs version mismatch!\n");
330 * sync_beg_tid - lowest TID on source after which a full history
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.
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
;
363 * Get the PFSD info from the target filesystem.
365 bzero(&pfs
, sizeof(pfs
));
366 bzero(&pfsd
, sizeof(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");
373 if (pfs
.version
!= HAMMER_IOC_PSEUDOFS_VERSION
) {
374 fprintf(stderr
, "mirror-write: HAMMER pfs version mismatch!\n");
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
);
386 if (n
!= HAMMER_MREC_HEADSIZE
) {
387 fprintf(stderr
, "mirror-write: short read of PFS header\n");
390 if (pfs_head
.mrec
.signature
!= HAMMER_IOC_MIRROR_SIGNATURE
) {
391 fprintf(stderr
, "mirror-write: PFS header has bad signature\n");
394 if (pfs_head
.mrec
.type
!= HAMMER_MREC_TYPE_PFSD
) {
395 fprintf(stderr
, "mirror-write: Expected PFS header, got mirroring record header instead!\n");
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
);
404 i
= read(fdin
, (char *)&pfs_head
+ n
, bytes
- n
);
410 fprintf(stderr
, "mirror-write: short read of PFS payload\n");
413 if (pfs_head
.version
!= pfs
.version
) {
414 fprintf(stderr
, "mirror-write: Version mismatch in PFS header\n");
417 if (pfs_head
.mrec
.rec_size
!= sizeof(pfs_head
)) {
418 fprintf(stderr
, "mirror-write: The PFS header has the wrong size!\n");
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");
429 if ((pfsd
.mirror_flags
& HAMMER_PFSD_SLAVE
) == 0) {
430 fprintf(stderr
, "mirror-write: target must be in slave mode\n");
433 *tid_begp
= pfs_head
.pfsd
.sync_beg_tid
;
434 *tid_endp
= pfs_head
.pfsd
.sync_end_tid
;
438 run_cmd(const char *path
, ...)
445 for (n
= 0; n
< 16; ++n
) {
446 av
[n
] = va_arg(va
, char *);
456 mirror_usage(int code
)
459 "hammer mirror-read <filesystem>\n"
460 "hammer mirror-write <filesystem>\n"
461 "hammer mirror-copy [[user@]host:]fs [[user@]host:]fs\n"