1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "include/compat.h"
5 #include "tools/rbd/ArgumentTypes.h"
6 #include "tools/rbd/Shell.h"
7 #include "tools/rbd/Utils.h"
8 #include "include/Context.h"
9 #include "common/errno.h"
10 #include "common/Throttle.h"
11 #include "include/encoding.h"
15 #include <boost/program_options.hpp>
16 #include <boost/scope_exit.hpp>
20 namespace export_full
{
22 struct ExportDiffContext
{
27 utils::ProgressContext pc
;
28 OrderedThrottle throttle
;
30 ExportDiffContext(librbd::Image
*i
, int f
, uint64_t t
, int max_ops
,
31 bool no_progress
, int eformat
) :
32 image(i
), fd(f
), export_format(eformat
), totalsize(t
), pc("Exporting image", no_progress
),
33 throttle(max_ops
, true) {
37 class C_ExportDiff
: public Context
{
39 C_ExportDiff(ExportDiffContext
*edc
, uint64_t offset
, uint64_t length
,
40 bool exists
, int export_format
)
41 : m_export_diff_context(edc
), m_offset(offset
), m_length(length
),
42 m_exists(exists
), m_export_format(export_format
) {
46 if (m_export_diff_context
->throttle
.pending_error()) {
47 return m_export_diff_context
->throttle
.wait_for_ret();
50 C_OrderedThrottle
*ctx
= m_export_diff_context
->throttle
.start_op(this);
52 librbd::RBD::AioCompletion
*aio_completion
=
53 new librbd::RBD::AioCompletion(ctx
, &utils::aio_context_callback
);
55 int op_flags
= LIBRADOS_OP_FLAG_FADVISE_NOCACHE
;
56 int r
= m_export_diff_context
->image
->aio_read2(
57 m_offset
, m_length
, m_read_data
, aio_completion
, op_flags
);
59 aio_completion
->release();
68 static int export_diff_cb(uint64_t offset
, size_t length
, int exists
,
70 ExportDiffContext
*edc
= reinterpret_cast<ExportDiffContext
*>(arg
);
72 C_ExportDiff
*context
= new C_ExportDiff(edc
, offset
, length
, exists
, edc
->export_format
);
73 return context
->send();
77 void finish(int r
) override
{
80 m_exists
= !m_read_data
.is_zero();
82 r
= write_extent(m_export_diff_context
, m_offset
, m_length
, m_exists
, m_export_format
);
83 if (r
== 0 && m_exists
) {
84 r
= m_read_data
.write_fd(m_export_diff_context
->fd
);
87 m_export_diff_context
->throttle
.end_op(r
);
91 ExportDiffContext
*m_export_diff_context
;
96 bufferlist m_read_data
;
98 static int write_extent(ExportDiffContext
*edc
, uint64_t offset
,
99 uint64_t length
, bool exists
, int export_format
) {
102 __u8 tag
= exists
? RBD_DIFF_WRITE
: RBD_DIFF_ZERO
;
105 if (export_format
== 2) {
106 if (tag
== RBD_DIFF_WRITE
)
107 len
= 8 + 8 + length
;
112 ::encode(offset
, bl
);
113 ::encode(length
, bl
);
114 int r
= bl
.write_fd(edc
->fd
);
116 edc
->pc
.update_progress(offset
, edc
->totalsize
);
122 int do_export_diff_fd(librbd::Image
& image
, const char *fromsnapname
,
123 const char *endsnapname
, bool whole_object
,
124 int fd
, bool no_progress
, int export_format
)
127 librbd::image_info_t info
;
129 r
= image
.stat(info
, sizeof(info
));
136 if (export_format
== 1)
137 bl
.append(utils::RBD_DIFF_BANNER
);
139 bl
.append(utils::RBD_DIFF_BANNER_V2
);
144 tag
= RBD_DIFF_FROM_SNAP
;
146 std::string
from(fromsnapname
);
147 if (export_format
== 2) {
148 len
= from
.length() + 4;
155 tag
= RBD_DIFF_TO_SNAP
;
157 std::string
to(endsnapname
);
158 if (export_format
== 2) {
159 len
= to
.length() + 4;
165 tag
= RBD_DIFF_IMAGE_SIZE
;
167 uint64_t endsize
= info
.size
;
168 if (export_format
== 2) {
172 ::encode(endsize
, bl
);
179 ExportDiffContext
edc(&image
, fd
, info
.size
,
180 g_conf
->rbd_concurrent_management_ops
, no_progress
,
182 r
= image
.diff_iterate2(fromsnapname
, 0, info
.size
, true, whole_object
,
183 &C_ExportDiff::export_diff_cb
, (void *)&edc
);
188 r
= edc
.throttle
.wait_for_ret();
194 __u8 tag
= RBD_DIFF_END
;
209 int do_export_diff(librbd::Image
& image
, const char *fromsnapname
,
210 const char *endsnapname
, bool whole_object
,
211 const char *path
, bool no_progress
)
216 if (strcmp(path
, "-") == 0)
219 fd
= open(path
, O_WRONLY
| O_CREAT
| O_EXCL
, 0644);
223 r
= do_export_diff_fd(image
, fromsnapname
, endsnapname
, whole_object
, fd
, no_progress
, 1);
227 if (r
< 0 && fd
!= 1) {
235 namespace at
= argument_types
;
236 namespace po
= boost::program_options
;
238 void get_arguments_diff(po::options_description
*positional
,
239 po::options_description
*options
) {
240 at::add_image_or_snap_spec_options(positional
, options
,
241 at::ARGUMENT_MODIFIER_SOURCE
);
242 at::add_path_options(positional
, options
,
243 "export file (or '-' for stdout)");
244 options
->add_options()
245 (at::FROM_SNAPSHOT_NAME
.c_str(), po::value
<std::string
>(),
246 "snapshot starting point")
247 (at::WHOLE_OBJECT
.c_str(), po::bool_switch(), "compare whole object");
248 at::add_no_progress_option(options
);
251 int execute_diff(const po::variables_map
&vm
) {
252 size_t arg_index
= 0;
253 std::string pool_name
;
254 std::string image_name
;
255 std::string snap_name
;
256 int r
= utils::get_pool_image_snapshot_names(
257 vm
, at::ARGUMENT_MODIFIER_SOURCE
, &arg_index
, &pool_name
, &image_name
,
258 &snap_name
, utils::SNAPSHOT_PRESENCE_PERMITTED
,
259 utils::SPEC_VALIDATION_NONE
);
265 r
= utils::get_path(vm
, utils::get_positional_argument(vm
, 1), &path
);
270 std::string from_snap_name
;
271 if (vm
.count(at::FROM_SNAPSHOT_NAME
)) {
272 from_snap_name
= vm
[at::FROM_SNAPSHOT_NAME
].as
<std::string
>();
275 librados::Rados rados
;
276 librados::IoCtx io_ctx
;
278 r
= utils::init_and_open_image(pool_name
, image_name
, "", snap_name
, true,
279 &rados
, &io_ctx
, &image
);
284 r
= do_export_diff(image
,
285 from_snap_name
.empty() ? nullptr : from_snap_name
.c_str(),
286 snap_name
.empty() ? nullptr : snap_name
.c_str(),
287 vm
[at::WHOLE_OBJECT
].as
<bool>(), path
.c_str(),
288 vm
[at::NO_PROGRESS
].as
<bool>());
290 std::cerr
<< "rbd: export-diff error: " << cpp_strerror(r
) << std::endl
;
296 Shell::SwitchArguments
switched_arguments({at::WHOLE_OBJECT
});
297 Shell::Action
action_diff(
298 {"export-diff"}, {}, "Export incremental diff to file.", "",
299 &get_arguments_diff
, &execute_diff
);
301 class C_Export
: public Context
304 C_Export(SimpleThrottle
&simple_throttle
, librbd::Image
&image
,
305 uint64_t fd_offset
, uint64_t offset
, uint64_t length
, int fd
)
307 new librbd::RBD::AioCompletion(this, &utils::aio_context_callback
)),
308 m_throttle(simple_throttle
), m_image(image
), m_dest_offset(fd_offset
),
309 m_offset(offset
), m_length(length
), m_fd(fd
)
315 m_throttle
.start_op();
317 int op_flags
= LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL
|
318 LIBRADOS_OP_FLAG_FADVISE_NOCACHE
;
319 int r
= m_image
.aio_read2(m_offset
, m_length
, m_bufferlist
,
320 m_aio_completion
, op_flags
);
322 cerr
<< "rbd: error requesting read from source image" << std::endl
;
323 m_aio_completion
->release();
324 m_throttle
.end_op(r
);
328 void finish(int r
) override
330 BOOST_SCOPE_EXIT((&m_throttle
) (&r
))
332 m_throttle
.end_op(r
);
333 } BOOST_SCOPE_EXIT_END
336 cerr
<< "rbd: error reading from source image at offset "
337 << m_offset
<< ": " << cpp_strerror(r
) << std::endl
;
341 assert(m_bufferlist
.length() == static_cast<size_t>(r
));
342 if (m_fd
!= STDOUT_FILENO
) {
343 if (m_bufferlist
.is_zero()) {
347 uint64_t chkret
= lseek64(m_fd
, m_dest_offset
, SEEK_SET
);
348 if (chkret
!= m_dest_offset
) {
349 cerr
<< "rbd: error seeking destination image to offset "
350 << m_dest_offset
<< std::endl
;
356 r
= m_bufferlist
.write_fd(m_fd
);
358 cerr
<< "rbd: error writing to destination image at offset "
359 << m_dest_offset
<< std::endl
;
364 librbd::RBD::AioCompletion
*m_aio_completion
;
365 SimpleThrottle
&m_throttle
;
366 librbd::Image
&m_image
;
367 bufferlist m_bufferlist
;
368 uint64_t m_dest_offset
;
374 static int do_export_v2(librbd::Image
& image
, librbd::image_info_t
&info
, int fd
,
375 uint64_t period
, int max_concurrent_ops
, utils::ProgressContext
&pc
)
380 bl
.append(utils::RBD_IMAGE_BANNER_V2
);
385 tag
= RBD_EXPORT_IMAGE_ORDER
;
388 ::encode(length
, bl
);
389 ::encode(uint64_t(info
.order
), bl
);
392 tag
= RBD_EXPORT_IMAGE_FEATURES
;
394 image
.features(&features
);
397 ::encode(length
, bl
);
398 ::encode(features
, bl
);
400 // encode stripe_unit and stripe_count
401 tag
= RBD_EXPORT_IMAGE_STRIPE_UNIT
;
402 uint64_t stripe_unit
;
403 stripe_unit
= image
.get_stripe_unit();
406 ::encode(length
, bl
);
407 ::encode(stripe_unit
, bl
);
409 tag
= RBD_EXPORT_IMAGE_STRIPE_COUNT
;
410 uint64_t stripe_count
;
411 stripe_count
= image
.get_stripe_count();
414 ::encode(length
, bl
);
415 ::encode(stripe_count
, bl
);
418 tag
= RBD_EXPORT_IMAGE_END
;
427 // header for snapshots
429 bl
.append(utils::RBD_IMAGE_DIFFS_BANNER_V2
);
431 std::vector
<librbd::snap_info_t
> snaps
;
432 r
= image
.snap_list(snaps
);
437 uint64_t diff_num
= snaps
.size() + 1;
438 ::encode(diff_num
, bl
);
445 const char *last_snap
= NULL
;
446 for (size_t i
= 0; i
< snaps
.size(); ++i
) {
447 utils::snap_set(image
, snaps
[i
].name
.c_str());
448 r
= do_export_diff_fd(image
, last_snap
, snaps
[i
].name
.c_str(), false, fd
, true, 2);
452 pc
.update_progress(i
, snaps
.size() + 1);
453 last_snap
= snaps
[i
].name
.c_str();
455 utils::snap_set(image
, std::string(""));
456 r
= do_export_diff_fd(image
, last_snap
, nullptr, false, fd
, true, 2);
460 pc
.update_progress(snaps
.size() + 1, snaps
.size() + 1);
464 static int do_export_v1(librbd::Image
& image
, librbd::image_info_t
&info
, int fd
,
465 uint64_t period
, int max_concurrent_ops
, utils::ProgressContext
&pc
)
468 size_t file_size
= 0;
469 SimpleThrottle
throttle(max_concurrent_ops
, false);
470 for (uint64_t offset
= 0; offset
< info
.size
; offset
+= period
) {
471 if (throttle
.pending_error()) {
475 uint64_t length
= min(period
, info
.size
- offset
);
476 C_Export
*ctx
= new C_Export(throttle
, image
, file_size
+ offset
, offset
, length
, fd
);
479 pc
.update_progress(offset
, info
.size
);
482 file_size
+= info
.size
;
483 r
= throttle
.wait_for_ret();
486 r
= ftruncate(fd
, file_size
);
490 uint64_t chkret
= lseek64(fd
, file_size
, SEEK_SET
);
491 if (chkret
!= file_size
)
498 static int do_export(librbd::Image
& image
, const char *path
, bool no_progress
, int export_format
)
500 librbd::image_info_t info
;
501 int64_t r
= image
.stat(info
, sizeof(info
));
506 int max_concurrent_ops
;
507 bool to_stdout
= (strcmp(path
, "-") == 0);
510 max_concurrent_ops
= 1;
512 max_concurrent_ops
= g_conf
->rbd_concurrent_management_ops
;
513 fd
= open(path
, O_WRONLY
| O_CREAT
| O_EXCL
, 0644);
517 #ifdef HAVE_POSIX_FADVISE
518 posix_fadvise(fd
, 0, 0, POSIX_FADV_SEQUENTIAL
);
522 utils::ProgressContext
pc("Exporting image", no_progress
);
523 uint64_t period
= image
.get_stripe_count() * (1ull << info
.order
);
525 if (export_format
== 1)
526 r
= do_export_v1(image
, info
, fd
, period
, max_concurrent_ops
, pc
);
528 r
= do_export_v2(image
, info
, fd
, period
, max_concurrent_ops
, pc
);
539 void get_arguments(po::options_description
*positional
,
540 po::options_description
*options
) {
541 at::add_image_or_snap_spec_options(positional
, options
,
542 at::ARGUMENT_MODIFIER_SOURCE
);
543 at::add_path_options(positional
, options
,
544 "export file (or '-' for stdout)");
545 at::add_no_progress_option(options
);
546 at::add_export_format_option(options
);
549 int execute(const po::variables_map
&vm
) {
550 size_t arg_index
= 0;
551 std::string pool_name
;
552 std::string image_name
;
553 std::string snap_name
;
554 int r
= utils::get_pool_image_snapshot_names(
555 vm
, at::ARGUMENT_MODIFIER_SOURCE
, &arg_index
, &pool_name
, &image_name
,
556 &snap_name
, utils::SNAPSHOT_PRESENCE_PERMITTED
,
557 utils::SPEC_VALIDATION_NONE
);
563 r
= utils::get_path(vm
, utils::get_positional_argument(vm
, 1), &path
);
568 librados::Rados rados
;
569 librados::IoCtx io_ctx
;
571 r
= utils::init_and_open_image(pool_name
, image_name
, "", snap_name
, true,
572 &rados
, &io_ctx
, &image
);
578 if (vm
.count("export-format"))
579 format
= vm
["export-format"].as
<uint64_t>();
581 r
= do_export(image
, path
.c_str(), vm
[at::NO_PROGRESS
].as
<bool>(), format
);
583 std::cerr
<< "rbd: export error: " << cpp_strerror(r
) << std::endl
;
589 Shell::Action
action(
590 {"export"}, {}, "Export image to file.", "", &get_arguments
, &execute
);
592 } // namespace export_full
593 } // namespace action