]> git.proxmox.com Git - ceph.git/blame - ceph/src/tools/rbd/action/Import.cc
update sources to v12.2.1
[ceph.git] / ceph / src / tools / rbd / action / Import.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
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"
17#include <iostream>
18#include <boost/program_options.hpp>
19#include <boost/scoped_ptr.hpp>
20#include "include/assert.h"
21
22#define dout_context g_ceph_context
23#define dout_subsys ceph_subsys_rbd
24
25namespace rbd {
26namespace action {
27namespace import {
28
29struct ImportDiffContext {
30 librbd::Image *image;
31 int fd;
32 size_t size;
33 utils::ProgressContext pc;
34 OrderedThrottle throttle;
35 uint64_t last_offset;
36
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 :
181888fb
FG
40 g_conf->get_val<int64_t>("rbd_concurrent_management_ops"),
41 false),
42 last_offset(0) {
7c673cae
FG
43 }
44
45 void update_size(size_t new_size)
46 {
47 if (fd == STDIN_FILENO) {
48 size = new_size;
49 }
50 }
51
52 void update_progress(uint64_t off)
53 {
54 if (size) {
55 pc.update_progress(off, size);
56 last_offset = off;
57 }
58 }
59
60 void update_progress()
61 {
62 uint64_t off = last_offset;
63 if (fd != STDIN_FILENO) {
64 off = lseek(fd, 0, SEEK_CUR);
65 }
66
67 update_progress(off);
68 }
69
70 void finish(int r)
71 {
72 if (r < 0) {
73 pc.fail();
74 } else {
75 pc.finish();
76 }
77 }
78};
79
80class C_ImportDiff : public Context {
81public:
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),
85 m_discard(discard) {
86 // use block offset (stdin) or import file position to report
87 // progress.
88 if (m_idiffctx->fd == STDIN_FILENO) {
89 m_prog_offset = offset;
90 } else {
91 m_prog_offset = lseek(m_idiffctx->fd, 0, SEEK_CUR);
92 }
93 }
94
95 int send()
96 {
97 if (m_idiffctx->throttle.pending_error()) {
98 return m_idiffctx->throttle.wait_for_ret();
99 }
100
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);
104
105 int r;
106 if (m_discard) {
107 r = m_idiffctx->image->aio_discard(m_offset, m_length, aio_completion);
108 } else {
109 r = m_idiffctx->image->aio_write2(m_offset, m_length, m_data,
110 aio_completion, LIBRADOS_OP_FLAG_FADVISE_NOCACHE);
111 }
112
113 if (r < 0) {
114 aio_completion->release();
115 ctx->complete(r);
116 }
117
118 return r;
119 }
120
121 void finish(int r) override
122 {
123 m_idiffctx->update_progress(m_prog_offset);
124 m_idiffctx->throttle.end_op(r);
125 }
126
127private:
128 ImportDiffContext *m_idiffctx;
129 bufferlist m_data;
130 uint64_t m_offset;
131 uint64_t m_length;
132 bool m_discard;
133 uint64_t m_prog_offset;
134};
135
136static int do_image_snap_from(ImportDiffContext *idiffctx)
137{
138 int r;
139 string from;
140 r = utils::read_string(idiffctx->fd, 4096, &from); // 4k limit to make sure we don't get a garbage string
141 if (r < 0) {
142 return r;
143 }
144
145 bool exists;
146 r = idiffctx->image->snap_exists2(from.c_str(), &exists);
147 if (r < 0) {
148 return r;
149 }
150
151 if (!exists) {
152 std::cerr << "start snapshot '" << from
153 << "' does not exist in the image, aborting" << std::endl;
154 return -EINVAL;
155 }
156
157 idiffctx->update_progress();
158 return 0;
159}
160
161static int do_image_snap_to(ImportDiffContext *idiffctx, std::string *tosnap)
162{
163 int r;
164 string to;
165 r = utils::read_string(idiffctx->fd, 4096, &to); // 4k limit to make sure we don't get a garbage string
166 if (r < 0) {
167 return r;
168 }
169
170 bool exists;
171 r = idiffctx->image->snap_exists2(to.c_str(), &exists);
172 if (r < 0) {
173 return r;
174 }
175
176 if (exists) {
177 std::cerr << "end snapshot '" << to << "' already exists, aborting" << std::endl;
178 return -EEXIST;
179 }
180
181 *tosnap = to;
182 idiffctx->update_progress();
183
184 return 0;
185}
186
187static int do_image_resize(ImportDiffContext *idiffctx)
188{
189 int r;
190 char buf[sizeof(uint64_t)];
191 uint64_t end_size;
192 r = safe_read_exact(idiffctx->fd, buf, sizeof(buf));
193 if (r < 0) {
194 return r;
195 }
196
197 bufferlist bl;
198 bl.append(buf, sizeof(buf));
199 bufferlist::iterator p = bl.begin();
200 ::decode(end_size, p);
201
202 uint64_t cur_size;
203 idiffctx->image->size(&cur_size);
204 if (cur_size != end_size) {
205 idiffctx->image->resize(end_size);
206 }
207
208 idiffctx->update_size(end_size);
209 idiffctx->update_progress();
210 return 0;
211}
212
213static int do_image_io(ImportDiffContext *idiffctx, bool discard, size_t sparse_size)
214{
215 int r;
216 char buf[16];
217 r = safe_read_exact(idiffctx->fd, buf, sizeof(buf));
218 if (r < 0) {
219 return r;
220 }
221
222 bufferlist bl;
223 bl.append(buf, sizeof(buf));
224 bufferlist::iterator p = bl.begin();
225
226 uint64_t image_offset, buffer_length;
227 ::decode(image_offset, p);
228 ::decode(buffer_length, p);
229
230 if (!discard) {
231 bufferptr bp = buffer::create(buffer_length);
232 r = safe_read_exact(idiffctx->fd, bp.c_str(), buffer_length);
233 if (r < 0) {
234 return r;
235 }
236
237 size_t buffer_offset = 0;
238 while (buffer_offset < buffer_length) {
239 size_t write_length = 0;
240 bool zeroed = false;
241 utils::calc_sparse_extent(bp, sparse_size, buffer_offset, buffer_length,
242 &write_length, &zeroed);
243 assert(write_length > 0);
244
245 bufferlist write_bl;
246 if (!zeroed) {
247 bufferptr write_ptr(bp, buffer_offset, write_length);
248 write_bl.push_back(write_ptr);
249 assert(write_bl.length() == write_length);
250 }
251
252 C_ImportDiff *ctx = new C_ImportDiff(idiffctx, write_bl,
253 image_offset + buffer_offset,
254 write_length, zeroed);
255 r = ctx->send();
256 if (r < 0) {
257 return r;
258 }
259
260 buffer_offset += write_length;
261 }
262 } else {
263 bufferlist data;
264 C_ImportDiff *ctx = new C_ImportDiff(idiffctx, data, image_offset,
265 buffer_length, true);
266 return ctx->send();
267 }
268 return r;
269}
270
271static int validate_banner(int fd, std::string banner)
272{
273 int r;
274 char buf[banner.size() + 1];
275 r = safe_read_exact(fd, buf, banner.size());
276 if (r < 0) {
277 return r;
278 }
279
280 buf[banner.size()] = '\0';
281 if (strcmp(buf, banner.c_str())) {
282 std::cerr << "invalid banner '" << buf << "', expected '" << banner << "'" << std::endl;
283 return -EINVAL;
284 }
285
286 return 0;
287}
288
289static int skip_tag(int fd, uint64_t length)
290{
291 int r;
292
293 if (fd == STDIN_FILENO) {
294 // read the appending data out to skip this tag.
295 char buf[4096];
296 uint64_t len = min<uint64_t>(length, sizeof(buf));
297 while (len > 0) {
298 r = safe_read_exact(fd, buf, len);
299 if (r < 0)
300 return r;
301 length -= len;
302 len = min<uint64_t>(length, sizeof(buf));
303 }
304 } else {
305 // lseek to skip this tag
306 off64_t offs = lseek64(fd, length, SEEK_CUR);
307 if (offs < 0) {
308 return -errno;
309 }
310 }
311
312 return 0;
313}
314
315static int read_tag(int fd, __u8 end_tag, int format, __u8 *tag, uint64_t *readlen)
316{
317 int r;
318 __u8 read_tag;
319
320 r = safe_read_exact(fd, &read_tag, sizeof(read_tag));
321 if (r < 0) {
322 return r;
323 }
324
325 *tag = 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));
329 if (r < 0) {
330 return r;
331 }
332
333 bufferlist bl;
334 bl.append(buf, sizeof(buf));
335 bufferlist::iterator p = bl.begin();
336 ::decode(*readlen, p);
337 }
338
339 return 0;
340}
341
342int do_import_diff_fd(librados::Rados &rados, librbd::Image &image, int fd,
343 bool no_progress, int format, size_t sparse_size)
344{
345 int r;
346
347 uint64_t size = 0;
348 bool from_stdin = (fd == STDIN_FILENO);
349 if (!from_stdin) {
350 struct stat stat_buf;
351 r = ::fstat(fd, &stat_buf);
352 if (r < 0) {
353 return r;
354 }
355 size = (uint64_t)stat_buf.st_size;
356 }
357
358 r = validate_banner(fd, (format == 1 ? utils::RBD_DIFF_BANNER :
359 utils::RBD_DIFF_BANNER_V2));
360 if (r < 0) {
361 return r;
362 }
363
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;
368 sparse_size = 0;
369 r = 0;
370 }
371
372 // begin image import
373 std::string tosnap;
374 ImportDiffContext idiffctx(&image, fd, size, no_progress);
375 while (r == 0) {
376 __u8 tag;
377 uint64_t length = 0;
378
379 r = read_tag(fd, RBD_DIFF_END, format, &tag, &length);
380 if (r < 0 || tag == RBD_DIFF_END) {
381 break;
382 }
383
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);
392 } else {
393 std::cerr << "unrecognized tag byte " << (int)tag << " in stream; skipping"
394 << std::endl;
395 r = skip_tag(fd, length);
396 }
397 }
398
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());
403 }
404
405 idiffctx.finish(r);
406 return r;
407}
408
409int do_import_diff(librados::Rados &rados, librbd::Image &image,
410 const char *path, bool no_progress, size_t sparse_size)
411{
412 int r;
413 int fd;
414
415 if (strcmp(path, "-") == 0) {
416 fd = STDIN_FILENO;
417 } else {
418 fd = open(path, O_RDONLY);
419 if (fd < 0) {
420 r = -errno;
421 std::cerr << "rbd: error opening " << path << std::endl;
422 return r;
423 }
424 }
425 r = do_import_diff_fd(rados, image, fd, no_progress, 1, sparse_size);
426
427 if (fd != 0)
428 close(fd);
429 return r;
430}
431
432namespace at = argument_types;
433namespace po = boost::program_options;
434
435void 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);
442}
443
444int execute_diff(const po::variables_map &vm) {
445 std::string path;
446 int r = utils::get_path(vm, utils::get_positional_argument(vm, 0), &path);
447 if (r < 0) {
448 return r;
449 }
450
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);
458 if (r < 0) {
459 return r;
460 }
461
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>();
465 }
466
467 librados::Rados rados;
468 librados::IoCtx io_ctx;
469 librbd::Image image;
470 r = utils::init_and_open_image(pool_name, image_name, "", "", false,
471 &rados, &io_ctx, &image);
472 if (r < 0) {
473 return r;
474 }
475
476 r = do_import_diff(rados, image, path.c_str(),
477 vm[at::NO_PROGRESS].as<bool>(), sparse_size);
478 if (r < 0) {
479 cerr << "rbd: import-diff failed: " << cpp_strerror(r) << std::endl;
480 return r;
481 }
482 return 0;
483}
484
485Shell::Action action_diff(
486 {"import-diff"}, {}, "Import an incremental diff.", "", &get_arguments_diff,
487 &execute_diff);
488
489class C_Import : public Context {
490public:
491 C_Import(SimpleThrottle &simple_throttle, librbd::Image &image,
492 bufferlist &bl, uint64_t offset)
493 : m_throttle(simple_throttle), m_image(image),
494 m_aio_completion(
495 new librbd::RBD::AioCompletion(this, &utils::aio_context_callback)),
496 m_bufferlist(bl), m_offset(offset)
497 {
498 }
499
500 void send()
501 {
502 m_throttle.start_op();
503
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);
508 if (r < 0) {
509 std::cerr << "rbd: error requesting write to destination image"
510 << std::endl;
511 m_aio_completion->release();
512 m_throttle.end_op(r);
513 }
514 }
515
516 void finish(int r) override
517 {
518 if (r < 0) {
519 std::cerr << "rbd: error writing to destination image at offset "
520 << m_offset << ": " << cpp_strerror(r) << std::endl;
521 }
522 m_throttle.end_op(r);
523 }
524
525private:
526 SimpleThrottle &m_throttle;
527 librbd::Image &m_image;
528 librbd::RBD::AioCompletion *m_aio_completion;
529 bufferlist m_bufferlist;
530 uint64_t m_offset;
531};
532
533static int decode_and_set_image_option(int fd, uint64_t imageopt, librbd::ImageOptions& opts)
534{
535 int r;
536 char buf[sizeof(uint64_t)];
537
538 r = safe_read_exact(fd, buf, sizeof(buf));
539 if (r < 0) {
540 return r;
541 }
542
543 bufferlist bl;
544 bl.append(buf, sizeof(buf));
545 bufferlist::iterator it;
546 it = bl.begin();
547
548 uint64_t val;
549 ::decode(val, it);
550
551 if (opts.get(imageopt, &val) != 0) {
552 opts.set(imageopt, val);
553 }
554
555 return 0;
556}
557
558static int do_import_header(int fd, int import_format, uint64_t &size, librbd::ImageOptions& opts)
559{
560 // There is no header in v1 image.
561 if (import_format == 1) {
562 return 0;
563 }
564
565 if (fd == STDIN_FILENO || size < utils::RBD_IMAGE_BANNER_V2.size()) {
566 return -EINVAL;
567 }
568
569 int r;
570 r = validate_banner(fd, utils::RBD_IMAGE_BANNER_V2);
571 if (r < 0) {
572 return r;
573 }
574
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);
579 }
580
581 while (r == 0) {
582 __u8 tag;
583 uint64_t length;
584 r = read_tag(fd, RBD_EXPORT_IMAGE_END, image_format, &tag, &length);
585 if (r < 0 || tag == RBD_EXPORT_IMAGE_END) {
586 break;
587 }
588
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);
597 } else {
598 std::cerr << "rbd: invalid tag in image properties zone: " << tag << "Skip it."
599 << std::endl;
600 r = skip_tag(fd, length);
601 }
602 }
603
604 return r;
605}
606
607static 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)
610{
611 int r = 0;
612 r = validate_banner(fd, utils::RBD_IMAGE_DIFFS_BANNER_V2);
613 if (r < 0) {
614 return r;
615 }
616
617 char buf[sizeof(uint64_t)];
618 r = safe_read_exact(fd, buf, sizeof(buf));
619 if (r < 0) {
620 return r;
621 }
622 bufferlist bl;
623 bl.append(buf, sizeof(buf));
624 bufferlist::iterator p = bl.begin();
625 uint64_t diff_num;
626 ::decode(diff_num, p);
627
628 for (size_t i = 0; i < diff_num; i++) {
629 r = do_import_diff_fd(rados, image, fd, true, 2, sparse_size);
630 if (r < 0) {
631 pc.fail();
632 std::cerr << "rbd: import-diff failed: " << cpp_strerror(r) << std::endl;
633 return r;
634 }
635 pc.update_progress(i + 1, diff_num);
636 }
637
638 return r;
639}
640
641static int do_import_v1(int fd, librbd::Image &image, uint64_t size,
642 size_t imgblklen, utils::ProgressContext &pc,
643 size_t sparse_size)
644{
645 int r = 0;
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;
653
654 if (from_stdin) {
655 throttle.reset(new SimpleThrottle(1, false));
656 } else {
657 throttle.reset(new SimpleThrottle(
181888fb 658 g_conf->get_val<int64_t>("rbd_concurrent_management_ops"), false));
7c673cae
FG
659 }
660
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()) {
665 break;
666 }
667
668 blklen += readlen;
669 // if read was short, try again to fill the block before writing
670 if (readlen && ((size_t)readlen < reqlen)) {
671 reqlen -= readlen;
672 continue;
673 }
674 if (!from_stdin)
675 pc.update_progress(image_pos, size);
676
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) {
680 size *= 2;
681 r = image.resize(size);
682 if (r < 0) {
683 std::cerr << "rbd: can't resize image during import" << std::endl;
684 goto out;
685 }
686 }
687
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;
693 bool zeroed = false;
694 utils::calc_sparse_extent(blkptr, sparse_size, buffer_offset, blklen,
695 &write_length, &zeroed);
696
697 if (!zeroed) {
698 bufferlist write_bl;
699 bufferptr write_ptr(blkptr, buffer_offset, write_length);
700 write_bl.push_back(write_ptr);
701 assert(write_bl.length() == write_length);
702
703 C_Import *ctx = new C_Import(*throttle, image, write_bl,
704 image_pos + buffer_offset);
705 ctx->send();
706 }
707
708 buffer_offset += write_length;
709 }
710
711 // done with whole block, whether written or not
712 image_pos += blklen;
713 if (!from_stdin && image_pos >= size)
714 break;
715 // if read had returned 0, we're at EOF and should quit
716 if (readlen == 0)
717 break;
718 blklen = 0;
719 reqlen = imgblklen;
720 }
721 r = throttle->wait_for_ret();
722 if (r < 0) {
723 goto out;
724 }
725
726 if (fd == STDIN_FILENO) {
727 r = image.resize(image_pos);
728 if (r < 0) {
729 std::cerr << "rbd: final image resize failed" << std::endl;
730 goto out;
731 }
732 }
733out:
734 delete[] p;
735 return r;
736}
737
738static 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)
742{
743 int fd, r;
744 struct stat stat_buf;
745 utils::ProgressContext pc("Importing image", no_progress);
746
747 assert(imgname);
748
749 uint64_t order;
750 if (opts.get(RBD_IMAGE_OPTION_ORDER, &order) != 0) {
181888fb 751 order = g_conf->get_val<int64_t>("rbd_default_order");
7c673cae
FG
752 }
753
754 // try to fill whole imgblklen blocks for sparsification
755 size_t imgblklen = 1 << order;
756 librbd::Image image;
757 uint64_t size = 0;
758
759 bool from_stdin = !strcmp(path, "-");
760 if (from_stdin) {
761 fd = STDIN_FILENO;
762 size = 1ULL << order;
763 } else {
764 if ((fd = open(path, O_RDONLY)) < 0) {
765 r = -errno;
766 std::cerr << "rbd: error opening " << path << std::endl;
767 goto done2;
768 }
769
770 if ((fstat(fd, &stat_buf)) < 0) {
771 r = -errno;
772 std::cerr << "rbd: stat error " << path << std::endl;
773 goto done;
774 }
775 if (S_ISDIR(stat_buf.st_mode)) {
776 r = -EISDIR;
777 std::cerr << "rbd: cannot import a directory" << std::endl;
778 goto done;
779 }
780 if (stat_buf.st_size)
781 size = (uint64_t)stat_buf.st_size;
782
783 if (!size) {
784 int64_t bdev_size = 0;
785 r = get_block_device_size(fd, &bdev_size);
786 if (r < 0) {
787 std::cerr << "rbd: unable to get size of file/block device"
788 << std::endl;
789 goto done;
790 }
791 assert(bdev_size >= 0);
792 size = (uint64_t) bdev_size;
793 }
794#ifdef HAVE_POSIX_FADVISE
795 posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
796#endif
797 }
798
799 r = do_import_header(fd, import_format, size, opts);
800 if (r < 0) {
801 std::cerr << "rbd: import header failed." << std::endl;
802 goto done;
803 }
804
805 r = rbd.create4(io_ctx, imgname, size, opts);
806 if (r < 0) {
807 std::cerr << "rbd: image creation failed" << std::endl;
808 goto done;
809 }
810
811 r = rbd.open(io_ctx, image, imgname);
812 if (r < 0) {
813 std::cerr << "rbd: failed to open image" << std::endl;
814 goto err;
815 }
816
817 if (import_format == 1) {
818 r = do_import_v1(fd, image, size, imgblklen, pc, sparse_size);
819 } else {
820 r = do_import_v2(rados, fd, image, size, imgblklen, pc, sparse_size);
821 }
822 if (r < 0) {
823 std::cerr << "rbd: failed to import image" << std::endl;
824 image.close();
825 goto err;
826 }
827
828 r = image.close();
829err:
830 if (r < 0)
831 rbd.remove(io_ctx, imgname);
832done:
833 if (r < 0)
834 pc.fail();
835 else
836 pc.finish();
837 if (!from_stdin)
838 close(fd);
839done2:
840 return r;
841}
842
843void 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);
852
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)");
857}
858
859int execute(const po::variables_map &vm) {
860 std::string path;
861 int r = utils::get_path(vm, utils::get_positional_argument(vm, 0), &path);
862 if (r < 0) {
863 return r;
864 }
865
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"
871 << std::endl;
872 }
873
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"
878 << std::endl;
879 } else {
880 deprecated_image_name = path.substr(path.find_last_of("/") + 1);
881 }
882
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);
887 if (r < 0) {
888 return r;
889 }
890
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>();
894 }
895
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,
903 false);
904 if (r < 0) {
905 return r;
906 }
907
908 if (image_name.empty()) {
909 image_name = deprecated_image_name;
910 }
911
912 librbd::ImageOptions opts;
913 r = utils::get_image_options(vm, true, &opts);
914 if (r < 0) {
915 return r;
916 }
917
918 librados::Rados rados;
919 librados::IoCtx io_ctx;
920 r = utils::init(pool_name, &rados, &io_ctx);
921 if (r < 0) {
922 return r;
923 }
924
925 int format = 1;
926 if (vm.count("export-format"))
927 format = vm["export-format"].as<uint64_t>();
928
929 librbd::RBD rbd;
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);
932 if (r < 0) {
933 std::cerr << "rbd: import failed: " << cpp_strerror(r) << std::endl;
934 return r;
935 }
936
937 return 0;
938}
939
940Shell::Action action(
941 {"import"}, {}, "Import image from file.", at::get_long_features_help(),
942 &get_arguments, &execute);
943
944} // namespace import
945} // namespace action
946} // namespace rbd