]>
git.proxmox.com Git - ceph.git/blob - ceph/src/tools/rbd/action/MergeDiff.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #define _LARGEFILE64_SOURCE
8 #include "include/compat.h"
9 #include "tools/rbd/ArgumentTypes.h"
10 #include "tools/rbd/Shell.h"
11 #include "tools/rbd/Utils.h"
12 #include "common/safe_io.h"
13 #include "common/debug.h"
14 #include "common/errno.h"
16 #include <boost/program_options.hpp>
18 #define dout_context g_ceph_context
19 #define dout_subsys ceph_subsys_rbd
23 namespace merge_diff
{
25 namespace at
= argument_types
;
26 namespace po
= boost::program_options
;
28 static int parse_diff_header(int fd
, __u8
*tag
, string
*from
, string
*to
, uint64_t *size
)
33 char buf
[utils::RBD_DIFF_BANNER
.size() + 1];
34 r
= safe_read_exact(fd
, buf
, utils::RBD_DIFF_BANNER
.size());
38 buf
[utils::RBD_DIFF_BANNER
.size()] = '\0';
39 if (strcmp(buf
, utils::RBD_DIFF_BANNER
.c_str())) {
40 std::cerr
<< "invalid banner '" << buf
<< "', expected '"
41 << utils::RBD_DIFF_BANNER
<< "'" << std::endl
;
47 r
= safe_read_exact(fd
, tag
, 1);
51 if (*tag
== RBD_DIFF_FROM_SNAP
) {
52 r
= utils::read_string(fd
, 4096, from
); // 4k limit to make sure we don't get a garbage string
55 dout(2) << " from snap " << *from
<< dendl
;
56 } else if (*tag
== RBD_DIFF_TO_SNAP
) {
57 r
= utils::read_string(fd
, 4096, to
); // 4k limit to make sure we don't get a garbage string
60 dout(2) << " to snap " << *to
<< dendl
;
61 } else if (*tag
== RBD_DIFF_IMAGE_SIZE
) {
63 r
= safe_read_exact(fd
, buf
, 8);
69 bufferlist::iterator p
= bl
.begin();
79 static int parse_diff_body(int fd
, __u8
*tag
, uint64_t *offset
, uint64_t *length
)
84 r
= safe_read_exact(fd
, tag
, 1);
89 if (*tag
== RBD_DIFF_END
) {
95 if (*tag
!= RBD_DIFF_WRITE
&& *tag
!= RBD_DIFF_ZERO
)
99 r
= safe_read_exact(fd
, buf
, 16);
105 bufferlist::iterator p
= bl
.begin();
106 ::decode(*offset
, p
);
107 ::decode(*length
, p
);
116 * fd: the diff file to read from
117 * pd: the diff file to be written into
119 static int accept_diff_body(int fd
, int pd
, __u8 tag
, uint64_t offset
, uint64_t length
)
121 if (tag
== RBD_DIFF_END
)
126 ::encode(offset
, bl
);
127 ::encode(length
, bl
);
133 if (tag
== RBD_DIFF_WRITE
) {
134 bufferptr bp
= buffer::create(length
);
135 r
= safe_read_exact(fd
, bp
.c_str(), length
);
140 r
= data
.write_fd(pd
);
149 * Merge two diff files into one single file
150 * Note: It does not do the merging work if
151 * either of the source diff files is stripped,
152 * since which complicates the process and is
155 static int do_merge_diff(const char *first
, const char *second
,
156 const char *path
, bool no_progress
)
158 utils::ProgressContext
pc("Merging image diff", no_progress
);
159 int fd
= -1, sd
= -1, pd
= -1, r
;
167 __u8 f_tag
= 0, s_tag
= 0;
168 uint64_t f_off
= 0, f_len
= 0;
169 uint64_t s_off
= 0, s_len
= 0;
170 bool f_end
= false, s_end
= false;
172 bool first_stdin
= !strcmp(first
, "-");
176 fd
= open(first
, O_RDONLY
);
179 std::cerr
<< "rbd: error opening " << first
<< std::endl
;
184 sd
= open(second
, O_RDONLY
);
187 std::cerr
<< "rbd: error opening " << second
<< std::endl
;
191 if (strcmp(path
, "-") == 0) {
194 pd
= open(path
, O_WRONLY
| O_CREAT
| O_EXCL
, 0644);
197 std::cerr
<< "rbd: error create " << path
<< std::endl
;
202 //We just handle the case like 'banner, [ftag], [ttag], stag, [wztag]*,etag',
203 // and the (offset,length) in wztag must be ascending order.
204 r
= parse_diff_header(fd
, &f_tag
, &f_from
, &f_to
, &f_size
);
206 std::cerr
<< "rbd: failed to parse first diff header" << std::endl
;
210 r
= parse_diff_header(sd
, &s_tag
, &s_from
, &s_to
, &s_size
);
212 std::cerr
<< "rbd: failed to parse second diff header" << std::endl
;
216 if (f_to
!= s_from
) {
218 std::cerr
<< "The first TO snapshot must be equal with the second FROM "
219 << "snapshot, aborting" << std::endl
;
226 bl
.append(utils::RBD_DIFF_BANNER
);
230 tag
= RBD_DIFF_FROM_SNAP
;
232 ::encode(f_from
, bl
);
236 tag
= RBD_DIFF_TO_SNAP
;
241 tag
= RBD_DIFF_IMAGE_SIZE
;
243 ::encode(s_size
, bl
);
247 std::cerr
<< "rbd: failed to write merged diff header" << std::endl
;
252 pc_size
= f_size
<< 1;
254 pc_size
= s_size
<< 1;
257 while (!f_end
|| !s_end
) {
258 // progress through input
259 pc
.update_progress(f_off
+ s_off
, pc_size
);
261 if (!f_end
&& !f_len
) {
262 uint64_t last_off
= f_off
;
264 r
= parse_diff_body(fd
, &f_tag
, &f_off
, &f_len
);
265 dout(2) << "first diff data chunk: tag=" << f_tag
<< ", "
266 << "off=" << f_off
<< ", "
267 << "len=" << f_len
<< dendl
;
269 std::cerr
<< "rbd: failed to read first diff data chunk header"
274 if (f_tag
== RBD_DIFF_END
) {
276 f_tag
= RBD_DIFF_ZERO
;
279 f_len
= s_size
- f_size
;
284 if (last_off
> f_off
) {
286 std::cerr
<< "rbd: out-of-order offset from first diff ("
287 << last_off
<< " > " << f_off
<< ")" << std::endl
;
292 if (!s_end
&& !s_len
) {
293 uint64_t last_off
= s_off
;
295 r
= parse_diff_body(sd
, &s_tag
, &s_off
, &s_len
);
296 dout(2) << "second diff data chunk: tag=" << s_tag
<< ", "
297 << "off=" << s_off
<< ", "
298 << "len=" << s_len
<< dendl
;
300 std::cerr
<< "rbd: failed to read second diff data chunk header"
305 if (s_tag
== RBD_DIFF_END
) {
309 s_len
= f_size
- s_size
;
314 if (last_off
> s_off
) {
316 std::cerr
<< "rbd: out-of-order offset from second diff ("
317 << last_off
<< " > " << s_off
<< ")" << std::endl
;
322 if (f_off
< s_off
&& f_len
) {
323 uint64_t delta
= s_off
- f_off
;
326 r
= accept_diff_body(fd
, pd
, f_tag
, f_off
, delta
);
328 std::cerr
<< "rbd: failed to merge diff chunk" << std::endl
;
339 assert(f_off
>= s_off
);
341 if (f_off
< s_off
+ s_len
&& f_len
) {
342 uint64_t delta
= s_off
+ s_len
- f_off
;
345 if (f_tag
== RBD_DIFF_WRITE
) {
347 bufferptr bp
= buffer::create(delta
);
348 r
= safe_read_exact(fd
, bp
.c_str(), delta
);
350 off64_t l
= lseek64(fd
, delta
, SEEK_CUR
);
351 r
= l
< 0 ? -errno
: 0;
354 std::cerr
<< "rbd: failed to skip first diff data" << std::endl
;
366 assert(f_off
>= s_off
+ s_len
);
368 r
= accept_diff_body(sd
, pd
, s_tag
, s_off
, s_len
);
370 std::cerr
<< "rbd: failed to merge diff chunk" << std::endl
;
377 assert(f_end
&& s_end
);
383 __u8 tag
= RBD_DIFF_END
;
407 void get_arguments(po::options_description
*positional
,
408 po::options_description
*options
) {
409 positional
->add_options()
410 ("diff1-path", "path to first diff (or '-' for stdin)")
411 ("diff2-path", "path to second diff");
412 at::add_path_options(positional
, options
,
413 "path to merged diff (or '-' for stdout)");
414 at::add_no_progress_option(options
);
417 int execute(const po::variables_map
&vm
) {
418 std::string first_diff
= utils::get_positional_argument(vm
, 0);
419 if (first_diff
.empty()) {
420 std::cerr
<< "rbd: first diff was not specified" << std::endl
;
424 std::string second_diff
= utils::get_positional_argument(vm
, 1);
425 if (second_diff
.empty()) {
426 std::cerr
<< "rbd: second diff was not specified" << std::endl
;
431 int r
= utils::get_path(vm
, utils::get_positional_argument(vm
, 2),
437 r
= do_merge_diff(first_diff
.c_str(), second_diff
.c_str(), path
.c_str(),
438 vm
[at::NO_PROGRESS
].as
<bool>());
440 cerr
<< "rbd: merge-diff error" << std::endl
;
447 Shell::Action
action(
448 {"merge-diff"}, {}, "Merge two diff exports together.", "",
449 &get_arguments
, &execute
);
451 } // namespace merge_diff
452 } // namespace action