1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "tools/rbd/Utils.h"
5 #include "include/ceph_assert.h"
6 #include "include/Context.h"
7 #include "include/encoding.h"
8 #include "common/common_init.h"
9 #include "include/stringify.h"
10 #include "include/rbd/features.h"
11 #include "common/config.h"
12 #include "common/errno.h"
13 #include "common/safe_io.h"
14 #include "global/global_context.h"
17 #include <boost/algorithm/string.hpp>
18 #include <boost/lexical_cast.hpp>
23 namespace at
= argument_types
;
24 namespace po
= boost::program_options
;
26 int ProgressContext::update_progress(uint64_t offset
, uint64_t total
) {
28 int pc
= total
? (offset
* 100ull / total
) : 0;
30 cerr
<< "\r" << operation
<< ": "
31 << pc
<< "% complete...";
39 void ProgressContext::finish() {
41 cerr
<< "\r" << operation
<< ": 100% complete...done." << std::endl
;
45 void ProgressContext::fail() {
47 cerr
<< "\r" << operation
<< ": " << last_pc
<< "% complete...failed."
52 void aio_context_callback(librbd::completion_t completion
, void *arg
)
54 librbd::RBD::AioCompletion
*aio_completion
=
55 reinterpret_cast<librbd::RBD::AioCompletion
*>(completion
);
56 Context
*context
= reinterpret_cast<Context
*>(arg
);
57 context
->complete(aio_completion
->get_return_value());
58 aio_completion
->release();
61 int read_string(int fd
, unsigned max
, std::string
*out
) {
64 int r
= safe_read_exact(fd
, buf
, 4);
77 r
= safe_read_exact(fd
, sbuf
, len
);
80 out
->assign(sbuf
, len
);
84 int extract_spec(const std::string
&spec
, std::string
*pool_name
,
85 std::string
*namespace_name
, std::string
*name
,
86 std::string
*snap_name
, SpecValidation spec_validation
) {
87 if (!g_ceph_context
->_conf
.get_val
<bool>("rbd_validate_names")) {
88 spec_validation
= SPEC_VALIDATION_NONE
;
92 switch (spec_validation
) {
93 case SPEC_VALIDATION_FULL
:
94 // disallow "/" and "@" in all names
95 pattern
= "^(?:([^/@]+)/(?:([^/@]+)/)?)?([^/@]+)(?:@([^/@]+))?$";
97 case SPEC_VALIDATION_SNAP
:
98 // disallow "/" and "@" in snap name
99 pattern
= "^(?:([^/]+)/(?:([^/@]+)/)?)?([^@]+)(?:@([^/@]+))?$";
101 case SPEC_VALIDATION_NONE
:
102 // relaxed pattern assumes pool is before first "/",
103 // namespace is before second "/", and snap name is after first "@"
104 pattern
= "^(?:([^/]+)/(?:([^/@]+)/)?)?([^@]+)(?:@(.+))?$";
112 if (!std::regex_match(spec
, match
, pattern
)) {
113 std::cerr
<< "rbd: invalid spec '" << spec
<< "'" << std::endl
;
117 if (match
[1].matched
) {
118 if (pool_name
!= nullptr) {
119 *pool_name
= match
[1];
121 std::cerr
<< "rbd: pool name specified for a command that doesn't use it"
127 if (match
[2].matched
) {
128 if (namespace_name
!= nullptr) {
129 *namespace_name
= match
[2];
131 std::cerr
<< "rbd: namespace name specified for a command that doesn't "
132 << "use it" << std::endl
;
137 if (name
!= nullptr) {
141 if (match
[4].matched
) {
142 if (snap_name
!= nullptr) {
143 *snap_name
= match
[4];
145 std::cerr
<< "rbd: snapshot name specified for a command that doesn't "
146 << "use it" << std::endl
;
153 std::string
get_positional_argument(const po::variables_map
&vm
, size_t index
) {
154 if (vm
.count(at::POSITIONAL_ARGUMENTS
) == 0) {
158 const std::vector
<std::string
> &args
=
159 boost::any_cast
<std::vector
<std::string
> >(
160 vm
[at::POSITIONAL_ARGUMENTS
].value());
161 if (index
< args
.size()) {
167 std::string
get_default_pool_name() {
168 return g_ceph_context
->_conf
.get_val
<std::string
>("rbd_default_pool");
171 int get_pool_and_namespace_names(
172 const boost::program_options::variables_map
&vm
,
173 bool default_empty_pool_name
, bool validate_pool_name
,
174 std::string
* pool_name
, std::string
* namespace_name
, size_t *arg_index
) {
175 if (namespace_name
!= nullptr && vm
.count(at::NAMESPACE_NAME
)) {
176 *namespace_name
= vm
[at::NAMESPACE_NAME
].as
<std::string
>();
179 if (vm
.count(at::POOL_NAME
)) {
180 *pool_name
= vm
[at::POOL_NAME
].as
<std::string
>();
182 *pool_name
= get_positional_argument(vm
, *arg_index
);
183 if (!pool_name
->empty()) {
184 if (namespace_name
!= nullptr) {
185 auto slash_pos
= pool_name
->find_last_of('/');
186 if (slash_pos
!= std::string::npos
) {
187 *namespace_name
= pool_name
->substr(slash_pos
+ 1);
189 *pool_name
= pool_name
->substr(0, slash_pos
);
195 if (default_empty_pool_name
&& pool_name
->empty()) {
196 *pool_name
= get_default_pool_name();
199 if (!g_ceph_context
->_conf
.get_val
<bool>("rbd_validate_names")) {
200 validate_pool_name
= false;
203 if (validate_pool_name
&&
204 pool_name
->find_first_of("/@") != std::string::npos
) {
205 std::cerr
<< "rbd: invalid pool '" << *pool_name
<< "'" << std::endl
;
207 } else if (namespace_name
!= nullptr &&
208 namespace_name
->find_first_of("/@") != std::string::npos
) {
209 std::cerr
<< "rbd: invalid namespace '" << *namespace_name
<< "'"
217 int get_pool_image_id(const po::variables_map
&vm
,
218 size_t *spec_arg_index
,
219 std::string
*pool_name
,
220 std::string
*namespace_name
,
221 std::string
*image_id
) {
223 if (vm
.count(at::POOL_NAME
) && pool_name
!= nullptr) {
224 *pool_name
= vm
[at::POOL_NAME
].as
<std::string
>();
226 if (vm
.count(at::NAMESPACE_NAME
) && namespace_name
!= nullptr) {
227 *namespace_name
= vm
[at::NAMESPACE_NAME
].as
<std::string
>();
229 if (vm
.count(at::IMAGE_ID
) && image_id
!= nullptr) {
230 *image_id
= vm
[at::IMAGE_ID
].as
<std::string
>();
234 if (image_id
!= nullptr && spec_arg_index
!= nullptr && image_id
->empty()) {
235 std::string spec
= get_positional_argument(vm
, (*spec_arg_index
)++);
237 r
= extract_spec(spec
, pool_name
, namespace_name
, image_id
, nullptr,
238 SPEC_VALIDATION_FULL
);
245 if (pool_name
!= nullptr && pool_name
->empty()) {
246 *pool_name
= get_default_pool_name();
249 if (image_id
!= nullptr && image_id
->empty()) {
250 std::cerr
<< "rbd: image id was not specified" << std::endl
;
257 int get_pool_image_snapshot_names(const po::variables_map
&vm
,
258 at::ArgumentModifier mod
,
259 size_t *spec_arg_index
,
260 std::string
*pool_name
,
261 std::string
*namespace_name
,
262 std::string
*image_name
,
263 std::string
*snap_name
,
264 bool image_name_required
,
265 SnapshotPresence snapshot_presence
,
266 SpecValidation spec_validation
) {
267 std::string pool_key
= (mod
== at::ARGUMENT_MODIFIER_DEST
?
268 at::DEST_POOL_NAME
: at::POOL_NAME
);
269 std::string image_key
= (mod
== at::ARGUMENT_MODIFIER_DEST
?
270 at::DEST_IMAGE_NAME
: at::IMAGE_NAME
);
271 return get_pool_generic_snapshot_names(vm
, mod
, spec_arg_index
, pool_key
,
272 pool_name
, namespace_name
, image_key
,
273 "image", image_name
, snap_name
,
274 image_name_required
, snapshot_presence
,
278 int get_pool_generic_snapshot_names(const po::variables_map
&vm
,
279 at::ArgumentModifier mod
,
280 size_t *spec_arg_index
,
281 const std::string
& pool_key
,
282 std::string
*pool_name
,
283 std::string
*namespace_name
,
284 const std::string
& generic_key
,
285 const std::string
& generic_key_desc
,
286 std::string
*generic_name
,
287 std::string
*snap_name
,
288 bool generic_name_required
,
289 SnapshotPresence snapshot_presence
,
290 SpecValidation spec_validation
) {
291 std::string namespace_key
= (mod
== at::ARGUMENT_MODIFIER_DEST
?
292 at::DEST_NAMESPACE_NAME
: at::NAMESPACE_NAME
);
293 std::string snap_key
= (mod
== at::ARGUMENT_MODIFIER_DEST
?
294 at::DEST_SNAPSHOT_NAME
: at::SNAPSHOT_NAME
);
296 if (vm
.count(pool_key
) && pool_name
!= nullptr) {
297 *pool_name
= vm
[pool_key
].as
<std::string
>();
299 if (vm
.count(namespace_key
) && namespace_name
!= nullptr) {
300 *namespace_name
= vm
[namespace_key
].as
<std::string
>();
302 if (vm
.count(generic_key
) && generic_name
!= nullptr) {
303 *generic_name
= vm
[generic_key
].as
<std::string
>();
305 if (vm
.count(snap_key
) && snap_name
!= nullptr) {
306 *snap_name
= vm
[snap_key
].as
<std::string
>();
310 if ((generic_key
== at::IMAGE_NAME
|| generic_key
== at::DEST_IMAGE_NAME
) &&
311 generic_name
!= nullptr && !generic_name
->empty()) {
312 // despite the separate pool and snapshot name options,
313 // we can also specify them via the image option
314 std::string
image_name_copy(*generic_name
);
315 r
= extract_spec(image_name_copy
, pool_name
, namespace_name
, generic_name
,
316 snap_name
, spec_validation
);
322 if (generic_name
!= nullptr && spec_arg_index
!= nullptr &&
323 generic_name
->empty()) {
324 std::string spec
= get_positional_argument(vm
, (*spec_arg_index
)++);
326 r
= extract_spec(spec
, pool_name
, namespace_name
, generic_name
, snap_name
,
334 if (pool_name
!= nullptr && pool_name
->empty()) {
335 *pool_name
= get_default_pool_name();
338 if (generic_name
!= nullptr && generic_name_required
&&
339 generic_name
->empty()) {
340 std::string prefix
= at::get_description_prefix(mod
);
342 << (mod
== at::ARGUMENT_MODIFIER_DEST
? prefix
: std::string())
343 << generic_key_desc
<< " name was not specified" << std::endl
;
347 std::regex
pattern("^[^@/]+?$");
348 if (spec_validation
== SPEC_VALIDATION_FULL
) {
349 // validate pool name while creating/renaming/copying/cloning/importing/etc
350 if ((pool_name
!= nullptr) && !std::regex_match (*pool_name
, pattern
)) {
351 std::cerr
<< "rbd: invalid pool name '" << *pool_name
<< "'" << std::endl
;
356 if (namespace_name
!= nullptr && !namespace_name
->empty() &&
357 !std::regex_match (*namespace_name
, pattern
)) {
358 std::cerr
<< "rbd: invalid namespace name '" << *namespace_name
<< "'"
363 if (snap_name
!= nullptr) {
364 r
= validate_snapshot_name(mod
, *snap_name
, snapshot_presence
,
373 int validate_snapshot_name(at::ArgumentModifier mod
,
374 const std::string
&snap_name
,
375 SnapshotPresence snapshot_presence
,
376 SpecValidation spec_validation
) {
377 std::string prefix
= at::get_description_prefix(mod
);
378 switch (snapshot_presence
) {
379 case SNAPSHOT_PRESENCE_PERMITTED
:
381 case SNAPSHOT_PRESENCE_NONE
:
382 if (!snap_name
.empty()) {
384 << (mod
== at::ARGUMENT_MODIFIER_DEST
? prefix
: std::string())
385 << "snapshot name specified for a command that doesn't use it"
390 case SNAPSHOT_PRESENCE_REQUIRED
:
391 if (snap_name
.empty()) {
393 << (mod
== at::ARGUMENT_MODIFIER_DEST
? prefix
: std::string())
394 << "snapshot name was not specified" << std::endl
;
400 if (spec_validation
== SPEC_VALIDATION_SNAP
) {
401 // disallow "/" and "@" in snap name
402 std::regex
pattern("^[^@/]*?$");
403 if (!std::regex_match (snap_name
, pattern
)) {
404 std::cerr
<< "rbd: invalid snap name '" << snap_name
<< "'" << std::endl
;
411 int get_image_options(const boost::program_options::variables_map
&vm
,
412 bool get_format
, librbd::ImageOptions
*opts
) {
413 uint64_t order
= 0, stripe_unit
= 0, stripe_count
= 0, object_size
= 0;
414 uint64_t features
= 0, features_clear
= 0;
415 std::string data_pool
;
416 bool order_specified
= true;
417 bool features_specified
= false;
418 bool features_clear_specified
= false;
419 bool stripe_specified
= false;
421 if (vm
.count(at::IMAGE_ORDER
)) {
422 order
= vm
[at::IMAGE_ORDER
].as
<uint64_t>();
423 std::cerr
<< "rbd: --order is deprecated, use --object-size"
425 } else if (vm
.count(at::IMAGE_OBJECT_SIZE
)) {
426 object_size
= vm
[at::IMAGE_OBJECT_SIZE
].as
<uint64_t>();
427 order
= std::round(std::log2(object_size
));
429 order_specified
= false;
432 if (vm
.count(at::IMAGE_FEATURES
)) {
433 features
= vm
[at::IMAGE_FEATURES
].as
<uint64_t>();
434 features_specified
= true;
436 features
= get_rbd_default_features(g_ceph_context
);
439 if (vm
.count(at::IMAGE_STRIPE_UNIT
)) {
440 stripe_unit
= vm
[at::IMAGE_STRIPE_UNIT
].as
<uint64_t>();
441 stripe_specified
= true;
444 if (vm
.count(at::IMAGE_STRIPE_COUNT
)) {
445 stripe_count
= vm
[at::IMAGE_STRIPE_COUNT
].as
<uint64_t>();
446 stripe_specified
= true;
449 if (vm
.count(at::IMAGE_SHARED
) && vm
[at::IMAGE_SHARED
].as
<bool>()) {
450 if (features_specified
) {
451 features
&= ~RBD_FEATURES_SINGLE_CLIENT
;
453 features_clear
|= RBD_FEATURES_SINGLE_CLIENT
;
454 features_clear_specified
= true;
458 if (vm
.count(at::IMAGE_DATA_POOL
)) {
459 data_pool
= vm
[at::IMAGE_DATA_POOL
].as
<std::string
>();
464 bool format_specified
= false;
465 if (vm
.count(at::IMAGE_NEW_FORMAT
)) {
467 format_specified
= true;
468 } else if (vm
.count(at::IMAGE_FORMAT
)) {
469 format
= vm
[at::IMAGE_FORMAT
].as
<uint32_t>();
470 format_specified
= true;
473 std::cerr
<< "rbd: image format 1 is deprecated" << std::endl
;
476 if (features_specified
&& features
!= 0) {
477 if (format_specified
&& format
== 1) {
478 std::cerr
<< "rbd: features not allowed with format 1; "
479 << "use --image-format 2" << std::endl
;
483 format_specified
= true;
487 if ((stripe_unit
|| stripe_count
) &&
488 (stripe_unit
!= (1ull << order
) && stripe_count
!= 1)) {
489 if (format_specified
&& format
== 1) {
490 std::cerr
<< "rbd: non-default striping not allowed with format 1; "
491 << "use --image-format 2" << std::endl
;
495 format_specified
= true;
499 if (!data_pool
.empty()) {
500 if (format_specified
&& format
== 1) {
501 std::cerr
<< "rbd: data pool not allowed with format 1; "
502 << "use --image-format 2" << std::endl
;
506 format_specified
= true;
510 if (format_specified
) {
511 int r
= g_conf().set_val("rbd_default_format", stringify(format
));
513 opts
->set(RBD_IMAGE_OPTION_FORMAT
, format
);
518 opts
->set(RBD_IMAGE_OPTION_ORDER
, order
);
519 if (features_specified
)
520 opts
->set(RBD_IMAGE_OPTION_FEATURES
, features
);
521 if (features_clear_specified
) {
522 opts
->set(RBD_IMAGE_OPTION_FEATURES_CLEAR
, features_clear
);
524 if (stripe_specified
) {
525 opts
->set(RBD_IMAGE_OPTION_STRIPE_UNIT
, stripe_unit
);
526 opts
->set(RBD_IMAGE_OPTION_STRIPE_COUNT
, stripe_count
);
528 if (!data_pool
.empty()) {
529 opts
->set(RBD_IMAGE_OPTION_DATA_POOL
, data_pool
);
531 int r
= get_journal_options(vm
, opts
);
536 r
= get_flatten_option(vm
, opts
);
544 int get_journal_options(const boost::program_options::variables_map
&vm
,
545 librbd::ImageOptions
*opts
) {
547 if (vm
.count(at::JOURNAL_OBJECT_SIZE
)) {
548 uint64_t size
= vm
[at::JOURNAL_OBJECT_SIZE
].as
<uint64_t>();
550 while ((1ULL << order
) < size
) {
553 opts
->set(RBD_IMAGE_OPTION_JOURNAL_ORDER
, order
);
555 int r
= g_conf().set_val("rbd_journal_order", stringify(order
));
558 if (vm
.count(at::JOURNAL_SPLAY_WIDTH
)) {
559 opts
->set(RBD_IMAGE_OPTION_JOURNAL_SPLAY_WIDTH
,
560 vm
[at::JOURNAL_SPLAY_WIDTH
].as
<uint64_t>());
562 int r
= g_conf().set_val("rbd_journal_splay_width",
564 vm
[at::JOURNAL_SPLAY_WIDTH
].as
<uint64_t>()));
567 if (vm
.count(at::JOURNAL_POOL
)) {
568 opts
->set(RBD_IMAGE_OPTION_JOURNAL_POOL
,
569 vm
[at::JOURNAL_POOL
].as
<std::string
>());
571 int r
= g_conf().set_val("rbd_journal_pool",
572 vm
[at::JOURNAL_POOL
].as
<std::string
>());
579 int get_flatten_option(const boost::program_options::variables_map
&vm
,
580 librbd::ImageOptions
*opts
) {
581 if (vm
.count(at::IMAGE_FLATTEN
) && vm
[at::IMAGE_FLATTEN
].as
<bool>()) {
582 uint64_t flatten
= 1;
583 opts
->set(RBD_IMAGE_OPTION_FLATTEN
, flatten
);
588 int get_image_size(const boost::program_options::variables_map
&vm
,
590 if (vm
.count(at::IMAGE_SIZE
) == 0) {
591 std::cerr
<< "rbd: must specify --size <M/G/T>" << std::endl
;
595 *size
= vm
[at::IMAGE_SIZE
].as
<uint64_t>();
599 int get_path(const boost::program_options::variables_map
&vm
,
600 size_t *arg_index
, std::string
*path
) {
601 if (vm
.count(at::PATH
)) {
602 *path
= vm
[at::PATH
].as
<std::string
>();
604 *path
= get_positional_argument(vm
, *arg_index
);
605 if (!path
->empty()) {
611 std::cerr
<< "rbd: path was not specified" << std::endl
;
617 int get_formatter(const po::variables_map
&vm
,
618 at::Format::Formatter
*formatter
) {
619 if (vm
.count(at::FORMAT
)) {
620 bool pretty
= vm
[at::PRETTY_FORMAT
].as
<bool>();
621 *formatter
= vm
[at::FORMAT
].as
<at::Format
>().create_formatter(pretty
);
622 if (*formatter
== nullptr && pretty
) {
623 std::cerr
<< "rbd: --pretty-format only works when --format "
624 << "is json or xml" << std::endl
;
626 } else if (*formatter
!= nullptr && !pretty
) {
627 formatter
->get()->enable_line_break();
629 } else if (vm
[at::PRETTY_FORMAT
].as
<bool>()) {
630 std::cerr
<< "rbd: --pretty-format only works when --format "
631 << "is json or xml" << std::endl
;
637 void init_context() {
638 g_conf().set_val_or_die("rbd_cache_writethrough_until_flush", "false");
639 g_conf().apply_changes(nullptr);
640 common_init_finish(g_ceph_context
);
643 int init_rados(librados::Rados
*rados
) {
646 int r
= rados
->init_with_context(g_ceph_context
);
648 std::cerr
<< "rbd: couldn't initialize rados!" << std::endl
;
652 r
= rados
->connect();
654 std::cerr
<< "rbd: couldn't connect to the cluster!" << std::endl
;
661 int init(const std::string
&pool_name
, const std::string
& namespace_name
,
662 librados::Rados
*rados
, librados::IoCtx
*io_ctx
) {
665 int r
= init_rados(rados
);
670 r
= init_io_ctx(*rados
, pool_name
, namespace_name
, io_ctx
);
677 int init_io_ctx(librados::Rados
&rados
, const std::string
&pool_name
,
678 const std::string
& namespace_name
, librados::IoCtx
*io_ctx
) {
679 int r
= rados
.ioctx_create(pool_name
.c_str(), *io_ctx
);
681 if (r
== -ENOENT
&& pool_name
== get_default_pool_name()) {
682 std::cerr
<< "rbd: error opening default pool "
683 << "'" << pool_name
<< "'" << std::endl
684 << "Ensure that the default pool has been created or specify "
685 << "an alternate pool name." << std::endl
;
687 std::cerr
<< "rbd: error opening pool '" << pool_name
<< "': "
688 << cpp_strerror(r
) << std::endl
;
693 return set_namespace(namespace_name
, io_ctx
);
696 int set_namespace(const std::string
& namespace_name
, librados::IoCtx
*io_ctx
) {
697 if (!namespace_name
.empty()) {
700 int r
= rbd
.namespace_exists(*io_ctx
, namespace_name
.c_str(), &exists
);
702 std::cerr
<< "rbd: error asserting namespace: "
703 << cpp_strerror(r
) << std::endl
;
707 std::cerr
<< "rbd: namespace '" << namespace_name
<< "' does not exist."
712 io_ctx
->set_namespace(namespace_name
);
716 void disable_cache() {
717 g_conf().set_val_or_die("rbd_cache", "false");
720 int open_image(librados::IoCtx
&io_ctx
, const std::string
&image_name
,
721 bool read_only
, librbd::Image
*image
) {
725 r
= rbd
.open_read_only(io_ctx
, *image
, image_name
.c_str(), NULL
);
727 r
= rbd
.open(io_ctx
, *image
, image_name
.c_str());
731 std::cerr
<< "rbd: error opening image " << image_name
<< ": "
732 << cpp_strerror(r
) << std::endl
;
738 int open_image_by_id(librados::IoCtx
&io_ctx
, const std::string
&image_id
,
739 bool read_only
, librbd::Image
*image
) {
743 r
= rbd
.open_by_id_read_only(io_ctx
, *image
, image_id
.c_str(), NULL
);
745 r
= rbd
.open_by_id(io_ctx
, *image
, image_id
.c_str());
749 std::cerr
<< "rbd: error opening image with id " << image_id
<< ": "
750 << cpp_strerror(r
) << std::endl
;
756 int init_and_open_image(const std::string
&pool_name
,
757 const std::string
&namespace_name
,
758 const std::string
&image_name
,
759 const std::string
&image_id
,
760 const std::string
&snap_name
, bool read_only
,
761 librados::Rados
*rados
, librados::IoCtx
*io_ctx
,
762 librbd::Image
*image
) {
763 int r
= init(pool_name
, namespace_name
, rados
, io_ctx
);
768 if (image_id
.empty()) {
769 r
= open_image(*io_ctx
, image_name
, read_only
, image
);
771 r
= open_image_by_id(*io_ctx
, image_id
, read_only
, image
);
777 if (!snap_name
.empty()) {
778 r
= snap_set(*image
, snap_name
);
786 int snap_set(librbd::Image
&image
, const std::string
&snap_name
) {
787 int r
= image
.snap_set(snap_name
.c_str());
789 std::cerr
<< "error setting snapshot context: " << cpp_strerror(r
)
796 void calc_sparse_extent(const bufferptr
&bp
,
798 size_t buffer_offset
,
799 uint64_t buffer_length
,
800 size_t *write_length
,
802 if (sparse_size
== 0) {
803 // sparse writes are disabled -- write the full extent
804 ceph_assert(buffer_offset
== 0);
805 *write_length
= buffer_length
;
811 size_t original_offset
= buffer_offset
;
812 while (buffer_offset
< buffer_length
) {
813 size_t extent_size
= std::min
<size_t>(
814 sparse_size
, buffer_length
- buffer_offset
);
816 bufferptr
extent(bp
, buffer_offset
, extent_size
);
818 bool extent_is_zero
= extent
.is_zero();
819 if (original_offset
== buffer_offset
) {
820 *zeroed
= extent_is_zero
;
821 } else if (*zeroed
!= extent_is_zero
) {
822 ceph_assert(*write_length
> 0);
826 buffer_offset
+= extent_size
;
827 *write_length
+= extent_size
;
831 std::string
image_id(librbd::Image
& image
) {
833 int r
= image
.get_id(&id
);
835 return std::string();
840 std::string
mirror_image_state(librbd::mirror_image_state_t state
) {
842 case RBD_MIRROR_IMAGE_DISABLING
:
844 case RBD_MIRROR_IMAGE_ENABLED
:
846 case RBD_MIRROR_IMAGE_DISABLED
:
853 std::string
mirror_image_status_state(librbd::mirror_image_status_state_t state
) {
855 case MIRROR_IMAGE_STATUS_STATE_UNKNOWN
:
857 case MIRROR_IMAGE_STATUS_STATE_ERROR
:
859 case MIRROR_IMAGE_STATUS_STATE_SYNCING
:
861 case MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY
:
862 return "starting_replay";
863 case MIRROR_IMAGE_STATUS_STATE_REPLAYING
:
865 case MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY
:
866 return "stopping_replay";
867 case MIRROR_IMAGE_STATUS_STATE_STOPPED
:
870 return "unknown (" + stringify(static_cast<uint32_t>(state
)) + ")";
874 std::string
mirror_image_status_state(librbd::mirror_image_status_t status
) {
875 return (status
.up
? "up+" : "down+") +
876 mirror_image_status_state(status
.state
);
879 std::string
timestr(time_t t
) {
882 localtime_r(&t
, &tm
);
885 strftime(buf
, sizeof(buf
), "%F %T", &tm
);
890 uint64_t get_rbd_default_features(CephContext
* cct
) {
891 auto features
= cct
->_conf
.get_val
<std::string
>("rbd_default_features");
892 return boost::lexical_cast
<uint64_t>(features
);
895 bool is_not_user_snap_namespace(librbd::Image
* image
,
896 const librbd::snap_info_t
&snap_info
)
898 librbd::snap_namespace_type_t namespace_type
;
899 int r
= image
->snap_get_namespace_type(snap_info
.id
, &namespace_type
);
903 return namespace_type
!= RBD_SNAP_NAMESPACE_TYPE_USER
;