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