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 #include "droplet/async.h"
37 
38 /** @file */
39 
40 /**
41  * @defgroup async Asynchronous management
42  * @addtogroup async
43  * @{
44  * Asynchronous REST operations
45  */
46 
47 //#define DPRINTF(fmt,...) fprintf(stderr, fmt, ##__VA_ARGS__)
48 #define DPRINTF(fmt, ...)
49 
50 #define DUP_IF_NOT_NULL(Struct, Member)  \
51   if (NULL != Member) {                  \
52     Struct.Member = strdup(Member);      \
53     if (Struct.Member == NULL) goto bad; \
54   }
55 
56 #define FREE_IF_NOT_NULL(StructMember) \
57   if (NULL != StructMember) free(StructMember);
58 
dpl_buf_new()59 dpl_buf_t* dpl_buf_new()
60 {
61   dpl_buf_t* buf = NULL;
62 
63   buf = malloc(sizeof(dpl_buf_t));
64   if (NULL == buf) return NULL;
65 
66   buf->ptr = NULL;
67   buf->size = 0;
68   buf->refcnt = 0;
69 
70   return buf;
71 }
72 
dpl_buf_acquire(dpl_buf_t * buf)73 void dpl_buf_acquire(dpl_buf_t* buf) { buf->refcnt++; }
74 
dpl_buf_release(dpl_buf_t * buf)75 void dpl_buf_release(dpl_buf_t* buf)
76 {
77   buf->refcnt--;
78   if (0 == buf->refcnt) {
79     free(buf->ptr);
80     free(buf);
81   }
82 }
83 
dpl_async_task_free(dpl_async_task_t * task)84 void dpl_async_task_free(dpl_async_task_t* task)
85 {
86   switch (task->type) {
87     case DPL_TASK_LIST_ALL_MY_BUCKETS:
88       /* input */
89       /* output */
90       if (NULL != task->u.list_all_my_buckets.buckets)
91         dpl_vec_buckets_free(task->u.list_all_my_buckets.buckets);
92       break;
93     case DPL_TASK_LIST_BUCKET:
94       /* input */
95       FREE_IF_NOT_NULL(task->u.list_bucket.bucket);
96       FREE_IF_NOT_NULL(task->u.list_bucket.prefix);
97       FREE_IF_NOT_NULL(task->u.list_bucket.delimiter);
98       /* output */
99       if (NULL != task->u.list_bucket.objects)
100         dpl_vec_objects_free(task->u.list_bucket.objects);
101       if (NULL != task->u.list_bucket.common_prefixes)
102         dpl_vec_common_prefixes_free(task->u.list_bucket.common_prefixes);
103       break;
104     case DPL_TASK_MAKE_BUCKET:
105       /* input */
106       FREE_IF_NOT_NULL(task->u.make_bucket.bucket);
107       /* output */
108       break;
109     case DPL_TASK_DELETE_BUCKET:
110       /* input */
111       FREE_IF_NOT_NULL(task->u.delete_bucket.bucket);
112       /* output */
113       break;
114     case DPL_TASK_POST:
115     case DPL_TASK_POST_ID:
116       /* input */
117       FREE_IF_NOT_NULL(task->u.post.bucket);
118       FREE_IF_NOT_NULL(task->u.post.resource);
119       if (NULL != task->u.post.option) dpl_option_free(task->u.post.option);
120       if (NULL != task->u.post.condition)
121         dpl_condition_free(task->u.post.condition);
122       if (NULL != task->u.put.range) dpl_range_free(task->u.put.range);
123       if (NULL != task->u.post.metadata) dpl_dict_free(task->u.post.metadata);
124       if (NULL != task->u.post.sysmd) dpl_sysmd_free(task->u.post.sysmd);
125       if (NULL != task->u.post.buf) dpl_buf_release(task->u.post.buf);
126       if (NULL != task->u.post.query_params)
127         dpl_dict_free(task->u.post.query_params);
128       /* output */
129       break;
130     case DPL_TASK_PUT:
131     case DPL_TASK_PUT_ID:
132       /* input */
133       FREE_IF_NOT_NULL(task->u.put.bucket);
134       FREE_IF_NOT_NULL(task->u.put.resource);
135       if (NULL != task->u.put.option) dpl_option_free(task->u.put.option);
136       if (NULL != task->u.put.condition)
137         dpl_condition_free(task->u.put.condition);
138       if (NULL != task->u.put.range) dpl_range_free(task->u.put.range);
139       if (NULL != task->u.put.metadata) dpl_dict_free(task->u.put.metadata);
140       if (NULL != task->u.put.sysmd) dpl_sysmd_free(task->u.put.sysmd);
141       if (NULL != task->u.put.buf) dpl_buf_release(task->u.put.buf);
142       /* output */
143       break;
144     case DPL_TASK_GET:
145     case DPL_TASK_GET_ID:
146       /* inget */
147       FREE_IF_NOT_NULL(task->u.get.bucket);
148       FREE_IF_NOT_NULL(task->u.get.resource);
149       if (NULL != task->u.get.option) dpl_option_free(task->u.get.option);
150       if (NULL != task->u.get.condition)
151         dpl_condition_free(task->u.get.condition);
152       if (NULL != task->u.get.range) dpl_range_free(task->u.get.range);
153       /* output */
154       if (NULL != task->u.get.metadata) dpl_dict_free(task->u.get.metadata);
155       if (NULL != task->u.get.buf) dpl_buf_release(task->u.get.buf);
156       break;
157     case DPL_TASK_HEAD:
158     case DPL_TASK_HEAD_ID:
159       /* inhead */
160       FREE_IF_NOT_NULL(task->u.head.bucket);
161       FREE_IF_NOT_NULL(task->u.head.resource);
162       if (NULL != task->u.head.option) dpl_option_free(task->u.head.option);
163       if (NULL != task->u.head.condition)
164         dpl_condition_free(task->u.head.condition);
165       /* output */
166       if (NULL != task->u.head.metadata) dpl_dict_free(task->u.head.metadata);
167       break;
168     case DPL_TASK_DELETE:
169     case DPL_TASK_DELETE_ID:
170       /* indelete */
171       FREE_IF_NOT_NULL(task->u.delete.bucket);
172       FREE_IF_NOT_NULL(task->u.delete.resource);
173       if (NULL != task->u.delete.option) dpl_option_free(task->u.delete.option);
174       if (NULL != task->u.delete.condition)
175         dpl_condition_free(task->u.delete.condition);
176       /* output */
177       break;
178     case DPL_TASK_COPY:
179     case DPL_TASK_COPY_ID:
180       /* incopy */
181       FREE_IF_NOT_NULL(task->u.copy.src_bucket);
182       FREE_IF_NOT_NULL(task->u.copy.src_resource);
183       FREE_IF_NOT_NULL(task->u.copy.dst_bucket);
184       FREE_IF_NOT_NULL(task->u.copy.dst_resource);
185       if (NULL != task->u.copy.option) dpl_option_free(task->u.copy.option);
186       if (NULL != task->u.copy.metadata) dpl_dict_free(task->u.copy.metadata);
187       if (NULL != task->u.copy.sysmd) dpl_sysmd_free(task->u.copy.sysmd);
188       if (NULL != task->u.copy.condition)
189         dpl_condition_free(task->u.copy.condition);
190       /* output */
191       break;
192   }
193   free(task);
194 }
195 
async_do(void * arg)196 static void async_do(void* arg)
197 {
198   dpl_async_task_t* task = (dpl_async_task_t*)arg;
199 
200   switch (task->type) {
201     case DPL_TASK_LIST_ALL_MY_BUCKETS:
202       task->ret = dpl_list_all_my_buckets(task->ctx,
203                                           &task->u.list_all_my_buckets.buckets);
204       break;
205     case DPL_TASK_LIST_BUCKET:
206       task->ret = dpl_list_bucket(
207           task->ctx, task->u.list_bucket.bucket, task->u.list_bucket.prefix,
208           task->u.list_bucket.delimiter, task->u.list_bucket.max_keys,
209           &task->u.list_bucket.objects, &task->u.list_bucket.common_prefixes);
210       break;
211     case DPL_TASK_MAKE_BUCKET:
212       task->ret = dpl_make_bucket(task->ctx, task->u.make_bucket.bucket,
213                                   task->u.make_bucket.location_constraint,
214                                   task->u.make_bucket.canned_acl);
215       break;
216     case DPL_TASK_DELETE_BUCKET:
217       task->ret = dpl_delete_bucket(task->ctx, task->u.make_bucket.bucket);
218       break;
219     case DPL_TASK_POST:
220       task->ret = dpl_post(
221           task->ctx, task->u.post.bucket, task->u.post.resource,
222           task->u.post.option, task->u.post.object_type, task->u.post.condition,
223           task->u.post.range, task->u.post.metadata, task->u.post.sysmd,
224           NULL != task->u.post.buf ? dpl_buf_ptr(task->u.post.buf) : NULL,
225           NULL != task->u.post.buf ? dpl_buf_size(task->u.post.buf) : 0,
226           task->u.post.query_params, &task->u.post.sysmd_returned);
227       break;
228     case DPL_TASK_PUT:
229       task->ret = dpl_put(
230           task->ctx, task->u.put.bucket, task->u.put.resource,
231           task->u.put.option, task->u.put.object_type, task->u.put.condition,
232           task->u.put.range, task->u.put.metadata, task->u.put.sysmd,
233           NULL != task->u.put.buf ? dpl_buf_ptr(task->u.put.buf) : NULL,
234           NULL != task->u.put.buf ? dpl_buf_size(task->u.put.buf) : 0);
235       break;
236     case DPL_TASK_GET:
237       task->u.get.buf = dpl_buf_new();
238       if (NULL == task->u.get.buf) {
239         task->ret = DPL_ENOMEM;
240         break;
241       }
242       dpl_buf_acquire(task->u.get.buf);
243       task->ret = dpl_get(task->ctx, task->u.get.bucket, task->u.get.resource,
244                           task->u.get.option, task->u.get.object_type,
245                           task->u.get.condition, task->u.get.range,
246                           &dpl_buf_ptr(task->u.get.buf),
247                           &dpl_buf_size(task->u.get.buf), &task->u.get.metadata,
248                           &task->u.get.sysmd);
249       break;
250     case DPL_TASK_HEAD:
251       task->ret = dpl_head(task->ctx, task->u.head.bucket,
252                            task->u.head.resource, task->u.head.option,
253                            task->u.head.object_type, task->u.head.condition,
254                            &task->u.head.metadata, &task->u.head.sysmd);
255       break;
256     case DPL_TASK_DELETE:
257       task->ret
258           = dpl_delete(task->ctx, task->u.delete.bucket,
259                        task->u.delete.resource, task->u.delete.option,
260                        task->u.delete.object_type, task->u.delete.condition);
261       break;
262     case DPL_TASK_COPY:
263       task->ret = dpl_copy(task->ctx, task->u.copy.src_bucket,
264                            task->u.copy.src_resource, task->u.copy.dst_bucket,
265                            task->u.copy.dst_resource, task->u.copy.option,
266                            task->u.copy.object_type,
267                            task->u.copy.copy_directive, task->u.copy.metadata,
268                            task->u.copy.sysmd, task->u.copy.condition);
269       break;
270     case DPL_TASK_POST_ID:
271       task->ret = dpl_post_id(
272           task->ctx, task->u.post.bucket, task->u.post.resource,
273           task->u.post.option, task->u.post.object_type, task->u.post.condition,
274           task->u.post.range, task->u.post.metadata, task->u.post.sysmd,
275           NULL != task->u.post.buf ? dpl_buf_ptr(task->u.post.buf) : NULL,
276           NULL != task->u.post.buf ? dpl_buf_size(task->u.post.buf) : 0,
277           task->u.post.query_params, &task->u.post.sysmd_returned);
278       break;
279     case DPL_TASK_PUT_ID:
280       task->ret = dpl_put_id(
281           task->ctx, task->u.put.bucket, task->u.put.resource,
282           task->u.put.option, task->u.put.object_type, task->u.put.condition,
283           task->u.put.range, task->u.put.metadata, task->u.put.sysmd,
284           NULL != task->u.put.buf ? dpl_buf_ptr(task->u.put.buf) : NULL,
285           NULL != task->u.put.buf ? dpl_buf_size(task->u.put.buf) : 0);
286       break;
287     case DPL_TASK_GET_ID:
288       task->u.get.buf = dpl_buf_new();
289       if (NULL == task->u.get.buf) {
290         task->ret = DPL_ENOMEM;
291         break;
292       }
293       dpl_buf_acquire(task->u.get.buf);
294       task->ret = dpl_get_id(task->ctx, task->u.get.bucket,
295                              task->u.get.resource, task->u.get.option,
296                              task->u.get.object_type, task->u.get.condition,
297                              task->u.get.range, &dpl_buf_ptr(task->u.get.buf),
298                              &dpl_buf_size(task->u.get.buf),
299                              &task->u.get.metadata, &task->u.get.sysmd);
300       break;
301     case DPL_TASK_HEAD_ID:
302       task->ret = dpl_head_id(task->ctx, task->u.head.bucket,
303                               task->u.head.resource, task->u.head.option,
304                               task->u.head.object_type, task->u.head.condition,
305                               &task->u.head.metadata, &task->u.head.sysmd);
306       break;
307     case DPL_TASK_DELETE_ID:
308       task->ret
309           = dpl_delete_id(task->ctx, task->u.delete.bucket,
310                           task->u.delete.resource, task->u.delete.option,
311                           task->u.delete.object_type, task->u.delete.condition);
312       break;
313     case DPL_TASK_COPY_ID:
314       task->ret = dpl_copy_id(
315           task->ctx, task->u.copy.src_bucket, task->u.copy.src_resource,
316           task->u.copy.dst_bucket, task->u.copy.dst_resource,
317           task->u.copy.option, task->u.copy.object_type,
318           task->u.copy.copy_directive, task->u.copy.metadata,
319           task->u.copy.sysmd, task->u.copy.condition);
320       break;
321   }
322   if (NULL != task->cb_func) task->cb_func(task->cb_arg);
323 }
324 
325 /**
326  * list all buckets
327  *
328  * @param ctx the droplect context
329  * @param cb_func all callback returning a vector of dpl_bucket_t *
330  * @param cb_arg closure
331  *
332  * @return task
333  */
dpl_list_all_my_buckets_async_prepare(dpl_ctx_t * ctx)334 dpl_task_t* dpl_list_all_my_buckets_async_prepare(dpl_ctx_t* ctx)
335 {
336   dpl_async_task_t* task = NULL;
337 
338   task = calloc(1, sizeof(*task));
339   if (NULL == task) goto bad;
340 
341   task->ctx = ctx;
342   task->type = DPL_TASK_LIST_ALL_MY_BUCKETS;
343   task->task.func = async_do;
344 
345   return (dpl_task_t*)task;
346 
347 bad:
348 
349   if (NULL != task) dpl_async_task_free(task);
350 
351   return NULL;
352 }
353 
354 /**
355  * list bucket or directory
356  *
357  * @param ctx the droplet context
358  * @param bucket can be NULL
359  * @param prefix directory can be NULL
360  * @param delimiter e.g. "/" can be NULL
361  * @param cb_func
362  * @param cb_arg
363  *
364  * @return task
365  */
dpl_list_bucket_async_prepare(dpl_ctx_t * ctx,const char * bucket,const char * prefix,const char * delimiter)366 dpl_task_t* dpl_list_bucket_async_prepare(dpl_ctx_t* ctx,
367                                           const char* bucket,
368                                           const char* prefix,
369                                           const char* delimiter)
370 {
371   dpl_async_task_t* task = NULL;
372 
373   task = calloc(1, sizeof(*task));
374   if (NULL == task) goto bad;
375 
376   task->ctx = ctx;
377   task->type = DPL_TASK_LIST_BUCKET;
378   task->task.func = async_do;
379   DUP_IF_NOT_NULL(task->u.list_bucket, bucket);
380   DUP_IF_NOT_NULL(task->u.list_bucket, prefix);
381   DUP_IF_NOT_NULL(task->u.list_bucket, delimiter);
382 
383   return (dpl_task_t*)task;
384 
385 bad:
386 
387   if (NULL != task) dpl_async_task_free(task);
388 
389   return NULL;
390 }
391 
392 /**
393  * make a bucket
394  *
395  * @param ctx the droplet context
396  * @param bucket can be NULL
397  * @param location_constraint geographic location
398  * @param canned_acl simplified ACL
399  *
400  * @return task
401  */
dpl_make_bucket_async_prepare(dpl_ctx_t * ctx,const char * bucket,dpl_location_constraint_t location_constraint,dpl_canned_acl_t canned_acl)402 dpl_task_t* dpl_make_bucket_async_prepare(
403     dpl_ctx_t* ctx,
404     const char* bucket,
405     dpl_location_constraint_t location_constraint,
406     dpl_canned_acl_t canned_acl)
407 {
408   dpl_async_task_t* task = NULL;
409 
410   task = calloc(1, sizeof(*task));
411   if (NULL == task) goto bad;
412 
413   task->ctx = ctx;
414   task->type = DPL_TASK_MAKE_BUCKET;
415   task->task.func = async_do;
416   DUP_IF_NOT_NULL(task->u.make_bucket, bucket);
417   task->u.make_bucket.location_constraint = location_constraint;
418   task->u.make_bucket.canned_acl = canned_acl;
419 
420   return (dpl_task_t*)task;
421 
422 bad:
423 
424   if (NULL != task) dpl_async_task_free(task);
425 
426   return NULL;
427 }
428 
429 /**
430  * delete a resource
431  *
432  * @param ctx the droplet context
433  * @param bucket can be NULL
434  *
435  * @return task
436  */
dpl_delete_bucket_prepare(dpl_ctx_t * ctx,const char * bucket)437 dpl_task_t* dpl_delete_bucket_prepare(dpl_ctx_t* ctx, const char* bucket)
438 {
439   dpl_async_task_t* task = NULL;
440 
441   task = calloc(1, sizeof(*task));
442   if (NULL == task) goto bad;
443 
444   task->ctx = ctx;
445   task->type = DPL_TASK_DELETE_BUCKET;
446   task->task.func = async_do;
447   DUP_IF_NOT_NULL(task->u.delete_bucket, bucket);
448 
449   return (dpl_task_t*)task;
450 
451 bad:
452 
453   if (NULL != task) dpl_async_task_free(task);
454 
455   return NULL;
456 }
457 
458 /**
459  * create or post data into a resource
460  *
461  * @note this function is expected to return a newly created object
462  *
463  * @param ctx the droplet context
464  * @param bucket can be NULL
465  * @param resource can be NULL
466  * @param option DPL_OPTION_HTTP_COMPAT use if possible the HTTP compat mode
467  * @param object_type DPL_FTYPE_REG create a file
468  * @param metadata the user metadata. optional
469  * @param sysmd the system metadata. optional
470  * @param buf the data buffer
471  * @param query_params can be NULL
472  * @param returned_sysmdp the returned system metadata passed through stack
473  *
474  * @return DPL_SUCCESS
475  * @return DPL_FAILURE
476  */
dpl_post_async_prepare(dpl_ctx_t * ctx,const char * bucket,const char * resource,const dpl_option_t * option,dpl_ftype_t object_type,const dpl_condition_t * condition,const dpl_range_t * range,const dpl_dict_t * metadata,const dpl_sysmd_t * sysmd,dpl_buf_t * buf,const dpl_dict_t * query_params)477 dpl_task_t* dpl_post_async_prepare(dpl_ctx_t* ctx,
478                                    const char* bucket,
479                                    const char* resource,
480                                    const dpl_option_t* option,
481                                    dpl_ftype_t object_type,
482                                    const dpl_condition_t* condition,
483                                    const dpl_range_t* range,
484                                    const dpl_dict_t* metadata,
485                                    const dpl_sysmd_t* sysmd,
486                                    dpl_buf_t* buf,
487                                    const dpl_dict_t* query_params)
488 {
489   dpl_async_task_t* task = NULL;
490 
491   task = calloc(1, sizeof(*task));
492   if (NULL == task) goto bad;
493 
494   task->ctx = ctx;
495   task->type = DPL_TASK_POST;
496   task->task.func = async_do;
497   DUP_IF_NOT_NULL(task->u.post, bucket);
498   DUP_IF_NOT_NULL(task->u.post, resource);
499   if (NULL != option) task->u.post.option = dpl_option_dup(option);
500   task->u.post.object_type = object_type;
501   if (NULL != condition) task->u.post.condition = dpl_condition_dup(condition);
502   if (NULL != range) task->u.post.range = dpl_range_dup(range);
503   if (NULL != metadata) task->u.post.metadata = dpl_dict_dup(metadata);
504   if (NULL != sysmd) task->u.post.sysmd = dpl_sysmd_dup(sysmd);
505   if (NULL != buf) {
506     task->u.post.buf = buf;
507     dpl_buf_acquire(buf);
508   }
509   if (NULL != query_params)
510     task->u.post.query_params = dpl_dict_dup(query_params);
511 
512   return (dpl_task_t*)task;
513 
514 bad:
515 
516   if (NULL != task) dpl_async_task_free(task);
517 
518   return NULL;
519 }
520 
521 /**
522  * put a resource
523  *
524  * @param ctx the droplet context
525  * @param bucket optional
526  * @param resource mandatory
527  * @param option DPL_OPTION_HTTP_COMPAT use if possible the HTTP compat mode
528  * @param object_type DPL_FTYPE_REG create a file
529  * @param object_type DPL_FTYPE_DIR create a directory
530  * @param condition the optional condition
531  * @param range optional range
532  * @param metadata the optional user metadata
533  * @param sysmd the optional system metadata
534  * @param buf the data buffer
535  *
536  * @return DPL_SUCCESS
537  * @return DPL_FAILURE
538  * @return DPL_EEXIST
539  */
dpl_put_async_prepare(dpl_ctx_t * ctx,const char * bucket,const char * resource,const dpl_option_t * option,dpl_ftype_t object_type,const dpl_condition_t * condition,const dpl_range_t * range,const dpl_dict_t * metadata,const dpl_sysmd_t * sysmd,dpl_buf_t * buf)540 dpl_task_t* dpl_put_async_prepare(dpl_ctx_t* ctx,
541                                   const char* bucket,
542                                   const char* resource,
543                                   const dpl_option_t* option,
544                                   dpl_ftype_t object_type,
545                                   const dpl_condition_t* condition,
546                                   const dpl_range_t* range,
547                                   const dpl_dict_t* metadata,
548                                   const dpl_sysmd_t* sysmd,
549                                   dpl_buf_t* buf)
550 {
551   dpl_async_task_t* task = NULL;
552 
553   task = calloc(1, sizeof(*task));
554   if (NULL == task) goto bad;
555 
556   task->ctx = ctx;
557   task->type = DPL_TASK_PUT;
558   task->task.func = async_do;
559   DUP_IF_NOT_NULL(task->u.put, bucket);
560   DUP_IF_NOT_NULL(task->u.put, resource);
561   if (NULL != option) task->u.put.option = dpl_option_dup(option);
562   task->u.put.object_type = object_type;
563   if (NULL != condition) task->u.put.condition = dpl_condition_dup(condition);
564   if (NULL != range) task->u.put.range = dpl_range_dup(range);
565   if (NULL != metadata) task->u.put.metadata = dpl_dict_dup(metadata);
566   if (NULL != sysmd) task->u.put.sysmd = dpl_sysmd_dup(sysmd);
567   if (NULL != buf) {
568     task->u.put.buf = buf;
569     dpl_buf_acquire(buf);
570   }
571 
572   return (dpl_task_t*)task;
573 
574 bad:
575 
576   if (NULL != task) dpl_async_task_free(task);
577 
578   return NULL;
579 }
580 
581 /**
582  * get a resource with range
583  *
584  * @param ctx the droplet context
585  * @param bucket the optional bucket
586  * @param resource the mandat resource
587  * @param option DPL_OPTION_HTTP_COMPAT use if possible the HTTP compat mode
588  * @param object_type DPL_FTYPE_ANY get any type of resource
589  * @param condition the optional condition
590  * @param range the optional range
591  *
592  * @return DPL_SUCCESS
593  * @return DPL_FAILURE
594  * @return DPL_ENOENT resource does not exist
595  */
dpl_get_async_prepare(dpl_ctx_t * ctx,const char * bucket,const char * resource,const dpl_option_t * option,dpl_ftype_t object_type,const dpl_condition_t * condition,const dpl_range_t * range)596 dpl_task_t* dpl_get_async_prepare(dpl_ctx_t* ctx,
597                                   const char* bucket,
598                                   const char* resource,
599                                   const dpl_option_t* option,
600                                   dpl_ftype_t object_type,
601                                   const dpl_condition_t* condition,
602                                   const dpl_range_t* range)
603 {
604   dpl_async_task_t* task = NULL;
605 
606   task = calloc(1, sizeof(*task));
607   if (NULL == task) goto bad;
608 
609   task->ctx = ctx;
610   task->type = DPL_TASK_GET;
611   task->task.func = async_do;
612   DUP_IF_NOT_NULL(task->u.get, bucket);
613   DUP_IF_NOT_NULL(task->u.get, resource);
614   if (NULL != option) task->u.get.option = dpl_option_dup(option);
615   task->u.get.object_type = object_type;
616   if (NULL != condition) task->u.get.condition = dpl_condition_dup(condition);
617   if (NULL != range) task->u.get.range = dpl_range_dup(range);
618 
619   return (dpl_task_t*)task;
620 
621 bad:
622 
623   if (NULL != task) dpl_async_task_free(task);
624 
625   return NULL;
626 }
627 
628 /**
629  * get user and system metadata
630  *
631  * @param ctx the droplet context
632  * @param bucket the optional bucket
633  * @param resource the mandat resource
634  * @param option DPL_OPTION_HTTP_COMPAT use if possible the HTTP compat mode
635  * @param object_type DPL_FTYPE_ANY get any type of resource
636  * @param condition the optional condition
637  * @param metadatap the returned user metadata client shall free
638  * @param sysmdp the returned system metadata passed through stack
639  *
640  * @return DPL_SUCCESS
641  * @return DPL_FAILURE
642  * @return DPL_ENOENT resource does not exist
643  */
dpl_head_async_prepare(dpl_ctx_t * ctx,const char * bucket,const char * resource,const dpl_option_t * option,dpl_ftype_t object_type,const dpl_condition_t * condition)644 dpl_task_t* dpl_head_async_prepare(dpl_ctx_t* ctx,
645                                    const char* bucket,
646                                    const char* resource,
647                                    const dpl_option_t* option,
648                                    dpl_ftype_t object_type,
649                                    const dpl_condition_t* condition)
650 {
651   dpl_async_task_t* task = NULL;
652 
653   task = calloc(1, sizeof(*task));
654   if (NULL == task) goto bad;
655 
656   task->ctx = ctx;
657   task->type = DPL_TASK_HEAD;
658   task->task.func = async_do;
659   DUP_IF_NOT_NULL(task->u.head, bucket);
660   DUP_IF_NOT_NULL(task->u.head, resource);
661   if (NULL != option) task->u.head.option = dpl_option_dup(option);
662   task->u.head.object_type = object_type;
663   if (NULL != condition) task->u.head.condition = dpl_condition_dup(condition);
664 
665   return (dpl_task_t*)task;
666 
667 bad:
668 
669   if (NULL != task) dpl_async_task_free(task);
670 
671   return NULL;
672 }
673 
674 /**
675  * delete a resource
676  *
677  * @param ctx the droplet context
678  * @param bucket the optional bucket
679  * @param resource the mandat resource
680  * @param option DPL_OPTION_HTTP_COMPAT use if possible the HTTP compat mode
681  * @param condition the optional condition
682  *
683  * @return DPL_SUCCESS
684  * @return DPL_FAILURE
685  * @return DPL_ENOENT resource does not exist
686  */
dpl_delete_async_prepare(dpl_ctx_t * ctx,const char * bucket,const char * resource,const dpl_option_t * option,dpl_ftype_t object_type,const dpl_condition_t * condition)687 dpl_task_t* dpl_delete_async_prepare(dpl_ctx_t* ctx,
688                                      const char* bucket,
689                                      const char* resource,
690                                      const dpl_option_t* option,
691                                      dpl_ftype_t object_type,
692                                      const dpl_condition_t* condition)
693 {
694   dpl_async_task_t* task = NULL;
695 
696   task = calloc(1, sizeof(*task));
697   if (NULL == task) goto bad;
698 
699   task->ctx = ctx;
700   task->type = DPL_TASK_DELETE;
701   task->task.func = async_do;
702   DUP_IF_NOT_NULL(task->u.delete, bucket);
703   DUP_IF_NOT_NULL(task->u.delete, resource);
704   if (NULL != option) task->u.delete.option = dpl_option_dup(option);
705   task->u.delete.object_type = object_type;
706   if (NULL != condition)
707     task->u.delete.condition = dpl_condition_dup(condition);
708 
709   return (dpl_task_t*)task;
710 
711 bad:
712 
713   if (NULL != task) dpl_async_task_free(task);
714 
715   return NULL;
716 }
717 
718 /**
719  * perform various flavors of server side copies
720  *
721  * @param ctx the droplet context
722  * @param src_bucket the optional source bucket
723  * @param src_resource the mandat source resource
724  * @param dst_bucket the optional destination bucket
725  * @param dst_resource the optional destination resource (if dst equals src)
726  * @param option unused
727  * @param object_type unused
728  * @param copy_directive DPL_COPY_DIRECTIVE_COPY server side copy
729  * @param copy_directive DPL_COPY_DIRECTIVE_METADATA_REPLACE setattr
730  * @param copy_directive DPL_COPY_DIRECTIVE_LINK hard link
731  * @param copy_directive DPL_COPY_DIRECTIVE_SYMLINK reference
732  * @param copy_directive DPL_COPY_DIRECTIVE_MOVE rename
733  * @param copy_directive DPL_COPY_DIRECTIVE_MKDENT create a directory entry
734  * @param metadata the optional user metadata
735  * @param sysmd the optional system metadata
736  * @param condition the optional condition
737  *
738  * @return DPL_SUCCESS
739  * @return DPL_FAILURE
740  */
dpl_copy_async_prepare(dpl_ctx_t * ctx,const char * src_bucket,const char * src_resource,const char * dst_bucket,const char * dst_resource,const dpl_option_t * option,dpl_ftype_t object_type,dpl_copy_directive_t copy_directive,const dpl_dict_t * metadata,const dpl_sysmd_t * sysmd,const dpl_condition_t * condition)741 dpl_task_t* dpl_copy_async_prepare(dpl_ctx_t* ctx,
742                                    const char* src_bucket,
743                                    const char* src_resource,
744                                    const char* dst_bucket,
745                                    const char* dst_resource,
746                                    const dpl_option_t* option,
747                                    dpl_ftype_t object_type,
748                                    dpl_copy_directive_t copy_directive,
749                                    const dpl_dict_t* metadata,
750                                    const dpl_sysmd_t* sysmd,
751                                    const dpl_condition_t* condition)
752 {
753   dpl_async_task_t* task = NULL;
754 
755   task = calloc(1, sizeof(*task));
756   if (NULL == task) goto bad;
757 
758   task->ctx = ctx;
759   task->type = DPL_TASK_COPY;
760   task->task.func = async_do;
761   DUP_IF_NOT_NULL(task->u.copy, src_bucket);
762   DUP_IF_NOT_NULL(task->u.copy, src_resource);
763   DUP_IF_NOT_NULL(task->u.copy, dst_bucket);
764   DUP_IF_NOT_NULL(task->u.copy, dst_resource);
765   if (NULL != option) task->u.copy.option = dpl_option_dup(option);
766   task->u.copy.object_type = object_type;
767   task->u.copy.copy_directive = copy_directive;
768   if (NULL != metadata) task->u.copy.metadata = dpl_dict_dup(metadata);
769   if (NULL != sysmd) task->u.copy.sysmd = dpl_sysmd_dup(sysmd);
770   if (NULL != condition) task->u.copy.condition = dpl_condition_dup(condition);
771 
772   return (dpl_task_t*)task;
773 
774 bad:
775 
776   if (NULL != task) dpl_async_task_free(task);
777 
778   return NULL;
779 }
780 
781 /**
782  * create or post data into a resource
783  *
784  * @note this function is expected to return a newly created object
785  *
786  * @param ctx the droplet context
787  * @param bucket can be NULL
788  * @param option DPL_OPTION_HTTP_COMPAT use if possible the HTTP compat mode
789  * @param object_type DPL_FTYPE_REG create a file
790  * @param metadata the user metadata. optional
791  * @param sysmd the system metadata. optional
792  * @param buf the data buffer
793  * @param query_params can be NULL
794  * @param returned_sysmdp the returned system metadata passed through stack
795  *
796  * @return DPL_SUCCESS
797  * @return DPL_FAILURE
798  */
dpl_post_id_async_prepare(dpl_ctx_t * ctx,const char * bucket,const char * id,const dpl_option_t * option,dpl_ftype_t object_type,const dpl_condition_t * condition,const dpl_range_t * range,const dpl_dict_t * metadata,const dpl_sysmd_t * sysmd,dpl_buf_t * buf,const dpl_dict_t * query_params)799 dpl_task_t* dpl_post_id_async_prepare(dpl_ctx_t* ctx,
800                                       const char* bucket,
801                                       const char* id,
802                                       const dpl_option_t* option,
803                                       dpl_ftype_t object_type,
804                                       const dpl_condition_t* condition,
805                                       const dpl_range_t* range,
806                                       const dpl_dict_t* metadata,
807                                       const dpl_sysmd_t* sysmd,
808                                       dpl_buf_t* buf,
809                                       const dpl_dict_t* query_params)
810 {
811   dpl_async_task_t* task = NULL;
812 
813   task = calloc(1, sizeof(*task));
814   if (NULL == task) goto bad;
815 
816   task->ctx = ctx;
817   task->type = DPL_TASK_POST_ID;
818   task->task.func = async_do;
819   DUP_IF_NOT_NULL(task->u.post, bucket);
820   if (NULL != option) task->u.post.option = dpl_option_dup(option);
821   task->u.post.object_type = object_type;
822   if (NULL != condition) task->u.post.condition = dpl_condition_dup(condition);
823   if (NULL != range) task->u.post.range = dpl_range_dup(range);
824   if (NULL != metadata) task->u.post.metadata = dpl_dict_dup(metadata);
825   if (NULL != sysmd) task->u.post.sysmd = dpl_sysmd_dup(sysmd);
826   if (NULL != buf) {
827     task->u.post.buf = buf;
828     dpl_buf_acquire(buf);
829   }
830   if (NULL != query_params)
831     task->u.post.query_params = dpl_dict_dup(query_params);
832 
833   return (dpl_task_t*)task;
834 
835 bad:
836 
837   if (NULL != task) dpl_async_task_free(task);
838 
839   return NULL;
840 }
841 
842 /**
843  * put a resource
844  *
845  * @param ctx the droplet context
846  * @param bucket optional
847  * @param resource mandatory
848  * @param option DPL_OPTION_HTTP_COMPAT use if possible the HTTP compat mode
849  * @param object_type DPL_FTYPE_REG create a file
850  * @param object_type DPL_FTYPE_DIR create a directory
851  * @param condition the optional condition
852  * @param range optional range
853  * @param metadata the optional user metadata
854  * @param sysmd the optional system metadata
855  * @param buf the data buffer
856  *
857  * @return DPL_SUCCESS
858  * @return DPL_FAILURE
859  * @return DPL_EEXIST
860  */
dpl_put_id_async_prepare(dpl_ctx_t * ctx,const char * bucket,const char * resource,const dpl_option_t * option,dpl_ftype_t object_type,const dpl_condition_t * condition,const dpl_range_t * range,const dpl_dict_t * metadata,const dpl_sysmd_t * sysmd,dpl_buf_t * buf)861 dpl_task_t* dpl_put_id_async_prepare(dpl_ctx_t* ctx,
862                                      const char* bucket,
863                                      const char* resource,
864                                      const dpl_option_t* option,
865                                      dpl_ftype_t object_type,
866                                      const dpl_condition_t* condition,
867                                      const dpl_range_t* range,
868                                      const dpl_dict_t* metadata,
869                                      const dpl_sysmd_t* sysmd,
870                                      dpl_buf_t* buf)
871 {
872   dpl_async_task_t* task = NULL;
873 
874   task = calloc(1, sizeof(*task));
875   if (NULL == task) goto bad;
876 
877   task->ctx = ctx;
878   task->type = DPL_TASK_PUT_ID;
879   task->task.func = async_do;
880   DUP_IF_NOT_NULL(task->u.put, bucket);
881   DUP_IF_NOT_NULL(task->u.put, resource);
882   if (NULL != option) task->u.put.option = dpl_option_dup(option);
883   task->u.put.object_type = object_type;
884   if (NULL != condition) task->u.put.condition = dpl_condition_dup(condition);
885   if (NULL != range) task->u.put.range = dpl_range_dup(range);
886   if (NULL != metadata) task->u.put.metadata = dpl_dict_dup(metadata);
887   if (NULL != sysmd) task->u.put.sysmd = dpl_sysmd_dup(sysmd);
888   if (NULL != buf) {
889     task->u.put.buf = buf;
890     dpl_buf_acquire(buf);
891   }
892 
893   return (dpl_task_t*)task;
894 
895 bad:
896 
897   if (NULL != task) dpl_async_task_free(task);
898 
899   return NULL;
900 }
901 
902 /**
903  * get a resource with range
904  *
905  * @param ctx the droplet context
906  * @param bucket the optional bucket
907  * @param resource the mandat resource
908  * @param option DPL_OPTION_HTTP_COMPAT use if possible the HTTP compat mode
909  * @param object_type DPL_FTYPE_ANY get any type of resource
910  * @param condition the optional condition
911  * @param range the optional range
912  *
913  * @return DPL_SUCCESS
914  * @return DPL_FAILURE
915  * @return DPL_ENOENT resource does not exist
916  */
dpl_get_id_async_prepare(dpl_ctx_t * ctx,const char * bucket,const char * resource,const dpl_option_t * option,dpl_ftype_t object_type,const dpl_condition_t * condition,const dpl_range_t * range)917 dpl_task_t* dpl_get_id_async_prepare(dpl_ctx_t* ctx,
918                                      const char* bucket,
919                                      const char* resource,
920                                      const dpl_option_t* option,
921                                      dpl_ftype_t object_type,
922                                      const dpl_condition_t* condition,
923                                      const dpl_range_t* range)
924 {
925   dpl_async_task_t* task = NULL;
926 
927   task = calloc(1, sizeof(*task));
928   if (NULL == task) goto bad;
929 
930   task->ctx = ctx;
931   task->type = DPL_TASK_GET_ID;
932   task->task.func = async_do;
933   DUP_IF_NOT_NULL(task->u.get, bucket);
934   DUP_IF_NOT_NULL(task->u.get, resource);
935   if (NULL != option) task->u.get.option = dpl_option_dup(option);
936   task->u.get.object_type = object_type;
937   if (NULL != condition) task->u.get.condition = dpl_condition_dup(condition);
938   if (NULL != range) task->u.get.range = dpl_range_dup(range);
939 
940   return (dpl_task_t*)task;
941 
942 bad:
943 
944   if (NULL != task) dpl_async_task_free(task);
945 
946   return NULL;
947 }
948 
949 /**
950  * get user and system metadata
951  *
952  * @param ctx the droplet context
953  * @param bucket the optional bucket
954  * @param resource the mandat resource
955  * @param option DPL_OPTION_HTTP_COMPAT use if possible the HTTP compat mode
956  * @param object_type DPL_FTYPE_ANY get any type of resource
957  * @param condition the optional condition
958  * @param metadatap the returned user metadata client shall free
959  * @param sysmdp the returned system metadata passed through stack
960  *
961  * @return DPL_SUCCESS
962  * @return DPL_FAILURE
963  * @return DPL_ENOENT resource does not exist
964  */
dpl_head_id_async_prepare(dpl_ctx_t * ctx,const char * bucket,const char * resource,const dpl_option_t * option,dpl_ftype_t object_type,const dpl_condition_t * condition)965 dpl_task_t* dpl_head_id_async_prepare(dpl_ctx_t* ctx,
966                                       const char* bucket,
967                                       const char* resource,
968                                       const dpl_option_t* option,
969                                       dpl_ftype_t object_type,
970                                       const dpl_condition_t* condition)
971 {
972   dpl_async_task_t* task = NULL;
973 
974   task = calloc(1, sizeof(*task));
975   if (NULL == task) goto bad;
976 
977   task->ctx = ctx;
978   task->type = DPL_TASK_HEAD_ID;
979   task->task.func = async_do;
980   DUP_IF_NOT_NULL(task->u.head, bucket);
981   DUP_IF_NOT_NULL(task->u.head, resource);
982   if (NULL != option) task->u.head.option = dpl_option_dup(option);
983   task->u.head.object_type = object_type;
984   if (NULL != condition) task->u.head.condition = dpl_condition_dup(condition);
985 
986   return (dpl_task_t*)task;
987 
988 bad:
989 
990   if (NULL != task) dpl_async_task_free(task);
991 
992   return NULL;
993 }
994 
995 /**
996  * delete a resource
997  *
998  * @param ctx the droplet context
999  * @param bucket the optional bucket
1000  * @param resource the mandat resource
1001  * @param option DPL_OPTION_HTTP_COMPAT use if possible the HTTP compat mode
1002  * @param condition the optional condition
1003  *
1004  * @return DPL_SUCCESS
1005  * @return DPL_FAILURE
1006  * @return DPL_ENOENT resource does not exist
1007  */
dpl_delete_id_async_prepare(dpl_ctx_t * ctx,const char * bucket,const char * resource,const dpl_option_t * option,dpl_ftype_t object_type,const dpl_condition_t * condition)1008 dpl_task_t* dpl_delete_id_async_prepare(dpl_ctx_t* ctx,
1009                                         const char* bucket,
1010                                         const char* resource,
1011                                         const dpl_option_t* option,
1012                                         dpl_ftype_t object_type,
1013                                         const dpl_condition_t* condition)
1014 {
1015   dpl_async_task_t* task = NULL;
1016 
1017   task = calloc(1, sizeof(*task));
1018   if (NULL == task) goto bad;
1019 
1020   task->ctx = ctx;
1021   task->type = DPL_TASK_DELETE_ID;
1022   task->task.func = async_do;
1023   DUP_IF_NOT_NULL(task->u.delete, bucket);
1024   DUP_IF_NOT_NULL(task->u.delete, resource);
1025   if (NULL != option) task->u.delete.option = dpl_option_dup(option);
1026   task->u.delete.object_type = object_type;
1027   if (NULL != condition)
1028     task->u.delete.condition = dpl_condition_dup(condition);
1029 
1030   return (dpl_task_t*)task;
1031 
1032 bad:
1033 
1034   if (NULL != task) dpl_async_task_free(task);
1035 
1036   return NULL;
1037 }
1038 
1039 /**
1040  * perform various flavors of server side copies
1041  *
1042  * @param ctx the droplet context
1043  * @param src_bucket the optional source bucket
1044  * @param src_resource the mandat source resource
1045  * @param dst_bucket the optional destination bucket
1046  * @param dst_resource the optional destination resource (if dst equals src)
1047  * @param option unused
1048  * @param object_type unused
1049  * @param copy_directive DPL_COPY_DIRECTIVE_COPY server side copy
1050  * @param copy_directive DPL_COPY_DIRECTIVE_METADATA_REPLACE setattr
1051  * @param copy_directive DPL_COPY_DIRECTIVE_LINK hard link
1052  * @param copy_directive DPL_COPY_DIRECTIVE_SYMLINK reference
1053  * @param copy_directive DPL_COPY_DIRECTIVE_MOVE rename
1054  * @param copy_directive DPL_COPY_DIRECTIVE_MKDENT create a directory entry
1055  * @param metadata the optional user metadata
1056  * @param sysmd the optional system metadata
1057  * @param condition the optional condition
1058  *
1059  * @return DPL_SUCCESS
1060  * @return DPL_FAILURE
1061  */
dpl_copy_id_async_prepare(dpl_ctx_t * ctx,const char * src_bucket,const char * src_resource,const char * dst_bucket,const char * dst_resource,const dpl_option_t * option,dpl_ftype_t object_type,dpl_copy_directive_t copy_directive,const dpl_dict_t * metadata,const dpl_sysmd_t * sysmd,const dpl_condition_t * condition)1062 dpl_task_t* dpl_copy_id_async_prepare(dpl_ctx_t* ctx,
1063                                       const char* src_bucket,
1064                                       const char* src_resource,
1065                                       const char* dst_bucket,
1066                                       const char* dst_resource,
1067                                       const dpl_option_t* option,
1068                                       dpl_ftype_t object_type,
1069                                       dpl_copy_directive_t copy_directive,
1070                                       const dpl_dict_t* metadata,
1071                                       const dpl_sysmd_t* sysmd,
1072                                       const dpl_condition_t* condition)
1073 {
1074   dpl_async_task_t* task = NULL;
1075 
1076   task = calloc(1, sizeof(*task));
1077   if (NULL == task) goto bad;
1078 
1079   task->ctx = ctx;
1080   task->type = DPL_TASK_COPY_ID;
1081   task->task.func = async_do;
1082   DUP_IF_NOT_NULL(task->u.copy, src_bucket);
1083   DUP_IF_NOT_NULL(task->u.copy, src_resource);
1084   DUP_IF_NOT_NULL(task->u.copy, dst_bucket);
1085   DUP_IF_NOT_NULL(task->u.copy, dst_resource);
1086   if (NULL != option) task->u.copy.option = dpl_option_dup(option);
1087   task->u.copy.object_type = object_type;
1088   task->u.copy.copy_directive = copy_directive;
1089   if (NULL != metadata) task->u.copy.metadata = dpl_dict_dup(metadata);
1090   if (NULL != sysmd) task->u.copy.sysmd = dpl_sysmd_dup(sysmd);
1091   if (NULL != condition) task->u.copy.condition = dpl_condition_dup(condition);
1092 
1093   return (dpl_task_t*)task;
1094 
1095 bad:
1096 
1097   if (NULL != task) dpl_async_task_free(task);
1098 
1099   return NULL;
1100 }
1101 
1102 /* @} */
1103