1 /*
2  * Copyright (C) 2020-2021 Bareos GmbH & Co. KG
3  * Copyright (C) 2010 SCALITY SA. All rights reserved.
4  * http://www.scality.com
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  * Redistributions of source code must retain the above copyright notice,
11  * this list of conditions and the following disclaimer.
12  *
13  * Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in the
15  * documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY SCALITY SA ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL SCALITY SA OR CONTRIBUTORS BE LIABLE FOR
21  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  * The views and conclusions contained in the software and documentation
30  * are those of the authors and should not be interpreted as representing
31  * official policies, either expressed or implied, of SCALITY SA.
32  *
33  * https://github.com/scality/Droplet
34  */
35 #include "dropletp.h"
36 
37 /** @file */
38 
39 int dpl_header_size;
40 
dpl_status_str(dpl_status_t status)41 const char* dpl_status_str(dpl_status_t status)
42 {
43   switch (status) {
44     case DPL_SUCCESS:
45       return "DPL_SUCCESS";
46     case DPL_FAILURE:
47       return "DPL_FAILURE";
48     case DPL_ENOENT:
49       return "DPL_ENOENT";
50     case DPL_EINVAL:
51       return "DPL_EINVAL";
52     case DPL_ETIMEOUT:
53       return "DPL_ETIMEOUT";
54     case DPL_ENOMEM:
55       return "DPL_ENOMEM";
56     case DPL_ESYS:
57       return "DPL_ESYS";
58     case DPL_EIO:
59       return "DPL_EIO";
60     case DPL_ELIMIT:
61       return "DPL_ELIMIT";
62     case DPL_ENAMETOOLONG:
63       return "DPL_ENAMETOOLONG";
64     case DPL_ENOTDIR:
65       return "DPL_ENOTDIR";
66     case DPL_ENOTEMPTY:
67       return "DPL_ENOTEMPTY";
68     case DPL_EISDIR:
69       return "DPL_EISDIR";
70     case DPL_EEXIST:
71       return "DPL_EEXIST";
72     case DPL_ENOTSUPP:
73       return "DPL_ENOTSUPP";
74     case DPL_EREDIRECT:
75       return "DPL_EREDIRECT";
76     case DPL_ETOOMANYREDIR:
77       return "DPL_ETOOMANYREDIR";
78     case DPL_ECONNECT:
79       return "DPL_ECONNECT";
80     case DPL_EPERM:
81       return "DPL_EPERM";
82     case DPL_EPRECOND:
83       return "DPL_EPRECOND";
84     case DPL_ECONFLICT:
85       return "DPL_ECONFLICT";
86     case DPL_ERANGEUNAVAIL:
87       return "DPL_ERANGEUNAVAIL";
88   }
89 
90   return "Unknown error";
91 }
92 
93 /**
94  * @defgroup init Initialization
95  * @addtogroup init
96  * @{
97  * Initializing the Droplet library.
98  *
99  * These functions are used to initialize global data used by Droplet
100  * library.
101  */
102 
103 /**
104  * Initialize Droplet global data.
105  *
106  * Initializes global data used by the library.  Must be called once
107  * and only once before any other Droplet library functions.  The next
108  * step after calling `dpl_init()` is probably `dpl_ctx_new()`.
109  *
110  * @retval DPL_SUCCESS this function cannot currently fail
111  */
dpl_init()112 dpl_status_t dpl_init()
113 {
114   SSL_library_init();
115   SSL_load_error_strings();
116   ERR_load_crypto_strings();
117 
118   int ret = RAND_status();
119   if (0 == ret) {
120     DPL_LOG(NULL, DPL_WARNING, "PRNG not properly seeded, seeding it...");
121     RAND_poll();
122     ret = RAND_status();
123     DPL_LOG(NULL, DPL_INFO, "PRNG state after seeding: %d", ret);
124   } else if (1 == ret) {
125     DPL_LOG(NULL, DPL_INFO, "PRNG has been seeded with enough data");
126   }
127 
128   dpl_base64_init();
129 
130   return DPL_SUCCESS;
131 }
132 
133 /**
134  * Free Droplet global data.
135  *
136  * Must be called once and only once after all Droplet library calls
137  * have stopped.
138  */
dpl_free()139 void dpl_free()
140 {
141   ERR_clear_error();
142 #if (OPENSSL_VERSION_NUMBER < 0x10000000L)
143   ERR_remove_state(0);
144 #elif (OPENSSL_VERSION_NUMBER < 0x10100000L)
145   ERR_remove_thread_state(NULL);
146 #endif
147 
148   ERR_free_strings();
149   EVP_cleanup();
150   sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
151   CRYPTO_cleanup_all_ex_data();
152 }
153 
154 /** @} */
155 
156 /**
157  * @defgroup ctx Contexts
158  * @addtogroup ctx
159  * @{
160  * State for communicating with a cloud service
161  *
162  * The `dpl_ctx_t` object, or "context", holds all the state and options
163  * needed to communicate with a cloud service instance.  All other
164  * functions that perform operations on a cloud service require a
165  * context.  Thus, creating a context is one of the very first things which
166  * a program will do when using Droplet, just after calling `dpl_init()`.
167  *
168  * Most programs will only ever create a single context object and will
169  * keep it for the lifetime of the program, only freeing it just before
170  * calling `dpl_free()` and exiting.  This is not the only possible approach
171  * but it makes the most sense because
172  *
173  * @arg The context holds some static configuration data which is read
174  *      from a profile file when the context is created.  Reading this
175  *      data is expensive, and it does not change.
176  * @arg The context caches connections to cloud service hosts, which
177  *      allows re-use of connections between requests and thus amortises
178  *      connection setup latency (such as DNS and TCP roundtrip latency)
179  *      over multiple requests.
180  * @arg Most programs only need to communicate with a single cloud
181  *      storage service instance, so there's no need to create a
182  *      second context.
183  *
184  * An important parameter of the context is the droplet directory, which
185  * is established when the context is created.  Several important files
186  * are located relative to this directory, notably the profile file.
187  * See the description of `dpl_ctx_new()` for details.
188  *
189  * The context object is thread-safe and contains an internal mutex
190  * which is used to safely share access between multiple threads.  This
191  * mutex is not held while requests are being made to the back end.  If
192  * you need to make requests to a cloud service from multiple threads in
193  * the same process, just create a single context object and share it
194  * between all threads.
195  */
196 
dpl_ctx_lock(dpl_ctx_t * ctx)197 void dpl_ctx_lock(dpl_ctx_t* ctx)
198 {
199   pthread_mutex_lock(&ctx->lock);
200   assert(0 == ctx->canary);
201   ctx->canary++;
202 }
203 
dpl_ctx_unlock(dpl_ctx_t * ctx)204 void dpl_ctx_unlock(dpl_ctx_t* ctx)
205 {
206   ctx->canary--;
207   assert(0 == ctx->canary);
208   pthread_mutex_unlock(&ctx->lock);
209 }
210 
dpl_ctx_alloc(void)211 static dpl_ctx_t* dpl_ctx_alloc(void)
212 {
213   dpl_ctx_t* ctx;
214 
215   ctx = malloc(sizeof(*ctx));
216   if (NULL == ctx) return NULL;
217 
218   memset(ctx, 0, sizeof(*ctx));
219 
220   pthread_mutex_init(&ctx->lock, NULL);
221 
222   return ctx;
223 }
224 
dpl_ctx_post_load(dpl_ctx_t * ctx)225 static void dpl_ctx_post_load(dpl_ctx_t* ctx)
226 {
227   char* str;
228 
229   if ((str = getenv("DPL_TRACE_LEVEL")))
230     ctx->trace_level = strtoul(str, NULL, 0);
231 
232   if ((str = getenv("DPL_TRACE_BUFFERS"))) ctx->trace_buffers = atoi(str);
233 
234   if ((str = getenv("DPL_TRACE_BINARY"))) ctx->trace_binary = atoi(str);
235 
236   dpl_header_size = ctx->header_size;
237 }
238 
239 /**
240  * Create a context.
241  *
242  * Creates and returns a new `dpl_ctx_t` object, or NULL on error.
243  *
244  * Possible errors include memory allocation failure, failure to find
245  * the profile, and failure to parse the profile.  @note If this function
246  * fails there is no way for the caller to discover what went wrong.
247  *
248  * The droplet directory is used as the base directory for finding several
249  * important files, notably the profile file.  It is the first non-NULL pathname
250  * of:
251  * @arg the parameter @a droplet_dir, or
252  * @arg the environmental variable `DPLDIR`, or
253  * @arg `"~/.droplet/"`.
254  *
255  * A profile file is read to set up options for the context.  The profile
256  * file is named `<name>.profile` where `<name>` is the profile's name,
257  * and is located in the droplet directory.  The profile's name is the
258  * first non-NULL name of:
259  * @arg the @a profile_name parameter, or
260  * @arg the environment variable `DPLPROFILE`, or
261  * @arg `"default"`.
262  *
263  * Thus, if both parameters are passed `NULL` the profile will be read
264  * from the file `~/.droplet/default.profile`.
265  *
266  * See @ref profile "Profile File" for details of the profile file format.
267  *
268  * @param droplet_dir pathname of directory containing Droplet state, or `NULL`
269  * @param profile_name name of the profile to use, or `NULL`
270  * @return a new context, or NULL on error
271  */
dpl_ctx_new(const char * droplet_dir,const char * profile_name)272 dpl_ctx_t* dpl_ctx_new(const char* droplet_dir, const char* profile_name)
273 {
274   dpl_ctx_t* ctx;
275   int ret;
276 
277   ctx = dpl_ctx_alloc();
278   if (NULL == ctx) {
279     DPL_LOG(NULL, DPL_ERROR, "No memory for droplet context creation.");
280     return NULL;
281   }
282 
283   ret = dpl_profile_load(ctx, droplet_dir, profile_name);
284   if (DPL_SUCCESS != ret) {
285     dpl_ctx_free(ctx);
286     return NULL;
287   }
288 
289   dpl_ctx_post_load(ctx);
290 
291   return ctx;
292 }
293 
294 /**
295  * Creates a new context with the profile specified in a dict.
296  *
297  * Creates a new profile and sets up the the profile information from a
298  * dict containing profile variable settings as if they had been read in
299  * from a profile file, without reading a file.  This function is provided
300  * for applications which need to use the Droplet library without a
301  * profile file.
302  *
303  * Note that to create a context without needing a droplet directory at
304  * all, applications should set the profile variable @c pricing_dir to
305  * an empty string.
306  *
307  * See @ref profile "Profile File" for details of the profile variables.
308  * The droplet directory is set by a special variable in the dict called
309  * @c droplet_dir, and the profile name by a special variable called @c
310  * profile_name.
311  *
312  * @param profile a dict containing profile variables
313  * @return a new context or NULL on error
314  */
dpl_ctx_new_from_dict(const dpl_dict_t * profile)315 dpl_ctx_t* dpl_ctx_new_from_dict(const dpl_dict_t* profile)
316 {
317   dpl_ctx_t* ctx;
318   int ret;
319 
320   ctx = dpl_ctx_alloc();
321   if (NULL == ctx) return NULL;
322 
323   ret = dpl_profile_set_from_dict(ctx, profile);
324   if (DPL_SUCCESS != ret) {
325     dpl_ctx_free(ctx);
326     return NULL;
327   }
328 
329   dpl_ctx_post_load(ctx);
330 
331   return ctx;
332 }
333 
334 /**
335  * Free a context.
336  *
337  * Free a context created earlier by `dpl_ctx_new()`.  All resources
338  * associated with the context (e.g. profile data and cached connections)
339  * are freed.
340  *
341  * @note If you use the connection API to create your own connections,
342  * you @b must release all your connections created from this context, by
343  * calling either `dpl_conn_release()` or `dpl_conn_terminate()`, @b before
344  * calling `dpl_ctx_free()`.
345  */
346 
dpl_ctx_free(dpl_ctx_t * ctx)347 void dpl_ctx_free(dpl_ctx_t* ctx)
348 {
349   dpl_profile_free(ctx);
350   pthread_mutex_destroy(&ctx->lock);
351   free(ctx);
352 }
353 
354 /** @} */
355 
356 /*
357  * eval
358  */
359 
dpl_price_storage(dpl_ctx_t * ctx,size_t size)360 double dpl_price_storage(dpl_ctx_t* ctx, size_t size)
361 {
362   int i;
363   struct dpl_data_pricing* datp = NULL;
364 
365   for (i = 0; i < ctx->data_pricing[DPL_DATA_TYPE_STORAGE]->n_items; i++) {
366     datp = (struct dpl_data_pricing*)dpl_vec_get(
367         ctx->data_pricing[DPL_DATA_TYPE_STORAGE], i);
368 
369     // dpl_data_pricing_print(datp);
370 
371     if (size < datp->limit) break;
372   }
373 
374   if (NULL == datp) return .0;
375 
376   return ((double)size / (double)datp->quantity) * datp->price;
377 }
378 
dpl_price_storage_str(dpl_ctx_t * ctx,size_t size)379 char* dpl_price_storage_str(dpl_ctx_t* ctx, size_t size)
380 {
381   static char str[256];
382 
383   snprintf(str, sizeof(str), "$%.03f", dpl_price_storage(ctx, size));
384 
385   return str;
386 }
387 
388 /**
389  * convenience function
390  *
391  * @note not thread safe since it returns a static buffer
392  *
393  * @param size
394  *
395  * @return
396  */
dpl_size_str(uint64_t size)397 char* dpl_size_str(uint64_t size)
398 {
399   static char str[256];
400   char* unit;
401   unsigned long long divisor;
402   double size_dbl;
403 
404   divisor = 1;
405 
406   if (size < 1000)
407     unit = "";
408   else if (size < (1000 * 1000)) {
409     divisor = 1000;
410     unit = "KB";
411   } else if (size < (1000 * 1000 * 1000)) {
412     divisor = 1000 * 1000;
413     unit = "MB";
414   } else if (size < (1000LL * 1000LL * 1000LL * 1000LL)) {
415     divisor = 1000LL * 1000LL * 1000LL;
416     unit = "GB";
417   } else {
418     divisor = 1000LL * 1000LL * 1000LL * 1000LL;
419     unit = "PB";
420   }
421 
422   size_dbl = (double)size / (double)divisor;
423 
424   snprintf(str, sizeof(str), "%.02f%s", size_dbl, unit);
425 
426   return str;
427 }
428 
429 /*
430  *
431  */
432 
433 extern dpl_backend_t dpl_backend_s3;
434 extern dpl_backend_t dpl_backend_cdmi;
435 extern dpl_backend_t dpl_backend_swift;
436 extern dpl_backend_t dpl_backend_srws;
437 extern dpl_backend_t dpl_backend_sproxyd;
438 extern dpl_backend_t dpl_backend_posix;
439 
dpl_backend_set(dpl_ctx_t * ctx,const char * name)440 int dpl_backend_set(dpl_ctx_t* ctx, const char* name)
441 {
442   int ret = 0;
443 
444   if (!strcmp(name, "s3"))
445     ctx->backend = &dpl_backend_s3;
446   else if (!strcmp(name, "cdmi")) {
447     ctx->preserve_root_path = 1;
448     ctx->backend = &dpl_backend_cdmi;
449   } else if (!strcmp(name, "swift"))
450     ctx->backend = &dpl_backend_swift;
451   else if (!strcmp(name, "srws"))
452     ctx->backend = &dpl_backend_srws;
453   else if (!strcmp(name, "sproxyd"))
454     ctx->backend = &dpl_backend_sproxyd;
455   else if (!strcmp(name, "posix"))
456     ctx->backend = &dpl_backend_posix;
457   else
458     ret = -1;
459 
460   return ret;
461 }
462 
dpl_print_capabilities(dpl_ctx_t * ctx)463 dpl_status_t dpl_print_capabilities(dpl_ctx_t* ctx)
464 {
465   dpl_status_t ret, ret2;
466   dpl_capability_t mask;
467 
468   if (NULL == ctx->backend->get_capabilities) {
469     ret = DPL_ENOTSUPP;
470     goto end;
471   }
472 
473   ret2 = ctx->backend->get_capabilities(ctx, &mask);
474   if (DPL_SUCCESS != ret2) {
475     ret = ret2;
476     goto end;
477   }
478 
479   printf("buckets:\t\t%d\n", mask & DPL_CAP_BUCKETS ? 1 : 0);
480   printf("fnames:\t\t\t%d\n", mask & DPL_CAP_FNAMES ? 1 : 0);
481   printf("ids:\t\t\t%d\n", mask & DPL_CAP_IDS ? 1 : 0);
482   printf("lazy:\t\t\t%d\n", mask & DPL_CAP_LAZY ? 1 : 0);
483   printf("http_compat:\t\t%d\n", mask & DPL_CAP_HTTP_COMPAT ? 1 : 0);
484   printf("raw:\t\t\t%d\n", mask & DPL_CAP_RAW ? 1 : 0);
485   printf("append_metadata:\t%d\n", mask & DPL_CAP_APPEND_METADATA ? 1 : 0);
486   printf("consistency:\t\t%d\n", mask & DPL_CAP_CONSISTENCY ? 1 : 0);
487   printf("versioning:\t\t%d\n", mask & DPL_CAP_VERSIONING ? 1 : 0);
488   printf("conditions:\t\t%d\n", mask & DPL_CAP_CONDITIONS ? 1 : 0);
489   printf("put_range:\t\t%d\n", mask & DPL_CAP_PUT_RANGE ? 1 : 0);
490 
491   ret = DPL_SUCCESS;
492 
493 end:
494 
495   return ret;
496 }
497 
498 /* other */
499 
dpl_bucket_free(dpl_bucket_t * bucket)500 void dpl_bucket_free(dpl_bucket_t* bucket)
501 {
502   free(bucket->name);
503   free(bucket);
504 }
505 
dpl_vec_buckets_free(dpl_vec_t * vec)506 void dpl_vec_buckets_free(dpl_vec_t* vec)
507 {
508   int i;
509 
510   for (i = 0; i < vec->n_items; i++)
511     dpl_bucket_free((dpl_bucket_t*)dpl_vec_get(vec, i));
512   dpl_vec_free(vec);
513 }
514 
dpl_object_free(dpl_object_t * object)515 void dpl_object_free(dpl_object_t* object)
516 {
517   if (NULL != object->path) free(object->path);
518 
519   free(object);
520 }
521 
dpl_vec_objects_free(dpl_vec_t * vec)522 void dpl_vec_objects_free(dpl_vec_t* vec)
523 {
524   int i;
525 
526   for (i = 0; i < vec->n_items; i++)
527     dpl_object_free((dpl_object_t*)dpl_vec_get(vec, i));
528   dpl_vec_free(vec);
529 }
530 
dpl_delete_object_free(dpl_delete_object_t * object)531 void dpl_delete_object_free(dpl_delete_object_t* object)
532 {
533   if (object->name != NULL) free(object->name);
534 
535   if (object->version_id != NULL) free(object->version_id);
536 
537   if (object->error != NULL) free(object->error);
538 
539   free(object);
540 }
541 
dpl_vec_delete_objects_free(dpl_vec_t * vec)542 void dpl_vec_delete_objects_free(dpl_vec_t* vec)
543 {
544   int i;
545 
546   for (i = 0; i < vec->n_items; i++)
547     dpl_delete_object_free((dpl_delete_object_t*)dpl_vec_get(vec, i));
548   dpl_vec_free(vec);
549 }
550 
dpl_common_prefix_free(dpl_common_prefix_t * common_prefix)551 void dpl_common_prefix_free(dpl_common_prefix_t* common_prefix)
552 {
553   if (NULL != common_prefix->prefix) free(common_prefix->prefix);
554 
555   free(common_prefix);
556 }
557 
dpl_vec_common_prefixes_free(dpl_vec_t * vec)558 void dpl_vec_common_prefixes_free(dpl_vec_t* vec)
559 {
560   int i;
561 
562   for (i = 0; i < vec->n_items; i++)
563     dpl_common_prefix_free((dpl_common_prefix_t*)dpl_vec_get(vec, i));
564   dpl_vec_free(vec);
565 }
566 
dpl_option_dup(const dpl_option_t * src)567 dpl_option_t* dpl_option_dup(const dpl_option_t* src)
568 {
569   dpl_option_t* dst = NULL;
570 
571   dst = malloc(sizeof(*dst));
572   if (NULL == dst) return NULL;
573 
574   memcpy(dst, src, sizeof(*src));
575 
576   return dst;
577 }
578 
dpl_option_free(dpl_option_t * option)579 void dpl_option_free(dpl_option_t* option) { free(option); }
580 
dpl_condition_dup(const dpl_condition_t * src)581 dpl_condition_t* dpl_condition_dup(const dpl_condition_t* src)
582 {
583   dpl_condition_t* dst = NULL;
584 
585   dst = malloc(sizeof(*dst));
586   if (NULL == dst) return NULL;
587 
588   memcpy(dst, src, sizeof(*src));
589 
590   return dst;
591 }
592 
dpl_condition_free(dpl_condition_t * condition)593 void dpl_condition_free(dpl_condition_t* condition) { free(condition); }
594 
dpl_range_dup(const dpl_range_t * src)595 dpl_range_t* dpl_range_dup(const dpl_range_t* src)
596 {
597   dpl_range_t* dst = NULL;
598 
599   dst = malloc(sizeof(*dst));
600   if (NULL == dst) return NULL;
601 
602   memcpy(dst, src, sizeof(*src));
603 
604   return dst;
605 }
606 
dpl_range_free(dpl_range_t * range)607 void dpl_range_free(dpl_range_t* range) { free(range); }
608