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/ArgumentTypes.h"
5 #include "tools/rbd/Shell.h"
6 #include "tools/rbd/Utils.h"
7 #include "include/Context.h"
8 #include "common/blkdev.h"
9 #include "common/debug.h"
10 #include "common/errno.h"
11 #include "common/Throttle.h"
12 #include "include/compat.h"
13 #include "include/encoding.h"
14 #include "common/debug.h"
15 #include "common/errno.h"
16 #include "common/safe_io.h"
18 #include <boost/program_options.hpp>
19 #include <boost/scoped_ptr.hpp>
20 #include "include/assert.h"
22 #define dout_context g_ceph_context
23 #define dout_subsys ceph_subsys_rbd
29 struct ImportDiffContext {
33 utils::ProgressContext pc;
34 OrderedThrottle throttle;
37 ImportDiffContext(librbd::Image *image, int fd, size_t size, bool no_progress)
38 : image(image), fd(fd), size(size), pc("Importing image diff", no_progress),
39 throttle((fd == STDIN_FILENO) ? 1 :
40 g_conf->get_val<int64_t>("rbd_concurrent_management_ops"),
45 void update_size(size_t new_size)
47 if (fd == STDIN_FILENO) {
52 void update_progress(uint64_t off)
55 pc.update_progress(off, size);
60 void update_progress()
62 uint64_t off = last_offset;
63 if (fd != STDIN_FILENO) {
64 off = lseek(fd, 0, SEEK_CUR);
80 class C_ImportDiff : public Context {
82 C_ImportDiff(ImportDiffContext *idiffctx, bufferlist data, uint64_t offset,
83 uint64_t length, bool discard)
84 : m_idiffctx(idiffctx), m_data(data), m_offset(offset), m_length(length),
86 // use block offset (stdin) or import file position to report
88 if (m_idiffctx->fd == STDIN_FILENO) {
89 m_prog_offset = offset;
91 m_prog_offset = lseek(m_idiffctx->fd, 0, SEEK_CUR);
97 if (m_idiffctx->throttle.pending_error()) {
98 return m_idiffctx->throttle.wait_for_ret();
101 C_OrderedThrottle *ctx = m_idiffctx->throttle.start_op(this);
102 librbd::RBD::AioCompletion *aio_completion =
103 new librbd::RBD::AioCompletion(ctx, &utils::aio_context_callback);
107 r = m_idiffctx->image->aio_discard(m_offset, m_length, aio_completion);
109 r = m_idiffctx->image->aio_write2(m_offset, m_length, m_data,
110 aio_completion, LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
114 aio_completion->release();
121 void finish(int r) override
123 m_idiffctx->update_progress(m_prog_offset);
124 m_idiffctx->throttle.end_op(r);
128 ImportDiffContext *m_idiffctx;
133 uint64_t m_prog_offset;
136 static int do_image_snap_from(ImportDiffContext *idiffctx)
140 r = utils::read_string(idiffctx->fd, 4096, &from); // 4k limit to make sure we don't get a garbage string
146 r = idiffctx->image->snap_exists2(from.c_str(), &exists);
152 std::cerr << "start snapshot '" << from
153 << "' does not exist in the image, aborting" << std::endl;
157 idiffctx->update_progress();
161 static int do_image_snap_to(ImportDiffContext *idiffctx, std::string *tosnap)
165 r = utils::read_string(idiffctx->fd, 4096, &to); // 4k limit to make sure we don't get a garbage string
171 r = idiffctx->image->snap_exists2(to.c_str(), &exists);
177 std::cerr << "end snapshot '" << to << "' already exists, aborting" << std::endl;
182 idiffctx->update_progress();
187 static int do_image_resize(ImportDiffContext *idiffctx)
190 char buf[sizeof(uint64_t)];
192 r = safe_read_exact(idiffctx->fd, buf, sizeof(buf));
198 bl.append(buf, sizeof(buf));
199 bufferlist::iterator p = bl.begin();
200 ::decode(end_size, p);
203 idiffctx->image->size(&cur_size);
204 if (cur_size != end_size) {
205 idiffctx->image->resize(end_size);
208 idiffctx->update_size(end_size);
209 idiffctx->update_progress();
213 static int do_image_io(ImportDiffContext *idiffctx, bool discard, size_t sparse_size)
217 r = safe_read_exact(idiffctx->fd, buf, sizeof(buf));
223 bl.append(buf, sizeof(buf));
224 bufferlist::iterator p = bl.begin();
226 uint64_t image_offset, buffer_length;
227 ::decode(image_offset, p);
228 ::decode(buffer_length, p);
231 bufferptr bp = buffer::create(buffer_length);
232 r = safe_read_exact(idiffctx->fd, bp.c_str(), buffer_length);
237 size_t buffer_offset = 0;
238 while (buffer_offset < buffer_length) {
239 size_t write_length = 0;
241 utils::calc_sparse_extent(bp, sparse_size, buffer_offset, buffer_length,
242 &write_length, &zeroed);
243 assert(write_length > 0);
247 bufferptr write_ptr(bp, buffer_offset, write_length);
248 write_bl.push_back(write_ptr);
249 assert(write_bl.length() == write_length);
252 C_ImportDiff *ctx = new C_ImportDiff(idiffctx, write_bl,
253 image_offset + buffer_offset,
254 write_length, zeroed);
260 buffer_offset += write_length;
264 C_ImportDiff *ctx = new C_ImportDiff(idiffctx, data, image_offset,
265 buffer_length, true);
271 static int validate_banner(int fd, std::string banner)
274 char buf[banner.size() + 1];
275 r = safe_read_exact(fd, buf, banner.size());
280 buf[banner.size()] = '\0';
281 if (strcmp(buf, banner.c_str())) {
282 std::cerr << "invalid banner '" << buf << "', expected '" << banner << "'" << std::endl;
289 static int skip_tag(int fd, uint64_t length)
293 if (fd == STDIN_FILENO) {
294 // read the appending data out to skip this tag.
296 uint64_t len = min<uint64_t>(length, sizeof(buf));
298 r = safe_read_exact(fd, buf, len);
302 len = min<uint64_t>(length, sizeof(buf));
305 // lseek to skip this tag
306 off64_t offs = lseek64(fd, length, SEEK_CUR);
315 static int read_tag(int fd, __u8 end_tag, int format, __u8 *tag, uint64_t *readlen)
320 r = safe_read_exact(fd, &read_tag, sizeof(read_tag));
326 if (read_tag != end_tag && format == 2) {
327 char buf[sizeof(uint64_t)];
328 r = safe_read_exact(fd, buf, sizeof(buf));
334 bl.append(buf, sizeof(buf));
335 bufferlist::iterator p = bl.begin();
336 ::decode(*readlen, p);
342 int do_import_diff_fd(librados::Rados &rados, librbd::Image &image, int fd,
343 bool no_progress, int format, size_t sparse_size)
348 bool from_stdin = (fd == STDIN_FILENO);
350 struct stat stat_buf;
351 r = ::fstat(fd, &stat_buf);
355 size = (uint64_t)stat_buf.st_size;
358 r = validate_banner(fd, (format == 1 ? utils::RBD_DIFF_BANNER :
359 utils::RBD_DIFF_BANNER_V2));
364 std::string skip_partial_discard;
365 r = rados.conf_get("rbd_skip_partial_discard", skip_partial_discard);
366 if (r < 0 || skip_partial_discard != "false") {
367 dout(1) << "disabling sparse import" << dendl;
372 // begin image import
374 ImportDiffContext idiffctx(&image, fd, size, no_progress);
379 r = read_tag(fd, RBD_DIFF_END, format, &tag, &length);
380 if (r < 0 || tag == RBD_DIFF_END) {
384 if (tag == RBD_DIFF_FROM_SNAP) {
385 r = do_image_snap_from(&idiffctx);
386 } else if (tag == RBD_DIFF_TO_SNAP) {
387 r = do_image_snap_to(&idiffctx, &tosnap);
388 } else if (tag == RBD_DIFF_IMAGE_SIZE) {
389 r = do_image_resize(&idiffctx);
390 } else if (tag == RBD_DIFF_WRITE || tag == RBD_DIFF_ZERO) {
391 r = do_image_io(&idiffctx, (tag == RBD_DIFF_ZERO), sparse_size);
393 std::cerr << "unrecognized tag byte " << (int)tag << " in stream; skipping"
395 r = skip_tag(fd, length);
399 int temp_r = idiffctx.throttle.wait_for_ret();
400 r = (r < 0) ? r : temp_r; // preserve original error
401 if (r == 0 && tosnap.length()) {
402 idiffctx.image->snap_create(tosnap.c_str());
409 int do_import_diff(librados::Rados &rados, librbd::Image &image,
410 const char *path, bool no_progress, size_t sparse_size)
415 if (strcmp(path, "-") == 0) {
418 fd = open(path, O_RDONLY);
421 std::cerr << "rbd: error opening " << path << std::endl;
425 r = do_import_diff_fd(rados, image, fd, no_progress, 1, sparse_size);
432 namespace at = argument_types;
433 namespace po = boost::program_options;
435 void get_arguments_diff(po::options_description *positional,
436 po::options_description *options) {
437 at::add_path_options(positional, options,
438 "import file (or '-' for stdin)");
439 at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
440 at::add_sparse_size_option(options);
441 at::add_no_progress_option(options);
444 int execute_diff(const po::variables_map &vm) {
446 int r = utils::get_path(vm, utils::get_positional_argument(vm, 0), &path);
451 size_t arg_index = 1;
452 std::string pool_name;
453 std::string image_name;
454 std::string snap_name;
455 r = utils::get_pool_image_snapshot_names(
456 vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
457 &snap_name, utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_NONE);
462 size_t sparse_size = utils::RBD_DEFAULT_SPARSE_SIZE;
463 if (vm.count(at::IMAGE_SPARSE_SIZE)) {
464 sparse_size = vm[at::IMAGE_SPARSE_SIZE].as<size_t>();
467 librados::Rados rados;
468 librados::IoCtx io_ctx;
470 r = utils::init_and_open_image(pool_name, image_name, "", "", false,
471 &rados, &io_ctx, &image);
476 r = do_import_diff(rados, image, path.c_str(),
477 vm[at::NO_PROGRESS].as<bool>(), sparse_size);
479 cerr << "rbd: import-diff failed: " << cpp_strerror(r) << std::endl;
485 Shell::Action action_diff(
486 {"import-diff"}, {}, "Import an incremental diff.", "", &get_arguments_diff,
489 class C_Import : public Context {
491 C_Import(SimpleThrottle &simple_throttle, librbd::Image &image,
492 bufferlist &bl, uint64_t offset)
493 : m_throttle(simple_throttle), m_image(image),
495 new librbd::RBD::AioCompletion(this, &utils::aio_context_callback)),
496 m_bufferlist(bl), m_offset(offset)
502 m_throttle.start_op();
504 int op_flags = LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL |
505 LIBRADOS_OP_FLAG_FADVISE_NOCACHE;
506 int r = m_image.aio_write2(m_offset, m_bufferlist.length(), m_bufferlist,
507 m_aio_completion, op_flags);
509 std::cerr << "rbd: error requesting write to destination image"
511 m_aio_completion->release();
512 m_throttle.end_op(r);
516 void finish(int r) override
519 std::cerr << "rbd: error writing to destination image at offset "
520 << m_offset << ": " << cpp_strerror(r) << std::endl;
522 m_throttle.end_op(r);
526 SimpleThrottle &m_throttle;
527 librbd::Image &m_image;
528 librbd::RBD::AioCompletion *m_aio_completion;
529 bufferlist m_bufferlist;
533 static int decode_and_set_image_option(int fd, uint64_t imageopt, librbd::ImageOptions& opts)
536 char buf[sizeof(uint64_t)];
538 r = safe_read_exact(fd, buf, sizeof(buf));
544 bl.append(buf, sizeof(buf));
545 bufferlist::iterator it;
551 if (opts.get(imageopt, &val) != 0) {
552 opts.set(imageopt, val);
558 static int do_import_header(int fd, int import_format, uint64_t &size, librbd::ImageOptions& opts)
560 // There is no header in v1 image.
561 if (import_format == 1) {
565 if (fd == STDIN_FILENO || size < utils::RBD_IMAGE_BANNER_V2.size()) {
570 r = validate_banner(fd, utils::RBD_IMAGE_BANNER_V2);
575 // As V1 format for image is already deprecated, import image in V2 by default.
576 uint64_t image_format = 2;
577 if (opts.get(RBD_IMAGE_OPTION_FORMAT, &image_format) != 0) {
578 opts.set(RBD_IMAGE_OPTION_FORMAT, image_format);
584 r = read_tag(fd, RBD_EXPORT_IMAGE_END, image_format, &tag, &length);
585 if (r < 0 || tag == RBD_EXPORT_IMAGE_END) {
589 if (tag == RBD_EXPORT_IMAGE_ORDER) {
590 r = decode_and_set_image_option(fd, RBD_IMAGE_OPTION_ORDER, opts);
591 } else if (tag == RBD_EXPORT_IMAGE_FEATURES) {
592 r = decode_and_set_image_option(fd, RBD_IMAGE_OPTION_FEATURES, opts);
593 } else if (tag == RBD_EXPORT_IMAGE_STRIPE_UNIT) {
594 r = decode_and_set_image_option(fd, RBD_IMAGE_OPTION_STRIPE_UNIT, opts);
595 } else if (tag == RBD_EXPORT_IMAGE_STRIPE_COUNT) {
596 r = decode_and_set_image_option(fd, RBD_IMAGE_OPTION_STRIPE_COUNT, opts);
598 std::cerr << "rbd: invalid tag in image properties zone: " << tag << "Skip it."
600 r = skip_tag(fd, length);
607 static int do_import_v2(librados::Rados &rados, int fd, librbd::Image &image,
608 uint64_t size, size_t imgblklen,
609 utils::ProgressContext &pc, size_t sparse_size)
612 r = validate_banner(fd, utils::RBD_IMAGE_DIFFS_BANNER_V2);
617 char buf[sizeof(uint64_t)];
618 r = safe_read_exact(fd, buf, sizeof(buf));
623 bl.append(buf, sizeof(buf));
624 bufferlist::iterator p = bl.begin();
626 ::decode(diff_num, p);
628 for (size_t i = 0; i < diff_num; i++) {
629 r = do_import_diff_fd(rados, image, fd, true, 2, sparse_size);
632 std::cerr << "rbd: import-diff failed: " << cpp_strerror(r) << std::endl;
635 pc.update_progress(i + 1, diff_num);
641 static int do_import_v1(int fd, librbd::Image &image, uint64_t size,
642 size_t imgblklen, utils::ProgressContext &pc,
646 size_t reqlen = imgblklen; // amount requested from read
647 ssize_t readlen; // amount received from one read
648 size_t blklen = 0; // amount accumulated from reads to fill blk
649 char *p = new char[imgblklen];
650 uint64_t image_pos = 0;
651 bool from_stdin = (fd == STDIN_FILENO);
652 boost::scoped_ptr<SimpleThrottle> throttle;
655 throttle.reset(new SimpleThrottle(1, false));
657 throttle.reset(new SimpleThrottle(
658 g_conf->get_val<int64_t>("rbd_concurrent_management_ops"), false));
661 reqlen = min<uint64_t>(reqlen, size);
662 // loop body handles 0 return, as we may have a block to flush
663 while ((readlen = ::read(fd, p + blklen, reqlen)) >= 0) {
664 if (throttle->pending_error()) {
669 // if read was short, try again to fill the block before writing
670 if (readlen && ((size_t)readlen < reqlen)) {
675 pc.update_progress(image_pos, size);
677 bufferptr blkptr(p, blklen);
678 // resize output image by binary expansion as we go for stdin
679 if (from_stdin && (image_pos + (size_t)blklen) > size) {
681 r = image.resize(size);
683 std::cerr << "rbd: can't resize image during import" << std::endl;
688 // write as much as we got; perhaps less than imgblklen
689 // but skip writing zeros to create sparse images
690 size_t buffer_offset = 0;
691 while (buffer_offset < blklen) {
692 size_t write_length = 0;
694 utils::calc_sparse_extent(blkptr, sparse_size, buffer_offset, blklen,
695 &write_length, &zeroed);
699 bufferptr write_ptr(blkptr, buffer_offset, write_length);
700 write_bl.push_back(write_ptr);
701 assert(write_bl.length() == write_length);
703 C_Import *ctx = new C_Import(*throttle, image, write_bl,
704 image_pos + buffer_offset);
708 buffer_offset += write_length;
711 // done with whole block, whether written or not
713 if (!from_stdin && image_pos >= size)
715 // if read had returned 0, we're at EOF and should quit
721 r = throttle->wait_for_ret();
726 if (fd == STDIN_FILENO) {
727 r = image.resize(image_pos);
729 std::cerr << "rbd: final image resize failed" << std::endl;
738 static int do_import(librados::Rados &rados, librbd::RBD &rbd,
739 librados::IoCtx& io_ctx, const char *imgname,
740 const char *path, librbd::ImageOptions& opts,
741 bool no_progress, int import_format, size_t sparse_size)
744 struct stat stat_buf;
745 utils::ProgressContext pc("Importing image", no_progress);
750 if (opts.get(RBD_IMAGE_OPTION_ORDER, &order) != 0) {
751 order = g_conf->get_val<int64_t>("rbd_default_order");
754 // try to fill whole imgblklen blocks for sparsification
755 size_t imgblklen = 1 << order;
759 bool from_stdin = !strcmp(path, "-");
762 size = 1ULL << order;
764 if ((fd = open(path, O_RDONLY)) < 0) {
766 std::cerr << "rbd: error opening " << path << std::endl;
770 if ((fstat(fd, &stat_buf)) < 0) {
772 std::cerr << "rbd: stat error " << path << std::endl;
775 if (S_ISDIR(stat_buf.st_mode)) {
777 std::cerr << "rbd: cannot import a directory" << std::endl;
780 if (stat_buf.st_size)
781 size = (uint64_t)stat_buf.st_size;
784 int64_t bdev_size = 0;
785 r = get_block_device_size(fd, &bdev_size);
787 std::cerr << "rbd: unable to get size of file/block device"
791 assert(bdev_size >= 0);
792 size = (uint64_t) bdev_size;
794 #ifdef HAVE_POSIX_FADVISE
795 posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
799 r = do_import_header(fd, import_format, size, opts);
801 std::cerr << "rbd: import header failed." << std::endl;
805 r = rbd.create4(io_ctx, imgname, size, opts);
807 std::cerr << "rbd: image creation failed" << std::endl;
811 r = rbd.open(io_ctx, image, imgname);
813 std::cerr << "rbd: failed to open image" << std::endl;
817 if (import_format == 1) {
818 r = do_import_v1(fd, image, size, imgblklen, pc, sparse_size);
820 r = do_import_v2(rados, fd, image, size, imgblklen, pc, sparse_size);
823 std::cerr << "rbd: failed to import image" << std::endl;
831 rbd.remove(io_ctx, imgname);
843 void get_arguments(po::options_description *positional,
844 po::options_description *options) {
845 at::add_path_options(positional, options,
846 "import file (or '-' for stdin)");
847 at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_DEST);
848 at::add_create_image_options(options, true);
849 at::add_sparse_size_option(options);
850 at::add_no_progress_option(options);
851 at::add_export_format_option(options);
853 // TODO legacy rbd allowed import to accept both 'image'/'dest' and
854 // 'pool'/'dest-pool'
855 at::add_pool_option(options, at::ARGUMENT_MODIFIER_NONE, " (deprecated)");
856 at::add_image_option(options, at::ARGUMENT_MODIFIER_NONE, " (deprecated)");
859 int execute(const po::variables_map &vm) {
861 int r = utils::get_path(vm, utils::get_positional_argument(vm, 0), &path);
866 // odd check to support legacy / deprecated behavior of import
867 std::string deprecated_pool_name;
868 if (vm.count(at::POOL_NAME)) {
869 deprecated_pool_name = vm[at::POOL_NAME].as<std::string>();
870 std::cerr << "rbd: --pool is deprecated for import, use --dest-pool"
874 std::string deprecated_image_name;
875 if (vm.count(at::IMAGE_NAME)) {
876 deprecated_image_name = vm[at::IMAGE_NAME].as<std::string>();
877 std::cerr << "rbd: --image is deprecated for import, use --dest"
880 deprecated_image_name = path.substr(path.find_last_of("/") + 1);
883 std::string deprecated_snap_name;
884 r = utils::extract_spec(deprecated_image_name, &deprecated_pool_name,
885 &deprecated_image_name, &deprecated_snap_name,
886 utils::SPEC_VALIDATION_FULL);
891 size_t sparse_size = utils::RBD_DEFAULT_SPARSE_SIZE;
892 if (vm.count(at::IMAGE_SPARSE_SIZE)) {
893 sparse_size = vm[at::IMAGE_SPARSE_SIZE].as<size_t>();
896 size_t arg_index = 1;
897 std::string pool_name = deprecated_pool_name;
898 std::string image_name;
899 std::string snap_name = deprecated_snap_name;
900 r = utils::get_pool_image_snapshot_names(
901 vm, at::ARGUMENT_MODIFIER_DEST, &arg_index, &pool_name, &image_name,
902 &snap_name, utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_FULL,
908 if (image_name.empty()) {
909 image_name = deprecated_image_name;
912 librbd::ImageOptions opts;
913 r = utils::get_image_options(vm, true, &opts);
918 librados::Rados rados;
919 librados::IoCtx io_ctx;
920 r = utils::init(pool_name, &rados, &io_ctx);
926 if (vm.count("export-format"))
927 format = vm["export-format"].as<uint64_t>();
930 r = do_import(rados, rbd, io_ctx, image_name.c_str(), path.c_str(),
931 opts, vm[at::NO_PROGRESS].as<bool>(), format, sparse_size);
933 std::cerr << "rbd: import failed: " << cpp_strerror(r) << std::endl;
940 Shell::Action action(
941 {"import"}, {}, "Import image from file.", at::get_long_features_help(),
942 &get_arguments, &execute);
944 } // namespace import
945 } // namespace action