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