Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / 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->get_val<int64_t>("rbd_concurrent_management_ops"),
41                false),
42       last_offset(0) {
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
80 class C_ImportDiff : public Context {
81 public:
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
127 private:
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
136 static 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
161 static 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
187 static 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
213 static 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
271 static 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
289 static 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
315 static 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
342 int 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
409 int 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
432 namespace at = argument_types;
433 namespace po = boost::program_options;
434
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);
442 }
443
444 int 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
485 Shell::Action action_diff(
486   {"import-diff"}, {}, "Import an incremental diff.", "", &get_arguments_diff,
487   &execute_diff);
488
489 class C_Import : public Context {
490 public:
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
525 private:
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
533 static 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
558 static 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
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)
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
641 static 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(
658       g_conf->get_val<int64_t>("rbd_concurrent_management_ops"), false));
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   }
733 out:
734   delete[] p;
735   return r;
736 }
737
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)
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) {
751     order = g_conf->get_val<int64_t>("rbd_default_order");
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();
829 err:
830   if (r < 0)
831     rbd.remove(io_ctx, imgname);
832 done:
833   if (r < 0)
834     pc.fail();
835   else
836     pc.finish();
837   if (!from_stdin)
838     close(fd);
839 done2:
840   return r;
841 }
842
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);
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
859 int 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
940 Shell::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