1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2014-2021 Bareos GmbH & Co. KG
5    Copyright (C) 2015-2015 Planets Communications B.V.
6 
7    This program is Free Software; you can redistribute it and/or
8    modify it under the terms of version three of the GNU Affero General Public
9    License as published by the Free Software Foundation, which is
10    listed in the file LICENSE.vadp.
11 
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15    Affero General Public License for more details.
16 
17    You should have received a copy of the GNU Affero General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20    02110-1301, USA.
21 */
22 /*
23  * VADP Dumper - vStorage APIs for Data Protection Dumper program.
24  *
25  * Marco van Wieringen, July 2014
26  * Renout Gerrits, Oct 2017, added thumbprint
27  */
28 #include <stdio.h>
29 #include <stdint.h>
30 #include <string.h>
31 #include <fcntl.h>
32 #include <time.h>
33 #include <unistd.h>
34 #include <signal.h>
35 
36 #include "copy_thread.hpp"
37 
38 #include <jansson.h>
39 
40 /*
41  * json_array_foreach macro was added in jansson version 2.5
42  * we can compile also against an lower version if we define it ourselves.
43  */
44 #if JANSSON_VERSION_HEX < 0x020500
45 #define json_array_foreach(array, index, value) \
46    for(index = 0; \
47             index < json_array_size(array) && (value = json_array_get(array, index)); \
48                   index++)
49 #endif
50 
51 #include <vixDiskLib.h>
52 
53 #define VIXDISKLIB_VERSION_MAJOR 6
54 #define VIXDISKLIB_VERSION_MINOR 5
55 
56 #define VSPHERE_DEFAULT_ADMIN_PORT 0
57 
58 /*
59  * VixDiskLib does all processing in sectors of 512 bytes.
60  */
61 #define DEFAULT_SECTOR_SIZE VIXDISKLIB_SECTOR_SIZE
62 
63 /*
64  * In each call to the VixDiskLib read/write this number of sectors.
65  * e.g. 512 means 256 Kb per call (e.g. 512 x 512 bytes)
66  */
67 #define SECTORS_PER_CALL 1024
68 
69 #define MIN(a, b) ((a) < b) ? (a) : (b)
70 #define MAX(a, b) ((a) > b) ? (a) : (b)
71 
72 #define CON_PARAMS_KEY "ConnParams"
73 #define CON_PARAMS_VM_MOREF_KEY "VmMoRef"
74 #define CON_PARAMS_HOST_KEY "VsphereHostName"
75 #define CON_PARAMS_THUMBPRINT_KEY "VsphereThumbPrint"
76 #define CON_PARAMS_USERNAME_KEY "VsphereUsername"
77 #define CON_PARAMS_PASSWORD_KEY "VspherePassword"
78 #define CON_PARAMS_SNAPSHOT_MOREF_KEY "VsphereSnapshotMoRef"
79 
80 #define DISK_PARAMS_KEY "DiskParams"
81 #define DISK_PARAMS_DISK_PATH_KEY "diskPath"
82 
83 #define CBT_DISKCHANGEINFO_KEY "DiskChangeInfo"
84 #define CBT_DISK_SIZE "length"
85 #define CBT_CHANGEDAREA_KEY "changedArea"
86 #define CBT_CHANGEDAREA_START_KEY "start"
87 #define CBT_CHANGEDAREA_LENGTH_KEY "length"
88 #define CBT_START_OFFSET "startOffset"
89 
90 #define BAREOSMAGIC 0x12122012
91 #define PROTOCOL_VERSION 1
92 
93 #define BAREOS_VADPDUMPER_IDENTITY "BareosVADPDumper"
94 
95 struct disk_type {
96    const char *type;
97    VixDiskLibDiskType vadp_type;
98 };
99 
100 static struct disk_type disk_types[] = {
101    { "monolithic_sparse", VIXDISKLIB_DISK_MONOLITHIC_SPARSE },
102    { "monolithic_flat", VIXDISKLIB_DISK_MONOLITHIC_FLAT },
103    { "split_sparse", VIXDISKLIB_DISK_SPLIT_SPARSE },
104    { "split_flat", VIXDISKLIB_DISK_SPLIT_FLAT },
105    { "vmfs_flat", VIXDISKLIB_DISK_VMFS_FLAT },
106    { "optimized", VIXDISKLIB_DISK_STREAM_OPTIMIZED },
107    { "vmfs_thin", VIXDISKLIB_DISK_VMFS_THIN },
108    { "vmfs_sparse", VIXDISKLIB_DISK_VMFS_SPARSE },
109    { NULL, VIXDISKLIB_DISK_UNKNOWN }
110 };
111 
112 /*
113  * Generic identification structure, 128 bytes with padding.
114  * This includes a protocol version.
115  */
116 struct runtime_disk_info_encoding {
117    uint32_t start_magic;
118    uint32_t protocol_version;
119    uint64_t absolute_disk_length;
120    uint64_t absolute_start_offset;
121    uint32_t bios_cylinders;
122    uint32_t bios_heads;
123    uint32_t bios_sectors;
124    uint32_t phys_cylinders;
125    uint32_t phys_heads;
126    uint32_t phys_sectors;
127    uint64_t phys_capacity;
128    uint32_t adapter_type;
129    uint32_t padding[16];
130    uint32_t end_magic;
131 };
132 const int rdie_size = sizeof(struct runtime_disk_info_encoding);
133 
134 /*
135  * Disk Meta data structure,
136  * Encodes what follows e.g. meta_key and meta_data.
137  * e.g. [META_META_DATA] [META_DATA_KEY] [META_DATA] ...
138  */
139 struct runtime_meta_data_encoding {
140    uint32_t start_magic;
141    uint32_t meta_key_length;
142    uint32_t meta_data_length;
143    uint32_t end_magic;
144 };
145 const int rmde_size = sizeof(struct runtime_meta_data_encoding);
146 
147 /*
148  * Changed Block Tracking structure.
149  * Encodes the CBT data followed by the actual data.
150  * e.g. [CBT] [DATA] ...
151  */
152 struct runtime_cbt_encoding {
153    uint32_t start_magic;
154    uint64_t start_offset;
155    uint64_t offset_length;
156    uint32_t end_magic;
157 };
158 const int rce_size = sizeof(struct runtime_cbt_encoding);
159 
160 static bool cleanup_on_start = false;
161 static bool cleanup_on_disconnect = false;
162 static bool save_metadata = false;
163 static bool verbose = false;
164 static bool check_size = true;
165 static bool create_disk = false;
166 static bool local_vmdk = false;
167 static bool multi_threaded = false;
168 static bool restore_meta_data = false;
169 static int sectors_per_call = SECTORS_PER_CALL;
170 static uint64_t absolute_start_offset = 0;
171 static char *vmdk_disk_name = NULL;
172 static char *raw_disk_name = NULL;
173 static int raw_disk_fd = -1;
174 static char *force_transport = NULL;
175 static char *disktype = NULL;
176 static VixDiskLibConnectParams cnxParams;
177 static VixDiskLibConnection connection = NULL;
178 static VixDiskLibHandle read_diskHandle = NULL;
179 static VixDiskLibHandle write_diskHandle = NULL;
180 static VixDiskLibInfo *info = NULL;
181 static json_t *json_config = NULL;
182 static int exit_code = 1;
183 
184 /*
185  * Encode the VDDK VixDiskLibInfo into an internal representation.
186  */
fill_runtime_disk_info_encoding(struct runtime_disk_info_encoding * rdie)187 static inline void fill_runtime_disk_info_encoding(struct runtime_disk_info_encoding *rdie)
188 {
189    memset(rdie, 0, rdie_size);
190 
191    rdie->protocol_version = PROTOCOL_VERSION;
192    rdie->start_magic = BAREOSMAGIC;
193    rdie->end_magic = BAREOSMAGIC;
194 
195    if (info->biosGeo.cylinders > 0) {
196       rdie->bios_cylinders = info->biosGeo.cylinders;
197    } else {
198       rdie->bios_cylinders = info->physGeo.cylinders;
199    }
200    if (info->biosGeo.heads > 0) {
201       rdie->bios_heads = info->biosGeo.heads;
202    } else {
203       rdie->bios_heads = info->physGeo.heads;
204    }
205    if (info->biosGeo.sectors > 0) {
206       rdie->bios_sectors = info->biosGeo.sectors;
207    } else {
208       rdie->bios_sectors = info->physGeo.sectors;
209    }
210 
211    rdie->phys_cylinders = info->physGeo.cylinders;
212    rdie->phys_heads = info->physGeo.heads;
213    rdie->phys_sectors = info->physGeo.sectors;
214 
215    rdie->phys_capacity = info->capacity;
216    rdie->adapter_type = info->adapterType;
217 }
218 
219 /*
220  * Dump the important content of the internal disk representation for verbose mode.
221  */
dump_runtime_disk_info_encoding(struct runtime_disk_info_encoding * rdie)222 static inline void dump_runtime_disk_info_encoding(struct runtime_disk_info_encoding *rdie)
223 {
224    fprintf(stderr, "Protocol version = %lld\n", rdie->protocol_version);
225    fprintf(stderr, "Absolute disk length = %lld\n", rdie->absolute_disk_length);
226    fprintf(stderr, "Absolute start offset = %lld\n", rdie->absolute_start_offset);
227    fprintf(stderr, "BIOS geometry (%ld cyl, %ld heads, %ld sectors)\n",
228            rdie->bios_cylinders, rdie->bios_heads, rdie->bios_sectors);
229    fprintf(stderr, "PHYS geometry (%ld cyl, %ld heads, %ld sectors)\n",
230            rdie->phys_cylinders, rdie->phys_heads, rdie->phys_sectors);
231    fprintf(stderr, "Physical capacity %lld\n", rdie->phys_capacity);
232    fprintf(stderr, "Adapter Type %ld\n", rdie->adapter_type);
233 }
234 
235 /*
236  * Validate the disk sizes from the internal disk representation to the current VDMK settings.
237  */
validate_runtime_disk_info_encoding(struct runtime_disk_info_encoding * rdie)238 static inline char validate_runtime_disk_info_encoding(struct runtime_disk_info_encoding *rdie)
239 {
240    if (info->biosGeo.cylinders > 0 && info->biosGeo.cylinders < rdie->bios_cylinders) {
241       fprintf(stderr, "[validate_runtime_disk_info_encoding] New disk has %ld BIOS cylinders original had %ld\n",
242               info->biosGeo.cylinders, rdie->bios_cylinders);
243       goto bail_out;
244    }
245 
246    if (info->biosGeo.heads > 0 && info->biosGeo.heads < rdie->bios_heads) {
247       fprintf(stderr, "[validate_runtime_disk_info_encoding] New disk has %ld BIOS heads original had %ld\n",
248               info->biosGeo.heads, rdie->bios_heads);
249       goto bail_out;
250    }
251 
252    if (info->biosGeo.cylinders > 0 && info->biosGeo.cylinders < rdie->bios_cylinders) {
253       fprintf(stderr, "[validate_runtime_disk_info_encoding] New disk has %ld BIOS sectors original had %ld\n",
254               info->biosGeo.sectors, rdie->bios_sectors);
255       goto bail_out;
256    }
257 
258    if (info->physGeo.cylinders < rdie->phys_cylinders) {
259       fprintf(stderr, "[validate_runtime_disk_info_encoding] New disk has %ld PHYS cylinders original had %ld\n",
260               info->physGeo.cylinders, rdie->phys_cylinders);
261       goto bail_out;
262    }
263 
264    if (info->physGeo.heads < rdie->phys_heads) {
265       fprintf(stderr, "[validate_runtime_disk_info_encoding] New disk has %ld PHYS heads original had %ld\n",
266               info->biosGeo.heads, rdie->phys_heads);
267       goto bail_out;
268    }
269 
270    if (info->physGeo.cylinders < rdie->phys_cylinders) {
271       fprintf(stderr, "[validate_runtime_disk_info_encoding] New disk has %ld PHYS sectors original had %ld\n",
272               info->biosGeo.sectors, rdie->phys_sectors);
273       goto bail_out;
274    }
275 
276    return 1;
277 
278 bail_out:
279    return 0;
280 }
281 
282 /*
283  * Writer function that handles partial writes.
284  */
robust_writer(int fd,void * buffer,int size)285 static inline size_t robust_writer(int fd, void *buffer, int size)
286 {
287    size_t total_bytes = 0;
288    size_t cnt = 0;
289 
290    do {
291       cnt = write(fd, (char *)buffer + total_bytes, size);
292       if (cnt > 0) {
293          size -= cnt;
294          total_bytes += cnt;
295       } else if (cnt < 0) {
296          total_bytes = -1;
297          goto bail_out;
298       }
299    } while (size > 0 && cnt > 0);
300 
301 bail_out:
302    return total_bytes;
303 }
304 
305 /*
306  * Reader function that handles partial reads.
307  */
robust_reader(int fd,void * buffer,int size)308 static inline size_t robust_reader(int fd, void *buffer, int size)
309 {
310    size_t total_bytes = 0;
311    size_t cnt = 0;
312 
313    do {
314       cnt = read(fd, (char *)buffer + total_bytes, size);
315       if (cnt > 0) {
316          size -= cnt;
317          total_bytes += cnt;
318       } else if (cnt < 0) {
319          total_bytes = -1;
320          goto bail_out;
321       }
322    } while (size > 0 && cnt > 0);
323 
324 bail_out:
325    return total_bytes;
326 }
327 
328 /*
329  * VDDK helper functions.
330  */
LogFunction(const char * fmt,va_list args)331 static void LogFunction(const char *fmt, va_list args)
332 {
333    if (verbose) {
334       fprintf(stderr, "Log: ");
335       vfprintf(stderr, fmt, args);
336    }
337 }
338 
WarningFunction(const char * fmt,va_list args)339 static void WarningFunction(const char *fmt, va_list args)
340 {
341    fprintf(stderr, "Warning: ");
342    vfprintf(stderr, fmt, args);
343 }
344 
PanicFunction(const char * fmt,va_list args)345 static void PanicFunction(const char *fmt, va_list args)
346 {
347    fprintf(stderr, "Log: ");
348    vfprintf(stderr, fmt, args);
349    exit_code = 10;
350    exit(exit_code);
351 }
352 
cleanup_cnxParams()353 static inline void cleanup_cnxParams()
354 {
355    if (cnxParams.vmxSpec) {
356       free(cnxParams.vmxSpec);
357       cnxParams.vmxSpec = NULL;
358    }
359 
360    if (cnxParams.serverName) {
361       free(cnxParams.serverName);
362       cnxParams.serverName = NULL;
363    }
364 
365    if (cnxParams.creds.uid.userName) {
366       free(cnxParams.creds.uid.userName);
367       cnxParams.creds.uid.userName = NULL;
368    }
369 
370    if (cnxParams.creds.uid.password) {
371       free(cnxParams.creds.uid.password);
372       cnxParams.creds.uid.password = NULL;
373    }
374 
375    if (cnxParams.thumbPrint) {
376       free(cnxParams.thumbPrint);
377       cnxParams.thumbPrint = NULL;
378    }
379 }
380 
cleanup_vixdisklib()381 static inline void cleanup_vixdisklib()
382 {
383    uint32_t numCleanedUp, numRemaining;
384 
385    VixDiskLib_Cleanup(&cnxParams, &numCleanedUp, &numRemaining);
386 }
387 
388 /*
389  * Generic cleanup function.
390  */
cleanup(void)391 static void cleanup(void)
392 {
393    VixError err;
394 
395    if (info) {
396       VixDiskLib_FreeInfo(info);
397    }
398 
399    if (read_diskHandle) {
400       VixDiskLib_Close(read_diskHandle);
401       read_diskHandle = NULL;
402    }
403 
404    if (write_diskHandle) {
405       VixDiskLib_Close(write_diskHandle);
406       write_diskHandle = NULL;
407    }
408 
409    if (connection) {
410       VixDiskLib_Disconnect(connection);
411       if (cleanup_on_disconnect) {
412          cleanup_vixdisklib();
413       }
414    }
415 
416    if (!local_vmdk) {
417       err = VixDiskLib_EndAccess(&cnxParams, BAREOS_VADPDUMPER_IDENTITY);
418       if (VIX_FAILED(err)) {
419          char *error_txt;
420 
421          error_txt = VixDiskLib_GetErrorText(err, NULL);
422          fprintf(stderr, "Failed to End Access: %s [%d]\n", error_txt, err);
423          VixDiskLib_FreeErrorText(error_txt);
424       }
425    }
426 
427    if (raw_disk_fd != -1) {
428       if (verbose) {
429          fprintf(stderr, "Log: RAWFILE: Closing RAW file\n");
430       }
431       close(raw_disk_fd);
432    }
433 
434    cleanup_cnxParams();
435 
436    VixDiskLib_Exit();
437 
438    if (json_config) {
439       json_decref(json_config);
440    }
441 
442    if (vmdk_disk_name) {
443       free(vmdk_disk_name);
444    }
445 
446    if (force_transport) {
447       free(force_transport);
448    }
449 
450    if (disktype) {
451       free(disktype);
452    }
453 
454    _exit(exit_code);
455 }
456 
457 /*
458  * Convert the configured disktype to the right VADP type.
459  */
lookup_disktype()460 static inline VixDiskLibDiskType lookup_disktype()
461 {
462    int cnt;
463 
464    for (cnt = 0; disk_types[cnt].type; cnt++) {
465       if (!strcasecmp(disk_types[cnt].type, disktype)) {
466          break;
467       }
468    }
469 
470    if (!disk_types[cnt].type) {
471       fprintf(stderr, "Unknown disktype %s\n", disktype);
472       exit(1);
473    }
474 
475    return disk_types[cnt].vadp_type;
476 }
477 
478 /*
479  * Connect using VDDK to a VSPHERE server.
480  */
do_vixdisklib_connect(const char * key,json_t * connect_params,bool readonly,bool need_snapshot_moref)481 static inline void do_vixdisklib_connect(const char *key, json_t *connect_params,
482                                          bool readonly, bool need_snapshot_moref)
483 {
484    int succeeded = 0;
485    VixError err;
486    const char *snapshot_moref = NULL;
487 
488    memset(&cnxParams, 0, sizeof(cnxParams));
489 
490    err = VixDiskLib_InitEx(VIXDISKLIB_VERSION_MAJOR,
491                            VIXDISKLIB_VERSION_MINOR,
492                            LogFunction,
493                            WarningFunction,
494                            PanicFunction,
495                            "/usr/lib/vmware-vix-disklib",
496                            NULL);
497 
498    if (VIX_FAILED(err)) {
499       char *error_txt;
500 
501       error_txt = VixDiskLib_GetErrorText(err, NULL);
502       fprintf(stderr, "Failed to initialize vixdisklib %s [%d]\n", error_txt, err);
503       VixDiskLib_FreeErrorText(error_txt);
504       goto bail_out;
505    }
506 
507    /*
508     * Start extracting the wanted information from the JSON passed in.
509     */
510    if (!local_vmdk) {
511       json_t *object;
512 
513       object = json_object_get(connect_params, CON_PARAMS_VM_MOREF_KEY);
514       if (!object) {
515          fprintf(stderr, "Failed to find %s in JSON definition of object %s\n", CON_PARAMS_VM_MOREF_KEY, key);
516          goto bail_out;
517       }
518       cnxParams.vmxSpec = strdup(json_string_value(object));
519       if (!cnxParams.vmxSpec) {
520          fprintf(stderr, "Failed to allocate memory for holding %s\n", CON_PARAMS_VM_MOREF_KEY);
521          goto bail_out;
522       }
523 
524       object = json_object_get(connect_params, CON_PARAMS_HOST_KEY);
525       if (!object) {
526          fprintf(stderr, "Failed to find %s in JSON definition of object %s\n", CON_PARAMS_HOST_KEY, key);
527          goto bail_out;
528       }
529       cnxParams.serverName = strdup(json_string_value(object));
530       if (!cnxParams.serverName) {
531          fprintf(stderr, "Failed to allocate memory for holding %s\n", CON_PARAMS_HOST_KEY);
532          goto bail_out;
533       }
534 
535       object = json_object_get(connect_params, CON_PARAMS_THUMBPRINT_KEY);
536       if (object) {
537 
538           cnxParams.thumbPrint = strdup(json_string_value(object));
539           if (!cnxParams.thumbPrint) {
540               fprintf(stderr, "Failed to allocate memory for holding %s\n", CON_PARAMS_USERNAME_KEY);
541               goto bail_out;
542           }
543       }
544 
545       object = json_object_get(connect_params, CON_PARAMS_USERNAME_KEY);
546       if (!object) {
547          fprintf(stderr, "Failed to find %s in JSON definition of object %s\n", CON_PARAMS_USERNAME_KEY, key);
548          goto bail_out;
549       }
550       cnxParams.credType = VIXDISKLIB_CRED_UID;
551       cnxParams.creds.uid.userName = strdup(json_string_value(object));
552       if (!cnxParams.creds.uid.userName) {
553          fprintf(stderr, "Failed to allocate memory for holding %s\n", CON_PARAMS_USERNAME_KEY);
554          goto bail_out;
555       }
556 
557 
558       object = json_object_get(connect_params, CON_PARAMS_PASSWORD_KEY);
559       if (!object) {
560          fprintf(stderr, "Failed to find %s in JSON definition of object %s\n", CON_PARAMS_PASSWORD_KEY, key);
561          goto bail_out;
562       }
563       cnxParams.creds.uid.password = strdup(json_string_value(object));
564       if (!cnxParams.creds.uid.password) {
565          fprintf(stderr, "Failed to allocate memory for holding %s\n", CON_PARAMS_PASSWORD_KEY);
566          goto bail_out;
567       }
568       cnxParams.port = VSPHERE_DEFAULT_ADMIN_PORT;
569 
570       if (need_snapshot_moref) {
571          object = json_object_get(connect_params, CON_PARAMS_SNAPSHOT_MOREF_KEY);
572          if (!object) {
573             fprintf(stderr, "Failed to find %s in JSON definition of object %s\n", CON_PARAMS_SNAPSHOT_MOREF_KEY, key);
574             goto bail_out;
575          }
576          snapshot_moref = json_string_value(object);
577       }
578 
579       if (!local_vmdk) {
580          err = VixDiskLib_PrepareForAccess(&cnxParams, BAREOS_VADPDUMPER_IDENTITY);
581          if (VIX_FAILED(err)) {
582             char *error_txt;
583 
584             error_txt = VixDiskLib_GetErrorText(err, NULL);
585             fprintf(stderr, "Failed to Prepare For Access: %s [%d]\n", error_txt, err);
586             VixDiskLib_FreeErrorText(error_txt);
587          }
588       }
589    }
590 
591    err = VixDiskLib_ConnectEx(&cnxParams, (readonly) ? TRUE : FALSE, snapshot_moref, force_transport, &connection);
592    if (VIX_FAILED(err)) {
593       char *error_txt;
594 
595       error_txt = VixDiskLib_GetErrorText(err, NULL);
596       fprintf(stderr, "Failed to connect to %s : %s [%d]\n", cnxParams.serverName, error_txt, err);
597       VixDiskLib_FreeErrorText(error_txt);
598       goto bail_out;
599    }
600 
601    /*
602     * Register our exit handler.
603     */
604    atexit(cleanup);
605 
606    succeeded = 1;
607 
608 bail_out:
609    if (!succeeded) {
610       exit(1);
611    }
612 }
613 
614 /*
615  * Open a VMDK using VDDK.
616  */
do_vixdisklib_open(const char * key,const char * disk_name,json_t * disk_params,bool readonly,bool getdiskinfo,VixDiskLibHandle * diskHandle)617 static inline void do_vixdisklib_open(const char *key, const char *disk_name, json_t *disk_params,
618                                       bool readonly, bool getdiskinfo, VixDiskLibHandle *diskHandle)
619 {
620    int succeeded = 0;
621    VixError err;
622    const char *disk_path;
623    uint32_t flags;
624 
625    if (!disk_name) {
626       json_t *object;
627 
628       /*
629        * Start extracting the wanted information from the JSON passed in.
630        */
631       object = json_object_get(disk_params, DISK_PARAMS_DISK_PATH_KEY);
632       if (!object) {
633          fprintf(stderr, "Failed to find %s in JSON definition of object %s\n", DISK_PARAMS_DISK_PATH_KEY, key);
634          goto bail_out;
635       }
636 
637       disk_path = json_string_value(object);
638    } else {
639       disk_path = vmdk_disk_name;
640    }
641 
642    flags = 0;
643    if (readonly) {
644       flags |= VIXDISKLIB_FLAG_OPEN_READ_ONLY;
645    }
646 
647    err = VixDiskLib_Open(connection, disk_path, flags, diskHandle);
648    if (VIX_FAILED(err)) {
649       char *error_txt;
650 
651       error_txt = VixDiskLib_GetErrorText(err, NULL);
652       fprintf(stderr, "Failed to open %s : %s [%d]\n", disk_path, error_txt, err);
653       VixDiskLib_FreeErrorText(error_txt);
654       goto bail_out;
655    }
656 
657    if (getdiskinfo) {
658       /*
659        * See how big the logical disk is.
660        */
661       err = VixDiskLib_GetInfo(*diskHandle, &info);
662       if (VIX_FAILED(err)) {
663          char *error_txt;
664 
665          error_txt = VixDiskLib_GetErrorText(err, NULL);
666          fprintf(stderr, "Failed to get Logical Disk Info for %s, %s [%d]\n", disk_path, error_txt, err);
667          VixDiskLib_FreeErrorText(error_txt);
668          goto bail_out;
669       }
670 #ifdef VIXDISKLIBCREATEPARAMS_HAS_PHYSICALSECTORSIZE
671       if (verbose) {
672         fprintf(stderr, "DiskInfo logicalSectorSize: %u\n",
673                 info->logicalSectorSize);
674         fprintf(stderr, "DiskInfo physicalSectorSize: %u\n",
675                 info->physicalSectorSize);
676       }
677 #endif
678    }
679 
680    if (verbose) {
681       fprintf(stderr, "Selected transport method: %s\n", VixDiskLib_GetTransportMode(*diskHandle));
682    }
683 
684    succeeded = 1;
685 
686 bail_out:
687    if (!succeeded) {
688       exit(1);
689    }
690 }
691 
692 /*
693  * Create a VMDK using VDDK.
694  */
do_vixdisklib_create(const char * key,const char * disk_name,json_t * disk_params,uint64_t absolute_disk_length)695 static inline void do_vixdisklib_create(const char *key, const char *disk_name,
696                                         json_t *disk_params, uint64_t absolute_disk_length)
697 {
698    int succeeded = 0;
699    VixError err;
700    const char *disk_path;
701    VixDiskLibCreateParams createParams;
702 
703    if (!local_vmdk) {
704       fprintf(stderr, "Cannot create a remote disk via VADP\n");
705       goto bail_out;
706    }
707 
708    if (!disk_name) {
709       json_t *object;
710 
711       /*
712        * Start extracting the wanted information from the JSON passed in.
713        */
714       object = json_object_get(disk_params, DISK_PARAMS_DISK_PATH_KEY);
715       if (!object) {
716          fprintf(stderr, "Failed to find %s in JSON definition of object %s\n", DISK_PARAMS_DISK_PATH_KEY, key);
717          goto bail_out;
718       }
719 
720       disk_path = json_string_value(object);
721    } else {
722       disk_path = vmdk_disk_name;
723    }
724 
725    createParams.adapterType = VIXDISKLIB_ADAPTER_SCSI_BUSLOGIC;
726    createParams.capacity = (absolute_disk_length / VIXDISKLIB_SECTOR_SIZE);
727    if (disktype) {
728       createParams.diskType = lookup_disktype();
729    } else {
730       createParams.diskType = VIXDISKLIB_DISK_MONOLITHIC_SPARSE;
731    }
732 #ifdef VIXDISKLIBCREATEPARAMS_HAS_PHYSICALSECTORSIZE
733    createParams.physicalSectorSize = VIXDISKLIB_SECTOR_SIZE;
734    createParams.logicalSectorSize = VIXDISKLIB_SECTOR_SIZE;
735 #endif
736    createParams.hwVersion = 7; /* for ESX(i)4 */
737    err = VixDiskLib_Create(connection, disk_path, &createParams, NULL, NULL);
738    if (VIX_FAILED(err)) {
739       char *error_txt;
740 
741       error_txt = VixDiskLib_GetErrorText(err, NULL);
742       fprintf(stderr, "Failed to create Logical Disk for %s, %s [%d]\n", disk_path, error_txt, err);
743       VixDiskLib_FreeErrorText(error_txt);
744       goto bail_out;
745    }
746 
747    succeeded = 1;
748 
749 bail_out:
750    if (!succeeded) {
751       exit(1);
752    }
753 }
754 
755 /*
756  * Read data from a VMDK using the VDDP functions.
757  */
read_from_vmdk(size_t sector_offset,size_t nbyte,void * buf)758 static size_t read_from_vmdk(size_t sector_offset, size_t nbyte, void *buf)
759 {
760    VixError err;
761 
762    err = VixDiskLib_Read(read_diskHandle, sector_offset, nbyte / DEFAULT_SECTOR_SIZE, (uint8 *)buf);
763    if (VIX_FAILED(err)) {
764       char *error_txt;
765 
766       error_txt = VixDiskLib_GetErrorText(err, NULL);
767       fprintf(stderr, "VMDK Read error: %s [%d]\n", error_txt, err);
768       return -1;
769    }
770 
771    return nbyte;
772 }
773 
774 /*
775  * Write data to a VMDK using the VDDP functions.
776  */
write_to_vmdk(size_t sector_offset,size_t nbyte,void * buf)777 static size_t write_to_vmdk(size_t sector_offset, size_t nbyte, void *buf)
778 {
779    VixError err;
780 
781    err = VixDiskLib_Write(write_diskHandle, sector_offset, nbyte / DEFAULT_SECTOR_SIZE, (uint8 *)buf);
782    if (VIX_FAILED(err)) {
783       char *error_txt;
784 
785       error_txt = VixDiskLib_GetErrorText(err, NULL);
786       fprintf(stderr, "VMDK Write error: %s [%d]\n", error_txt, err);
787       return -1;
788    }
789 
790    return nbyte;
791 }
792 
793 /*
794  * Read data from a stream using the robust reader function.
795  */
read_from_stream(size_t sector_offset,size_t nbyte,void * buf)796 static size_t read_from_stream(size_t sector_offset, size_t nbyte, void *buf)
797 {
798    return robust_reader(STDOUT_FILENO, buf, nbyte);
799 }
800 
801 /*
802  * Write data from a stream using the robust reader function.
803  */
write_to_stream(size_t sector_offset,size_t nbyte,void * buf)804 static size_t write_to_stream(size_t sector_offset, size_t nbyte, void *buf)
805 {
806    /*
807     * Should we clone to rawdevice ?
808     */
809    if (raw_disk_fd != -1) {
810       robust_writer(raw_disk_fd, buf, nbyte);
811    }
812 
813    /*
814     * Should we clone to new VMDK file ?
815     */
816    if (write_diskHandle) {
817       VixError err;
818 
819       err = VixDiskLib_Write(write_diskHandle, sector_offset, nbyte / DEFAULT_SECTOR_SIZE, (uint8 *)buf);
820       if (VIX_FAILED(err)) {
821          char *error_txt;
822 
823          error_txt = VixDiskLib_GetErrorText(err, NULL);
824          fprintf(stderr, "VMDK Write error: %s [%d]\n", error_txt, err);
825       }
826    }
827 
828    return robust_writer(STDOUT_FILENO, buf, nbyte);
829 }
830 
831 /*
832  * Encode the disk info of the disk saved into the backup output stream.
833  */
save_disk_info(const char * key,json_t * cbt,uint64_t * absolute_disk_length)834 static inline bool save_disk_info(const char *key, json_t *cbt, uint64_t *absolute_disk_length)
835 {
836    bool retval = false;
837    struct runtime_disk_info_encoding rdie;
838    json_t *object;
839 
840    fill_runtime_disk_info_encoding(&rdie);
841 
842    object = json_object_get(cbt, CBT_DISK_SIZE);
843    if (!object) {
844       fprintf(stderr, "Failed to find %s in JSON definition of object %s\n", CBT_DISK_SIZE, key);
845       goto bail_out;
846    }
847 
848    rdie.absolute_disk_length = json_integer_value(object);
849 
850    object = json_object_get(cbt, CBT_START_OFFSET);
851    if (!object) {
852       fprintf(stderr, "Failed to find %s in JSON definition of object %s\n", CBT_START_OFFSET, key);
853       goto bail_out;
854    }
855 
856    rdie.absolute_start_offset = json_integer_value(object);
857 
858    /*
859     * Save the absolute offset we should use.
860     */
861    absolute_start_offset = rdie.absolute_start_offset;
862 
863    if (robust_writer(STDOUT_FILENO, (char *)&rdie, rdie_size) != rdie_size) {
864       fprintf(stderr, "Failed to write runtime_disk_info_encoding structure to output datastream\n");
865       goto bail_out;
866    }
867 
868    retval = true;
869    if (absolute_disk_length) {
870       *absolute_disk_length = rdie.absolute_disk_length;
871    }
872 
873 bail_out:
874    return retval;
875 }
876 
877 /*
878  * Decode the disk info of the disk restored from the backup input stream.
879  */
process_disk_info(bool validate_only,json_t * value)880 static inline bool process_disk_info(bool validate_only, json_t *value)
881 {
882    struct runtime_disk_info_encoding rdie;
883 
884    memset(&rdie, 0, rdie_size);
885    if (robust_reader(STDIN_FILENO, (char *)&rdie, rdie_size) != rdie_size) {
886       fprintf(stderr, "Failed to read a valid runtime_disk_info_encoding\n");
887       goto bail_out;
888    }
889 
890    if (rdie.start_magic != BAREOSMAGIC) {
891       fprintf(stderr, "[runtime_disk_info_encoding] Failed to find valid MAGIC start marker read %lld should have been %lld\n", rdie.start_magic, BAREOSMAGIC);
892       goto bail_out;
893    }
894 
895    if (rdie.end_magic != BAREOSMAGIC) {
896       fprintf(stderr, "[runtime_disk_info_encoding] Failed to find valid MAGIC end marker read %lld should have been %lld\n", rdie.end_magic, BAREOSMAGIC);
897       goto bail_out;
898    }
899 
900    if (verbose) {
901       dump_runtime_disk_info_encoding(&rdie);
902    }
903 
904    if (create_disk && !validate_only) {
905       do_vixdisklib_create(DISK_PARAMS_KEY, vmdk_disk_name, value, rdie.phys_capacity * VIXDISKLIB_SECTOR_SIZE);
906       do_vixdisklib_open(DISK_PARAMS_KEY, vmdk_disk_name, value, false, true, &write_diskHandle);
907 
908       if (!write_diskHandle) {
909          fprintf(stderr, "Cannot process restore data as no VixDiskLib disk handle opened\n");
910          goto bail_out;
911       }
912    }
913 
914    /*
915     * Validate that things make sense to restore on the opened VMDK.
916     */
917    if (!validate_only && check_size) {
918       if (!validate_runtime_disk_info_encoding(&rdie)) {
919          fprintf(stderr, "[runtime_disk_info_encoding] Invalid disk geometry for restoring to this volume\n");
920          goto bail_out;
921       }
922    }
923 
924    /*
925     * Save the absolute offset we should use.
926     */
927    absolute_start_offset = rdie.absolute_start_offset;
928 
929    return true;
930 
931 bail_out:
932    return false;
933 }
934 
935 /*
936  * Read a specific meta data key and encode it into the output stream.
937  */
read_meta_data_key(char * key)938 static inline bool read_meta_data_key(char *key)
939 {
940    bool retval = false;
941    VixError err;
942    size_t requiredLen;
943    char *buffer = NULL;
944    struct runtime_meta_data_encoding rmde;
945 
946    if (verbose) {
947       fprintf(stderr, "Processing metadata key %s\n", key);
948    }
949 
950    err = VixDiskLib_ReadMetadata(read_diskHandle, key, NULL, 0, &requiredLen);
951    if (err != VIX_OK && err != VIX_E_BUFFER_TOOSMALL) {
952       return false;
953    }
954 
955    buffer = (char *)malloc(requiredLen);
956    if (!buffer) {
957       fprintf(stderr, "Failed to allocate memory for holding metadata keys, exiting ...\n");
958       return false;
959    }
960 
961    err = VixDiskLib_ReadMetadata(read_diskHandle, key, buffer, requiredLen, NULL);
962    if (VIX_FAILED(err)) {
963       char *error_txt;
964 
965       error_txt = VixDiskLib_GetErrorText(err, NULL);
966       fprintf(stderr, "Failed to read metadata for key %s : %s [%d] exiting ...\n", key, error_txt, err);
967       goto bail_out;
968    }
969 
970    /*
971     * Should we clone metadata to new VMDK file ?
972     */
973    if (write_diskHandle) {
974       VixError err;
975 
976       err = VixDiskLib_WriteMetadata(write_diskHandle, key, buffer);
977       if (VIX_FAILED(err)) {
978          char *error_txt;
979 
980          error_txt = VixDiskLib_GetErrorText(err, NULL);
981          fprintf(stderr, "Failed to write metadata for key %s : %s [%d] exiting ...\n", key, error_txt, err);
982          goto bail_out;
983       }
984    }
985 
986    rmde.start_magic = BAREOSMAGIC;
987    rmde.end_magic = BAREOSMAGIC;
988    rmde.meta_key_length = strlen(key) + 1;
989    rmde.meta_data_length = requiredLen + 1;
990 
991    if (robust_writer(STDOUT_FILENO, &rmde, rmde_size) != rmde_size) {
992       fprintf(stderr, "Failed to write runtime_meta_data_encoding structure to output datastream\n");
993       goto bail_out;
994    }
995 
996    if (robust_writer(STDOUT_FILENO, key, rmde.meta_key_length) != rmde.meta_key_length) {
997       fprintf(stderr, "Failed to write meta data key to output datastream\n");
998       goto bail_out;
999    }
1000 
1001    if (robust_writer(STDOUT_FILENO, buffer, rmde.meta_data_length) != rmde.meta_data_length) {
1002       fprintf(stderr, "Failed to write meta data to output datastream\n");
1003       goto bail_out;
1004    }
1005 
1006    retval = true;
1007 
1008 bail_out:
1009    if (buffer) {
1010       free(buffer);
1011    }
1012 
1013    return retval;
1014 }
1015 
1016 /*
1017  * Read all meta data from a disk and encode it into the output stream.
1018  */
save_meta_data()1019 static inline bool save_meta_data()
1020 {
1021    bool retval = false;
1022    VixError err;
1023    size_t requiredLen;
1024    char *bp;
1025    char *buffer = NULL;
1026    struct runtime_meta_data_encoding rmde;
1027 
1028    /*
1029     * See if we are actually saving all meta data or should only write the META data end marker.
1030     */
1031    if (save_metadata) {
1032       err = VixDiskLib_GetMetadataKeys(read_diskHandle, NULL, 0, &requiredLen);
1033       if (err != VIX_OK && err != VIX_E_BUFFER_TOOSMALL) {
1034          return false;
1035       }
1036 
1037       buffer = (char *)malloc(requiredLen);
1038       if (!buffer) {
1039          fprintf(stderr, "Failed to allocate memory for holding metadata keys, exiting ...\n");
1040          return false;
1041       }
1042 
1043       err = VixDiskLib_GetMetadataKeys(read_diskHandle, buffer, requiredLen, NULL);
1044       if (VIX_FAILED(err)) {
1045          char *error_txt;
1046 
1047          error_txt = VixDiskLib_GetErrorText(err, NULL);
1048          fprintf(stderr, "Failed to read metadata keys : %s [%d] exiting ...\n", error_txt, err);
1049          goto bail_out;
1050       }
1051 
1052       bp = buffer;
1053       while (*bp) {
1054          if (!read_meta_data_key(bp)) {
1055             goto bail_out;
1056          }
1057 
1058          bp += strlen(bp) + 1;
1059       }
1060    }
1061 
1062    /*
1063     * Write an META data end marker.
1064     * e.g. metadata header with key and data length == 0
1065     */
1066    rmde.start_magic = BAREOSMAGIC;
1067    rmde.end_magic = BAREOSMAGIC;
1068    rmde.meta_key_length = 0;
1069    rmde.meta_data_length = 0;
1070 
1071    if (robust_writer(STDOUT_FILENO, &rmde, rmde_size) != rmde_size) {
1072       fprintf(stderr, "Failed to write runtime_meta_data_encoding structure to output datastream\n");
1073       goto bail_out;
1074    }
1075 
1076    retval = true;
1077 
1078 bail_out:
1079    if (buffer) {
1080       free(buffer);
1081    }
1082 
1083    return retval;
1084 }
1085 
1086 /*
1087  * Read a backup stream from STDIN and process its metadata.
1088  * Stop processing when we encounter the special end of metadata tag
1089  * which is when the meta_key_length and meta_data_length are zero.
1090  */
process_meta_data(bool validate_only)1091 static inline bool process_meta_data(bool validate_only)
1092 {
1093    struct runtime_meta_data_encoding rmde;
1094    char *key = NULL;
1095    char *buffer = NULL;
1096 
1097    while (1) {
1098       if (robust_reader(STDIN_FILENO, &rmde, rmde_size) != rmde_size) {
1099          fprintf(stderr, "Failed to read runtime_meta_data_encoding structure from input datastream\n");
1100          goto bail_out;
1101       }
1102 
1103       if (rmde.start_magic != BAREOSMAGIC) {
1104          fprintf(stderr, "[runtime_meta_data_encoding] Failed to find valid MAGIC start marker read %lld should have been %lld\n", rmde.start_magic, BAREOSMAGIC);
1105          goto bail_out;
1106       }
1107 
1108       if (rmde.end_magic != BAREOSMAGIC) {
1109          fprintf(stderr, "[runtime_meta_data_encoding] Failed to find valid MAGIC end marker read %lld should have been %lld\n", rmde.end_magic, BAREOSMAGIC);
1110          goto bail_out;
1111       }
1112 
1113       /*
1114        * See if we processed the last meta data item.
1115        */
1116       if (rmde.meta_key_length == 0 && rmde.meta_data_length == 0) {
1117          break;
1118       }
1119 
1120       key = (char *)malloc(rmde.meta_key_length);
1121       if (!key) {
1122          fprintf(stderr, "Failed to allocate %d bytes for meta data key\n", rmde.meta_key_length);
1123          goto bail_out;
1124       }
1125 
1126       if (robust_reader(STDIN_FILENO, key, rmde.meta_key_length) != rmde.meta_key_length) {
1127          fprintf(stderr, "Failed to read meta data key from input datastream\n");
1128          goto bail_out;
1129       }
1130 
1131       buffer = (char *)malloc(rmde.meta_data_length);
1132       if (!key) {
1133          fprintf(stderr, "Failed to allocate %d bytes for meta data\n", rmde.meta_data_length);
1134          goto bail_out;
1135       }
1136 
1137       if (robust_reader(STDIN_FILENO, buffer, rmde.meta_data_length) != rmde.meta_data_length) {
1138          fprintf(stderr, "Failed to read meta data from input datastream\n");
1139          goto bail_out;
1140       }
1141 
1142       if (verbose) {
1143          fprintf(stderr, "Meta data key %s, value %s\n", key, buffer);
1144       }
1145 
1146       if (!validate_only && restore_meta_data) {
1147          VixError err;
1148 
1149          err = VixDiskLib_WriteMetadata(write_diskHandle, key, buffer);
1150          if (VIX_FAILED(err)) {
1151             char *error_txt;
1152 
1153             error_txt = VixDiskLib_GetErrorText(err, NULL);
1154             fprintf(stderr, "Failed to write metadata for key %s : %s [%d] exiting ...\n", key, error_txt, err);
1155             goto bail_out;
1156          }
1157       }
1158 
1159       free(key);
1160       free(buffer);
1161    }
1162 
1163    return true;
1164 
1165 bail_out:
1166    return false;
1167 }
1168 
1169 /*
1170  * Process the Change Block Tracking information and write the wanted sectors
1171  * to the output stream. We self encode the data using a prefix header that describes
1172  * the data that is encoded after it including a MAGIC key and the actual CBT information
1173  * e.g. start of the read sectors and the number of sectors that follow.
1174  */
process_cbt(const char * key,json_t * cbt)1175 static inline bool process_cbt(const char *key, json_t *cbt)
1176 {
1177    bool retval = false;
1178    size_t index;
1179    json_t *object, *array_element, *start, *length;
1180    uint64_t start_offset, offset_length, current_offset, max_offset;
1181    uint64_t sector_offset, sectors_to_read;
1182    uint8 buf[DEFAULT_SECTOR_SIZE * SECTORS_PER_CALL];
1183    struct runtime_cbt_encoding rce;
1184 
1185    if (!read_diskHandle) {
1186       fprintf(stderr, "Cannot process CBT data as no VixDiskLib disk handle opened\n");
1187       goto bail_out;
1188    }
1189 
1190    object = json_object_get(cbt, CBT_CHANGEDAREA_KEY);
1191    if (!object) {
1192       fprintf(stderr, "Failed to find %s in JSON definition of object %s\n", CBT_CHANGEDAREA_KEY, key);
1193       goto bail_out;
1194    }
1195 
1196    /*
1197     * Iterate over each element of the JSON array and get the "start" and "length" member.
1198     */
1199    rce.start_magic = BAREOSMAGIC;
1200    rce.end_magic = BAREOSMAGIC;
1201    json_array_foreach(object, index, array_element) {
1202       /*
1203        * Get the two members we are interested in.
1204        */
1205       start = json_object_get(array_element, CBT_CHANGEDAREA_START_KEY);
1206       length = json_object_get(array_element, CBT_CHANGEDAREA_LENGTH_KEY);
1207 
1208       if (!start || !length) {
1209          continue;
1210       }
1211 
1212       start_offset = json_integer_value(start);
1213       offset_length = json_integer_value(length);
1214 
1215       if (verbose) {
1216          fprintf(stderr, "start = %lld\n", start_offset);
1217          fprintf(stderr, "length = %lld\n", offset_length);
1218          fprintf(stderr, "nr length = %lld\n", offset_length / DEFAULT_SECTOR_SIZE);
1219          fflush(stderr);
1220       }
1221 
1222       rce.start_offset = start_offset;
1223       rce.offset_length = offset_length;
1224 
1225       /*
1226        * Write the CBT info into the output stream.
1227        */
1228       if (robust_writer(STDOUT_FILENO, (char *)&rce, rce_size) != rce_size) {
1229          fprintf(stderr, "Failed to write runtime_cbt_encoding structure to output datastream\n");
1230          goto bail_out;
1231       }
1232 
1233       if (raw_disk_fd != -1) {
1234          lseek(raw_disk_fd, start_offset, SEEK_SET);
1235          if (verbose) {
1236             fprintf(stderr, "Log: RAWFILE: Adusting seek position in file\n");
1237          }
1238       }
1239 
1240       /*
1241        * Calculate the start offset and read as many sectors as defined by the
1242        * length element of the JSON structure.
1243        */
1244       current_offset = absolute_start_offset + start_offset;
1245       max_offset = current_offset + offset_length;
1246       sector_offset = current_offset / DEFAULT_SECTOR_SIZE;
1247       while (current_offset < max_offset) {
1248          /*
1249           * The number of sectors to read is the minimum of either the total number
1250           * of sectors still available in this CBT range or the upper setting specified
1251           * in the sectors_per_call variable.
1252           */
1253          sectors_to_read = MIN(sectors_per_call, (offset_length / DEFAULT_SECTOR_SIZE));
1254 
1255          if (multi_threaded) {
1256             if (!send_to_copy_thread(sector_offset, sectors_to_read * DEFAULT_SECTOR_SIZE)) {
1257                goto bail_out;
1258             }
1259          } else {
1260             if (read_from_vmdk(sector_offset, sectors_to_read * DEFAULT_SECTOR_SIZE,
1261                                buf) != sectors_to_read * DEFAULT_SECTOR_SIZE) {
1262                fprintf(stderr, "Read error on VMDK\n");
1263                goto bail_out;
1264             }
1265 
1266             if (write_to_stream(sector_offset, sectors_to_read * DEFAULT_SECTOR_SIZE,
1267                                 buf) != sectors_to_read * DEFAULT_SECTOR_SIZE) {
1268                fprintf(stderr, "Failed to write data to output datastream\n");
1269                goto bail_out;
1270             }
1271          }
1272 
1273          /*
1274           * Calculate the new offsets for a next run.
1275           */
1276          current_offset += (sectors_to_read * DEFAULT_SECTOR_SIZE);
1277          sector_offset += sectors_to_read;
1278          offset_length -= sectors_to_read * DEFAULT_SECTOR_SIZE;
1279       }
1280 
1281       if (multi_threaded) {
1282          flush_copy_thread();
1283       }
1284 
1285       if (verbose) {
1286          fflush(stderr);
1287       }
1288    }
1289 
1290    if (multi_threaded) {
1291       cleanup_copy_thread();
1292    }
1293 
1294    retval = true;
1295 
1296 bail_out:
1297    if (read_diskHandle) {
1298       VixDiskLib_Close(read_diskHandle);
1299       read_diskHandle = NULL;
1300    }
1301 
1302    return retval;
1303 }
1304 
1305 /*
1306  * Read a backup stream from STDIN and process it. When validate_only is set
1307  * to true we only try to process the data but do not actually write it back
1308  * to the VMDK.
1309  */
process_restore_stream(bool validate_only,json_t * value)1310 static inline bool process_restore_stream(bool validate_only, json_t *value)
1311 {
1312    bool retval = false;
1313    size_t cnt;
1314    uint64_t current_offset, max_offset;
1315    uint64_t sector_offset, sectors_to_read;
1316    uint8 buf[DEFAULT_SECTOR_SIZE * SECTORS_PER_CALL];
1317    struct runtime_cbt_encoding rce;
1318 
1319    if (!create_disk && !validate_only) {
1320       do_vixdisklib_open(DISK_PARAMS_KEY, vmdk_disk_name, value, false, true, &write_diskHandle);
1321 
1322       if (!write_diskHandle) {
1323          fprintf(stderr, "Cannot process restore data as no VixDiskLib disk handle opened\n");
1324          goto bail_out;
1325       }
1326    }
1327 
1328    /*
1329     * Setup multi threading if requested.
1330     */
1331    if (!validate_only && multi_threaded) {
1332       if (!setup_copy_thread(read_from_stream, write_to_vmdk)) {
1333          fprintf(stderr, "Failed to initialize multithreading\n");
1334          goto bail_out;
1335       }
1336    }
1337 
1338    /*
1339     * Process the disk info data.
1340     */
1341    if (!process_disk_info(validate_only, value)) {
1342       goto bail_out;
1343    }
1344 
1345    /*
1346     * Process the disk meta data,
1347     */
1348    if (!process_meta_data(validate_only)) {
1349       goto bail_out;
1350    }
1351 
1352    memset(&rce, 0, rce_size);
1353    while (robust_reader(STDIN_FILENO, (char *)&rce, rce_size) == rce_size) {
1354       if (rce.start_magic != BAREOSMAGIC) {
1355          fprintf(stderr, "[runtime_cbt_encoding] Failed to find valid MAGIC start marker read %lld should have been %lld\n", rce.start_magic, BAREOSMAGIC);
1356          goto bail_out;
1357       }
1358 
1359       if (rce.end_magic != BAREOSMAGIC) {
1360          fprintf(stderr, "[runtime_cbt_encoding] Failed to find valid MAGIC end marker read %lld should have been %lld\n", rce.end_magic, BAREOSMAGIC);
1361          goto bail_out;
1362       }
1363 
1364       if (verbose) {
1365          fprintf(stderr, "start = %lld\n", rce.start_offset);
1366          fprintf(stderr, "length = %lld\n", rce.offset_length);
1367          fprintf(stderr, "nr length = %lld\n", rce.offset_length / DEFAULT_SECTOR_SIZE);
1368          fflush(stderr);
1369       }
1370 
1371       current_offset = absolute_start_offset + rce.start_offset;
1372       max_offset = current_offset + rce.offset_length;
1373       sector_offset = current_offset / DEFAULT_SECTOR_SIZE;
1374       while (current_offset < max_offset) {
1375          /*
1376           * The number of sectors to read is the minimum of either the total number
1377           * of sectors still available in this CBT range or the upper setting specified
1378           * in the sectors_per_call variable.
1379           */
1380          sectors_to_read = MIN(sectors_per_call, (rce.offset_length / DEFAULT_SECTOR_SIZE));
1381 
1382          if (!validate_only && multi_threaded) {
1383             if (!send_to_copy_thread(sector_offset, sectors_to_read * DEFAULT_SECTOR_SIZE)) {
1384                goto bail_out;
1385             }
1386          } else {
1387             cnt = robust_reader(STDIN_FILENO, (char *)buf, sectors_to_read * DEFAULT_SECTOR_SIZE);
1388             if (cnt != sectors_to_read * DEFAULT_SECTOR_SIZE) {
1389                goto bail_out;
1390             }
1391 
1392             if (!validate_only) {
1393                if (write_to_vmdk(sector_offset, cnt, buf) != cnt) {
1394                   goto bail_out;
1395                }
1396             }
1397          }
1398 
1399          /*
1400           * Calculate the new offsets for a next run.
1401           */
1402          current_offset += (sectors_to_read * DEFAULT_SECTOR_SIZE);
1403          sector_offset += sectors_to_read;
1404          rce.offset_length -= sectors_to_read * DEFAULT_SECTOR_SIZE;
1405       }
1406 
1407       if (multi_threaded) {
1408          flush_copy_thread();
1409       }
1410 
1411       memset(&rce, 0, rce_size);
1412    }
1413 
1414    if (multi_threaded) {
1415       cleanup_copy_thread();
1416    }
1417 
1418    retval = true;
1419 
1420 bail_out:
1421    if (write_diskHandle) {
1422       VixDiskLib_Close(write_diskHandle);
1423       write_diskHandle = NULL;
1424    }
1425 
1426    return retval;
1427 }
1428 
1429 /*
1430  * All work to this program is passed in using a JSON work file which has the needed
1431  * information to perform the wanted operation. This function loads the JSON data into
1432  * memory using the Jansson JSON library.
1433  */
process_json_work_file(const char * json_work_file)1434 static inline void process_json_work_file(const char *json_work_file)
1435 {
1436    json_error_t error;
1437 
1438    /*
1439     * Open the JSON work file.
1440     */
1441    json_config = json_load_file(json_work_file, JSON_DECODE_ANY, &error);
1442    if (!json_config) {
1443       fprintf(stderr, "Failed to parse JSON config %s [%s at line %d column %d]\n",
1444               json_work_file, error.text, error.line, error.column);
1445       exit(1);
1446    }
1447 
1448    if (verbose) {
1449       /*
1450        * Dump the internal parsed data in pretty print format.
1451        */
1452       json_dumpf(json_config, stderr, JSON_PRESERVE_ORDER | JSON_COMPACT | JSON_INDENT(3));
1453       fprintf(stderr, "\n");
1454       fflush(stderr);
1455    }
1456 }
1457 
1458 /*
1459  * Worker function that performs the dump operation of the program.
1460  */
dump_vmdk_stream(const char * json_work_file)1461 static inline bool dump_vmdk_stream(const char *json_work_file)
1462 {
1463    json_t *value;
1464    uint64_t absolute_disk_length = 0;
1465 
1466    process_json_work_file(json_work_file);
1467 
1468    value = json_object_get(json_config, CON_PARAMS_KEY);
1469    if (!value) {
1470       fprintf(stderr, "Failed to find %s in JSON definition\n", CON_PARAMS_KEY);
1471       exit(1);
1472    }
1473 
1474    do_vixdisklib_connect(CON_PARAMS_KEY, value, true, true);
1475 
1476    if (cleanup_on_start) {
1477       cleanup_vixdisklib();
1478    }
1479 
1480    value = json_object_get(json_config, DISK_PARAMS_KEY);
1481    if (!value) {
1482       fprintf(stderr, "Failed to find %s in JSON definition\n", DISK_PARAMS_KEY);
1483       exit(1);
1484    }
1485 
1486    do_vixdisklib_open(DISK_PARAMS_KEY, NULL, value, true, true, &read_diskHandle);
1487 
1488    value = json_object_get(json_config, CBT_DISKCHANGEINFO_KEY);
1489    if (!value) {
1490       fprintf(stderr, "Failed to find %s in JSON definition\n", CBT_DISKCHANGEINFO_KEY);
1491       exit(1);
1492    }
1493 
1494    /*
1495     * Setup multi threading if requested.
1496     */
1497    if (multi_threaded) {
1498       if (!setup_copy_thread(read_from_vmdk, write_to_stream)) {
1499          fprintf(stderr, "Failed to initialize multithreading\n");
1500          exit(1);
1501       }
1502    }
1503 
1504    if (!save_disk_info(CBT_DISKCHANGEINFO_KEY, value, &absolute_disk_length)) {
1505       exit(1);
1506    }
1507 
1508    /*
1509     * See if we are requested to clone the content to a new VMDK.
1510     * save_disk_info() initializes absolute_disk_length.
1511     */
1512    if (vmdk_disk_name) {
1513       if (create_disk) {
1514          do_vixdisklib_create(NULL, vmdk_disk_name, value, absolute_disk_length);
1515       }
1516 
1517       do_vixdisklib_open(NULL, vmdk_disk_name, value, false, false, &write_diskHandle);
1518    }
1519 
1520    if (!save_meta_data()) {
1521       exit(1);
1522    }
1523 
1524    /*
1525     * See if we are requested to clone the content to a rawdevice.
1526     */
1527    if (raw_disk_name) {
1528       if (verbose) {
1529          fprintf(stderr, "Log: RAWFILE: Trying to open RAW file\n");
1530       }
1531 
1532       if ((raw_disk_fd = open(raw_disk_name, O_WRONLY | O_TRUNC)) == -1) {
1533          fprintf(stderr, "Error: Failed to open the RAW DISK FILE\n");
1534          exit(1);
1535       }
1536    }
1537 
1538    return process_cbt(CBT_DISKCHANGEINFO_KEY, value);
1539 }
1540 
1541 /*
1542  * Worker function that performs the restore operation of the program.
1543  */
restore_vmdk_stream(const char * json_work_file)1544 static inline bool restore_vmdk_stream(const char *json_work_file)
1545 {
1546    json_t *value;
1547 
1548    process_json_work_file(json_work_file);
1549 
1550    value = json_object_get(json_config, CON_PARAMS_KEY);
1551    if (!value) {
1552       fprintf(stderr, "Failed to find %s in JSON definition\n", CON_PARAMS_KEY);
1553       exit(1);
1554    }
1555 
1556    if (cleanup_on_start) {
1557       cleanup_vixdisklib();
1558    }
1559 
1560    do_vixdisklib_connect(CON_PARAMS_KEY, value, false, false);
1561 
1562    value = json_object_get(json_config, DISK_PARAMS_KEY);
1563    if (!value) {
1564       fprintf(stderr, "Failed to find %s in JSON definition\n", DISK_PARAMS_KEY);
1565       exit(1);
1566    }
1567 
1568    return process_restore_stream(false, value);
1569 }
1570 
1571 /*
1572  * Worker function that performs the show operation of the program.
1573  */
show_backup_stream()1574 static inline int show_backup_stream()
1575 {
1576    return process_restore_stream(true, NULL);
1577 }
1578 
signal_handler(int sig)1579 static void signal_handler(int sig)
1580 {
1581    exit_code = sig;
1582    exit(sig);
1583 }
1584 
usage(const char * program_name)1585 void usage(const char *program_name)
1586 {
1587    fprintf(stderr, "Usage: %s [-d <vmdk_diskname>] [-f force_transport] [-s sectors_per_call] [-t disktype] [-CcDlMmRSv] dump <workfile> | restore <workfile> | show\n", program_name);
1588    fprintf(stderr, "Where:\n");
1589    fprintf(stderr, "   -C - Create local VMDK\n");
1590    fprintf(stderr, "   -c - Don't check size of VMDK\n");
1591    fprintf(stderr, "   -D - Cleanup on Disconnect\n");
1592    fprintf(stderr, "   -d - Specify local VMDK name\n");
1593    fprintf(stderr, "   -f - Specify forced transport method\n");
1594    fprintf(stderr, "   -h - This help text\n");
1595    fprintf(stderr, "   -l - Write to a local VMDK\n");
1596    fprintf(stderr, "   -M - Save metadata of VMDK on dump action\n");
1597    fprintf(stderr, "   -m - Use multithreading\n");
1598    fprintf(stderr, "   -r - RAW Image disk name\n");
1599    fprintf(stderr, "   -R - Restore metadata of VMDK on restore action\n");
1600    fprintf(stderr, "   -S - Cleanup on Start\n");
1601    fprintf(stderr, "   -s - Sectors to read per call to VDDK\n");
1602    fprintf(stderr, "   -t - Disktype to create for local VMDK\n");
1603    fprintf(stderr, "   -v - Verbose output\n");
1604    fprintf(stderr, "   -? - This help text\n");
1605    exit(1);
1606 }
1607 
main(int argc,char ** argv)1608 int main(int argc, char **argv)
1609 {
1610    bool retval;
1611    const char *program_name;
1612    int ch;
1613 
1614    program_name = argv[0];
1615    while ((ch = getopt(argc, argv, "CcDd:r:f:hlMmRSs:t:v?")) != -1) {
1616       switch (ch) {
1617       case 'C':
1618          create_disk = true;
1619          /*
1620           * If we create the disk we should not check for the size as that won't match.
1621           */
1622          check_size = false;
1623          break;
1624       case 'c':
1625          check_size = false;
1626          break;
1627       case 'D':
1628          cleanup_on_disconnect = true;
1629          break;
1630       case 'd':
1631          vmdk_disk_name = strdup(optarg);
1632          if (!vmdk_disk_name) {
1633             fprintf(stderr, "Failed to allocate memory to hold diskname, exiting ...\n");
1634             exit(1);
1635          }
1636          break;
1637      case 'r':
1638          raw_disk_name = strdup(optarg);
1639          if (!raw_disk_name) {
1640             fprintf(stderr, "Failed to allocate memory to hold rawdiskname, exiting ...\n");
1641             exit(1);
1642          }
1643          break;
1644       case 'f':
1645          force_transport = strdup(optarg);
1646          if (!force_transport) {
1647             fprintf(stderr, "Failed to allocate memory to hold forced transport, exiting ...\n");
1648             exit(1);
1649          }
1650          break;
1651       case 'l':
1652          local_vmdk = true;
1653          break;
1654       case 'M':
1655          save_metadata = true;
1656          break;
1657       case 'm':
1658          multi_threaded = true;
1659          break;
1660       case 'R':
1661          restore_meta_data = true;
1662          break;
1663       case 'S':
1664          cleanup_on_start = true;
1665          break;
1666       case 's':
1667          sectors_per_call = atoi(optarg);
1668          break;
1669       case 't':
1670          disktype = strdup(optarg);
1671          if (!disktype) {
1672             fprintf(stderr, "Failed to allocate memory to hold disktype, exiting ...\n");
1673             exit(1);
1674          }
1675          break;
1676       case 'v':
1677          verbose = true;
1678          break;
1679       case 'h':
1680       case '?':
1681       default:
1682          usage(program_name);
1683          break;
1684       }
1685    }
1686 
1687    /*
1688     * Install signal handlers for the most important signals.
1689     */
1690    signal(SIGHUP, signal_handler);
1691    signal(SIGINT, signal_handler);
1692    signal(SIGTERM, signal_handler);
1693 
1694    argc -= optind;
1695    argv += optind;
1696 
1697    if (argc <= 0) {
1698       usage(program_name);
1699    }
1700 
1701    if (strcasecmp(argv[0], "dump") == 0) {
1702       if (argc <= 1) {
1703          usage(program_name);
1704       }
1705 
1706       retval = dump_vmdk_stream(argv[1]);
1707    } else if (strcasecmp(argv[0], "restore") == 0) {
1708       if (argc <= 1) {
1709          usage(program_name);
1710       }
1711 
1712       retval = restore_vmdk_stream(argv[1]);
1713    } else if (strcasecmp(argv[0], "show") == 0) {
1714       retval = show_backup_stream();
1715    } else {
1716       fprintf(stderr, "Unknown action %s\n", argv[1]);
1717    }
1718 
1719    if (retval) {
1720       exit_code = 0;
1721    }
1722 
1723    exit(exit_code);
1724 }
1725